Skip to content

feat(release): auto-update version files from .release.yml config#49

Merged
ThirteenLLB merged 4 commits intomainfrom
feat/m-6822381131/auto-version-bump
Feb 27, 2026
Merged

feat(release): auto-update version files from .release.yml config#49
ThirteenLLB merged 4 commits intomainfrom
feat/m-6822381131/auto-version-bump

Conversation

@ThirteenLLB
Copy link
Contributor

@ThirteenLLB ThirteenLLB commented Feb 22, 2026

Summary

  • 新增 scripts/update-versions.py 脚本,读取 .release.yml 配置自动替换版本号文件
  • 修改 centralized-release.yml,在创建 release PR 时自动更新版本号文件并纳入提交
  • 支持 dry-run 预览,向后兼容(无 .release.yml 的仓库行为不变)

Resolve m-6822381131

Test plan

验证步骤:

  1. 在有 .release.yml 的仓库(wuji-sdk-dev、wuji-studio-dev)用 dry_run 模式触发 centralized-release,确认输出正确
  2. 在无 .release.yml 的仓库触发,确认行为不变(仅更新 CHANGELOG)
  3. 实际执行完整流程,验证 PR 内容包含版本文件更新

🤖 Generated with Claude Code

Summary by CodeRabbit

发布说明

  • Chores
    • 发布流程增强:自动检测并更新项目配置中列出的版本文件,支持真实更新与干运行预览,并在预览中展示将被修改的文件列表。
    • 发布信息同步:在发布分支、提交信息、PR 描述和通知中同时说明 CHANGELOG 与版本文件的更新状态。
    • 校验与可靠性:新增版本格式、路径与替换规则校验,确保仅对存在的目标文件执行单次替换以降低风险。

Add update-versions.py script and integrate it into centralized-release
workflow. Repos with a .release.yml config will have their version files
(Cargo.toml, package.json, tauri.conf.json, etc.) automatically updated
when creating release PRs.

Resolve m-6822381131

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 22, 2026

Walkthrough

在发布工作流中引入基于 .release.yml 的版本文件更新:工作流在存在配置时调用新脚本 scripts/update-versions.py,在干运行时生成预览并写入摘要/PR 正文,实际运行时替换并暂存版本文件变更后提交并在通知中报告。

Changes

Cohort / File(s) Summary
GitHub Actions 工作流
\.github/workflows/centralized-release.yml
新增“Update version files”步骤;当存在 .release.yml 时在 dry-run 路径调用脚本预览并将预览加入干运行摘要/PR 正文,非 dry-run 时执行实际写回并将更新文件暂存入发布提交;更新 PR 内容与 Feishu 通知负载以反映版本文件变更。
版本更新脚本
scripts/update-versions.py
新增脚本及 CLI:验证语义化版本、验证正则(要求正则有且仅有 2 个捕获组)、验证相对路径;读取 .release.ymlversion_files 列表,对每个目标文件应用一次正则替换;支持 --dry-run 生成变更预览或写回并返回已更新文件列表与状态。

Sequence Diagram(s)

sequenceDiagram
    rect rgba(200,220,255,0.5)
    participant Workflow as GitHub Actions<br/>Workflow
    end
    rect rgba(200,255,200,0.5)
    participant Config as .release.yml
    end
    rect rgba(255,230,200,0.5)
    participant Script as scripts/update-versions.py
    end
    rect rgba(255,200,200,0.5)
    participant Files as Version Files
    end

    Workflow->>Config: 读取 `.release.yml`
    Workflow->>Script: 调用脚本 (--version, --config, --dry-run?)
    Script->>Config: 加载并验证配置
    Script->>Script: 验证版本格式、正则(2 捕获组)、路径合法性
    loop 每个 version_files 条目
        Script->>Files: 读取目标文件
        Script->>Script: 使用正则替换版本(一次替换)
        alt dry-run
            Script->>Workflow: 返回变更预览
        else 实际更新
            Script->>Files: 写回文件并保存更改
            Script->>Workflow: 返回已更新文件列表
        end
    end
    Workflow->>Workflow: 将变更包含进发布分支提交与 PR 描述,并更新 Feishu 通知内容
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • chenjunnn
  • yuanyuanxin

Poem

🐰 版本换新衣,列表排排站,
脚本轻轻跑,先瞧干运行盘。
真跑写回去,提交带清单,
兔子点点头,庆祝一小段,
胡萝卜请客乐开颜! 🥕


✅ Pre-merge checks override applied

The pre-merge checks have been overridden successfully. You can now proceed with the merge.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Changelog ❌ Error [IGNORED] 此PR引入了用户可见的变更(新的版本文件自动更新功能),但未修改CHANGELOG.md或public/CHANGELOG.md文件 需要在CHANGELOG.md和public/CHANGELOG.md中添加条目,说明新增的版本文件自动更新功能的改进
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题遵循Conventional Commits格式,具体明确地描述了主要变更:在发布流程中自动更新.release.yml配置的版本文件。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Pr Description ✅ Passed PR描述满足所有要求。包含三个具体的测试计划项目,明确引用Feishu工作项ID'm-6822381131',格式正确。
Branch Name ✅ Passed 源分支名称 feat/m-6822381131/auto-version-bump 完全符合功能分支的规定模式 feat/m-/。
✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/m-6822381131/auto-version-bump

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
scripts/update-versions.py (1)

74-78: 避免捕获过宽的 Exception。

建议收敛到可预期的异常类型(如 OSError / yaml.YAMLError),避免吞掉编程错误。

♻️ 建议修改
-    except Exception as e:
+    except (OSError, yaml.YAMLError) as e:
         return {
             "success": False,
             "message": f"读取配置文件失败: {e}",
             "updated": [],
         }

-        except Exception as e:
+        except OSError as e:
             return {
                 "success": False,
                 "message": f"读取文件失败 ({entry['path']}): {e}",
                 "updated": updated,
             }

-            except Exception as e:
+            except OSError as e:
                 return {
                     "success": False,
                     "message": f"写入文件失败 ({entry['path']}): {e}",
                     "updated": updated,
                 }

Also applies to: 139-142, 167-169

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/update-versions.py` around lines 74 - 78, 当前在
scripts/update-versions.py 对 config_file 打开和 yaml.safe_load 的 try/except 捕获了过宽的
Exception;请将这些捕获限定为可预期的异常(例如对文件操作使用 OSError,解析 YAML 使用
yaml.YAMLError),分别处理错误返回或日志,并对所有其它异常重新抛出(不要吞掉编程错误);同样修复文件中另外两个宽泛 except
的位置(涉及相似的 open/config/yaml.safe_load 逻辑,参考在文件中出现的相同 try/except 模式),确保引用到的符号包括
config_file、yaml.safe_load 以及相关返回/错误处理路径。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/centralized-release.yml:
- Around line 196-208: The PR description renders the version_files_info block
as a code block because the string assigned to version_files_info contains >4
leading spaces; update the assignment for version_files_info (the variable
constructed after computing vfiles) to avoid leading spaces — e.g. build the
string without indentation or use ANSI-C quoting ($'...') or a non-indented
here-doc (<<'EOF') so the resulting Markdown line starts at column 0 (for
example: version_files_info=$'- 更新版本号文件: ${vfiles}' or using a here-doc without
leading spaces) ensuring the expanded Markdown is not treated as a code block.

In `@scripts/update-versions.py`:
- Around line 160-165: The dry-run branch currently defines new_match but never
uses it and prints only the literal version instead of the actual replaced line;
update the dry-run logic in the function that handles version replacement (the
block using dry_run, pattern, content, new_content, entry and version) to remove
the unused new_match variable and instead run re.search(pattern, new_content,
re.MULTILINE) to capture the replaced line, extract new_line =
match.group(0).strip() (or "?" if no match) and print that new_line in the DRY
RUN message (print f"  📦 [DRY RUN] {entry['path']}: {old_line} → {new_line}"),
ensuring no unused variables remain.

---

Nitpick comments:
In `@scripts/update-versions.py`:
- Around line 74-78: 当前在 scripts/update-versions.py 对 config_file 打开和
yaml.safe_load 的 try/except 捕获了过宽的 Exception;请将这些捕获限定为可预期的异常(例如对文件操作使用
OSError,解析 YAML 使用
yaml.YAMLError),分别处理错误返回或日志,并对所有其它异常重新抛出(不要吞掉编程错误);同样修复文件中另外两个宽泛 except
的位置(涉及相似的 open/config/yaml.safe_load 逻辑,参考在文件中出现的相同 try/except 模式),确保引用到的符号包括
config_file、yaml.safe_load 以及相关返回/错误处理路径。

- Fix version_files_info indentation causing Markdown code block rendering
- Fix unused new_match variable in dry-run preview, now shows actual replaced line
- Narrow exception catches from Exception to OSError/yaml.YAMLError

Resolve m-6822381131

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/centralized-release.yml:
- Around line 199-205: The Python snippet that builds vfiles uses an escaped
backtick f'\`{p}\`' which produces literal backslashes in the output; update the
string expression in the vfiles assignment to use unescaped backticks (f'`{p}`')
so the generated list prints normal Markdown code spans, keeping the surrounding
join/print logic and the variable name vfiles unchanged.

In `@scripts/update-versions.py`:
- Around line 100-117: The loop over version_files lacks type checks for each
entry and its 'path'/'pattern' fields, causing TypeError later; modify the loop
that iterates version_files to first verify each entry is a dict and that
entry['path'] and entry['pattern'] exist and are strings before calling
validate_path/validate_pattern, and if any check fails return the same
structured error object (use the same keys "success","message","updated") with a
clear message referencing the offending entry; update the block around the for
loop and the checks that call validate_path and validate_pattern (symbols:
version_files, entry, validate_path, validate_pattern) so all invalid types are
caught early.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5c967c and 7532583.

📒 Files selected for processing (2)
  • .github/workflows/centralized-release.yml
  • scripts/update-versions.py

- Remove redundant backslash escaping on backticks in Python f-string
- Add isinstance(entry, dict) check before accessing version_files entries

Resolve m-6822381131

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
scripts/update-versions.py (1)

107-128: ⚠️ Potential issue | 🟠 Major

entry['path']entry['pattern'] 缺少字符串类型校验,仍存在 TypeError 崩溃风险。

当前代码已补充 isinstance(entry, dict) 检查(Line 101),但 entry['path'] / entry['pattern'] 若为非字符串(如 YAML 中写 path: 1),Path(entry['path']) 会在 validate_path 内抛出未捕获的 TypeError,导致脚本以 traceback 而非结构化错误退出。

🛡️ 建议补充类型守卫
         if 'path' not in entry or 'pattern' not in entry:
             return {
                 "success": False,
                 "message": f"每条规则必须包含 path 和 pattern 字段: {entry}",
                 "updated": [],
             }
+
+        if not isinstance(entry['path'], str) or not isinstance(entry['pattern'], str):
+            return {
+                "success": False,
+                "message": f"path/pattern 必须为字符串: {entry}",
+                "updated": [],
+            }
+
         path_ok, path_err = validate_path(entry['path'])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/update-versions.py` around lines 107 - 128, Add explicit string-type
guards before calling validate_path/validate_pattern: verify entry['path'] and
entry['pattern'] are instances of str and return the same structured failure
object if not (same shape as the existing returns). In other words, inside the
loop where you currently call validate_path(entry['path']) and
validate_pattern(entry['pattern']), first check isinstance(entry['path'], str)
and isinstance(entry['pattern'], str) and return a {"success": False, "message":
... , "updated": []} describing the type error (include the offending entry
value), so TypeError from Path(...) or regex validation cannot propagate; keep
using the existing validate_path and validate_pattern functions for further
validation if types are correct.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/update-versions.py`:
- Around line 184-189: The success message incorrectly claims files were updated
even in dry_run; modify the logic in the function that builds the return value
so dry_run is respected: only append entry['path'] to updated when dry_run is
False (or alternatively keep appending but change the message and key names),
and adjust the returned "message" string to say "模拟更新 {len(updated)} 个版本号文件" (or
"未实际写入(dry_run): X") when dry_run is True; update references to the updated list
and the return dict so callers clearly see whether changes were actually written
(use the dry_run variable and the updated list and message keys to determine
content).
- Around line 130-184: The current loop in the version_files processing (the for
entry in version_files: block using file_path, re.subn, and
file_path.write_text) risks partial updates if a later file is missing or
unreadable; change to a two-phase approach: first iterate over version_files and
for each entry validate file existence and readability, read content, compute
new_content (using the same replacement logic with re.subn) and collect tuples
(entry['path'], new_content) into a pending_writes list while still honoring
dry_run behavior; only after all files succeed, perform the second phase that
writes new_content to each file (file_path.write_text) and then append to
updated; on errors in phase one return a failure without writing anything.
Ensure you reuse the same pattern, replacement and counting logic so behavior is
unchanged.

---

Duplicate comments:
In `@scripts/update-versions.py`:
- Around line 107-128: Add explicit string-type guards before calling
validate_path/validate_pattern: verify entry['path'] and entry['pattern'] are
instances of str and return the same structured failure object if not (same
shape as the existing returns). In other words, inside the loop where you
currently call validate_path(entry['path']) and
validate_pattern(entry['pattern']), first check isinstance(entry['path'], str)
and isinstance(entry['pattern'], str) and return a {"success": False, "message":
... , "updated": []} describing the type error (include the offending entry
value), so TypeError from Path(...) or regex validation cannot propagate; keep
using the existing validate_path and validate_pattern functions for further
validation if types are correct.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7532583 and 9c57048.

📒 Files selected for processing (2)
  • .github/workflows/centralized-release.yml
  • scripts/update-versions.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/centralized-release.yml

- Phase 1: read all files and compute replacements (no side effects)
- Phase 2: write all files only after all validations pass
- Distinguish dry_run vs actual update in success message

Resolve m-6822381131

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
scripts/update-versions.py (1)

84-123: ⚠️ Potential issue | 🟠 Major

补齐配置结构类型校验,避免异常配置触发未捕获崩溃。

Line 84config 为真值且非映射类型时(例如 YAML 顶层是 1)会因 'version_files' not in configTypeError。另外 Line 114 / Line 122pathpattern 不是字符串时也可能直接异常,绕过统一返回结构。

🔧 建议修改
-    if not config or 'version_files' not in config:
+    if not isinstance(config, dict) or 'version_files' not in config:
         return {
             "success": False,
             "message": "配置文件缺少 version_files 字段",
             "updated": [],
         }

@@
     for entry in version_files:
         if not isinstance(entry, dict):
             return {
                 "success": False,
                 "message": f"version_files 条目必须为对象: {entry}",
                 "updated": [],
             }
         if 'path' not in entry or 'pattern' not in entry:
             return {
                 "success": False,
                 "message": f"每条规则必须包含 path 和 pattern 字段: {entry}",
                 "updated": [],
             }
+        if not isinstance(entry['path'], str) or not isinstance(entry['pattern'], str):
+            return {
+                "success": False,
+                "message": f"path/pattern 必须为字符串: {entry}",
+                "updated": [],
+            }
#!/bin/bash
set -euo pipefail

echo "=== 查看关键代码段 (Lines 84-123) ==="
nl -ba scripts/update-versions.py | sed -n '84,123p'

echo "=== 检查是否存在关键类型防御 ==="
python - <<'PY'
from pathlib import Path
src = Path("scripts/update-versions.py").read_text(encoding="utf-8")

checks = {
    "config 顶层 dict 校验": "isinstance(config, dict)" in src,
    "entry.path 字符串校验": ("isinstance(entry['path'], str)" in src) or ("isinstance(entry.get('path'), str)" in src),
    "entry.pattern 字符串校验": ("isinstance(entry['pattern'], str)" in src) or ("isinstance(entry.get('pattern'), str)" in src),
}
for k, v in checks.items():
    print(f"{k}: {v}")
PY
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/update-versions.py` around lines 84 - 123, The config parsing
currently assumes config is a mapping and entry fields are strings which can
raise TypeError for malformed YAML; add defensive type checks: first verify
config is a dict (e.g. isinstance(config, dict)) before checking
'version_files', return the same structured error if not; ensure version_files
is a list and each entry is a dict, and check that entry.get('path') and
entry.get('pattern') are strings (or use isinstance(entry['path'], str) /
isinstance(entry['pattern'], str)) before calling validate_path and
validate_pattern, returning a consistent {"success": False, "message": "...",
"updated": []} when types are wrong so validation functions are never called on
non-string values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@scripts/update-versions.py`:
- Around line 84-123: The config parsing currently assumes config is a mapping
and entry fields are strings which can raise TypeError for malformed YAML; add
defensive type checks: first verify config is a dict (e.g. isinstance(config,
dict)) before checking 'version_files', return the same structured error if not;
ensure version_files is a list and each entry is a dict, and check that
entry.get('path') and entry.get('pattern') are strings (or use
isinstance(entry['path'], str) / isinstance(entry['pattern'], str)) before
calling validate_path and validate_pattern, returning a consistent {"success":
False, "message": "...", "updated": []} when types are wrong so validation
functions are never called on non-string values.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c57048 and faf911a.

📒 Files selected for processing (1)
  • scripts/update-versions.py

@ThirteenLLB
Copy link
Contributor Author

@coderabbitai approve

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

✅ Actions performed

Comments resolved and changes approved.

@ThirteenLLB ThirteenLLB merged commit 1c25c7b into main Feb 27, 2026
1 check passed
@ThirteenLLB ThirteenLLB deleted the feat/m-6822381131/auto-version-bump branch February 27, 2026 05:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants