فهرست منبع

fix: prevent plugin release workflow from mutating release metadata

lingfengQAQ 3 ماه پیش
والد
کامیت
580e7fe418
3فایلهای تغییر یافته به همراه30 افزوده شده و 21 حذف شده
  1. 6 14
      .github/workflows/plugin-release.yml
  2. 10 5
      README.md
  3. 14 2
      webnovel-writer/scripts/sync_plugin_version.py

+ 6 - 14
.github/workflows/plugin-release.yml

@@ -4,11 +4,11 @@ on:
   workflow_dispatch:
     inputs:
       version:
-        description: 'Plugin version to release, for example 5.5.2'
+        description: 'Plugin version to release, for example 5.5.4'
         required: true
         type: string
       release_notes:
-        description: 'Release notes used in README and GitHub Release'
+        description: 'Release notes used in GitHub Release'
         required: true
         type: string
 
@@ -32,20 +32,11 @@ jobs:
         with:
           python-version: '3.11'
 
-      - name: Update release metadata
+      - name: Validate release metadata
         run: |
           python webnovel-writer/scripts/sync_plugin_version.py \
-            --version "$RELEASE_VERSION" \
-            --release-notes "$RELEASE_NOTES"
-
-      - name: Commit release metadata
-        run: |
-          git config user.name github-actions
-          git config user.email github-actions[bot]@users.noreply.github.com
-          git add README.md webnovel-writer/.claude-plugin/plugin.json .claude-plugin/marketplace.json
-          git diff --cached --quiet && exit 0
-          git commit -m "chore: release v$RELEASE_VERSION"
-          git push origin HEAD
+            --check \
+            --expected-version "$RELEASE_VERSION"
 
       - name: Create and push tag
         run: |
@@ -53,6 +44,7 @@ jobs:
             echo "Tag v$RELEASE_VERSION already exists"
             exit 1
           fi
+          echo "Releasing commit $(git rev-parse HEAD) as v$RELEASE_VERSION"
           git tag "v$RELEASE_VERSION"
           git push origin "v$RELEASE_VERSION"
 

+ 10 - 5
README.md

@@ -130,11 +130,16 @@ model: sonnet
 
 推荐使用 GitHub Actions 的 `Plugin Release` 工作流统一发版:
 
-1. 打开仓库的 Actions 页面,选择 `Plugin Release`。
-2. 输入 `version`(例如 `5.5.4`)和 `release_notes`。
-3. 工作流会自动完成以下动作:
-   - 同步 `plugin.json`、`marketplace.json` 与 README 当前版本
-   - 提交版本变更
+1. 先在本地同步版本信息:
+   ```bash
+   python -X utf8 webnovel-writer/scripts/sync_plugin_version.py --version 5.5.4 --release-notes "本次版本说明"
+   ```
+2. 提交并推送版本变更(`README.md`、`plugin.json`、`marketplace.json`)。
+3. 打开仓库的 Actions 页面,选择 `Plugin Release`。
+4. 输入与当前仓库元数据一致的 `version`(例如 `5.5.4`)和用于 GitHub Release 的 `release_notes`。
+5. 工作流会执行以下动作:
+   - 校验 `plugin.json`、`marketplace.json` 与 README 当前版本已经一致
+   - 校验当前版本与输入的 `version` 一致
    - 创建并推送 `vX.Y.Z` Tag
    - 创建同名 GitHub Release
 

+ 14 - 2
webnovel-writer/scripts/sync_plugin_version.py

@@ -137,7 +137,7 @@ def sync_versions(version: str | None = None, release_notes: str | None = None)
     return previous_version, target_version, changed
 
 
-def check_versions() -> int:
+def check_versions(expected_version: str | None = None) -> int:
     plugin_payload = load_json(PLUGIN_JSON_PATH)
     marketplace_payload = load_json(MARKETPLACE_JSON_PATH)
     readme_content = load_text(README_PATH)
@@ -154,6 +154,10 @@ def check_versions() -> int:
         )
     if plugin_version != readme_version:
         mismatches.append(f"plugin.json={plugin_version}, README.md={readme_version}")
+    if expected_version and plugin_version != expected_version:
+        mismatches.append(
+            f"expected={expected_version}, current release metadata={plugin_version}"
+        )
 
     if mismatches:
         print("Version mismatch detected:")
@@ -176,6 +180,10 @@ def main() -> int:
         "--version",
         help="Update release metadata to the given semantic version",
     )
+    parser.add_argument(
+        "--expected-version",
+        help="When used with --check, require the current release metadata to match this version",
+    )
     parser.add_argument(
         "--release-notes",
         help="Release notes used for the README current release row",
@@ -184,10 +192,14 @@ def main() -> int:
 
     if args.version and not VERSION_PATTERN.fullmatch(args.version):
         parser.error("--version must look like X.Y.Z")
+    if args.expected_version and not VERSION_PATTERN.fullmatch(args.expected_version):
+        parser.error("--expected-version must look like X.Y.Z")
+    if args.expected_version and not args.check:
+        parser.error("--expected-version can only be used together with --check")
 
     try:
         if args.check:
-            return check_versions()
+            return check_versions(expected_version=args.expected_version)
 
         previous_version, target_version, changed = sync_versions(
             version=args.version,