From e30199b29d8381cf2eb3203fa7fae021237005f8 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Mon, 5 Oct 2026 08:21:34 +0800 Subject: [PATCH] =?UTF-8?q?chore(packaging):=20=E5=BD=92=E6=A1=A3=20Releas?= =?UTF-8?q?e=20=E9=99=84=E4=BB=B6=E4=B8=8A=E4=BC=A0=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v1.9.5 上传时踩了两个坑,都不好从错误信息看出来: 1. 端点形状不对称且反直觉 GET /releases/tags/{tag}/upload_url → 404(tags/ 只用于读取) POST /releases/{tag}/assets → 404(该端点不存在) 看起来像认证失败,实际是路径不对。 2. PUT 必须逐字回放 upload_url 响应里的 headers (x-obs-meta-project-id / x-obs-acl / x-obs-callback / Content-Type)。 少一个就变成未签名请求,报错是存储层的模糊错误 —— 看着像大小超限或文件损坏。 当时先试了三个直觉端点全 404,之后 grep 自己的记忆日志才发现 9-26 就记着 「upload_url 路径不带 tags、PUT 必须回放 headers」。路径形状有疑问时先查历史, 比继续试快。 用法: packaging/upload-release.sh ... --- packaging/upload-release.sh | 71 +++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100755 packaging/upload-release.sh 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