diff --git a/.agents/skills/mcpp-release/SKILL.md b/.agents/skills/mcpp-release/SKILL.md index 3140d7be..72b406b7 100644 --- a/.agents/skills/mcpp-release/SKILL.md +++ b/.agents/skills/mcpp-release/SKILL.md @@ -228,10 +228,39 @@ git commit -am "ci: workspace mcpp bootstrap pin -> $NEW_VERSION (released, mirr **顺序不能反**:pin 一旦领先于"索引里真实存在的版本",每个 CI job 的 bootstrap 都会 `package 'mcpp@X.Y.Z' not found`。这就是 bump PR 里不许碰这两处的原因。 +**索引 PR 不要手写**:`publish-ecosystem` 已经开好了 bot PR —— 分支 +`bump/mcpp-`,提交者 `xlings-ci `,diff 恒为 **+22/-3**。 +承重的是那三行删除:生成器对每个平台表**替换** `["latest"]` 那一行,而不是新增一行。 +收尾时只要合入它。 + +2026-09-05 手写了一个平行的索引 PR(`xim-pkgindex#764`,分支 `feat/mcpp-`, ++22/**-0**),两个缺陷一起产生: + +- 追加而非替换,于是每个平台表里有两个 `["latest"]` 键。Lua 表构造器以最后一次赋值 + 为准,`latest` 静默解析回上一个发布。精确版本条目仍在且正确,**所以 mcpp 自己的 + CI(精确 pin)完全看不见**;只有不带版本的 `xlings install mcpp` 会拿到旧二进制。 +- 它先落地,bot 那份正确的 PR 变成冲突。解冲突时取 **bot 分支**那一侧的 + `pkgs/m/mcpp.lua` —— 它与手写后的 main 只差那几行陈旧的 `["latest"]`。 + **索引传播有滞后**:索引 artifact 发布后,`latest` tag 上的指针文件在 GitHub 资产 CDN 上可能还要几分钟才更新。紧接着跑的 CI 可能仍拿到旧索引并报 `package 'mcpp@X.Y.Z' not found` —— 这不是 release 坏了,等指针稳定后重跑即可。 +两个判据(都不是"文件里有这个版本字符串"): + +```bash +# 1) 索引 main 上,latest 解析到新版本:恰好三行,每平台一行,都指向新版本 +curl -fsSL https://raw.githubusercontent.com/openxlings/xim-pkgindex/main/pkgs/m/mcpp.lua \ + | grep -n '\["latest"\]' + +# 2) 推 pin 之前,指针已经跟上索引 main 的短 SHA +curl -fsSL https://github.com/xlings-res/xim-index/releases/download/latest/xim-index-latest.json \ + | grep -E '"(index_version|source_commit)"' +``` + +滞后的量级实测过:2026-09-05 那次,指针资产 17:20:32 就替换完了,CI 17:21:23 才去解析 +(晚 51 秒),拿到的仍是上一份 artifact。所以"索引 PR 合入了"不足以放行 pin,要看指针。 + ### 下游分发渠道(都是自动的,只需核验) 两条渠道都挂在 `release` workflow 的 `workflow_run: completed` 上,不需要人工推: @@ -256,6 +285,8 @@ gh workflow run bump-formula.yml -R mcpp-community/homebrew-mcpp |------|------|------| | `mcpp X.Y.Z-1` 但 tag 是 `vX.Y.Z` | `fingerprint.cppm` 版本未更新 | 更新 `MCPP_VERSION`,重新打 tag | | bump PR 里**所有** CI job 都红在 bootstrap,报 `package 'mcpp@X.Y.Z' not found` | 把 `.xlings.json` bootstrap pin 一起 bump 了,CI 去装一个还没发布的版本 | 把 `.xlings.json` 回退到上一个已发布版本;不要修改运行时推导的 `MCPP_PIN` | +| main 上**所有** workflow 都红在 bootstrap,报 `package 'mcpp@X.Y.Z' not found`,而该版本确实已发布并已合入索引 | pin 推得比索引指针稳定早;客户端读 CDN artifact 不读 git 树 | 等 `xim-index-latest.json` 的 `index_version` 等于索引 main 短 SHA 后重跑;不要改 pin | +| `xlings install mcpp`(不带 `@版本`)装到**上一个**版本,而索引文件里明明有新版本 | 同一平台表里两个 `["latest"]` 键(手写索引 PR 追加而非替换),Lua 后写的赢 | 删掉陈旧的那行,使每个平台表只剩一个 `["latest"]`。**精确 pin 仍可用,所以 mcpp 自己的 CI 不会报这个** | | 自查 `--version` 显示旧版本,但源码已改 | `target//<指纹>/` 的指纹随版本变,`ls \| head -1` 取到了上一次构建的目录 | 用 `ls -dt … \| head -1` 取最新构建 | | Smoke test 输出旧版本 | CI 缓存了旧的 sandbox/target | 删除 GitHub Actions cache 后重跑 | | e2e `01_help_and_version.sh` 挂 | 只改了 `mcpp.toml` 没改 `fingerprint.cppm`(它把两者交叉比对) | 同步两处正在构建的版本;注意这个 e2e 只在部分分片里跑,可能表现为"只有某个平台红" | diff --git a/docs/09-release.md b/docs/09-release.md index 144aeaf0..29871f24 100644 --- a/docs/09-release.md +++ b/docs/09-release.md @@ -89,6 +89,36 @@ Two steps are **not** automated: the released version is downloadable but not installable via `xlings install`. - **bumping `.xlings.json`** — see §4. +The bump PR is machine-authored: branch `bump/mcpp-`, commits by +`xlings-ci `, and a diff that is invariably +22/-3. The three +deletions are the load-bearing part — the generator *replaces* each platform +table's `["latest"]` line rather than adding one. A hand-written index PR is not +an equivalent shortcut, and filing one alongside the bot's carries two distinct +costs, both incurred on 2026-09-05 by `xim-pkgindex#764`: + +- The hand-written diff appended `["latest"] = { ref = "" }` above the + existing line instead of replacing it, leaving two `["latest"]` keys in every + platform table. A Lua table constructor takes the last assignment, so `latest` + resolved back to the previous release. The exact-version entry was present and + correct, so every pinned consumer stayed green — mcpp's own CI pins exactly and + never saw it. Only `xlings install mcpp` without a version served the stale + binary. +- Landing ahead of the bot's PR left that PR conflicting. The resolution is to + take the bot branch's side of `pkgs/m/mcpp.lua`; it differs from the + hand-written `main` only by the stale `["latest"]` lines. + +A merged bump PR is therefore confirmed by what `latest` resolves to, which the +presence of the version string elsewhere in the file does not answer: + +```bash +curl -fsSL https://raw.githubusercontent.com/openxlings/xim-pkgindex/main/pkgs/m/mcpp.lua \ + | grep -n '\["latest"\]' +``` + +Exactly three lines come back, one per platform table, and each names the version +just released. A fourth line, or one naming the previous release, is the +duplicate-key defect above. + ## 3. Verifying a release The mirror script verifies its own uploads, but the checks worth doing by hand @@ -169,6 +199,22 @@ is not yet installable. Bump it only after the release is published, mirrored, `check_version_pins.sh` enforces the weaker "never newer than the version being built"; the index condition requires human judgement. +"Merged into xim-pkgindex" is necessary but not sufficient: clients read a CDN +artifact, not the git tree. On 2026-09-05 the pin commit reached `main` 19 +seconds before the index PR merged, and the CI jobs it triggered resolved the +index 51 seconds *after* the new pointer asset had been replaced — and still +received the previous artifact. All nine workflows failed at bootstrap, none of +them having compiled a line. The observable condition is the pointer itself: + +```bash +curl -fsSL https://github.com/xlings-res/xim-index/releases/download/latest/xim-index-latest.json \ + | grep -E '"(index_version|source_commit)"' +``` + +`index_version` must equal the short SHA of xim-pkgindex `main` before the pin is +pushed. Nothing enforces this, and in the job log the resulting failure is +indistinguishable from a genuinely wrong version name. + ## 5. `MCPP_PIN` is derived, and why that matters `ci-fresh-install.yml` used to carry a second hand-edited copy of the pin. It was @@ -226,6 +272,8 @@ would again let the index guard and the installed version drift apart. [ ] downloaded mcpp-release.json regenerates byte-identically from public assets [ ] mirrors serve all four platforms on BOTH hosts, sha256 recomputed [ ] merge the xim-pkgindex bump PR +[ ] index `latest` resolves to the new version in all three platform tables +[ ] xim-index-latest.json's `index_version` equals xim-pkgindex `main` [ ] clean-room XLINGS_HOME: xlings install mcpp@ succeeds [ ] (optional) bump .xlings.json — only now, never earlier ``` diff --git a/docs/zh/09-release.md b/docs/zh/09-release.md index 8dc89010..947f5976 100644 --- a/docs/zh/09-release.md +++ b/docs/zh/09-release.md @@ -81,6 +81,29 @@ workflow 随后会再次下载公开 release、重新生成 manifest,并要求 可以下载,但无法通过 `xlings install` 安装。 - **bump `.xlings.json`** —— 见 §4。 +bump PR 是机器生成的:分支 `bump/mcpp-`,提交者 `xlings-ci +`,diff 恒为 +22/-3。承重的是那三行删除 —— 生成器对每个平台表是 +**替换** `["latest"]` 那一行,而不是新增一行。手写一个索引 PR 并不是等价的捷径; +在 bot 的 PR 之外另开一个,代价有两份,2026-09-05 的 `xim-pkgindex#764` 两份都付了: + +- 手写的 diff 把 `["latest"] = { ref = "<新版>" }` **追加**在原有那行上面而不是替换 + 它,于是每个平台表里留下两个 `["latest"]` 键。Lua 的表构造器以最后一次赋值为准, + `latest` 因此解析回上一个发布。精确版本条目存在且正确,所以每个按精确版本 pin 的 + 消费者都是绿的 —— mcpp 自己的 CI 正是精确 pin,完全看不见这件事。只有不带版本的 + `xlings install mcpp` 会拿到过期的二进制。 +- 它先于 bot 的 PR 落地,使那个 PR 变成冲突。解法是取 bot 分支那一侧的 + `pkgs/m/mcpp.lua`;它与手写后的 `main` 只差那几行陈旧的 `["latest"]`。 + +因此,bump PR 合入的判据是 `latest` **解析成什么**,而不是文件里别处出现了版本字符串: + +```bash +curl -fsSL https://raw.githubusercontent.com/openxlings/xim-pkgindex/main/pkgs/m/mcpp.lua \ + | grep -n '\["latest"\]' +``` + +恰好回来三行,每个平台表一行,且都指向刚发布的版本。出现第四行,或某一行指向上一个 +发布,就是上述的重复键缺陷。 + ## 3. 验证一次发布 镜像脚本会自校验上传,但真正值得手工做的是那些**不信任边车文件**的检查: @@ -153,6 +176,19 @@ $(find "$XLINGS_HOME" -name mcpp -type f -path '*/bin/*' | head -1) --version `package 'mcpp@' not found` 失败。待其语法问题修复后, `check_version_pins.sh` 能卡住较弱的「不得新于正在构建的版本」;索引那个条件需要人工把关。 +「已合入 xim-pkgindex」是必要条件而非充分条件:客户端读的是 CDN 上的 artifact,不是 +git 树。2026-09-05 那次,pin 的 commit 比索引 PR 的合入早到 `main` 19 秒,而它触发的 +CI 作业在新指针资产被替换**之后** 51 秒才去解析索引 —— 拿到的仍然是上一份 artifact。 +九条 workflow 全部死在 bootstrap,没有一条编译过一行。可观测的条件是指针本身: + +```bash +curl -fsSL https://github.com/xlings-res/xim-index/releases/download/latest/xim-index-latest.json \ + | grep -E '"(index_version|source_commit)"' +``` + +推 pin 之前,`index_version` 必须等于 xim-pkgindex `main` 的短 SHA。没有任何东西强制 +这一点,而在作业日志里,由此产生的失败与「版本名真的写错了」无法区分。 + ## 5. `MCPP_PIN` 改为推导,以及这为什么重要 `ci-fresh-install.yml` 过去带着 pin 的第二份手工副本。它们从来就不是一回事: @@ -191,6 +227,8 @@ $(find "$XLINGS_HOME" -name mcpp -type f -path '*/bin/*' | head -1) --version [ ] 下载的 mcpp-release.json 能从公开资产逐字节重新生成 [ ] 双端都服务四个平台,sha256 重新算过 [ ] 合并 xim-pkgindex 的 bump PR +[ ] 索引的 `latest` 在三个平台表里都解析到新版本 +[ ] xim-index-latest.json 的 `index_version` 等于 xim-pkgindex `main` [ ] clean-room XLINGS_HOME:xlings install mcpp@ 成功 [ ] (可选)bump .xlings.json —— 只在此刻,绝不提前 ```