From cc365c2cd876febc653ae514b28a4c064c44fa5c Mon Sep 17 00:00:00 2001 From: zzzjy Date: Thu, 17 Sep 2026 01:11:34 +0800 Subject: [PATCH] =?UTF-8?q?ci:=20Release=20=E6=94=AF=E6=8C=81=20workflow?= =?UTF-8?q?=5Fdispatch=20=E7=BD=91=E9=A1=B5=E6=89=8B=E5=8A=A8=E8=A7=A6?= =?UTF-8?q?=E5=8F=91=E5=8F=91=E7=89=88=EF=BC=88=E8=87=AA=E5=8A=A8=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E7=89=88=E6=9C=AC/=E6=89=93=20tag=EF=BC=8C=E5=B9=82?= =?UTF-8?q?=E7=AD=89=E5=8F=AF=E9=87=8D=E8=AF=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/release.yml | 130 ++++++++++++++++++++++++++++------- README.md | 12 +++- 2 files changed, 118 insertions(+), 24 deletions(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 849782d..c5d4fdd 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -4,6 +4,14 @@ on: push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' + workflow_dispatch: + inputs: + version: + description: '要发布的版本号(不含 v 前缀,例如 1.0.1)' + required: true + notes: + description: '发布说明(可选,留空自动生成 changelog)' + required: false jobs: release: @@ -14,17 +22,76 @@ jobs: with: fetch-depth: 0 # 生成 changelog 需要完整历史 - - name: 校验 tag 与 manifest 版本一致 + - name: 确定版本号 + env: + EVENT_NAME: ${{ github.event_name }} + INPUT_VERSION: ${{ inputs.version }} run: | set -euo pipefail - TAG="${GITHUB_REF_NAME#v}" - VERSION=$(python3 -c "import json; print(json.load(open('manifest.json'))['version'])") - if [ "$TAG" != "$VERSION" ]; then - echo "::error::tag ${GITHUB_REF_NAME} 与 manifest.json 版本 ${VERSION} 不一致。请先更新 manifest.json 的 version 再打 tag。" + if [ "$EVENT_NAME" = "workflow_dispatch" ]; then + VERSION="$INPUT_VERSION" + else + VERSION="${GITHUB_REF_NAME#v}" + fi + if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::版本号格式应为 X.Y.Z(收到:${VERSION:-空})" exit 1 fi echo "VERSION=$VERSION" >> "$GITHUB_ENV" - echo "版本校验通过: v$VERSION" + echo "TAG=v$VERSION" >> "$GITHUB_ENV" + echo "本次发版:v$VERSION(触发方式:$EVENT_NAME)" + + - name: 网页触发:同步 manifest 版本并打 tag + if: ${{ github.event_name == 'workflow_dispatch' }} + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + ACTOR: ${{ github.actor }} + run: | + set -euo pipefail + CUR=$(python3 -c "import json; print(json.load(open('manifest.json'))['version'])") + if [ "$CUR" != "$VERSION" ]; then + python3 - "$VERSION" <<'PY' + import json, sys + v = sys.argv[1] + m = json.load(open('manifest.json')) + m['version'] = v + with open('manifest.json', 'w') as f: + json.dump(m, f, ensure_ascii=False, indent=2) + f.write('\n') + PY + git config user.name "${ACTOR}" + git config user.email "${ACTOR}@users.noreply.${GITHUB_SERVER_URL#https://}" + git add manifest.json + git commit -m "release: v${VERSION} [skip ci]" + echo "manifest.json 版本 $CUR -> $VERSION,已提交([skip ci] 防止重复触发流水线)" + else + echo "manifest.json 已是 $VERSION,无需提交" + fi + if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then + TAG_SHA=$(git rev-parse "${TAG}^{commit}") + HEAD_SHA=$(git rev-parse HEAD) + if [ "$TAG_SHA" != "$HEAD_SHA" ]; then + echo "::error::tag ${TAG} 已存在且指向其他提交,请先处理(删除旧 tag 或换版本号)" + exit 1 + fi + echo "tag ${TAG} 已存在且指向当前提交,复用" + else + git tag "$TAG" + fi + HOST="${GITHUB_SERVER_URL#https://}" + PUSH_URL="https://x-access-token:${GITEA_TOKEN}@${HOST}/${GITHUB_REPOSITORY}.git" + git push "$PUSH_URL" "HEAD:${GITHUB_REF_NAME}" 2>/dev/null || echo "分支无新提交,跳过推送" + git push "$PUSH_URL" "$TAG" 2>/dev/null || echo "tag 已在远端,跳过推送" + + - name: 校验版本一致性 + run: | + set -euo pipefail + MV=$(python3 -c "import json; print(json.load(open('manifest.json'))['version'])") + if [ "$MV" != "$VERSION" ]; then + echo "::error::manifest.json 版本 $MV 与目标 $VERSION 不一致" + exit 1 + fi + echo "版本一致性校验通过:v$VERSION" - name: 打包扩展 run: | @@ -50,15 +117,22 @@ jobs: echo "ZIP=$ZIP" >> "$GITHUB_ENV" - name: 生成 Release Notes + env: + INPUT_NOTES: ${{ inputs.notes }} run: | set -euo pipefail - PREV_TAG=$(git tag --sort=-creatordate | grep -v "^${GITHUB_REF_NAME}$" | head -1 || true) - if [ -n "$PREV_TAG" ]; then - NOTES=$(git log --pretty=format:'- %s' "${PREV_TAG}..${GITHUB_REF_NAME}") - echo "changelog 范围: ${PREV_TAG}..${GITHUB_REF_NAME}" + if [ -n "${INPUT_NOTES:-}" ]; then + NOTES="$INPUT_NOTES" + echo "使用手动填写的发布说明" else - NOTES=$(git log --pretty=format:'- %s') - echo "首个版本,changelog 包含全部提交" + PREV_TAG=$(git tag --sort=-creatordate | grep -v "^${TAG}$" | head -1 || true) + if [ -n "$PREV_TAG" ]; then + NOTES=$(git log --pretty=format:'- %s' "${PREV_TAG}..${TAG}") + echo "changelog 范围: ${PREV_TAG}..${TAG}" + else + NOTES=$(git log --pretty=format:'- %s') + echo "首个版本,changelog 包含全部提交" + fi fi export RELEASE_NOTES="${NOTES:-初始发布}" python3 <<'PY' @@ -70,7 +144,7 @@ jobs: f"3. 点击工具栏图标完成模型配置即可使用" ) meta = { - 'tag_name': os.environ['GITHUB_REF_NAME'], + 'tag_name': os.environ['TAG'], 'name': '译泡 PopLingo v' + os.environ['VERSION'], 'body': body, 'draft': False, @@ -81,20 +155,30 @@ jobs: print('release_meta.json 已生成') PY - - name: 创建 Release 并上传附件 + - name: 创建 Release 并上传附件(幂等,可安全重试) env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} run: | set -euo pipefail API="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}" - curl -sf -X POST "${API}/releases" \ - -H "Authorization: token ${GITEA_TOKEN}" \ - -H "Content-Type: application/json" \ - --data-binary @release_meta.json > release_resp.json - ID=$(python3 -c 'import json; print(json.load(open("release_resp.json"))["id"])') - echo "Release 已创建 (id=${ID})" + AUTH="Authorization: token ${GITEA_TOKEN}" + EXIST=$(curl -sf -H "$AUTH" "${API}/releases/tags/${TAG}" || true) + if [ -n "$EXIST" ]; then + ID=$(echo "$EXIST" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])') + HAS_ASSET=$(echo "$EXIST" | python3 -c "import json,sys; r=json.load(sys.stdin); print(1 if any(a['name']=='$ZIP' for a in r.get('assets',[])) else 0)") + echo "Release ${TAG} 已存在(id=${ID}),复用" + if [ "$HAS_ASSET" = "1" ]; then + echo "附件 ${ZIP} 已存在,无需重复上传" + exit 0 + fi + else + curl -sf -X POST "${API}/releases" \ + -H "$AUTH" -H "Content-Type: application/json" \ + --data-binary @release_meta.json > release_resp.json + ID=$(python3 -c 'import json; print(json.load(open("release_resp.json"))["id"])') + echo "Release 已创建(id=${ID})" + fi curl -sf -X POST "${API}/releases/${ID}/assets?name=${ZIP}" \ - -H "Authorization: token ${GITEA_TOKEN}" \ - -F "attachment=@${ZIP}" + -H "$AUTH" -F "attachment=@${ZIP}" echo - echo "✅ 发布完成: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/tag/${GITHUB_REF_NAME}" + echo "✅ 发布完成: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/tag/${TAG}" diff --git a/README.md b/README.md index f212d89..9db2c35 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,17 @@ fanyi/ | `ci.yml` | push 到 `main` / PR | manifest 校验 + JS 语法检查 | | `release.yml` | 推送 `vX.Y.Z` 标签 | 校验版本一致 → 打包 zip → 生成 Changelog → 创建 Release 并上传附件 | -发版三步: +### 方式一:网页触发(推荐) + +打开仓库 **Actions → Release workflow → Run workflow**,填入版本号(如 `1.0.2`),点 Run 即可。流水线会自动: + +1. 把 `manifest.json` 的 `version` 同步为该版本号并提交(提交带 `[skip ci]`,避免重复触发) +2. 创建并推送 `vX.Y.Z` 标签 +3. 打包 zip、生成 changelog、创建 Release 并上传附件 + +可选填「发布说明」,留空则自动生成;重复触发同一版本是幂等的,不会重复发布。 + +### 方式二:本地打 tag ```bash # 1. 更新 manifest.json 中的 version 字段