Skip to content

fix(release-prep): CHANGELOG 占位节在 CRLF 检出上不再静默失效 - #360

Merged
modusensus merged 2 commits into
mainfrom
fix/release-prep-crlf
Oct 3, 2026
Merged

modusensus merged 2 commits into
mainfrom
fix/release-prep-crlf

Conversation

@modusensus

@modusensus modusensus commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator

问题

scripts/release-prep.mjs(本机发版 prep 的入口)用 /^(# Changelog\n\n)/ 匹配 CHANGELOG
文件头,而 Windows 检出是 CRLF —— 正则命中不了,replace 退化成空操作,但脚本照样
打印 ✓ dsh-mneme/CHANGELOG.md 占位节
,git status 里看不出任何异常。

CI 的 release-prep 工作流跑在 ubuntu(LF)所以从未暴露,只有本机发版会中招:v0.8.13 那次
就是这样——脚本报成功、实际什么都没写,最后靠人工补的占位节。

复现(实跑证据)

CRLF = true | 旧正则能否命中 = false

改后在同一棵 CRLF 检出上真跑脚本:✓ … 占位节 且 git diff 里确实多了 6 行。

改法

规则抽成 dsh-mneme/scripts/changelog-prep.mjs 的纯函数——入口在仓库根、import 即执行,
测不了;与 test-count-sync.mjs 收拢替换规则是同一个理由(规则收在共用模块里才测得到,
也才不会两处各写一份)。

  • 行尾 \r?\n 两种都吃,插入内容跟随原文件行尾(不混排),带 BOM 也认;
  • 匹配不上时返回 {ok:false, reason:"header-not-found"} 而不是静默返回原文——入口据此
    报错并 exit 1,绝不假打印成功。

验证

  • 新增 6 条回归测试(LF / CRLF / BOM / 幂等 / 回报契约 / detectEol):全量
    1529 tests / 1528 pass / 0 fail / 1 skip
  • check-sync:src ↔ lib 一致(52 文件)
  • 执行级验证(不只跑测试):
    • 在真实 CRLF 检出上跑 node scripts/release-prep.mjs 9.9.9 → 这次真的写进去了(git diff +6 行)
    • 把文件头改坏再跑 → 打印 ✗ …(reason=header-not-found),exit 1,CHANGELOG 一个字节没动
  • 变异检验(把三处修复分别改坏,确认用例不是空转):
变异 变红的用例
正则改回只认 LF CRLF 输入:同样能插
失败时改回「返回原文」 文件头不匹配:回报 header-not-found
固定用 LF 插入 CRLF 输入:同样能插(行尾混排)

顺带

新增 6 条用例后,双 README 的测试数由 badge:sync 从 1523 刷到 1529(6 处)。

没做的

第 4 步(双 README 版本表占位行)是死代码——两张表早就不存在。它不算静默失败(脚本会
如实打印 无版本表格行,跳过),所以本批不动:要么将来表格回来还有用,要么该单独判断要不要
删,混进来只会让 diff 变难审。

Summary by CodeRabbit

  • 修复
    • 修正发布准备过程中变更日志章节插入失败却未提示的问题;现可正确处理不同换行格式及文件开头的 BOM,找不到标题时会明确报错。
  • 文档
    • README 中显示的测试数量已更新至 1529。
    • 更新变更日志,记录相关修复及回归测试。

原实现用 /^(# Changelog\n\n)/ 匹配 CHANGELOG 文件头,而 Windows 检出是 CRLF —— 正则命中
不了,`replace` 退化成空操作,**但脚本照样打印 `✓ … 占位节`**,`git status` 里看不出任何
异常。CI 的 release-prep 工作流跑在 ubuntu(LF)所以从未暴露,只有本机发版会中招——v0.8.13
那次即如此,最后靠人工补的占位节。

改法:规则抽成 dsh-mneme/scripts/changelog-prep.mjs 的纯函数(入口在仓库根、import 即执行,
测不了;与 test-count-sync.mjs 收拢替换规则同一个理由)。

- 行尾 `\r?\n` 两种都吃,插入内容跟随原文件行尾(不混排),带 BOM 也认;
- 匹配不上时返回 {ok:false, reason:"header-not-found"} 而不是静默返回原文,入口据此报错
  并 exit 1 —— 绝不假打印成功。

6 条回归测试:LF / CRLF / BOM / 幂等 / 回报契约 / detectEol。变异检验:把正则改回只认 LF、
失败时改回「返回原文」、固定用 LF 插入——三条各自让对应用例变红。
本批新增 6 条用例,双 README 的测试数随之从 1523 用 npm run badge:sync 刷到 1529(6 处),
不手改。
Copilot AI lite review requested due to automatic review settings October 3, 2026 17:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: slow-stack/mneme/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: bee4f1a1-eab4-45a6-84d0-d1ae9e6659f5
📥 Commits

Reviewing files that changed from the base of the PR and between d3cce29 and 2047801.

📒 Files selected for processing (6)
  • README.md
  • dsh-mneme/CHANGELOG.md
  • dsh-mneme/README.md
  • dsh-mneme/scripts/changelog-prep.mjs
  • dsh-mneme/test/changelog-prep.test.js
  • scripts/release-prep.mjs

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.


📝 Walkthrough

Walkthrough

发布准备脚本现在使用新增的 CHANGELOG 章节插入函数。该函数识别 LF、CRLF 和可选 BOM,处理重复版本及未匹配标题的情况。新增回归测试,并将文档中的测试数量更新为 1529。

Changes

CHANGELOG 发布准备

层 / 文件 摘要
章节插入与验证
dsh-mneme/scripts/changelog-prep.mjs, dsh-mneme/test/changelog-prep.test.js, README.md, dsh-mneme/README.md
新增行尾检测和版本章节插入函数。测试覆盖 LF、CRLF、BOM、重复版本、标题不匹配及空输入;文档中的测试数量更新为 1529。
发布脚本接入与变更记录
scripts/release-prep.mjs, dsh-mneme/CHANGELOG.md
发布脚本根据插入结果写入 CHANGELOG、跳过已存在版本,或在其他失败情况下以状态码 1 退出。CHANGELOG 新增对应的未发布条目。

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Suggested reviewers: heptaspirit

Merge Risk: ⚪ Minimal · up to 20478

The reported version-string issue cannot occur in the release workflow. The change is mergeable after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 20478

The change improves failure reporting without adding public access or privileges. Release preparation still has existing non-transactional writes and workflow input-handling risks; recovery ownership and dispatch authorization were not established.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The inspected write scope is release metadata in the working checkout. Automated preparation runs with repository contents and pull-request write permissions. Those permissions and workflow definitions are unchanged by this PR.

Security Findings and Attack Paths

  • inferred — The existing workflow interpolates the dispatch version into shell source before checking its format. Shell-active input could therefore execute before validation if an actor can supply that input. The same construction exists in the base revision; this PR does not introduce or widen it. Effective dispatch authorization was not established, so this is an inherited architecture fact, not an introduced PR concern.

Trust Boundaries and Controls

  • observed — The script constrains versions before passing them to the helper. Preparation failure blocks automatic PR creation, and the downstream release workflow independently checks tag-to-package equality and CHANGELOG section presence. These consistency controls do not address shell interpretation preceding the workflow's input check.

Resilience and Maintainability Implications

  • inferred — Same-version reruns avoid duplicate CHANGELOG insertion. Nevertheless, interruption or failed preparation can leave earlier writes behind, and the inspected flow provides no transaction, rollback or run serialization. Recovery ownership and real concurrent-run behavior remain unverified; no resulting remote publication bypass was established.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了主要修复:处理 CRLF 检出下 CHANGELOG 占位节插入失败且未报告错误的问题。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 3 files. (3 skipped: 3 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov

codecov Bot commented Oct 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@modusensus
modusensus merged commit 59029fe into main Oct 3, 2026
11 checks passed
@modusensus
modusensus deleted the fix/release-prep-crlf branch October 3, 2026 18:03
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