wolf: fix GE-Proton download URL resolution (malformed URL)

./manage.sh ge-proton failed live with:
  curl: (3) URL rejected: Malformed input to a URL function

Root cause: the GitHub release JSON is parsed with a plain
grep browser_download_url | grep '\.tar\.gz' | cut -d'"' -f4 pipeline.
Once a release publishes more than one asset whose name contains
".tar.gz" (a second architecture build, a checksum-adjacent file,
etc.), grep returns more than one line and $(...) glues them together
with an embedded newline instead of yielding a single URL — curl then
rejects the whole multi-line string outright. Reproduced synthetically
with a release carrying two .tar.gz assets: the old pipeline emits two
lines where exactly one is expected.

Fixed all three independent copies of this same extraction (manage.sh's
_cache_ge_proton() helper and its ge-proton command, plus install_wolf()'s
own pre-download step) to parse the release JSON for real via python3
instead of pattern-matching the raw text — filtering by an actual
".tar.gz" suffix (not substring) and preferring an x86_64-tagged asset
if more than one still matches, since every container this repo runs
is x86_64. Verified against a synthetic multi-asset release JSON that
the new logic picks exactly one clean URL where the old one produced
two concatenated lines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BWYKEERLA1a7gv86Z4W23
This commit is contained in:
Claude
2026-09-10 16:51:11 +00:00
parent 05942fb44a
commit 52c7609050
+52 -3
View File
@@ -2024,8 +2024,27 @@ _cache_ge_proton() {
url="https://github.com/GloriousEggroll/proton-ge-custom/releases/download/$version/$version.tar.gz" url="https://github.com/GloriousEggroll/proton-ge-custom/releases/download/$version/$version.tar.gz"
else else
echo "Fetching latest GE-Proton release info..." echo "Fetching latest GE-Proton release info..."
# A plain grep+cut over the raw JSON used to break outright once a
# release started shipping more than one .tar.gz-suffixed asset (a
# second architecture build, a differently-named tarball, etc.) —
# grep then returns more than one line, and $(...) glues them
# together with an embedded newline instead of picking just one,
# which curl rejects outright ("URL rejected: Malformed input to a
# URL function") rather than a clean single URL. Parse the JSON for
# real instead of pattern-matching the raw text.
url=$(curl -sL https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest \ url=$(curl -sL https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest \
| grep browser_download_url | grep '\.tar\.gz' | cut -d'"' -f4) | python3 -c '
import sys, json
try:
release = json.load(sys.stdin)
except Exception:
print("")
raise SystemExit
assets = [a for a in release.get("assets", []) if a.get("name", "").endswith(".tar.gz")]
x64 = [a for a in assets if "x86_64" in a.get("name", "")]
pick = x64 or assets
print(pick[0]["browser_download_url"] if pick else "")
' 2>/dev/null)
if [ -z "$url" ]; then if [ -z "$url" ]; then
echo "Could not determine GE-Proton download URL (GitHub rate-limited or offline?)." echo "Could not determine GE-Proton download URL (GitHub rate-limited or offline?)."
return 1 return 1
@@ -3143,8 +3162,23 @@ PYEOF
echo "Using pre-downloaded GE-Proton: $NAME" echo "Using pre-downloaded GE-Proton: $NAME"
else else
echo "Fetching latest GE-Proton release info..." echo "Fetching latest GE-Proton release info..."
# See _cache_ge_proton()'s own comment above for why this parses
# the JSON for real instead of grep+cut over the raw text —
# more than one matching .tar.gz asset used to glue into one
# multi-line, curl-rejected "Malformed input to a URL function".
URL=$(curl -sL https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest \ URL=$(curl -sL https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest \
| grep browser_download_url | grep '\.tar\.gz' | cut -d'"' -f4) | python3 -c '
import sys, json
try:
release = json.load(sys.stdin)
except Exception:
print("")
raise SystemExit
assets = [a for a in release.get("assets", []) if a.get("name", "").endswith(".tar.gz")]
x64 = [a for a in assets if "x86_64" in a.get("name", "")]
pick = x64 or assets
print(pick[0]["browser_download_url"] if pick else "")
' 2>/dev/null)
if [ -z "$URL" ]; then if [ -z "$URL" ]; then
echo "Could not determine GE-Proton download URL (GitHub rate-limited or offline?)." echo "Could not determine GE-Proton download URL (GitHub rate-limited or offline?)."
exit 1 exit 1
@@ -6175,8 +6209,23 @@ MD
( (
CACHE_DIR="$WOLF_DIR/ge-proton-cache" CACHE_DIR="$WOLF_DIR/ge-proton-cache"
mkdir -p "$CACHE_DIR" mkdir -p "$CACHE_DIR"
# See the matching comment on manage.sh's own ge-proton command for
# why this parses the JSON for real instead of grep+cut over the raw
# text — more than one matching .tar.gz asset used to glue into one
# multi-line, curl-rejected "Malformed input to a URL function".
URL=$(curl -sL https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest \ URL=$(curl -sL https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest \
| grep browser_download_url | grep '\.tar\.gz' | cut -d'"' -f4) | python3 -c '
import sys, json
try:
release = json.load(sys.stdin)
except Exception:
print("")
raise SystemExit
assets = [a for a in release.get("assets", []) if a.get("name", "").endswith(".tar.gz")]
x64 = [a for a in assets if "x86_64" in a.get("name", "")]
pick = x64 or assets
print(pick[0]["browser_download_url"] if pick else "")
' 2>/dev/null)
if [ -z "$URL" ]; then if [ -z "$URL" ]; then
log_warning "Could not fetch GE-Proton URL — run './manage.sh ge-proton' later." log_warning "Could not fetch GE-Proton URL — run './manage.sh ge-proton' later."
else else