From 655a19d41bfa17f92217443f408f78e758299f7a Mon Sep 17 00:00:00 2001 From: wuji-tech-dev Date: Tue, 14 Jul 2026 15:29:02 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(release):=20update-versions.py=20?= =?UTF-8?q?=E5=90=8C=E4=B8=80=E6=96=87=E4=BB=B6=E5=A4=9A=E6=9D=A1=E8=A7=84?= =?UTF-8?q?=E5=88=99=E4=BA=92=E7=9B=B8=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 此前阶段 1 每条规则都从磁盘读原始内容独立计算替换结果,阶段 2 依次写盘, 同一文件配多条规则时后写的会覆盖先写的——只有最后一条规则生效, 且脚本仍报「成功」,属静默丢改动。 改为同一文件的多条规则在内存中累积应用:每个文件只读一次、阶段 2 只写一次。 单文件单规则的既有仓库(wuji-sdk-dev/wuji-studio-dev/wuji-cli-dev)行为不变。 场景:wuji-sdk-dev 是 workspace,Cargo.lock 里 11 个成员 crate 条目 需要各配一条规则,全部落在同一个文件上,没有本修复则只有最后一条生效。 Co-Authored-By: Claude Fable 5 --- scripts/update-versions.py | 68 +++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/scripts/update-versions.py b/scripts/update-versions.py index 982bf32..32721e6 100644 --- a/scripts/update-versions.py +++ b/scripts/update-versions.py @@ -128,29 +128,33 @@ def update_versions(config_path, version, dry_run=False): } # 阶段 1:预读并生成所有替换结果,遇错立即返回(无副作用) - pending_writes = [] # list of (file_path, new_content, entry_path) + # 同一文件的多条规则在内存中累积应用(每个文件只读一次、阶段 2 只写一次), + # 否则后一条规则会基于原始内容覆盖掉前一条的替换结果 + file_contents = {} # file_path -> 累积应用替换后的内容 config_dir = config_file.parent for entry in version_files: file_path = config_dir / entry['path'] pattern = entry['pattern'] - if not file_path.exists(): - return { - "success": False, - "message": f"文件不存在: {entry['path']}", - "updated": [], - } - - try: - content = file_path.read_text(encoding='utf-8') - except OSError as e: - return { - "success": False, - "message": f"读取文件失败 ({entry['path']}): {e}", - "updated": [], - } - + if file_path not in file_contents: + if not file_path.exists(): + return { + "success": False, + "message": f"文件不存在: {entry['path']}", + "updated": [], + } + + try: + file_contents[file_path] = file_path.read_text(encoding='utf-8') + except OSError as e: + return { + "success": False, + "message": f"读取文件失败 ({entry['path']}): {e}", + "updated": [], + } + + content = file_contents[file_path] replacement = rf'\g<1>{version}\g<2>' new_content, count = re.subn( pattern, replacement, content, count=1, flags=re.MULTILINE @@ -163,31 +167,33 @@ def update_versions(config_path, version, dry_run=False): "updated": [], } + file_contents[file_path] = new_content + if dry_run: match = re.search(pattern, content, re.MULTILINE) old_line = match.group(0).strip() if match else "?" new_match = re.search(pattern, new_content, re.MULTILINE) new_line = new_match.group(0).strip() if new_match else "?" print(f" 📦 [DRY RUN] {entry['path']}: {old_line} → {new_line}") - else: - pending_writes.append((file_path, new_content, entry['path'])) # 阶段 2:统一写入(仅 non-dry-run 且所有预检通过后执行) updated = [] - for file_path, new_content, entry_path in pending_writes: - try: - file_path.write_text(new_content, encoding='utf-8') - except OSError as e: - return { - "success": False, - "message": f"写入文件失败 ({entry_path}): {e}", - "updated": updated, - } - print(f" 📦 已更新: {entry_path} → {version}") - updated.append(entry_path) + if not dry_run: + for file_path, new_content in file_contents.items(): + entry_path = str(file_path.relative_to(config_dir)) + try: + file_path.write_text(new_content, encoding='utf-8') + except OSError as e: + return { + "success": False, + "message": f"写入文件失败 ({entry_path}): {e}", + "updated": updated, + } + print(f" 📦 已更新: {entry_path} → {version}") + updated.append(entry_path) if dry_run: - updated = [e['path'] for e in version_files] + updated = sorted({e['path'] for e in version_files}) action = "预览" if dry_run else "更新" return { From 418c0787c2501bcbf46f0d6413efac96396537d8 Mon Sep 17 00:00:00 2001 From: wuji-tech-dev Date: Tue, 14 Jul 2026 15:43:09 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(release):=20workflow=20=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E6=96=87=E4=BB=B6=E5=88=97=E8=A1=A8=E5=8E=BB=E9=87=8D?= =?UTF-8?q?=20+=20=E4=BF=AE=20PR=20body=20=E5=8F=8D=E5=BC=95=E5=8F=B7?= =?UTF-8?q?=E8=A2=AB=20shell=20=E5=90=9E=E6=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .release.yml 同一文件配多条规则时(如 Cargo.lock 多个 crate 条目), staging 日志和 PR body 会重复列同一路径,按 path 去重 - PR body 生成处 python -c 双引号串里的反引号未转义,被 shell 当命令 替换执行({p}: command not found),版本文件列表一直渲染成空逗号串, 补反斜杠转义(存量 bug,本地已复现并验证修复) Co-Authored-By: Claude Fable 5 --- .github/workflows/centralized-release.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/centralized-release.yml b/.github/workflows/centralized-release.yml index 459c31e..4ed2359 100644 --- a/.github/workflows/centralized-release.yml +++ b/.github/workflows/centralized-release.yml @@ -179,8 +179,9 @@ jobs: import yaml with open('.release.yml') as f: config = yaml.safe_load(f) - for entry in config.get('version_files', []): - print(entry['path']) + # 同一文件可配多条规则(如 Cargo.lock 多个 crate 条目),路径去重 + for path in dict.fromkeys(e['path'] for e in config.get('version_files', [])): + print(path) " | while read -r vfile; do [ -f "$vfile" ] && git add "$vfile" && echo " 📦 Staged: $vfile" done @@ -225,8 +226,8 @@ jobs: import yaml with open('.release.yml') as f: config = yaml.safe_load(f) - paths = [e['path'] for e in config.get('version_files', [])] - print(', '.join(f'`{p}`' for p in paths)) + paths = dict.fromkeys(e['path'] for e in config.get('version_files', [])) + print(', '.join(f'\`{p}\`' for p in paths)) ") version_files_info=$'\n- 更新版本号文件: '"${vfiles}" fi