mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-07 09:47:23 +00:00
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 <tag> <file>...
72 lines
2.6 KiB
Bash
Executable File
72 lines
2.6 KiB
Bash
Executable File
#!/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 <tag> <file>...}"
|
|
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
|