fix(ci): 发版 create_release/upload_asset 改幂等,重跑不再 409 崩

根因:release-client.sh 建 Release 非幂等——首跑已建好 Release+传齐产物但
deploy 未跑完时,retry 再 POST 同 tag 的 Release,Forgejo 回 409
("Release is has no Tag"),整个 release-deploy 在部署前就硬失败,发版卡死。

- create_release:撞 409 时改为按 tag 取回既有 Release 复用其 id 继续。
- upload_asset:同名 asset 已存在时先删旧再重传,避免重名 409。

重跑发版从此安全;免重编上线用 Manual Deploy(manual.yml)拉既有 Release 产物部署。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bupi8Kdqkfx2N5acFsHTx5
This commit is contained in:
wangjia
2026-08-28 12:21:06 +08:00
parent 90e6b25cec
commit 2797c998f8
+22 -1
View File
@@ -50,6 +50,8 @@ PYEOF
}
# create_release <tag> <body_json_string> — sets global RELEASE_ID on success.
# 幂等:若该 tag 的 Release 已存在(重跑发版时 Forgejo 回 409),不再硬失败,
# 改为取回既有 Release 复用其 id 继续(配合 upload_asset 的覆盖上传,重跑安全)。
create_release() {
local tag="$1" body="$2" http_code resp
echo "==> release: creating Forgejo Release ${tag}"
@@ -60,6 +62,15 @@ create_release() {
-d "{\"tag_name\":\"${tag}\",\"name\":\"Release ${tag}\",\"body\":${body},\"draft\":false,\"prerelease\":false}")
resp=$(cat /tmp/release_resp.json)
echo "==> release: API response (HTTP ${http_code}): ${resp}"
if [ "$http_code" = "409" ]; then
# Release 已存在(重跑)→ 幂等复用:按 tag 取回既有 Release。
echo "==> release: 该 tag 的 Release 已存在,改为复用(幂等重跑)"
http_code=$(curl -k -w "%{http_code}" -o /tmp/release_resp.json \
-H "Authorization: token ${FORGEJO_TOKEN}" \
"${FORGEJO_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases/tags/${tag}")
resp=$(cat /tmp/release_resp.json)
echo "==> release: 既有 Release 查询 (HTTP ${http_code}): ${resp}"
fi
if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
echo "==> release: FAILED — HTTP ${http_code}" >&2
return 1
@@ -69,8 +80,18 @@ create_release() {
}
# upload_asset <file> — requires RELEASE_ID.
# 幂等:若同名 asset 已存在(重跑),先删旧再传新,避免 Forgejo 409 重名冲突。
upload_asset() {
local file="$1" code
local file="$1" code name existing_id
name=$(basename "$file")
existing_id=$(curl -ks -H "Authorization: token ${FORGEJO_TOKEN}" \
"${FORGEJO_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases/${RELEASE_ID}/assets" \
| python3 -c "import sys,json; a=json.load(sys.stdin); print(next((str(x['id']) for x in a if x.get('name')==sys.argv[1]), ''))" "$name" 2>/dev/null || true)
if [ -n "$existing_id" ]; then
echo "==> release: asset ${name} 已存在(id=${existing_id}),先删旧再重传"
curl -ks -X DELETE -H "Authorization: token ${FORGEJO_TOKEN}" \
"${FORGEJO_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases/${RELEASE_ID}/assets/${existing_id}" >/dev/null || true
fi
code=$(curl -k -w "%{http_code}" -o /tmp/upload_resp.json \
-X POST "${FORGEJO_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases/${RELEASE_ID}/assets" \
-H "Authorization: token ${FORGEJO_TOKEN}" \