From 2797c998f8983e6d315a329172f4d5db1b636e72 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Fri, 28 Aug 2026 12:21:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(ci):=20=E5=8F=91=E7=89=88=20create=5Freleas?= =?UTF-8?q?e/upload=5Fasset=20=E6=94=B9=E5=B9=82=E7=AD=89,=E9=87=8D?= =?UTF-8?q?=E8=B7=91=E4=B8=8D=E5=86=8D=20409=20=E5=B4=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因: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 Claude-Session: https://claude.ai/code/session_01Bupi8Kdqkfx2N5acFsHTx5 --- scripts/ci/lib-forgejo.sh | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/scripts/ci/lib-forgejo.sh b/scripts/ci/lib-forgejo.sh index 6d82338..ade9a81 100644 --- a/scripts/ci/lib-forgejo.sh +++ b/scripts/ci/lib-forgejo.sh @@ -50,6 +50,8 @@ PYEOF } # create_release — 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 — 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}" \