diff --git a/packaging/upload-release.sh b/packaging/upload-release.sh new file mode 100755 index 0000000..1895789 --- /dev/null +++ b/packaging/upload-release.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# Upload release assets to a GitCode release. +# +# The endpoint shapes are asymmetric and both differ from the "obvious" guess: +# +# GET /releases/tags/{tag} → read a release +# GET /releases/{tag}/upload_url → get a presigned PUT (NO "tags/") +# PUT {presigned url} → upload, REPLAYING the headers verbatim +# +# Two mistakes cost real time on v1.9.5 before this script existed: +# +# 1. POSTing straight to /releases/{tag}/assets returns 404 — that endpoint +# does not exist. It reads as "authentication failed" if you only check +# the status code. +# 2. PUTting without the headers fails with an opaque storage error, not a +# permission one, so it looks like a size limit or a corrupt file. +# +# The x-obs-* headers are part of the signature: dropping any one of them turns +# the request into an unsigned one. +set -uo pipefail + +TOKEN="$(tr -d '\n\r' < /tmp/gc_token)" +REPO="JianFeeeee/ModelRouter" +TAG="${1:?usage: upload-release.sh ...}" +shift + +API="https://gitcode.com/api/v5/repos/${REPO}" + +for f in "$@"; do + name="$(basename "$f")" + size="$(stat -c%s "$f")" + printf ' %-46s %10s bytes ' "$name" "$size" + + # 1) presigned upload location. The file name must be URL-encoded; the v1.9.4 + # assets show "~" surviving in a stored name, so encoding is per-component. + enc="$(python3 -c 'import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))' "$name")" + resp="$(curl -sS -m 60 -H "Authorization: Bearer ${TOKEN}" \ + "${API}/releases/${TAG}/upload_url?file_name=${enc}" 2>&1)" + url="$(printf '%s' "$resp" | python3 -c 'import json,sys +try: + d = json.load(sys.stdin) + print(d.get("url") or d.get("uploadUrl") or "") +except Exception: + print("")' 2>/dev/null)" + if [ -z "$url" ]; then + echo "✗ 取得上传地址失败: ${resp:0:140}" + continue + fi + + # 2) PUT with the headers replayed exactly as returned. + code="$(printf '%s' "$resp" | python3 -c 'import json,sys +try: + print(json.dumps(json.load(sys.stdin).get("headers") or {})) +except Exception: + print("{}")' 2>/dev/null | python3 -c ' +import json, subprocess, sys +headers = json.load(sys.stdin) +args = ["curl", "-sS", "-o", "/dev/null", "-w", "%{http_code}", "-X", "PUT"] +for k, v in headers.items(): + args += ["-H", f"{k}: {v}"] +args += ["--data-binary", "@" + sys.argv[1], sys.argv[2]] +print(subprocess.run(args, capture_output=True, text=True).stdout) +' "$f" "$url" 2>/dev/null)" + + if [ "$code" = "200" ] || [ "$code" = "201" ] || [ "$code" = "204" ]; then + echo "✓ HTTP=$code" + else + echo "✗ HTTP=$code" + fi + sleep 1 +done