feat(release): auto-update version files from .release.yml config#49
feat(release): auto-update version files from .release.yml config#49ThirteenLLB merged 4 commits intomainfrom
Conversation
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>
Walkthrough在发布工作流中引入基于 Changes
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 通知内容
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
✅ Pre-merge checks override appliedThe pre-merge checks have been overridden successfully. You can now proceed with the merge. ❌ Failed checks (1 error)
✅ Passed checks (5 passed)
✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
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.
- 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>
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
.github/workflows/centralized-release.ymlscripts/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>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
scripts/update-versions.py (1)
84-123:⚠️ Potential issue | 🟠 Major补齐配置结构类型校验,避免异常配置触发未捕获崩溃。
Line 84在config为真值且非映射类型时(例如 YAML 顶层是1)会因'version_files' not in config抛TypeError。另外Line 114/Line 122在path或pattern不是字符串时也可能直接异常,绕过统一返回结构。🔧 建议修改
- 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.
|
@coderabbitai approve |
✅ Actions performedComments resolved and changes approved. |
Summary
scripts/update-versions.py脚本,读取.release.yml配置自动替换版本号文件centralized-release.yml,在创建 release PR 时自动更新版本号文件并纳入提交.release.yml的仓库行为不变)Resolve m-6822381131
Test plan
验证步骤:
.release.yml的仓库(wuji-sdk-dev、wuji-studio-dev)用 dry_run 模式触发 centralized-release,确认输出正确.release.yml的仓库触发,确认行为不变(仅更新 CHANGELOG)🤖 Generated with Claude Code
Summary by CodeRabbit
发布说明