relizy PR #58 实战复盘与经验教训
适用范围
本文总结的是一次完整的、真实落地的 relizy 上游 issue / PR 实战过程,目标问题是:
- Windows 环境下 GNU 工具缺失导致
relizy直接失效。 - 全新 monorepo 首次发版时没有 baseline tag,导致 independent release 无法继续。
这次实战最终对应:
- upstream issue:
#57 - upstream PR:
#58 - PR 标题:
fix: support Windows initial independent releases
这份文档不是 issue 草稿,也不是流水账。它的目标是让下次再处理 relizy 相关 issue / PR 时,能够直接绕开本次已经踩过的坑,不要重复绕圈。
最终结论
这次 PR 真正解决了什么
这次 PR 真正解决的是两类问题:
relizy内部仍然依赖grep/head/sed这类 GNU 工具,导致 Windows 原生环境不兼容。- independent mode 下,首次发版没有 package baseline tag 时,
relizy无法正确计算 release 边界。
这次 PR 没有解决什么
这次 PR 没有解决 nested git root 问题。
也就是:
- 当前运行目录不是 Git 顶层目录。
- 代码目录和真正
.git所在目录之间隔了一层。 relizy在git add/git commit/git tag/git push阶段,使用了错误的工作目录,最终导致待提交文件路径被拼错。
这个问题必须单独作为下一个 issue / PR 处理,不能混进本次 GNU / baseline 修复 PR 里。
本次任务里最重要的判断
1. 先把问题拆成两条线
一开始最容易犯的错误,是把所有实际遇到的故障都混成“这次 PR 要一起修”。
这次实际碰到的问题至少有四类:
- Windows 缺 GNU 工具。
- 首次发版缺 baseline tag。
- nested git root。
- 某些托管平台 compare URL 格式与 GitHub 不兼容。
正确做法是先拆线:
- 本次 PR 只处理
GNU tools + baseline tag。 nested git root下一个 PR 再处理。- 自定义 compare URL 再下一个 PR 处理。
如果不拆线,PR 会变得又大又乱,维护者很难审核,而且你自己也会越来越难判断“当前失败到底是不是这次改动带来的”。
2. patch coverage 不够,不等于生产代码错了
本次 PR 中,很多时间花在了覆盖率追踪上。
必须明确:
test-unit通过,说明主路径没有明显被打坏。- Codecov 报
patch coverage不够,尤其是0 missing, 2 partials这种情况,通常说明:- 新增分支没有被测到;
- 或者 if / else / throw 的某一边没走到;
- 不代表生产逻辑一定写错了。
这次后续做对的事情是:
- 不靠修改
vitest.config.ts阈值过关。 - 不为了“看起来更合理”去重写生产代码。
- 而是根据 Codecov 的缺口,定点补测试。
这才是对维护者最有说服力的做法。
3. vitest.config.ts 被自动改,不是应该提交的改动
这个仓库的 vitest.config.ts 开了 coverage autoUpdate。
结果是:
- 只要本地跑
pnpm test:unit:coverage - Vitest 就可能自动回写
statements / functions / branches / lines的数值
这类改动在这个仓库历史里本来就频繁出现,但在这次 PR 场景里,它是副作用,不是本次问题的真实修复。
因此结论很明确:
- 可以观察它。
- 可以知道这是仓库工具行为。
- 但不应该把它当成这次 PR 的有效修复提交上去。
4. 不要把 pre-existing workflow 问题混进当前 PR
本次过程中,一度把 dry-run-release 的失败和当前 PR 混在了一起。
后来验证才清楚:
test-unit红,是这次 PR 自己引入的 coverage 缺口。dry-run-release红,不是这次 PR 才出现,之前的 PR 也已经这样。
因此原则是:
- 当前 PR 只修自己确认引入的问题。
- 仓库既有 workflow 问题不要顺手混修。
- 否则会让 PR 目标失焦。
本次 PR 中哪些代码修改是必要的
核心必要修改
这几处是根因修复,属于必须改:
src/core/tags.tssrc/core/repo.ts
原因:
tags.ts里原本有 GNU 工具链依赖,需要改成 Node 内过滤和排序,才能真正 Windows 兼容。repo.ts里路径分隔符和首个 package commit 处理必须改,否则 Windows 环境和首次 independent release 仍然会失败。
闭环必要修改
这几处不是“顺手重构”,而是为了让 baseline 语义贯通到底:
src/core/changelog.tssrc/core/github.tssrc/core/gitlab.ts
原因:
NEW_PACKAGE_MARKER不能只在 bump 阶段出现。- 如果 changelog / provider release 这些下游消费者不理解这个 marker,那么修复是不完整的。
- 所以必须把它解释成真正的 bootstrap baseline,例如
pkg-a@0.0.0。
可批评但不算错的地方
本次里有些 diff 看起来偏大,主要是 helper 拆分导致。
尤其是 github.ts:
- 行为改动本身是必要的。
- 但 helper 拆分让 diff 体积扩大。
- 这会提高维护者阅读成本。
经验教训:
- 行为必须改,就改。
- 但尽量减少“为了写法更优雅而扩大 diff”的倾向。
覆盖率阶段的真实经验
不要猜,直接根据 Codecov 缺口定位
本次真正提升效率的一步,是不再泛泛地“多补点测试”,而是根据:
- PR diff
- 本地
coverage/lcov.info - Codecov comment 点名的文件和 partial branch
去精确定位缺口。
例如后面能明确定位到:
src/core/tags.ts:54src/core/tags.ts:253
说明应该去补 legacy tag lookup 和 non-stable pattern 分支,而不是继续盲目扩大测试范围。
正确策略是“补测试证明逻辑”,不是“改逻辑凑覆盖率”
本次后期做对的策略是:
- 先确认主逻辑方向是对的。
- 再补 focused test 去打掉 branch partial。
- 只在测试过程中发现语义含混时,才考虑最小重构。
不要反过来做。
最终有效的覆盖率策略
后面实际有效的测试补充方向主要是:
- bootstrap tag helper 的真假分支
- legacy tag lookup 分支
- independent mode 下 guard / throw 分支
config.fromoverride 优先级- provider 层对 bootstrap baseline 的消费路径
也就是说,下次如果你再做类似 PR,应该优先先想:
本次新增的 if / else / throw / fallback,我有没有让它真的执行过?
而不是先想:
能不能顺便把这块代码改得更漂亮?
本地验证阶段的关键经验
1. 两个项目都必须真实验证
只在 relizy 仓库里看 unit test 通过,不够。
这次必须做的,是在真实本地 monorepo 中验证:
D:\code\github-desktop-store\01s-11commD:\code\01s\202603-13hzb\yunxiao\01s-2603-13eams\eams-frontend-monorepo
而且不是跑 wrapper 脚本,而是直接验证链接后的 relizy 本体能否完成发版。
2. pnpm link --global relizy 在 Windows 下不稳定
这次实测里,直接:
pnpm link --global relizy在目标项目中会撞上:
Symlink path is the same as the target path
这说明不能把“全局 link 流程”想得过于理想。
更稳妥的替代方案有两个:
- 目录链接:
pnpm link D:\code\github-desktop-store\relizy__ruan-catfile:依赖 override:
overrides:
relizy: file:../../../../../github-desktop-store/relizy__ruan-cat3. pnpm link 会把工作树弄脏,导致 relizy clean check 失败
这是一个非常容易忽略的坑。
在目标仓库里执行 link / install 后,经常会导致:
pnpm-lock.yamlpnpm-workspace.yaml
发生修改。
而 relizy release 默认会检查 Git 工作树是否干净,于是会直接失败。
因此正确测试步骤是:
- 完成 link / install。
- 确认
node_modules/relizy已经指向本地目标。 - 把 link 带来的配置改动临时 stash 掉。
- 再执行 release 命令。
4. 真实验证命令必须补 --patch
这次两个项目都证明了同一件事:
直接执行:
pnpm exec relizy release --no-publish --no-provider-release --yes --force如果当前仓库没有可自动推导的 release type,就会失败。
而补上:
pnpm exec relizy release --no-publish --no-provider-release --yes --force --patch就能进入真实 release 流程。
因此下次做本地验收时,不要忘记:
- 本地验证命令需要显式 release type。
- 这里默认用
--patch是合理的验收方式。
两个真实项目的最终验证结论
01s-11comm
这是本次最干净的成功案例。
验证过程:
- 将本地
relizy链接进项目。 - 暂时 stash 掉
pnpm link带来的pnpm-lock.yaml/pnpm-workspace.yaml脏改动。 - 执行:
pnpm exec relizy release --no-publish --no-provider-release --yes --force --patch结果:
- bump 成功
- changelog 成功
- commit 成功
- tag 成功
- push 成功
最终证据:
- 最新提交:
b0771e71 - 新 tag:
@01s-11comm/admin@6.1.2@01s-11comm/type@1.1.1
结论:
- 这次 GNU / baseline 修复在一个真实 monorepo 上已经完整跑通。
eams-frontend-monorepo
这是本次最有价值的复杂验证案例。
首次直接验证时,情况是:
- GNU / baseline 修复已经生效
- bump 和 changelog 都成功
- 但 commit 阶段失败
失败根因不是本次 PR 本身,而是:
- 当前运行目录不是 Git 顶层目录
git rev-parse --show-prefix返回eams-frontend-monorepo/relizy在 commit 阶段把待提交路径拼成了重复前缀
也就是本地报告里说的 nested git root 问题。
这个问题的关键经验是:
- 不能因为它还存在,就误以为本次 GNU / baseline 修复无效。
- 实际上,本次 PR 的修复已经让流程推进到了更后面的阶段。
为了完成本地最终验收,本次采用了项目侧临时兜底:
- 不改上游当前 PR。
- 不把 nested-root 修复混进本次 PR。
- 仅在
eams-frontend-monorepo内部使用:- 本地
file:依赖形式的relizy - 项目侧临时
relizy@1.2.2-beta.1.patch
- 本地
注意一个非常关键的细节:
- 这份 patch 不是多余 patch。
- 它修的是 nested git root。
- 只是原 patch 针对的是旧的
dist/shared/relizy.*.mjshash。 - 当本地
relizy构建产物 hash 变了以后,patch 也必须同步更新到新的文件名,否则 patch 无法应用。
最终执行:
pnpm exec relizy release --no-publish --no-provider-release --yes --force --patch结果:
- bump 成功
- changelog 成功
- commit 成功
- tag 成功
- push 成功
最终证据:
- 最新提交:
03d09899 - 新 tag:
@eams-monorepo/admin@0.0.3@eams-monorepo/stu-app@0.0.3@eams-monorepo/tea-app@0.0.3hello-world@0.1.2@eams-monorepo/vue-element-cui@0.0.3@eams-monorepo/vue-element-cui-nuxt@0.0.3
结论:
- 在 nested git root 通过项目侧临时 patch 兜底后,本次 GNU / baseline 修复也在第二个真实项目上验证成功。
本次最容易再犯的误区
误区 1:想一次性把所有 relizy 问题都修掉
错误。
必须按问题类型拆 PR。
误区 2:看到 Codecov 红了,就怀疑生产逻辑错了
错误。
先看是 missing line,还是 partial branch。
误区 3:私自提交 vitest.config.ts 数值更新
错误。
那通常只是 coverage autoUpdate 的副作用。
误区 4:把 pre-existing CI 问题混进当前 PR
错误。
当前 PR 只修自己确认引入的问题。
误区 5:以为 link 成功就等于 relizy 真被用了
错误。
必须验证:
node_modules/relizy实际指向哪里pnpm exec relizy --version输出什么版本
误区 6:看到 eams 的 patch 文件就默认它是“应该删掉的多余 patch”
错误。
必须先阅读 patch 内容,确认它修的到底是什么。
本次实际证明:
eams的relizy@1.2.2-beta.1.patch- 修的是 nested git root
- 不是 GNU / baseline
- 因此它是“另一个问题的临时修复”,不是“无意义 patch”
下次再做 relizy issue / PR 的建议顺序
如果是 GNU / baseline 类问题
- 先阅读下游 wrapper / runner 文档和事故报告。
- 明确区分:
- 上游应修的问题
- 下游临时兼容层做的事情
- issue 草稿里必须写清楚:
- 现在下游是如何临时兼容的
- 为什么这不是上游已修复的证据
- PR 里优先做最小根因修复。
- 覆盖率不够时优先补测试,不随意改生产代码。
- 本地至少找两个真实项目验收。
如果是 nested git root 类问题
- 先确认:
git rev-parse --show-toplevelgit rev-parse --show-prefix
- 如果两者不一致,先认定这是独立问题。
- 检查 commit / tag / push 是否使用了错误
cwd。 - 不要把它混进 GNU / baseline PR。
如果要做本地真实发版验证
推荐顺序:
pnpm build- 验证本地产物存在
- 采用目录链接或
file:override - 验证
node_modules/relizy的真实指向 - 验证
pnpm exec relizy --version - 清理或 stash link / install 带来的脏工作树
- 执行:
pnpm exec relizy release --no-publish --no-provider-release --yes --force --patch这次任务的高价值证据
下次和维护者沟通时,最有价值的不是“我觉得修好了”,而是下面这些证据:
- PR #58 在上游仓库中通过了 unit test / coverage。
01s-11comm在本地直接用链接的relizy成功发版。eams-frontend-monorepo在 nested-root 由项目侧临时 patch 兜底后,也成功发版。- 因此可以明确区分:
- GNU / baseline 修复已经有效
- nested git root 仍是独立未合并问题
这套证据链非常重要,因为它能避免维护者把所有问题都误解成“你这个 PR 还没修好”。
面向下次会话的直接行动建议
如果下次继续推进 relizy:
- 先单独起
nested git root的上游 issue / PR。 - 直接引用本次
eams-frontend-monorepo的本地 patch 证据。 - 明确说明:
- GNU / baseline 已在 PR #58 处理
- nested-root 是下一条独立修复线
- 本地验证时,继续保留“两个真实项目都要成功”的标准。
一句话版本
这次最大的经验不是“怎么把代码改对”,而是:
必须把
GNU tools / baseline tag / nested git root / compare URL这些问题彻底拆开,分别立 issue、分别做 PR、分别给出本地验证证据;覆盖率问题优先补测试,不要靠改阈值或扩大生产代码修改来糊过去。
贡献者
Cursor