From 40f40dfbdeb29d7496d18abc42c0567ac3b43977 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Fri, 31 Jul 2026 05:30:04 +0800 Subject: [PATCH] ci: derive the fresh-install version instead of hand-editing it, and document releasing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `ci-fresh-install.yml` carried MCPP_PIN as a hardcoded literal that a maintainer had to bump on every release. It was never the same thing as the `.xlings.json` pin it was kept equal to: MCPP_PIN is the version UNDER TEST — always the newest published release — while `.xlings.json` is the version BOOTSTRAPPED FROM, which only has to be a released mcpp that can build the current tree. The workflow already computed the right answer. Its `wait-index` guard queries the releases API for the newest tag, waits for xim-pkgindex to track it — and then discards it, while the install jobs used the literal. That gap is exactly the #265 failure: the guard reported "index tracks 0.0.102" and the jobs installed 0.0.100 ten seconds later, meeting an index whose floor was 0.0.101. wait-index now exports the derived version and all four install jobs consume it. Both properties the literal was protecting hold, and one of them structurally: explicit version a derived string is as explicit as a literal, so a lagging runner index still fails loudly with `version not found` rather than silently testing an older binary one source the guard and the jobs cannot disagree about which version is under test, because there is only one value Deriving happens on every trigger, not just post-release — "the newest published release" is the version under test on cron and manual runs too. Only the WAIT stays conditional, since only a post-release run can legitimately race the index. A non-numeric or empty API result is refused, because `mcpp@` with an empty version degrades into bare `mcpp` — straight back to "newest in the runner's index copy". check_version_pins.sh loses the now-meaningless "both pin sites agree" invariant and gains a guard against a literal MCPP_PIN reappearing (probe-tested: it fails when the literal is restored, passes when it is not). Also adds docs/09-release.md (+ zh), which did not exist. The release process lived in commit messages and workflow comments only. It documents the two version groups, what the pipeline automates and what it does not, how to verify a release without trusting a sha256 sidecar, and the index's CDN propagation lag. It also corrects 3b1cb6b, which claimed "the index no longer serves .1" and made the bootstrap bump look mandatory. That diagnosis is wrong — 2026.7.29.1 installs fine from the current index, which retains all 105 published versions; the real cause was a stale local index copy. The bump is a useful check (a green round proves the new release can self-host), not a prerequisite. The one hard rule is direction: never pin a version that is not yet installable. --- .github/tools/check_version_pins.sh | 36 +++--- .github/workflows/ci-fresh-install.yml | 71 ++++++++++-- docs/09-release.md | 145 +++++++++++++++++++++++++ docs/README.md | 1 + docs/zh/09-release.md | 131 ++++++++++++++++++++++ docs/zh/README.md | 1 + 6 files changed, 361 insertions(+), 24 deletions(-) create mode 100644 docs/09-release.md create mode 100644 docs/zh/09-release.md diff --git a/.github/tools/check_version_pins.sh b/.github/tools/check_version_pins.sh index 3fdb8a06..604cbcd4 100755 --- a/.github/tools/check_version_pins.sh +++ b/.github/tools/check_version_pins.sh @@ -104,32 +104,42 @@ v_src=$(grep -oE 'MCPP_VERSION[[:space:]]*=[[:space:]]*"[^"]+"' src/toolchain/fi | grep -oE '"[^"]+"' | tr -d '"' | head -1) v_xl=$(grep -oE '"mcpp"[[:space:]]*:[[:space:]]*"[^"]+"' .xlings.json \ | grep -oE '"[^"]+"$' | tr -d '"' | head -1) -v_pin=$(grep -oE "MCPP_PIN:[[:space:]]*'[^']+'" .github/workflows/ci-fresh-install.yml \ - | grep -oE "'[^']+'" | tr -d "'" | head -1) - -note "mcpp version: building=$v_toml (fingerprint=$v_src) bootstrap pin=$v_xl (MCPP_PIN=$v_pin)" +note "mcpp version: building=$v_toml (fingerprint=$v_src) bootstrap pin=$v_xl" for n in "mcpp.toml:$v_toml" "src/toolchain/fingerprint.cppm:$v_src" \ - ".xlings.json:$v_xl" "ci-fresh-install.yml MCPP_PIN:$v_pin"; do + ".xlings.json:$v_xl"; do [ -n "${n##*:}" ] || bad "${n%:*} — could not read the mcpp version" done +# ci-fresh-install.yml used to carry a SECOND hand-edited copy of the bootstrap +# pin (MCPP_PIN), and this script enforced that the two stayed equal. They are +# not the same thing and never were: MCPP_PIN is the version UNDER TEST (always +# the newest published release), while .xlings.json is the version BOOTSTRAPPED +# FROM (any released mcpp that can build the current tree). Keeping them equal +# forced a manual edit on every release for a value the workflow could derive — +# and the workflow's own index guard was already deriving it from the releases +# API and discarding it. MCPP_PIN is now that derived value, so there is no +# second site left to agree with. Guard against a silent regression to a +# literal: +if grep -qE "MCPP_PIN:[[:space:]]*['\"]?[0-9]" .github/workflows/ci-fresh-install.yml; then + bad "ci-fresh-install.yml hardcodes MCPP_PIN again — it must stay derived from + wait-index's releases-API lookup, or the index guard and the install jobs + can once more disagree about which version is under test (#265)" +fi + # (a) The version being BUILT: mcpp.toml and the compiled-in constant are the # same number by definition — release.yml derives the tag from the former # and the smoke test greps the latter out of `mcpp --version`. [ -z "$v_src" ] || [ "$v_src" = "$v_toml" ] \ || bad "src/toolchain/fingerprint.cppm has '$v_src' but mcpp.toml has '$v_toml'" -# (b) The version BOOTSTRAPPED FROM: both sites name a mcpp that is already -# published, so they must agree with each other — but they are NOT required -# to equal the version being built. They deliberately lag, and are bumped in -# a separate commit AFTER the release exists in xim-pkgindex (see the -# MCPP_PIN comment in ci-fresh-install.yml). Requiring equality here is what -# an earlier revision of this script got wrong: it sent CI to install a +# (b) The version BOOTSTRAPPED FROM (.xlings.json) names a mcpp that is already +# published, and is NOT required to equal the version being built. It +# deliberately lags, and is bumped in a separate commit AFTER the release +# exists in xim-pkgindex (see docs/09-release.md). Requiring equality here is +# what an earlier revision of this script got wrong: it sent CI to install a # version that did not exist yet, and every job died with # `package 'mcpp@' not found`. -[ -z "$v_xl" ] || [ -z "$v_pin" ] || [ "$v_xl" = "$v_pin" ] \ - || bad ".xlings.json pins '$v_xl' but ci-fresh-install.yml MCPP_PIN is '$v_pin' — both bootstrap the same released mcpp" # (c) …and the bootstrap pin must never run AHEAD of the version being built. # Four-key numeric sort, so the date scheme orders correctly (a plain diff --git a/.github/workflows/ci-fresh-install.yml b/.github/workflows/ci-fresh-install.yml index 2f816427..e33ab2de 100644 --- a/.github/workflows/ci-fresh-install.yml +++ b/.github/workflows/ci-fresh-install.yml @@ -31,16 +31,31 @@ concurrency: group: ci-fresh-install cancel-in-progress: false # use false to test in PRs, true to only test released mcpp -# Version under test. Bare `xlings install mcpp` resolves "newest in the -# runner's index copy", which is NOT the same source the wait-index job -# polls (raw.githubusercontent.com) — on 2026-07-21 the guard reported -# "index tracks 0.0.102" and the jobs still installed 0.0.100 ten seconds -# later, which then met an index whose floor was 0.0.101 (#265). Pinning -# makes the version explicit: if the index cannot serve it yet, the job -# fails with `version not found` instead of silently testing an older -# binary. Bump together with the .xlings.json workspace pin at release. -env: - MCPP_PIN: '2026.7.30.3' +# The version under test is DERIVED, once, by the resolve-version job below, +# and every install job consumes that one value. +# +# Two things had to hold, and a hardcoded pin only bought the first: +# +# 1. It must be an explicit version string. Bare `xlings install mcpp` +# resolves "newest in the runner's index copy", so a runner whose copy +# lags silently tests an OLDER binary and reports green. Naming the +# version makes a lagging index fail loudly with `version not found`. +# +# 2. The version the index guard waits for must be the version the jobs +# install. On 2026-07-21 they disagreed: the guard reported "index tracks +# 0.0.102" and the jobs installed 0.0.100 ten seconds later, which then met +# an index whose floor was 0.0.101 (#265). The guard already derived the +# real answer from the releases API and threw it away. +# +# Deriving once and feeding both satisfies (1) and makes (2) structurally +# impossible, instead of relying on a human to keep two hand-edited numbers in +# step with a third that moves on its own. `xlings install mcpp@` is +# every bit as explicit as `mcpp@`. +# +# NOT to be confused with the .xlings.json workspace pin, which this used to be +# kept equal to. That one is the BOOTSTRAP compiler for the self-host builds and +# has a different requirement — it must be a released mcpp that can build the +# CURRENT source tree — so it stays hand-maintained. See docs/09-release.md. jobs: # ────────────────────────────────────────────────────────────────── @@ -56,11 +71,31 @@ jobs: runs-on: ubuntu-latest if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }} timeout-minutes: 20 + outputs: + version: ${{ steps.resolve.outputs.version }} steps: + # Derive on EVERY trigger, not just post-release. "The newest published + # release" is the version under test whether we got here from the release + # pipeline, from cron, or by hand — only the WAIT below is specific to a + # post-release run, because only then can the index legitimately lag. + - name: Resolve the version under test (newest published release) + id: resolve + run: | + VER=$(curl -fsSL "https://api.github.com/repos/mcpp-community/mcpp/releases/latest" \ + | python3 -c "import json,sys; print(json.load(sys.stdin)['tag_name'].lstrip('v'))") + # A blank version would silently degrade `mcpp@$VER` into bare `mcpp`, + # i.e. straight back to "newest in the runner's index copy" — the exact + # failure this job exists to prevent. Refuse instead. + case "$VER" in + ''|*[!0-9.]*) echo "::error::could not resolve a version from the releases API (got '$VER')"; exit 1 ;; + esac + echo "version=$VER" >> "$GITHUB_OUTPUT" + echo "version under test: $VER" - name: Wait for xim-pkgindex to track the released mcpp if: ${{ github.event_name == 'workflow_run' }} + env: + VER: ${{ steps.resolve.outputs.version }} run: | - VER=$(curl -fsSL "https://api.github.com/repos/mcpp-community/mcpp/releases/latest" | python3 -c "import json,sys; print(json.load(sys.stdin)['tag_name'].lstrip('v'))") 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 @@ -81,6 +116,9 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 60 env: + # The one derived value (see the header comment): every install job names + # the SAME version the index guard waited for, so the two cannot disagree. + MCPP_PIN: ${{ needs.wait-index.outputs.version }} # Verbose every mcpp invocation — fresh-install is the cold index/sandbox # bootstrap path, exactly where extra diagnostics matter (src/cli.cppm). MCPP_VERBOSE: "1" @@ -217,6 +255,9 @@ jobs: image: debian:11 setup: apt-get update && apt-get -y install curl bash tar gzip xz-utils git ca-certificates binutils findutils file env: + # The one derived value (see the header comment): every install job names + # the SAME version the index guard waited for, so the two cannot disagree. + MCPP_PIN: ${{ needs.wait-index.outputs.version }} XLINGS_NON_INTERACTIVE: '1' HOME: /root steps: @@ -277,6 +318,10 @@ jobs: # independence (this image has no sha256sum; macos-15 does). runs-on: macos-14 timeout-minutes: 30 + env: + # The one derived value (see the header comment): every install job names + # the SAME version the index guard waited for, so the two cannot disagree. + MCPP_PIN: ${{ needs.wait-index.outputs.version }} steps: - uses: actions/checkout@v4 @@ -337,6 +382,10 @@ jobs: if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }} runs-on: windows-latest timeout-minutes: 30 + env: + # The one derived value (see the header comment): every install job names + # the SAME version the index guard waited for, so the two cannot disagree. + MCPP_PIN: ${{ needs.wait-index.outputs.version }} steps: - uses: actions/checkout@v4 diff --git a/docs/09-release.md b/docs/09-release.md new file mode 100644 index 00000000..26f3c328 --- /dev/null +++ b/docs/09-release.md @@ -0,0 +1,145 @@ +# 09 — Releasing mcpp + +How a release of **mcpp itself** reaches users. This is maintainer-facing; for +packaging *your own* project see [02 — Packaging for Release](02-pack-and-release.md). + +Until now this process lived only in commit messages and workflow comments. One +of those commit messages contains a misdiagnosis that is corrected in §5. + +## 1. The four version sites are two groups + +| Site | Group | Moves when | +|---|---|---| +| `mcpp.toml` `[package].version` | **being built** | you start work on a new version | +| `src/toolchain/fingerprint.cppm` `MCPP_VERSION` | **being built** | same commit as above (compiled-in copy) | +| `.xlings.json` `[workspace].mcpp` | **bootstrapped from** | separately, *after* a release is installable | +| `ci-fresh-install.yml` `MCPP_PIN` | ~~bootstrapped from~~ | **nothing — it is derived at run time** (§4) | + +`.github/tools/check_version_pins.sh` enforces what is left mechanically. The two +"being built" sites must be equal; the bootstrap pin must never be **newer** than +the version being built. + +The two groups are deliberately allowed to differ. Bumping them together is what +an earlier revision of the pin checker required, and it sent every CI job to +install a version that did not exist yet. + +## 2. The pipeline + +`release.yml` (tag push, or `workflow_dispatch` with no input, which derives the +tag from `mcpp.toml`) does all of this: + +``` +build ×4 (linux x86_64 / linux aarch64 / macOS ARM64 / Windows x64) + → GitHub Release v with tarballs + .sha256 sidecars + → mirror to xlings-res/mcpp on BOTH GitHub and GitCode + → open the version-bump PR against openxlings/xim-pkgindex + → workflow_run hook fires ci-fresh-install +``` + +Two steps are **not** automated: + +- **merging the xim-pkgindex bump PR** — a maintainer does it. Until it lands, + the released version is downloadable but not installable via `xlings install`. +- **bumping `.xlings.json`** — see §4. + +## 3. Verifying a release + +The mirror script verifies its own uploads, but the checks worth doing by hand +are the ones that do not trust a sidecar: + +```bash +V= +# both hosts serve every platform, byte-exact +for a in linux-x86_64.tar.gz linux-aarch64.tar.gz macosx-arm64.tar.gz windows-x86_64.zip; do + for h in github.com gitcode.com; do + curl -fsSL -o /dev/null -w "$h $a %{http_code} %{size_download}\n" \ + "https://$h/xlings-res/mcpp/releases/download/$V/mcpp-$V-$a" + done +done +# the index's sha256 values match the payloads (recompute; do not read the sidecar) +curl -fsSL -o /tmp/p.tgz "https://github.com/xlings-res/mcpp/releases/download/$V/mcpp-$V-linux-x86_64.tar.gz" +sha256sum /tmp/p.tgz # compare against pkgs/m/mcpp.lua in xim-pkgindex +``` + +Then a real install, in a **clean-room `XLINGS_HOME`** — never the machine's own +`~/.xlings`, which can mask a broken index with cached state: + +```bash +export XLINGS_HOME=$(mktemp -d) +xlings update +xlings install mcpp@$V -y +$(find "$XLINGS_HOME" -name mcpp -type f -path '*/bin/*' | head -1) --version +``` + +**Index propagation is not instant.** `xim-pkgindex` reaches clients as a CDN +artifact, not a git clone, so a freshly merged bump is invisible for a while +(measured at ~5 min on 2026-07-30; the documented worst case is ~40 min). A +clean-room that still reports the old `latest` has not failed — it has not caught +up. `ci-fresh-install`'s `wait-index` job encodes exactly this with a bounded +15-minute wait. + +## 4. The bootstrap pin: what it is, and when to bump it + +`.xlings.json`'s `[workspace].mcpp` is the **starting point of self-hosting** — +the released mcpp that `xlings install mcpp` puts in the workspace so CI can build +mcpp from source. Its only requirement is that it can build the **current** tree. + +**It does not have to move with every release.** The index retains every +published version (105 entries at the time of writing, back to the 0.0.x series), +so an older pin keeps resolving indefinitely — verified by installing a +two-releases-old version against the current index. + +Bumping it anyway is reasonable and is what this repository does in practice: a +green CI round on the bumped pin is a direct proof that the new release can build +mcpp itself on every platform. Treat it as a *useful check*, not a prerequisite. + +**The one hard constraint is direction**: the pin must never name a version that +is not yet installable. Bump it only after the release is published, mirrored, +**and merged into xim-pkgindex** — otherwise every CI job fails with +`package 'mcpp@' not found`. `check_version_pins.sh` enforces the +weaker "never newer than the version being built"; the index condition is on you. + +## 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 +never the same thing: `MCPP_PIN` is the version **under test** — always the newest +published release — while `.xlings.json` is the version **bootstrapped from**. + +It is now derived once, by the `wait-index` job, from the releases API, and every +install job consumes that single output. Two properties had to hold, and the +hardcoded literal only bought the first: + +1. **The version must be explicit.** Bare `xlings install mcpp` resolves "newest + in the runner's index copy", so a lagging runner silently tests an *older* + binary and reports green. Naming the version makes a lagging index fail loudly + with `version not found`. A derived string is every bit as explicit as a + literal one. +2. **The guard and the jobs must name the same version.** On 2026-07-21 they did + not: the index guard reported "index tracks 0.0.102" while the jobs installed + 0.0.100 ten seconds later, meeting an index whose floor was 0.0.101 (#265). + The guard was already deriving the right answer and throwing it away. Feeding + both from one value makes that disagreement structurally impossible. + +`check_version_pins.sh` fails if a literal `MCPP_PIN:` reappears. + +> **Correction.** Commit `3b1cb6b` ("bootstrap pin -> 2026.7.29.2") states *"the +> index no longer serves .1"* and quotes `version '2026.7.29.1' not found`. That +> diagnosis is wrong: `2026.7.29.1` installs fine from the current index. The real +> cause was a **stale local index copy** — the same propagation lag described in +> §3, seen from the other side. Nothing about a release removes older versions, +> and no reasoning should be built on the idea that it does. + +## 6. Checklist + +``` +[ ] version bumped in mcpp.toml + fingerprint.cppm (one commit) +[ ] CHANGELOG entry +[ ] bash .github/tools/check_version_pins.sh +[ ] merge to main, CI green +[ ] gh workflow run release.yml --ref main +[ ] release.yml green (4 builds + publish-ecosystem) +[ ] mirrors serve all four platforms on BOTH hosts, sha256 recomputed +[ ] merge the xim-pkgindex bump PR +[ ] clean-room XLINGS_HOME: xlings install mcpp@ succeeds +[ ] (optional) bump .xlings.json — only now, never earlier +``` diff --git a/docs/README.md b/docs/README.md index d68b45d1..f3d8ee53 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,6 +11,7 @@ - [06 - Workspaces](06-workspace.md) - [07 - build.mcpp Build Program](07-build-mcpp.md) - [08 - Toolchain Internals](08-toolchain-internals.md) +- [09 - Releasing mcpp](09-release.md) ## Specifications diff --git a/docs/zh/09-release.md b/docs/zh/09-release.md new file mode 100644 index 00000000..2c99275a --- /dev/null +++ b/docs/zh/09-release.md @@ -0,0 +1,131 @@ +# 09 — 发布 mcpp + +**mcpp 自身**的发布如何到达用户手上。这是给维护者看的;打包**你自己的项目**见 +[02 — 打包发布](02-pack-and-release.md)。 + +在此之前这套流程只活在 commit message 和 workflow 注释里,其中一条 commit message +里的诊断是错的,已在 §5 更正。 + +## 1. 四处版本号 = 两组 + +| 位置 | 组 | 何时变 | +|---|---|---| +| `mcpp.toml` `[package].version` | **正在构建的** | 开始做新版本时 | +| `src/toolchain/fingerprint.cppm` `MCPP_VERSION` | **正在构建的** | 与上一行同一个 commit(编译进二进制的副本) | +| `.xlings.json` `[workspace].mcpp` | **自举起点** | 单独地、在某个版本**已可安装之后** | +| `ci-fresh-install.yml` `MCPP_PIN` | ~~自举起点~~ | **不变 —— 运行时推导**(§4) | + +`.github/tools/check_version_pins.sh` 机器校验剩下的部分:两处"正在构建的"必须相等; +自举 pin 永远不得**新于**正在构建的版本。 + +两组刻意允许不同。把它们一起 bump 正是 pin 校验器早期版本要求过的做法, +结果是所有 CI 都去装一个还不存在的版本。 + +## 2. 发布管线 + +`release.yml`(推 tag,或 `workflow_dispatch` 不带输入时从 `mcpp.toml` 推导 tag)会做完这些: + +``` +四平台构建(linux x86_64 / linux aarch64 / macOS ARM64 / Windows x64) + → GitHub Release v,含 tarball 与 .sha256 边车文件 + → 镜像到 xlings-res/mcpp 的 GitHub 与 GitCode 双端 + → 向 openxlings/xim-pkgindex 开版本 bump PR + → workflow_run 钩子触发 ci-fresh-install +``` + +两步**没有**自动化: + +- **合并 xim-pkgindex 的 bump PR** —— 由维护者完成。在它落地之前,发布出来的版本 + 可以下载,但无法通过 `xlings install` 安装。 +- **bump `.xlings.json`** —— 见 §4。 + +## 3. 验证一次发布 + +镜像脚本会自校验上传,但真正值得手工做的是那些**不信任边车文件**的检查: + +```bash +V= +# 双端都服务全部四个平台,字节数一致 +for a in linux-x86_64.tar.gz linux-aarch64.tar.gz macosx-arm64.tar.gz windows-x86_64.zip; do + for h in github.com gitcode.com; do + curl -fsSL -o /dev/null -w "$h $a %{http_code} %{size_download}\n" \ + "https://$h/xlings-res/mcpp/releases/download/$V/mcpp-$V-$a" + done +done +# 索引里的 sha256 与载荷相符(**重新计算**,不要读边车文件) +curl -fsSL -o /tmp/p.tgz "https://github.com/xlings-res/mcpp/releases/download/$V/mcpp-$V-linux-x86_64.tar.gz" +sha256sum /tmp/p.tgz # 与 xim-pkgindex 的 pkgs/m/mcpp.lua 对照 +``` + +然后在 **clean-room `XLINGS_HOME`** 里真装一次 —— 绝不要用本机的 `~/.xlings`, +它的缓存状态会把一个坏掉的索引掩盖过去: + +```bash +export XLINGS_HOME=$(mktemp -d) +xlings update +xlings install mcpp@$V -y +$(find "$XLINGS_HOME" -name mcpp -type f -path '*/bin/*' | head -1) --version +``` + +**索引传播不是即时的。** `xim-pkgindex` 是以 CDN artifact 而非 git clone 的形式 +到达客户端的,所以刚合并的 bump 会有一段时间不可见(2026-07-30 实测 ~5 分钟, +记录在案的上限约 40 分钟)。clean-room 里仍然报旧的 `latest` 不是失败,是**还没追上**。 +`ci-fresh-install` 的 `wait-index` job 正是把这件事编码成了 15 分钟有界等待。 + +## 4. 自举 pin:它是什么,什么时候该 bump + +`.xlings.json` 的 `[workspace].mcpp` 是**自举的起点** —— 那个由 +`xlings install mcpp` 装进 workspace、供 CI 从源码构建 mcpp 的已发布 mcpp。 +它唯一的要求是:能构建**当前**这棵源码树。 + +**它不必每次发布都跟着动。** 索引保留每一个已发布版本(撰写时 105 个条目, +一直回溯到 0.0.x 系列),旧 pin 可以无限期继续解析 —— 这一点用「在当前索引下安装 +一个隔了两个版本的旧版」实测验证过。 + +跟着 bump 仍然是合理的,也是本仓库的实际做法:bump 后 CI 一轮全绿,直接证明了 +新发布能在每个平台上构建 mcpp 自己。把它当作**一项有用的检查**,而不是前置条件。 + +**唯一的硬约束是方向**:pin 绝不能指向一个尚不可安装的版本。只在发布已完成、 +已镜像、**且已合入 xim-pkgindex 之后**再 bump —— 否则所有 CI 会以 +`package 'mcpp@' not found` 失败。`check_version_pins.sh` 能卡住较弱的 +「不得新于正在构建的版本」;索引那个条件靠你自己把关。 + +## 5. `MCPP_PIN` 改为推导,以及这为什么重要 + +`ci-fresh-install.yml` 过去带着 pin 的第二份手工副本。它们从来就不是一回事: +`MCPP_PIN` 是**被测版本** —— 永远是最新的已发布版本;而 `.xlings.json` 是 +**自举来源**。 + +现在它由 `wait-index` job 从 releases API 推导一次,所有安装 job 消费同一个输出。 +有两条性质必须成立,而写死的字面量只买到了第一条: + +1. **版本必须是显式的。** 裸 `xlings install mcpp` 解析的是「runner 自己那份索引副本里 + 的最新」,于是副本落后的 runner 会**悄悄测一个旧二进制然后报绿**。写明版本能让落后的 + 索引以 `version not found` 响亮失败。推导出来的字符串与字面量一样显式。 +2. **守卫等的版本必须就是 job 装的版本。** 2026-07-21 它们不是:索引守卫报 + 「index tracks 0.0.102」,10 秒后 job 装的是 0.0.100,撞上 floor 为 0.0.101 的索引(#265)。 + 守卫本来就推导出了正确答案,然后把它扔掉了。让两者吃同一个值, + 使这种不一致在结构上不可能发生。 + +`check_version_pins.sh` 会在字面量 `MCPP_PIN:` 重新出现时报错。 + +> **更正。** commit `3b1cb6b`("bootstrap pin -> 2026.7.29.2")写着 +> *"the index no longer serves .1"* 并引用了 `version '2026.7.29.1' not found`。 +> **这个诊断是错的**:`2026.7.29.1` 在当前索引下能正常安装。真正的原因是 +> **本地索引副本陈旧** —— 就是 §3 描述的那个传播滞后,只是从另一侧看到的。 +> 发布不会移除任何旧版本,任何推理都不该建立在「会移除」这个前提上。 + +## 6. 检查清单 + +``` +[ ] mcpp.toml + fingerprint.cppm 版本号已 bump(同一个 commit) +[ ] CHANGELOG 条目 +[ ] bash .github/tools/check_version_pins.sh +[ ] 合入 main,CI 全绿 +[ ] gh workflow run release.yml --ref main +[ ] release.yml 全绿(4 个构建 + publish-ecosystem) +[ ] 双端都服务四个平台,sha256 重新算过 +[ ] 合并 xim-pkgindex 的 bump PR +[ ] clean-room XLINGS_HOME:xlings install mcpp@ 成功 +[ ] (可选)bump .xlings.json —— 只在此刻,绝不提前 +``` diff --git a/docs/zh/README.md b/docs/zh/README.md index e354f609..27518dda 100644 --- a/docs/zh/README.md +++ b/docs/zh/README.md @@ -11,3 +11,4 @@ - [06 - 工作空间](06-workspace.md) - [07 - build.mcpp 构建程序](07-build-mcpp.md) - [08 - 工具链机制内幕](08-toolchain-internals.md) +- [09 - 发布 mcpp](09-release.md)