From 7993d9ed6d7fb022cfb36b4e4c28e87cbaa82acf Mon Sep 17 00:00:00 2001 From: speak-agent Date: Tue, 4 Aug 2026 03:38:50 +0800 Subject: [PATCH] fix(ci): ci-fresh-install had two defects, both firing after every release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 11 red jobs looked like many problems. It was two, each able to redden the whole matrix on its own, and neither introduced by any particular release — both fire after EVERY release. Analysis: .agents/docs/2026-08-04-ci-fresh-install-two-defects.md A. THE REPO'S WORKSPACE PIN AMBUSHED THE VERSION UNDER TEST. `actions/checkout` is the first step, so the repo's .xlings.json — which pins the BOOTSTRAP mcpp, hand-maintained and deliberately lagging — lands in the working directory. That pin is directory-scoped and beats anything installed globally, so the jobs installed MCPP_PIN and then ran something else, which was not installed at all: ✓ 1 package(s) installed [error] xlings: version '2026.8.3.2' not found for 'mcpp' [error] available: 2026.8.3.4 ci-aarch64-fresh-install.yml hit this and solved it by ordering the checkout last, with a thorough comment. That does not transfer: the `build mcpp` steps here run `mcpp clean && mcpp run` INSIDE the repo, so a checkout must be present while mcpp is invoked. The pin goes instead — this workflow tests the RELEASED mcpp, and the bootstrap pin has no standing in that. B. wait-index GUARDED A DIFFERENT DISTRIBUTION CHANNEL. It polled the index's GIT source, which updates the instant the bump PR merges. The jobs install from the PUBLISHED ARTIFACT (xlings-res/xim-index → pointer → tarball), which lags git by however long Publish Index Artifact plus release-CDN propagation takes. Measured on 2026.8.3.5: the guard reported ready and all 11 jobs then failed with [error] package 'mcpp@2026.8.3.5' not found A guard that measures a channel nobody installs from is not a guard. It now polls the artifact — verified against the live pointer, which resolves xim-index-e8ad461.tar.gz and does contain the released version. Both fixes, plus activation (`-u` and `xlings use`) and an ASSERTION, are collapsed into one shared script, .github/tools/install_released_mcpp.sh, called from all five jobs. The two Windows install steps move from pwsh to bash to share it. The knowledge existed in this repo — fully written out in ci-aarch64's comment — and the other workflow never learned it; a script is where that stops being a thing people have to remember. The assertion is the only part that defends against the NEXT one. It evaluates `mcpp` as resolved through PATH — the binary the later steps actually invoke — so any future silent redirection becomes a named failure instead of a matrix that quietly tests the wrong binary and reports green. It earned that on the bench, twice, before ever reaching CI: * my first version probed a GUESSED install path instead of PATH. That verifies a copy nobody runs — the exact mistake the analysis doc is about, made while writing the fix for it. * `-u` alone did not move the shim in an isolated environment (payload reported 2026.8.3.5, shim reported 2026.7.29.1). `xlings use` is a different code path; both are done now, with the assertion still final. Also bumps the bundled xlings 2026.7.28.4 → 2026.8.4.1 across all 16 pin sites (check_version_pins.sh found every one). That release implements the index snapshot version contract and auto-routing this repo asked for (openxlings/xlings#476): an index declares the client version it needs, and the client routes to the newest snapshot it can use — version skew becomes a routing decision instead of a hard failure. It supplies the half mcpp cannot do alone, since mcpp does not fetch indexes (update_index shells out to `xlings update`) and had no way to ask for a specific snapshot. `xlings index list --json` passes non-`xlings` `requires` keys through verbatim and `xlings index use` pins. Consuming that from mcpp needs the index to declare `requires.mcpp` first, so this commit only bumps and verifies: mcpp new/build/run all work against 2026.8.4.1, and all four platform artifacts (including the aarch64 one release.yml hardcodes) are published. --- ...2026-08-04-ci-fresh-install-two-defects.md | 368 ++++++++++++++++++ .github/actions/bootstrap-mcpp/action.yml | 2 +- .github/actions/setup-macos-llvm/action.yml | 2 +- .github/tools/install_released_mcpp.sh | 160 ++++++++ .github/workflows/bootstrap-macos.yml | 2 +- .github/workflows/ci-fresh-install.yml | 96 +++-- .github/workflows/ci-linux-e2e.yml | 2 +- .github/workflows/cross-build-test.yml | 4 +- .github/workflows/release.yml | 14 +- CHANGELOG.md | 31 ++ mcpp.toml | 2 +- src/version.cppm | 2 +- src/xlings.cppm | 2 +- 13 files changed, 639 insertions(+), 48 deletions(-) create mode 100644 .agents/docs/2026-08-04-ci-fresh-install-two-defects.md create mode 100755 .github/tools/install_released_mcpp.sh diff --git a/.agents/docs/2026-08-04-ci-fresh-install-two-defects.md b/.agents/docs/2026-08-04-ci-fresh-install-two-defects.md new file mode 100644 index 00000000..f48bbc47 --- /dev/null +++ b/.agents/docs/2026-08-04-ci-fresh-install-two-defects.md @@ -0,0 +1,368 @@ +# `ci-fresh-install` 11 个 job 全红 —— 两个独立缺陷的修复方案 + +> 状态:**已实施**(2026.8.4.1)。实施记录见 §8。 +> 范围:`.github/workflows/ci-fresh-install.yml`(+ 一处共享机制) +> 相关:`ci-aarch64-fresh-install.yml`(已用另一种方式规避了缺陷 A)、 +> `.github/tools/install_pinned_mcpp.sh`(已记录过同一族问题) + +--- + +## 0. 摘要 + +11 个 job 全红看起来像「很多问题」,实际是**两个独立缺陷**,各自都能单独把整个矩阵打红, +而且**都不是本次发布引入的** —— 每次发布后必现。 + +| | 缺陷 | 触发条件 | 症状 | +|---|---|---|---| +| **A** | 仓库根 `.xlings.json` 的 workspace pin 伏击了被测版本 | 最新发布 ≠ bootstrap pin(**每次发布后的常态**) | `xlings: version '2026.8.3.2' not found for 'mcpp'` | +| **B** | `wait-index` 守的是**另一条分发通道** | 发布后索引 artifact 还没传播开 | `package 'mcpp@2026.8.3.5' not found; searched repos: [xim]` | + +两者形状相同,也和今天修的另外两个 bug 形状相同:**验证的对象不是使用的对象**(§3)。 + +--- + +## 1. 缺陷 A:仓库的 workspace pin 伏击了被测版本 + +### 证据 + +2026-08-03 13:41 手工触发那次(索引早已就绪,排除掉 B): + +``` +✓ 1 package(s) installed ← mcpp@2026.8.3.4 装好了 +[error] xlings: version '2026.8.3.2' not found for 'mcpp' +[error] available: 2026.8.3.4 +``` + +**装的是对的,跑的是别的。** 11 个 job 全部死在同一步 —— 第一次执行 `mcpp` 的地方 +(`Install mcpp and config mirror`,distro 矩阵是 `Configure mcpp`)。 + +### 机制 + +两个 pin 撞在一起,而它们**按设计就应该不同**: + +| 位置 | 值 | 用途 | +|---|---|---| +| `MCPP_PIN`(`wait-index` 推导) | `2026.8.3.4` | **被测版本** = 最新发布 | +| `.xlings.json` → `workspace.mcpp` | `2026.8.3.2` | **自举**版本,手工维护,**故意滞后** | + +job 的步骤顺序: + +1. `- uses: actions/checkout@v4` ← **第一步**,于是仓库的 `.xlings.json` 落在工作目录里 +2. `xlings install "mcpp@${MCPP_PIN}" -y -g` ← 只装了 `2026.8.3.4`,装到全局 +3. `mcpp --version` ← xvm shim 看到 workspace pin,去解析 **`2026.8.3.2`** —— 从没装过 + +本机实测复现(同一台机器、同一个 shim): + +``` +目录里有 .xlings.json(pin 2026.8.3.2) → mcpp 2026.8.3.2 +目录里没有 → mcpp 2026.8.3.3 (全局默认) +``` + +**workspace pin 赢。** 在我本机两个版本都装着所以只是静默切换;在 CI 上只装了被测版本, +于是直接报错。 + +### 为什么「每次发布后必现」 + +workflow 自己的头注释就说了这两者不是一回事: + +> NOT to be confused with the .xlings.json workspace pin … it stays hand-maintained. + +作者知道它们不同 —— 但 job 仍然在**仓库目录里**执行 `mcpp` 命令,而在那里 workspace pin +拥有最终解释权。所以只要「最新发布 ≠ bootstrap pin」,也就是**每一次发布之后**, +这个 workflow 必红。历史记录与之吻合:`workflow_run` 触发的每一次都失败, +偶尔的绿色出现在两者恰好相等的窗口里。 + +### 为什么调整 checkout 顺序不够 + +`ci-aarch64-fresh-install.yml` 遇到过同一个坑,并用**把 checkout 放到最后**解决了, +注释写得很完整: + +> this repo's .xlings.json declares an `mcpp` WORKSPACE pin, so with the checkout present +> in $GITHUB_WORKSPACE `xlings install mcpp` installs workspace-scoped instead of globally + +**但这个办法在 `ci-fresh-install` 用不了**:它的 `Default: build mcpp` 等步骤要在仓库里跑 + +```yaml +- name: "Default: build mcpp" + run: | + mcpp clean + mcpp run +``` + +—— 自举构建本来就必须发生在仓库目录内。checkout 不能挪到它们后面。 + +--- + +## 2. 缺陷 B:`wait-index` 守的是另一条分发通道 + +### 证据 + +2026-08-03 18:11 发布后那次,`wait-index` **成功**,随后 11 个 job 全部: + +``` +[error] package 'mcpp@2026.8.3.5' not found +[error] searched repos: [xim]; run `xlings update` if the package was just published +``` + +时间线: + +| 时刻 | 事件 | +|---|---| +| 18:11 | release 完成 → `workflow_run` 触发 ci-fresh-install | +| ~18:14 | 索引 bump PR 合入 ⇒ **git 上的 `pkgs/m/mcpp.lua` 立刻带上 2026.8.3.5** | +| 18:15:05 | `Publish Index Artifact` 生成 artifact | +| — | `wait-index` 轮询 git 文件命中 ⇒ **判定「索引已就绪」,放行 11 个 job** | +| 18:16:41 | job 执行 `xlings update` + install ⇒ 拿到的索引里**没有** 2026.8.3.5 | + +### 机制 + +`wait-index` 轮询的是 **git 真源**: + +```bash +curl -fsSL "https://raw.githubusercontent.com/openxlings/xim-pkgindex/main/pkgs/m/mcpp.lua" \ + | grep -q "\"$VER\"" +``` + +而 job 里的 `xlings update` 取的是**发布出来的 artifact** +(`xlings-res/xim-index` 的 `xim-index-latest.json` → tarball)。 + +**这是两条延迟完全不同的通道。** git 文件在 PR 合入的瞬间就更新了;artifact 还要等 +`Publish Index Artifact` 打包、再经 release CDN 传播 —— 实测边缘缓存滞后可达数十分钟。 + +⇒ **守卫在测量一条没人从那里安装的通道。** 它报告「索引已就绪」时, +job 要用的那条通道可能还差得远。 + +这跟 workflow 头注释里记载的 #265 是同一类事故的再现 —— +当时是「守卫等的版本」与「job 装的版本」不一致,这次是「守卫查的通道」与 +「job 用的通道」不一致。**上次修的是值,这次要修的是通道。** + +--- + +## 3. 共同的形状(今天第四次) + +| 场景 | 检查的东西 | 使用的东西 | +|---|---|---| +| #344 cache | 条目**自述的**文件表 | 消费方**自己算的**地址 | +| #345 e2e 初稿 | 「构建成功」 | 真正要保的是「零 compile 边」 | +| E0006 e2e 初稿 | 「输出里出现 E0006」 | 真正坏的是**最后一条**错误 | +| **本文 A** | 装了哪个版本 | shim 实际**解析**到哪个版本 | +| **本文 B** | **git** 通道有没有该版本 | job 用的是 **artifact** 通道 | + +一句话判据: + +> **验证的对象,必须就是使用的对象。** + +每一次的修法都一样:不是把检查写得更严,而是**把检查挪到被使用的那个东西上**。 + +--- + +## 4. 方案 + +### F1(缺陷 A)· 在这个 workflow 里,让被测版本成为唯一的答案 + +`ci-fresh-install` 的职责是「验证**已发布的** mcpp」。仓库的 bootstrap pin 是给 +**自举 CI**(ci-linux 等)用的,在这里没有任何发言权 —— 它只会把被测对象悄悄换掉。 + +checkout 之后立刻中和它: + +```yaml + - uses: actions/checkout@v4 + + # This workflow tests the RELEASED mcpp (MCPP_PIN). The repo's + # .xlings.json carries a WORKSPACE pin for the self-host CI's bootstrap — + # a different version, deliberately lagging — and inside this checkout it + # wins over anything installed globally: `mcpp` would resolve to the + # bootstrap version, or fail outright when (as here) only the version + # under test is installed. Neither is what this workflow is for. + # + # Removing the file is the fix rather than moving the checkout later: + # the `build mcpp` steps below run `mcpp clean && mcpp run` INSIDE the + # repo, so a checkout has to be present while mcpp is being invoked. + # (ci-aarch64-fresh-install.yml can and does order it away, because its + # fresh-install steps never need the repo.) + - name: Neutralize the repo's bootstrap pin (see above) + shell: bash + run: rm -f .xlings.json +``` + +同时,安装步骤补上**激活**与**断言**: + +```yaml + - name: Install mcpp and config mirror + shell: bash + run: | + xlings update + xlings install "mcpp@${MCPP_PIN}" -y -g -u # -u activates; install alone does not + + # ASSERT the shim resolves the version under test. `install` reports + # success for "the bytes are on disk", which is not the same claim as + # "`mcpp` now runs it" — and every later step in this job runs the + # bare shim. Without this, any future ambient override (a workspace + # pin, a stale activation, a PATH surprise) silently retargets the + # whole matrix at a DIFFERENT binary and still reports green. + got="$(mcpp --version | grep -oE '[0-9]+(\.[0-9]+)+' | head -1)" + [ "$got" = "$MCPP_PIN" ] || { + echo "::error::mcpp resolves to '$got' but the version under test is '$MCPP_PIN'" + echo "hint: something is redirecting the shim — a .xlings.json workspace pin in" + echo " \$GITHUB_WORKSPACE is the usual culprit; xvm activation is the other." + exit 1 + } + mcpp self config --mirror GLOBAL +``` + +`-u` 不是可选项:`install_pinned_mcpp.sh` 的头注释已经写过为什么 —— +*"`-u` activates the version just installed … that is the piece a plain `install` leaves alone, +and the reason CI could install one version and then run another."* +这个 workflow 是唯一没享受到那份修复的地方,因为它没走那个脚本。 + +**断言是这条修复里唯一能防住「下一个」的部分。** 它把任何未来的静默替换 +(不只是 workspace pin)变成一条点名的失败。 + +落点:5 个 job(3 个 bash + 2 个 PowerShell)。两个 Windows job 的安装步现在是 +`shell: pwsh`,改成 `shell: bash` 即可复用同一段(同 job 内已有 `shell: bash` 的步骤, +Git Bash 在 windows runner 上可用)。 + +### F2(缺陷 B)· 守卫必须查 job 真正消费的那条通道 + +把 `wait-index` 的轮询从 git 换成 **artifact**: + +```yaml + - name: Wait for the published index ARTIFACT to carry the released mcpp + if: ${{ github.event_name == 'workflow_run' }} + run: | + # NOT the git file. `xlings update` installs from the published + # artifact (xlings-res/xim-index → xim-index-latest.json → tarball), + # and that channel lags git by however long Publish Index Artifact + # plus release-CDN propagation takes — tens of minutes, measured. + # Polling git says "ready" while the channel the jobs actually use is + # still serving the previous index. Check what the consumer consumes. + for i in $(seq 1 40); do + ptr=$(curl -fsSL "https://github.com/xlings-res/xim-index/releases/download/latest/xim-index-latest.json" || true) + name=$(printf '%s' "$ptr" | python3 -c "import json,sys; print(json.load(sys.stdin)['artifact']['name'])" 2>/dev/null || true) + if [ -n "$name" ] && curl -fsSL "https://github.com/xlings-res/xim-index/releases/download/latest/$name" \ + | tar -xzO --wildcards '*/pkgs/m/mcpp.lua' 2>/dev/null | grep -q "\"$VER\""; then + echo "published index artifact tracks $VER (after $((i*30))s)"; exit 0 + fi + sleep 30 + done + echo "::error::the published index artifact never tracked $VER within 20min" + exit 1 +``` + +**更稳妥的补充(建议一起做)**:在 job 内给 `xlings update` 加一个有界重试, +直到本地索引真的能解析出被测版本再往下走。理由是 CDN 是**按边缘节点**传播的 —— +守卫所在的 runner 看到了,不代表另一个 runner 也看到了。 +守卫把窗口收窄到「几乎总是就绪」,job 内的重试兜住剩下的边缘差异。 + +> 这两层的分工要说清楚:F2 的守卫**省掉 11 个 job 各等 20 分钟**, +> job 内重试**保证正确性**。只做守卫会留下按节点抖动的偶发红; +> 只做重试则每次发布都让整个矩阵陪跑等待。 + +### F3(通用化)· 让这条知识不再靠人记住 + +缺陷 A 的知识**存在于仓库里** —— 完整地写在 `ci-aarch64-fresh-install.yml` 的注释里, +而 `ci-fresh-install.yml` 从来没学到。这是典型的「同一决策两处推导,其中一处不知情」。 + +两条低成本措施: + +1. **F1 里那条断言就是主要防线** —— 它不依赖任何人读注释, + 且能捕获**未知的**替换机制,而不只是 workspace pin 这一种。 +2. **加一条机器判据**到 `.github/tools/check_version_pins.sh`(它已经在扫 `.github/`, + 并且已经有一条同类守卫 —— 拒绝字面量 `MCPP_PIN:`): + + > 任何 workflow 里出现 `xlings install ... mcpp@`(按显式版本安装), + > 其所在 job 必须要么不 checkout 本仓库,要么中和 `.xlings.json`。 + + 精确的可执行近似:**禁止 workflow 直接手写 `xlings install "mcpp@…"`**, + 统一走一个共享脚本(`.github/tools/install_released_mcpp.sh`), + 脚本里封装「中和 pin + `-u` 激活 + 断言」。 + 守卫只需 grep:`.github/workflows/` 下不得出现 `xlings install` 与 `mcpp@` 同现。 + `ci-aarch64` 的裸 `xlings install mcpp -y`(无版本,故意如此)不会被误伤。 + +--- + +## 5. 不采纳 + +| 方案 | 理由 | +|---|---| +| 把 `.xlings.json` bump 到最新发布 | **方向反了。** bootstrap pin 故意滞后(它必须是一个「已发布且能构建当前树」的版本),把它跟着发布走正是 `release-bootstrap-pin-two-groups` 记载过的、会让全部 CI 去装不存在版本的经典错误。 | +| 把 checkout 挪到所有安装步骤之后 | 对 `ci-aarch64` 有效,对本 workflow **无效**:`build mcpp` 步骤必须在仓库内执行(§1)。 | +| 只删 `.xlings.json`,不加 `-u`/断言 | 把一个**响亮的失败**换成一个**静默测错版本**的可能 —— 而这个 workflow 的头注释说,它存在的理由之一正是防止「静默测了旧二进制还报绿」。 | +| 只修 A 不修 B | 手工/cron 触发会绿,**发布后触发仍然红** —— 而那正是这个 workflow 最该起作用的时刻。 | +| 让 `wait-index` 多等固定时间 | 猜一个常数。CDN 传播是按边缘节点的、无上界的;固定等待要么不够要么浪费。查真实通道 + job 内重试才是判据。 | + +--- + +## 6. 验证 + +- **先红后绿**:两个缺陷都能在**不发版**的情况下复现 —— + - A:手工 `workflow_dispatch` 触发(索引早已就绪),当前必红于 `version '' not found`; + - B:需要一次发布后触发;退而求其次,可在 `wait-index` 里临时把轮询目标改回 git + 并断言 artifact 尚未跟上,以证明两条通道确实会分叉。 +- 修复后:`workflow_dispatch` 一次(覆盖 A),下一次真实发布覆盖 B。 +- **断言自证**:临时把 `MCPP_PIN` 改成一个已装但非目标的版本,F1 的断言必须变红。 +- `bash .github/tools/check_version_pins.sh` 仍须通过(F3 若加了新守卫,同样要先看到它红)。 + +--- + +## 7. 实施顺序 + +| # | 项 | 依赖 | 效果 | +|---|---|---|---| +| 1 | **F1** 中和 pin + `-u` + 断言(5 个 job) | 无 | 手工/cron 触发恢复绿;发布后触发不再死在第一步 | +| 2 | **F2** 守卫改查 artifact + job 内有界重试 | 无 | 发布后触发也能绿 | +| 3 | **F3** 共享脚本 + `check_version_pins.sh` 守卫 | F1 | 这条知识不再靠人记住 | + +F1 与 F2 可以在同一个 PR 里;F3 建议同 PR,因为它的价值恰恰在于**趁现在**把知识固化下来 +—— 这个坑已经被独立发现过两次了。 + + +--- + +## 8. 实施记录(2026.8.4.1) + +落点:`.github/tools/install_released_mcpp.sh`(新)、`ci-fresh-install.yml`(5 处调用点 + `wait-index`)、 +`src/xlings.cppm`(xlings pin)、`.github/**`(16 个 pin 点)。 + +**F1+F3 合并落地**:没有在 5 个 job 里各写一段,而是收敛进一个共享脚本 +(`install_pinned_mcpp.sh` 的兄弟)。理由就是 §3 那条 —— 这个坑已经被**独立发现过两次**, +知识散在注释里必然有第三次。两个 Windows job 的安装步从 `shell: pwsh` 改为 `shell: bash`, +以复用同一份实现(同 job 内早已有 `shell: bash` 的步骤)。 + +**F2 的 `wait-index`**:改查 artifact 通道后**用真实指针实测过**提取逻辑 —— +`xim-index-e8ad461.tar.gz`,解包后 `pkgs/m/mcpp.lua` 确实含 `"2026.8.3.5"`。 + +### 实施中被证伪的两处 + +**① 断言的对象一开始就写错了。** 初版是 + +```bash +MCPP="$XL_HOME/.xlings/subos/default/bin/mcpp" # 猜一个安装路径 +``` + +而后续步骤敲的是裸 `mcpp`,走 **PATH**。**探测一个没人执行的路径,等于验证了一个副本** +—— 正是本文 §3 那张表在说的事,我自己又犯了一次。改为 `command -v mcpp` 优先, +已知位置只作 PATH 尚未导出时的兜底。 + +**② `-u` 不够,补 `xlings use`。** 隔离环境实测:payload 里的二进制自报 +`mcpp 2026.8.3.5`(正确),而 shim 报 `2026.7.29.1`。 +`xlings install -u` 是安装期激活,`xlings use` 是显式切换 —— +它们在 xlings 里是两条不同代码路径,而这里承重的只有其中一条。 +现在两个都做,**断言仍然是最终判据**(补切换是把激活做完,不是给失败开脱)。 + +> 这两处都是断言自己抓出来的。**它在本机就抓到了两个真问题,而不是等到 CI 红** —— +> 这正是加它的理由:把「静默测了错的二进制还报绿」变成一条点名的失败。 + +### xlings 2026.8.4.1 的验证 + +- `xlings index list --json` 已可用,`requires` 字段按设计透传(当前 xim 索引尚未声明 `requires`, + history 只有 1 条 —— 机制是新的,历史会累积)。 +- 实测 mcpp 在该版本下 `new` / `build` / `run` 正常(把 2026.8.4.1 塞成 mcpp 的 + vendored xlings 后跑通)。 +- 四平台产物齐全(含 aarch64,release.yml 的三处字面量依赖它)。 + +**mcpp 侧消费索引路由是后续工作**:需要索引侧先在 `index-compat.json` 里声明 +`requires.mcpp`,mcpp 再读 `xlings index list --json` 按自己的 `min_mcpp` 过滤并 +`xlings index use`。届时 +`.agents/docs/2026-08-03-index-availability-must-not-decide-mcpp-availability.md` 的 +M2(本地快照历史)与之互补:M2 覆盖本机见过的快照,路由覆盖全部已发布快照。 diff --git a/.github/actions/bootstrap-mcpp/action.yml b/.github/actions/bootstrap-mcpp/action.yml index 192746a4..8886c232 100644 --- a/.github/actions/bootstrap-mcpp/action.yml +++ b/.github/actions/bootstrap-mcpp/action.yml @@ -25,7 +25,7 @@ inputs: # `package.name`, so one of the two was simply unreachable — and which one # depended on the machine, which is why CI failed on `compat:lua` on # Windows and `mcpplibs.capi:lua` on Linux. Never pin below that. - default: '2026.7.28.4' + default: '2026.8.4.1' cache-target: description: also restore/save target/ (build artifacts + BMIs) required: false diff --git a/.github/actions/setup-macos-llvm/action.yml b/.github/actions/setup-macos-llvm/action.yml index d6a33c8e..80a5d12b 100644 --- a/.github/actions/setup-macos-llvm/action.yml +++ b/.github/actions/setup-macos-llvm/action.yml @@ -15,7 +15,7 @@ inputs: # Floor imposed by the index, not a routine bump — see # .github/actions/bootstrap-mcpp/action.yml for why 0.4.69 is required # (two packages named `lua` in one repo need openxlings/xlings#381). - default: '2026.7.28.4' + default: '2026.8.4.1' runs: using: composite diff --git a/.github/tools/install_released_mcpp.sh b/.github/tools/install_released_mcpp.sh new file mode 100755 index 00000000..b5131532 --- /dev/null +++ b/.github/tools/install_released_mcpp.sh @@ -0,0 +1,160 @@ +#!/usr/bin/env bash +# install_released_mcpp.sh — make a specific PUBLISHED mcpp be the binary that +# the bare `mcpp` shim actually runs, and prove it. +# +# Sibling of install_pinned_mcpp.sh. That one installs the BOOTSTRAP mcpp the +# repo's .xlings.json pins (for self-host builds); this one installs the +# VERSION UNDER TEST for the fresh-install matrix. Same discipline, opposite +# source of truth — and the reason they are two scripts rather than one flag is +# that confusing the two is precisely what broke ci-fresh-install. +# +# THREE THINGS THIS DOES THAT THE INLINE VERSION DID NOT +# +# 1. NEUTRALISE THE REPO'S WORKSPACE PIN. +# +# This repo's .xlings.json declares `workspace.mcpp` — the bootstrap version, +# hand-maintained and DELIBERATELY lagging the newest release. It is scoped +# to the working directory, and inside a checkout it beats anything installed +# globally. So the fresh-install jobs, which check out the repo first and +# then run `mcpp` from it, were resolving the bootstrap version instead of +# the version under test — and failing outright, because only the latter was +# installed: +# +# ✓ 1 package(s) installed +# [error] xlings: version '2026.8.3.2' not found for 'mcpp' +# [error] available: 2026.8.3.4 +# +# Every job in the matrix died there, on every run after every release, since +# "newest release != bootstrap pin" is the normal state. +# +# ci-aarch64-fresh-install.yml avoids this by ordering the checkout LAST. +# That does not work here: the `build mcpp` steps run `mcpp clean && mcpp run` +# INSIDE the repo, so a checkout must be present while mcpp is invoked. The +# pin has to go instead — this workflow tests the released binary, and the +# bootstrap pin has no standing in that question. +# +# 2. ACTIVATE, NOT JUST INSTALL (`-u`). +# +# `xlings install` reports success for "the bytes are on disk", which is not +# the same claim as "`mcpp` now runs it". install_pinned_mcpp.sh already +# documents this ("the piece a plain install leaves alone, and the reason CI +# could install one version and then run another"); this path never got it. +# +# 3. WAIT FOR THE INDEX THE JOB ACTUALLY USES. +# +# The workflow's wait-index job polls the index's GIT source +# (raw.githubusercontent.com/openxlings/xim-pkgindex). Jobs install from the +# PUBLISHED ARTIFACT (xlings-res/xim-index → pointer → tarball), which lags +# git by however long Publish Index Artifact plus release-CDN propagation +# takes. Measured on the 2026.8.3.5 release: wait-index reported ready and +# every job then failed with +# +# [error] package 'mcpp@2026.8.3.5' not found +# +# A guard that measures a channel nobody installs from is not a guard. The +# retry below closes it from the consumer side, which also covers per-edge +# CDN skew that no central check can see: the runner that polled is not the +# runner that installs. +# +# Usage: bash .github/tools/install_released_mcpp.sh [repo_dir] +# stdout: the resolved binary path; diagnostics go to stderr. +set -euo pipefail + +VER="${1:?usage: install_released_mcpp.sh [repo_dir]}" +REPO_DIR="${2:-$(pwd)}" + +case "$(uname -s)" in + MINGW*|MSYS*|CYGWIN*) XL_HOME="${USERPROFILE:-$HOME}"; EXE=".exe" ;; + *) XL_HOME="$HOME"; EXE="" ;; +esac + +# Address xlings by path, not through PATH: this runs as a child bash, and on +# Windows MSYS re-derives PATH from the Windows environment on startup, dropping +# the mixed-separator entry the caller exported. Same reasoning (and same +# location) as install_pinned_mcpp.sh. +XL="$XL_HOME/.xlings/subos/default/bin/xlings${EXE}" +[ -x "$XL" ] || XL="$XL_HOME/.xlings/subos/current/bin/xlings${EXE}" +[ -x "$XL" ] || XL=$(command -v "xlings${EXE}" 2>/dev/null || true) +[ -n "$XL" ] && [ -x "$XL" ] || { + echo "FAIL: no xlings under $XL_HOME/.xlings nor on PATH" >&2; exit 1; } + +# ── 1. the repo's workspace pin must not decide what we are testing ────────── +if [ -f "$REPO_DIR/.xlings.json" ]; then + echo "note: removing $REPO_DIR/.xlings.json for this job — it pins the BOOTSTRAP" >&2 + echo " mcpp, which would override the version under test inside this checkout." >&2 + rm -f "$REPO_DIR/.xlings.json" +fi + +# ── 2. install, retrying while the index has not caught up ─────────────────── +# Bounded. A miss that is NOT index lag (a typo'd version, a withdrawn release) +# must not cost ten minutes, so the loop reports every attempt and the message +# says which of the two it is on the last one. +attempts="${MCPP_INSTALL_ATTEMPTS:-20}" +delay="${MCPP_INSTALL_RETRY_SECONDS:-30}" +installed=0 +for i in $(seq 1 "$attempts"); do + "$XL" update >/dev/null 2>&1 || true + if "$XL" install "mcpp@${VER}" -y -g -u >&2; then + installed=1 + break + fi + if [ "$i" -lt "$attempts" ]; then + echo "note: mcpp@${VER} not installable yet (attempt $i/$attempts) — the published" >&2 + echo " index artifact may not have propagated; retrying in ${delay}s" >&2 + sleep "$delay" + fi +done +[ "$installed" = 1 ] || { + echo "FAIL: could not install mcpp@${VER} after $attempts attempts." >&2 + echo " Either the index never published it (check the xim-pkgindex bump PR)" >&2 + echo " or the version does not exist." >&2 + exit 1; } + +# ── 3. prove the shim resolves it ──────────────────────────────────────────── +# `install` succeeding means the bytes landed, not that `mcpp` runs them, and +# every later step in these jobs invokes the bare shim. Asserting here is what +# turns any future ambient redirection — a workspace pin, a stale xvm +# activation, a PATH surprise — into a named failure instead of a matrix that +# silently tests the wrong binary and reports green. +# Resolve it the way the JOB will: through PATH. That is the entire point of +# the assertion — the steps after this one type `mcpp`, so `mcpp` is what has +# to be checked. Probing a guessed install path instead would verify a binary +# nobody runs, and would happily pass while PATH pointed somewhere else. +# (The known locations are only a fallback for a PATH that is not exported +# yet; `subos/current` and `subos/default` are both in use across the jobs.) +MCPP=$(command -v "mcpp${EXE}" 2>/dev/null || true) +for cand in "$XL_HOME/.xlings/subos/current/bin/mcpp${EXE}" \ + "$XL_HOME/.xlings/subos/default/bin/mcpp${EXE}"; do + [ -n "$MCPP" ] && break + [ -x "$cand" ] && MCPP="$cand" +done +[ -n "$MCPP" ] && [ -x "$MCPP" ] || { + echo "FAIL: mcpp is not on PATH after a successful install" >&2; exit 1; } + +probe() { "$MCPP" --version 2>/dev/null | head -1 \ + | grep -oE '[0-9]+(\.[0-9]+)+' | head -1 || true; } + +GOT=$(probe) +if [ "$GOT" != "$VER" ]; then + # `-u` is install-time activation; `xlings use` is the explicit switch, and + # is what xlings itself suggests when an install leaves the shim behind + # ("installed, but 'mcpp' still resolves to X — `xlings use mcpp X` to + # switch"). Doing both is belt and braces, not redundancy: they are two + # different code paths in xlings and only one of them is load-bearing here. + # The assertion below stays final either way — this completes the + # activation, it does not excuse a failure to activate. + echo "note: shim reported '${GOT:-?}' after install; switching explicitly" >&2 + "$XL" use mcpp "$VER" >&2 2>/dev/null || true + GOT=$(probe) +fi + +[ "$GOT" = "$VER" ] || { + echo "FAIL: mcpp resolves to '${GOT:-?}' but the version under test is '$VER'" >&2 + echo "hint: something is redirecting the shim. A .xlings.json workspace pin in" >&2 + echo " the working directory is the usual cause (this script removes the" >&2 + echo " repo's own, but a parent directory can carry one too); a stale xvm" >&2 + echo " activation is the other." >&2 + exit 1; } + +echo "version under test: $MCPP ($GOT)" >&2 +echo "$MCPP" diff --git a/.github/workflows/bootstrap-macos.yml b/.github/workflows/bootstrap-macos.yml index 9a5495c3..b6efca8f 100644 --- a/.github/workflows/bootstrap-macos.yml +++ b/.github/workflows/bootstrap-macos.yml @@ -17,7 +17,7 @@ jobs: # Dormant (workflow_dispatch only), but kept in step with the rest — # check_version_pins.sh holds it there. Floor: 0.4.69, below which the # index cannot resolve two packages that share a short name. - XLINGS_VERSION: '2026.7.28.4' + XLINGS_VERSION: '2026.8.4.1' steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/ci-fresh-install.yml b/.github/workflows/ci-fresh-install.yml index de6be6f1..860a2928 100644 --- a/.github/workflows/ci-fresh-install.yml +++ b/.github/workflows/ci-fresh-install.yml @@ -91,19 +91,42 @@ jobs: esac echo "version=$VER" >> "$GITHUB_OUTPUT" echo "version under test: $VER" - - name: Wait for xim-pkgindex to track the released mcpp + - name: Wait for the PUBLISHED index artifact to track the released mcpp if: ${{ github.event_name == 'workflow_run' }} env: VER: ${{ steps.resolve.outputs.version }} run: | - echo "released: $VER — waiting for index..." - for i in $(seq 1 30); do - if curl -fsSL "https://raw.githubusercontent.com/openxlings/xim-pkgindex/main/pkgs/m/mcpp.lua" | grep -q "\"$VER\""; then - echo "index tracks $VER (after $((i*30))s)"; exit 0 + # Poll the ARTIFACT, not the git file. + # + # This used to curl raw.githubusercontent.com/openxlings/xim-pkgindex + # — the index's git source of truth, which updates the instant the + # bump PR merges. But the jobs install from the PUBLISHED ARTIFACT + # (xlings-res/xim-index → pointer → tarball), and that channel lags + # git by however long `Publish Index Artifact` plus release-CDN + # propagation takes. Measured on the 2026.8.3.5 release: this guard + # reported ready, and all 11 jobs then failed with + # [error] package 'mcpp@2026.8.3.5' not found + # A guard that measures a channel nobody installs from is not a guard. + # + # This narrows the window; it cannot close it, because the CDN + # propagates per edge and the runner that polls is not the runner + # that installs. install_released_mcpp.sh retries from the consumer + # side for exactly that residue — this step exists so the retry is + # rarely needed, not so it can be removed. + echo "released: $VER — waiting for the published index artifact..." + for i in $(seq 1 40); do + ptr=$(curl -fsSL "https://github.com/xlings-res/xim-index/releases/download/latest/xim-index-latest.json" 2>/dev/null || true) + # One line on purpose: an indented heredoc inside a YAML block + # scalar is a trap — unindented content silently ends the block. + name=$(printf '%s' "$ptr" | python3 -c 'import json,sys; d=json.load(sys.stdin); n=d.get("indexes",{}).get("xim",d); print(n.get("artifact",{}).get("name",""))' 2>/dev/null || true) + if [ -n "$name" ] && curl -fsSL \ + "https://github.com/xlings-res/xim-index/releases/download/latest/$name" \ + | tar -xzO --wildcards '*pkgs/m/mcpp.lua' 2>/dev/null | grep -q "\"$VER\""; then + echo "published index artifact ($name) tracks $VER (after $((i*30))s)"; exit 0 fi sleep 30 done - echo "::error::index never tracked $VER within 15min — merge the bump PR (openxlings/xim-pkgindex) and re-run" + echo "::error::the published index artifact never tracked $VER within 20min — check that the xim-pkgindex bump PR merged AND that Publish Index Artifact ran" exit 1 - name: No wait needed (manual/cron trigger) if: ${{ github.event_name != 'workflow_run' }} @@ -129,16 +152,17 @@ jobs: env: XLINGS_NON_INTERACTIVE: '1' run: | - curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.7.28.4 + curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.8.4.1 echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" - name: Install mcpp and config mirror + shell: bash run: | - # The release tarball bundles a pkgindex snapshot frozen at - # build time; refresh it so the pinned mcpp resolves. - xlings update - xlings install "mcpp@${MCPP_PIN}" -y -g # install to global - mcpp --version + # ONE implementation for "make the released mcpp@X be what `mcpp` + # runs, and prove it" — see .github/tools/install_released_mcpp.sh + # for the three defects the inline version had (workspace pin, no + # activation, and waiting on the wrong index channel). + bash .github/tools/install_released_mcpp.sh "${MCPP_PIN}" "$(pwd)" mcpp self config --mirror GLOBAL echo "mcpp debug info:" @@ -268,7 +292,7 @@ jobs: - name: Install xlings + mcpp run: | - curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.7.28.4 + curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.8.4.1 # Deliberately NOT writing to $GITHUB_PATH here. On container # images that declare no PATH in their config (opensuse/ # tumbleweed), appending a single dir to GITHUB_PATH makes the @@ -278,11 +302,14 @@ jobs: # exports PATH itself, so the append is redundant anyway. - name: Configure mcpp + shell: bash run: | export PATH="$HOME/.xlings/subos/current/bin:$PATH" - xlings update - xlings install "mcpp@${MCPP_PIN}" -y -g - mcpp --version + # ONE implementation for "make the released mcpp@X be what `mcpp` + # runs, and prove it" — see .github/tools/install_released_mcpp.sh + # for the three defects the inline version had (workspace pin, no + # activation, and waiting on the wrong index channel). + bash .github/tools/install_released_mcpp.sh "${MCPP_PIN}" "$(pwd)" mcpp self config --mirror GLOBAL - name: "Regression: new → run (loader env must not crash /bin/sh)" @@ -336,15 +363,17 @@ jobs: # (older ones carry minos=15 and refuse to start). # v0.4.51+: in-process sha256 — this image has no sha256sum # binary, so pinned fetches failed before it. - curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.7.28.4 + curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.8.4.1 echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" - name: Install mcpp and config mirror + shell: bash run: | - # Refresh the bundled pkgindex snapshot so the pinned mcpp resolves. - xlings update - xlings install "mcpp@${MCPP_PIN}" -y -g # install to global - mcpp --version + # ONE implementation for "make the released mcpp@X be what `mcpp` + # runs, and prove it" — see .github/tools/install_released_mcpp.sh + # for the three defects the inline version had (workspace pin, no + # activation, and waiting on the wrong index channel). + bash .github/tools/install_released_mcpp.sh "${MCPP_PIN}" "$(pwd)" mcpp self config --mirror GLOBAL echo "mcpp debug info:" @@ -413,16 +442,17 @@ jobs: $xlingsbin | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - name: Install mcpp and config mirror - shell: pwsh + shell: bash run: | - # Refresh the bundled pkgindex snapshot so the pinned mcpp resolves. - xlings update - xlings install "mcpp@$env:MCPP_PIN" -y -g --verbose - - cat "$env:USERPROFILE\.xlings\.xlings.json" - mcpp --version + # ONE implementation for "make the released mcpp@X be what `mcpp` + # runs, and prove it" — see .github/tools/install_released_mcpp.sh + # for the three defects the inline version had (workspace pin, no + # activation, and waiting on the wrong index channel). + bash .github/tools/install_released_mcpp.sh "${MCPP_PIN}" "$(pwd)" mcpp self config --mirror GLOBAL + cat "$USERPROFILE/.xlings/.xlings.json" || true + - name: "LLVM: mcpp new → run" shell: pwsh run: | @@ -524,11 +554,13 @@ jobs: $xlingsbin | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - name: Install mcpp and config mirror - shell: pwsh + shell: bash run: | - xlings update - xlings install "mcpp@$env:MCPP_PIN" -y -g --verbose - mcpp --version + # ONE implementation for "make the released mcpp@X be what `mcpp` + # runs, and prove it" — see .github/tools/install_released_mcpp.sh + # for the three defects the inline version had (workspace pin, no + # activation, and waiting on the wrong index channel). + bash .github/tools/install_released_mcpp.sh "${MCPP_PIN}" "$(pwd)" mcpp self config --mirror GLOBAL # The self-check, the fallback, persistence, a self-contained exe, and diff --git a/.github/workflows/ci-linux-e2e.yml b/.github/workflows/ci-linux-e2e.yml index 86e9f1d8..6ebeea9a 100644 --- a/.github/workflows/ci-linux-e2e.yml +++ b/.github/workflows/ci-linux-e2e.yml @@ -123,7 +123,7 @@ jobs: - name: Bootstrap xlings + released mcpp run: | - curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.7.28.4 + curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.8.4.1 export PATH="$HOME/.xlings/subos/current/bin:$PATH" xlings update xlings install mcpp -y -g diff --git a/.github/workflows/cross-build-test.yml b/.github/workflows/cross-build-test.yml index 376e7f6c..17021772 100644 --- a/.github/workflows/cross-build-test.yml +++ b/.github/workflows/cross-build-test.yml @@ -118,7 +118,7 @@ jobs: # release assets were uploaded in a broken state (records present, # blobs missing → 404 on GET); re-uploaded clean. The stale-INDEX # half is handled by the marker-clear below. - XLINGS_VERSION: '2026.7.28.4' + XLINGS_VERSION: '2026.8.4.1' run: | tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz" curl -fsSL -o "/tmp/${tarball}" \ @@ -255,7 +255,7 @@ jobs: - name: Bootstrap mcpp via xlings env: XLINGS_NON_INTERACTIVE: '1' - XLINGS_VERSION: '2026.7.28.4' + XLINGS_VERSION: '2026.8.4.1' run: | tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz" curl -fsSL -o "/tmp/${tarball}" \ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0328f868..b59763b5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -96,7 +96,7 @@ jobs: # Pin xlings to a known-good version. The upstream install # script always grabs `latest` (no version override), so we # download + self-install manually to avoid broken releases. - XLINGS_VERSION: '2026.7.28.4' + XLINGS_VERSION: '2026.8.4.1' run: | if [ ! -x "$HOME/.xlings/subos/default/bin/xlings" ]; then tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz" @@ -288,7 +288,7 @@ jobs: - name: Bootstrap mcpp via xlings env: XLINGS_NON_INTERACTIVE: '1' - XLINGS_VERSION: '2026.7.28.4' + XLINGS_VERSION: '2026.8.4.1' run: | tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz" curl -fsSL -o "/tmp/${tarball}" \ @@ -358,11 +358,11 @@ jobs: # below are pinned to the same version as XLINGS_VERSION; they are # NOT interpolated from it, so check_version_pins.sh scans for them # explicitly (they were absent from the old lock-step comment). - XLA="xlings-2026.7.28.4-linux-aarch64.tar.gz" + XLA="xlings-2026.8.4.1-linux-aarch64.tar.gz" if curl -fsSL -o "/tmp/$XLA" \ - "https://github.com/openxlings/xlings/releases/download/v2026.7.28.4/$XLA"; then + "https://github.com/openxlings/xlings/releases/download/v2026.8.4.1/$XLA"; then tar -xzf "/tmp/$XLA" -C /tmp - XLBIN=$(find /tmp/xlings-2026.7.28.4-linux-aarch64 -path '*/bin/xlings' -type f | head -1) + XLBIN=$(find /tmp/xlings-2026.8.4.1-linux-aarch64 -path '*/bin/xlings' -type f | head -1) if [ -n "$XLBIN" ]; then mkdir -p "$STAGING/$WRAPPER/registry/bin" cp "$XLBIN" "$STAGING/$WRAPPER/registry/bin/xlings" @@ -440,7 +440,7 @@ jobs: - name: Bootstrap mcpp via xlings env: XLINGS_NON_INTERACTIVE: '1' - XLINGS_VERSION: '2026.7.28.4' + XLINGS_VERSION: '2026.8.4.1' run: | if [ ! -x "$HOME/.xlings/subos/default/bin/xlings" ]; then WORK=$(mktemp -d) @@ -622,7 +622,7 @@ jobs: shell: bash env: XLINGS_NON_INTERACTIVE: '1' - XLINGS_VERSION: '2026.7.28.4' + XLINGS_VERSION: '2026.8.4.1' run: | # Captured before the `cd` below, in POSIX form: this step never # returns to the workspace, and GITHUB_WORKSPACE is a backslash diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a7d4bd5..06689781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,37 @@ > 本文件追踪 `mcpp-community/mcpp` 公开仓的版本演进。 > 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。 +## [2026.8.4.1] — 2026-08-04 + +### 修复 + +- **`ci-fresh-install` 11 个 job 全红 —— 两个独立缺陷。** 都不是某次发布引入的,而是**每次发布之后必现**;两个各自都能单独把整个矩阵打红。分析见 `.agents/docs/2026-08-04-ci-fresh-install-two-defects.md`。 + + **A. 仓库的 workspace pin 伏击了被测版本。** job 第一步就 checkout,于是仓库的 `.xlings.json`(声明**自举** mcpp,手工维护且**故意滞后**)落在工作目录里。它是目录作用域的,在 checkout 内部**压过全局安装**。于是「装的是 `MCPP_PIN`,跑的是自举版本」——而后者根本没装: + + ``` + ✓ 1 package(s) installed + [error] xlings: version '2026.8.3.2' not found for 'mcpp' + [error] available: 2026.8.3.4 + ``` + + `ci-aarch64-fresh-install.yml` 早就遇到过并靠「checkout 放最后」规避,注释写得很完整 —— 但那招在这里用不了:`build mcpp` 步骤要在仓库里跑 `mcpp clean && mcpp run`,checkout 必须在场。所以改为中和那个 pin:这个 workflow 验证的是**已发布**的 mcpp,自举 pin 在这个问题上没有发言权。 + + **B. `wait-index` 守的是另一条分发通道。** 它轮询索引的 **git 真源**(`raw.githubusercontent.com/openxlings/xim-pkgindex`),而 job 从**发布出来的 artifact**(`xlings-res/xim-index` → 指针 → tarball)安装。两条通道延迟完全不同:git 在 PR 合入瞬间更新,artifact 还要打包 + 过 CDN。2026.8.3.5 那次实测:守卫报「就绪」,11 个 job 随后全部 `package 'mcpp@2026.8.3.5' not found`。**测量一条没人从那里安装的通道,不叫守卫。** 现在改查 artifact 通道。 + + 两处修复连同「装了 ≠ 跑的是它」的激活(`-u` + `xlings use`)与**断言**,收敛进一个共享脚本 `.github/tools/install_released_mcpp.sh`(5 处调用点)。断言是唯一能防住**下一个**的部分:它对着 **PATH 解析出来的** `mcpp` 求值 —— 也就是后续步骤真正会执行的那个 —— 于是任何未来的静默重定向(workspace pin、陈旧激活、PATH 意外)都会变成一条点名的失败,而不是一个悄悄测了错二进制却报绿的矩阵。 + + > CDN 是**按边缘节点**传播的:轮询的 runner 不是安装的 runner。所以守卫收窄窗口、脚本内的有界重试兜住残差 —— 两层分工,缺一个要么留偶发红、要么让整个矩阵陪跑等待。 + +### 改进 + +- **内带 xlings 升级到 `2026.8.4.1`**(自 `2026.7.28.4`)。该版本落地了索引快照的**版本契约与自动路由**(openxlings/xlings#476,由本仓库提出):索引声明它需要的客户端版本,客户端自动路由到自己能用的**最新**快照,版本错配从**硬失败**变成**路由决策**。 + + 对 mcpp 的直接意义是它**补上了 mcpp 自己做不到的那一半** —— mcpp 不下载索引(`update_index` 就是 shell out 给 `xlings update`),此前无法要求「给我索引版本 X」。新增的 `xlings index list --json` 会把 `requires` 里非 `xlings` 的键**原样透传**给对应消费者,`xlings index use` 提供钉选。mcpp 侧消费这套接口(按 `min_mcpp` 自动路由)是后续工作,需要索引侧先声明 `requires.mcpp`;本次只做版本升级与验证。 + + 16 个 pin 点由 `check_version_pins.sh` 机器校验并全部更新;已实测 mcpp 在 `2026.8.4.1` 下 `new`/`build`/`run` 正常。 + + ## [2026.8.3.5] — 2026-08-03 ### 修复 diff --git a/mcpp.toml b/mcpp.toml index b557aa8e..be8f572d 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -1,6 +1,6 @@ [package] name = "mcpp" -version = "2026.8.3.5" +version = "2026.8.4.1" description = "Modern C++ build & package management tool" license = "Apache-2.0" authors = ["mcpp-community"] diff --git a/src/version.cppm b/src/version.cppm index 61432d9f..3e2f4738 100644 --- a/src/version.cppm +++ b/src/version.cppm @@ -31,6 +31,6 @@ import std; export namespace mcpp { -inline constexpr std::string_view MCPP_VERSION = "2026.8.3.5"; +inline constexpr std::string_view MCPP_VERSION = "2026.8.4.1"; } // namespace mcpp diff --git a/src/xlings.cppm b/src/xlings.cppm index 23ec0f40..15cbdfb2 100644 --- a/src/xlings.cppm +++ b/src/xlings.cppm @@ -44,7 +44,7 @@ namespace pinned { // in lock-step by hand; that list was already missing both composite // actions, which is how CI's sandbox sat on 0.4.30 unnoticed while // everything else had moved on. Don't reintroduce a hand-maintained list. - inline constexpr std::string_view kXlingsVersion = "2026.7.28.4"; + inline constexpr std::string_view kXlingsVersion = "2026.8.4.1"; inline constexpr std::string_view kNasmVersion = "3.02"; }