diff --git a/.agents/docs/2026-07-26-bare-name-wire-address-implementation-plan.md b/.agents/docs/2026-07-26-bare-name-wire-address-implementation-plan.md index 56e0189c..56fb19a8 100644 --- a/.agents/docs/2026-07-26-bare-name-wire-address-implementation-plan.md +++ b/.agents/docs/2026-07-26-bare-name-wire-address-implementation-plan.md @@ -39,18 +39,67 @@ - [x] **B2** 本地跑 163 验证四个 sub-case(step 4 依赖 xlings >= 0.4.69,按现有逻辑 自动 skip 则接受)。 -### P3 — 失效面 C:bootstrap pin +### P3 — 失效面 C:pin 与版本漂移(实施中范围扩大) - [x] **C1** `.github/actions/bootstrap-mcpp/action.yml`:unix + windows 两段 `xlings install mcpp -y` → 读 `.xlings.json` 的 pin 装指定版本。 -- [x] **C2** 装完断言 `mcpp --version` == pin,不等则 fail(不再静默漂移)。 +- [x] **C2** 装完断言解析出的版本**精确等于** pin(子串匹配会让 `0.0.10` 被 + `0.0.109` 满足),不等则 fail。 +- [x] **C3** 按「版本目录」定位二进制而非硬编码内部布局:Linux 是 + `~/.xlings/data/xpkgs/xim-x-mcpp//mcpp`,Windows 是 + `.../subos/default/data/xpkgs/xim-x-mcpp//bin/mcpp.exe`,根与层级都不同。 + **教训**:只凭 Windows 一条日志推断跨平台路径,白烧一轮 CI。 +- [x] **C4** `.xlings.json` 用 `cd` 之前捕获的 `REPO_DIR` 绝对路径读取——Windows + leg 解压后 `cd "$WORK"` 且从不切回;不用 `GITHUB_WORKSPACE`(git-bash 下 + 是反斜杠 Windows 路径)。 +- [x] **C5** `setup-macos-llvm` 同样是未 pin 的 bootstrap 点(冷启动拿到 0.0.105 + < 索引 floor 0.0.108 → 描述符全读不到 → 依赖回落 legacy 地址失败)。同法 pin。 +- [x] **C6** bootstrap 打印 system / sandbox 两个 xlings 版本。沙箱那份才是真正 + 解析依赖的,而它从不更新,此前完全不可见。 + +### P3b — 沙箱 xlings 版本(实测后追加,非推断) + +- [x] **C7** 实测 CI 沙箱 xlings = **0.4.30**(先加零风险诊断打印,再动手)。 + 索引短名迁移后同一 repo 有两个 `name = "lua"`(`compat:lua` 与 + `mcpplibs.capi:lua`,经 `mcpplibs.xpkg` 传递同时需要),0.4.69 前 xlings 按 + 裸 name 建表 → 必有一个够不到,**够不到哪个取决于机器**。 +- [x] **C8** `bootstrap-mcpp` / `setup-macos-llvm` 的 xlings 0.4.30 → **0.4.69**, + 与 `release.yml`/`cross-build-test.yml`(早已 0.4.69)对齐; + `bootstrap-macos.yml`(dispatch-only)一并跟上。 +- [x] **C9** xlings 版本进入**缓存血统**而非仅 key:`restore-keys` 是前缀匹配, + 只放进 key 的话 0.4.69 的 bootstrap 仍会恢复 0.4.30 的沙箱。代价=一次冷启动。 + +### P3c — 全仓 bootstrap 收敛(release 失败后追加) + +0.0.109 的 release 在 `build + upload (linux/x86_64)` 挂掉,同一个症状:裸 +`xlings install mcpp` 拿到 **0.0.105** < 索引 floor 0.0.108 → 描述符全读不到 → +依赖回落 legacy 地址 → 死在 `mcpplibs.cmdline@0.0.1`。P3 只修了两个 composite +action,`release.yml` 自己还有 4 处内联 bootstrap。 + +- [x] **C10** 全仓盘点:裸 `xlings install mcpp` 共 **6+ 处**(release.yml ×4、 + cross-build-test.yml ×2、两个 action、ci-aarch64-fresh-install、ci-linux-e2e), + 各自还有细微漂移(Windows 那处用 `find | head -1` 取到任意版本)。 +- [x] **C11** 抽 `.github/tools/install_pinned_mcpp.sh` 作为**唯一实现**:读 pin → + 装 → 按版本目录定位 → 精确版本断言 → stdout 只输出路径、诊断走 stderr。 + 六处调用它,而不是写第七份拷贝。 +- [x] **C12** 修 `set -eo pipefail` 下的雷:`find ~/.xlings ... 2>/dev/null` 遇到 + 不可读目录返回非零(`2>/dev/null` 只藏消息不藏状态)→ 整条管道非零 → 脚本 + 在能报出任何有用信息之前就被 errexit 杀掉。全部 `|| true` + 显式 `return 0`。 + **这个雷在 P3 写进两个 action 的版本里同样存在**,只是循环提前 return 才没炸。 +- [x] **C13** Windows release leg 同样在 `cd "$WORK"` 后不回仓库 → 用 cd 前捕获的 + `REPO_DIR="$(pwd)"`,不用 `GITHUB_WORKSPACE`(git-bash 下是反斜杠路径)。 +- [ ] **C14**(follow-up,不在本次)`ci-aarch64-fresh-install` 的裸安装是**故意的** + (验证用户 fresh-install 路径);`ci-linux-e2e` 的 hermetic job 目前是绿的, + 连跑六轮后为风格一致去动绿 job 是坏判断。两处单独评估。 ### P4 — 版本 + 收口 - [x] **D1** 版本 0.0.108 → 0.0.109,两处同步。 - [x] **D2** 本地全量单测。 -- [ ] **D3** 本地 e2e(至少 100 / 163 / 165 / 78 / 79 + 全量能跑的部分)。 -- [ ] **D4** 单 PR(附版本)→ 7 个 workflow 全绿。 +- [x] **D3** 本地全量 e2e:155 passed / 唯一非预期失败是本机 g++ shim 环境问题(22)。 + **负向对照**用 `git checkout main -- ` 重建(`git stash` 做不了—— + 修复已在 commit 里时它只回退未提交部分,测试会假通过):165 与 69-L8 都失败。 +- [x] **D4** 单 PR #286(0.0.109)→ 15 个 check 全绿。 - [ ] **D5** bypass squash 合入 main。 ### P5 — xlings 全生态验证 diff --git a/.github/actions/bootstrap-mcpp/action.yml b/.github/actions/bootstrap-mcpp/action.yml index 393c8f7a..009ce3d7 100644 --- a/.github/actions/bootstrap-mcpp/action.yml +++ b/.github/actions/bootstrap-mcpp/action.yml @@ -77,70 +77,10 @@ runs: export PATH="$HOME/.xlings/subos/default/bin:$PATH" echo "$HOME/.xlings/subos/default/bin" >> "$GITHUB_PATH" xlings --version - # xim:mcpp — pinned, NOT bare `xlings install mcpp`. - # - # A bare install is idempotent, so on a cache hit it keeps whatever - # version the cache already holds. `.xlings.json` only fed the cache - # KEY, and `restore-keys` prefix-matches, so a stale sandbox got - # restored and the pin was never applied: jobs ran 0.0.107 and 0.0.102 - # against an index whose floor was 0.0.108 (E0006). Warm caches hid it - # — a cold one would have been a hard failure. - # Absolute, POSIX-form path captured before any `cd`: the Windows leg - # cds into the unpack dir and never returns, so a relative - # `.xlings.json` resolves to nothing there — and GITHUB_WORKSPACE is a - # backslash Windows path that git-bash tools mangle. - XJSON="$REPO_DIR/.xlings.json" - MCPP_PIN=$(grep -oE '"mcpp"[[:space:]]*:[[:space:]]*"[^"]+"' "$XJSON" \ - | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) - test -n "$MCPP_PIN" || { echo "FAIL: no mcpp pin in $XJSON"; exit 1; } - echo "bootstrap pin: mcpp@${MCPP_PIN}" - # Address the pinned version through ITS OWN version directory rather - # than the shim in subos/default/bin (which is a symlink to xlings and - # resolves to whatever version is currently selected — exactly the - # drift this pin exists to prevent). - # - # Locate it by searching inside `xim-x-mcpp//` instead of assuming - # the layout underneath: it is `/mcpp` on Linux and `/bin/ - # mcpp.exe` on Windows, and the roots differ too. Anything found under - # that directory IS the pinned version, so this keeps the guarantee - # without hardcoding a shape that varies per platform. - find_pinned_mcpp() { - local d r - for d in "$HOME/.xlings/data/xpkgs/xim-x-mcpp/${MCPP_PIN}" \ - "$HOME/.xlings/subos/default/data/xpkgs/xim-x-mcpp/${MCPP_PIN}"; do - [ -d "$d" ] || continue - r=$(find "$d" -type f -name mcpp 2>/dev/null | head -1) - [ -n "$r" ] && { echo "$r"; return 0; } - done - # Last resort: the roots above are the two layouts seen in practice, - # but the VERSION DIRECTORY is the actual contract — find it wherever - # xlings put it rather than fail on a layout we have not met yet. - find "$HOME/.xlings" -type d -path "*xim-x-mcpp/${MCPP_PIN}" \ - -exec find {} -type f -name mcpp ';' 2>/dev/null | head -1 - } - xlings install "mcpp@${MCPP_PIN}" -y || true - MCPP=$(find_pinned_mcpp) - if [ -z "$MCPP" ]; then - # A restored ~/.xlings can carry a xim-pkgindex clone that predates - # this release, and the refresh marker can read fresh while the clone - # is behind. Refresh once and retry before calling it a failure. - echo "pinned install did not land; refreshing the index and retrying" - xlings update || true - xlings install "mcpp@${MCPP_PIN}" -y || true - MCPP=$(find_pinned_mcpp) - fi - test -n "$MCPP" || { - echo "FAIL: no mcpp binary under xim-x-mcpp/${MCPP_PIN}" - ls -d "$HOME"/.xlings/data/xpkgs/xim-x-mcpp/* \ - "$HOME"/.xlings/subos/default/data/xpkgs/xim-x-mcpp/* 2>/dev/null - exit 1; } - "$MCPP" --version - # Compare the parsed version exactly: a substring match would let pin - # 0.0.10 be satisfied by a 0.0.109 binary. - MCPP_GOT=$("$MCPP" --version 2>/dev/null | head -1 \ - | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) - [ "$MCPP_GOT" = "$MCPP_PIN" ] || { - echo "FAIL: mcpp at $MCPP reports '$MCPP_GOT', pin is '$MCPP_PIN'"; exit 1; } + # xim:mcpp — pinned, NOT bare `xlings install mcpp`. One shared + # implementation for every bootstrap point in the repo; see + # .github/tools/install_pinned_mcpp.sh for why. + MCPP=$(bash "$REPO_DIR/.github/tools/install_pinned_mcpp.sh" "$REPO_DIR") # The SANDBOX xlings is a separate copy: mcpp vendors it into # ~/.mcpp/registry/bin at `self init` and never revisits it # (acquire_xlings_binary returns early when the file exists), while @@ -174,50 +114,8 @@ runs: export PATH="$USERPROFILE/.xlings/subos/default/bin:$PATH" echo "$USERPROFILE/.xlings/subos/default/bin" >> "$GITHUB_PATH" xlings.exe --version - # Pinned install — see the unix leg for why a bare `install mcpp` let - # the version drift behind the index floor unnoticed. The old `find | - # head -1` here made it worse: with several versions in the sandbox it - # returned whichever the directory walk hit first. - # Absolute, POSIX-form path captured before any `cd`: the Windows leg - # cds into the unpack dir and never returns, so a relative - # `.xlings.json` resolves to nothing there — and GITHUB_WORKSPACE is a - # backslash Windows path that git-bash tools mangle. - XJSON="$REPO_DIR/.xlings.json" - MCPP_PIN=$(grep -oE '"mcpp"[[:space:]]*:[[:space:]]*"[^"]+"' "$XJSON" \ - | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) - test -n "$MCPP_PIN" || { echo "FAIL: no mcpp pin in $XJSON"; exit 1; } - echo "bootstrap pin: mcpp@${MCPP_PIN}" - # Search inside the pinned version's own directory rather than assuming - # the layout under it — see the unix leg. - find_pinned_mcpp() { - local d r - for d in "$USERPROFILE/.xlings/subos/default/data/xpkgs/xim-x-mcpp/${MCPP_PIN}" \ - "$USERPROFILE/.xlings/data/xpkgs/xim-x-mcpp/${MCPP_PIN}"; do - [ -d "$d" ] || continue - r=$(find "$d" -type f -name "mcpp.exe" 2>/dev/null | head -1) - [ -n "$r" ] && { echo "$r"; return 0; } - done - find "$USERPROFILE/.xlings" -type d -path "*xim-x-mcpp/${MCPP_PIN}" \ - -exec find {} -type f -name "mcpp.exe" ';' 2>/dev/null | head -1 - } - xlings.exe install "mcpp@${MCPP_PIN}" -y || true - MCPP=$(find_pinned_mcpp) - if [ -z "$MCPP" ]; then - echo "pinned install did not land; refreshing the index and retrying" - xlings.exe update || true - xlings.exe install "mcpp@${MCPP_PIN}" -y || true - MCPP=$(find_pinned_mcpp) - fi - test -n "$MCPP" || { - echo "FAIL: no mcpp.exe under xim-x-mcpp/${MCPP_PIN}" - ls -d "$USERPROFILE"/.xlings/subos/default/data/xpkgs/xim-x-mcpp/* \ - "$USERPROFILE"/.xlings/data/xpkgs/xim-x-mcpp/* 2>/dev/null - exit 1; } - "$MCPP" --version - MCPP_GOT=$("$MCPP" --version 2>/dev/null | head -1 \ - | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) - [ "$MCPP_GOT" = "$MCPP_PIN" ] || { - echo "FAIL: mcpp at $MCPP reports '$MCPP_GOT', pin is '$MCPP_PIN'"; exit 1; } + # Pinned install — shared implementation, see the unix leg. + MCPP=$(bash "$REPO_DIR/.github/tools/install_pinned_mcpp.sh" "$REPO_DIR") # See the unix leg: the sandbox keeps its own vendored xlings copy. echo "system xlings: $(xlings.exe --version 2>/dev/null | head -1)" if [ -x "$USERPROFILE/.mcpp/registry/bin/xlings.exe" ]; then diff --git a/.github/actions/setup-macos-llvm/action.yml b/.github/actions/setup-macos-llvm/action.yml index b8d0cbe9..60bb0cc6 100644 --- a/.github/actions/setup-macos-llvm/action.yml +++ b/.github/actions/setup-macos-llvm/action.yml @@ -61,45 +61,10 @@ runs: - name: Bootstrap mcpp via xlings shell: bash run: | - # Pinned to .xlings.json, same as .github/actions/bootstrap-mcpp. - # A bare `xlings install mcpp` takes "newest in whatever index copy - # this runner happens to have", which on a cold cache resolved 0.0.105 - # — below the index's own floor (0.0.108). The bootstrap then could not - # read ANY descriptor (the floor check makes the read return nothing), - # so every dependency fell back to its legacy derived address and the - # build died on `mcpplibs.cmdline@0.0.1`. - MCPP_PIN=$(grep -oE '"mcpp"[[:space:]]*:[[:space:]]*"[^"]+"' "$GITHUB_WORKSPACE/.xlings.json" \ - | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) - test -n "$MCPP_PIN" || { echo "FAIL: no mcpp pin in .xlings.json"; exit 1; } - echo "bootstrap pin: mcpp@${MCPP_PIN}" - find_pinned_mcpp() { - local d r - for d in "$HOME/.xlings/data/xpkgs/xim-x-mcpp/${MCPP_PIN}" \ - "$HOME/.xlings/subos/default/data/xpkgs/xim-x-mcpp/${MCPP_PIN}"; do - [ -d "$d" ] || continue - r=$(find "$d" -type f -name mcpp 2>/dev/null | head -1) - [ -n "$r" ] && { echo "$r"; return 0; } - done - find "$HOME/.xlings" -type d -path "*xim-x-mcpp/${MCPP_PIN}" \ - -exec find {} -type f -name mcpp ';' 2>/dev/null | head -1 - } - xlings install "mcpp@${MCPP_PIN}" -y || true - MCPP=$(find_pinned_mcpp) - if [ -z "$MCPP" ]; then - echo "pinned install did not land; refreshing the index and retrying" - xlings update || true - xlings install "mcpp@${MCPP_PIN}" -y || true - MCPP=$(find_pinned_mcpp) - fi - test -n "$MCPP" || { - echo "FAIL: no mcpp binary under xim-x-mcpp/${MCPP_PIN}" - ls -d "$HOME"/.xlings/data/xpkgs/xim-x-mcpp/* 2>/dev/null - exit 1; } - "$MCPP" --version - MCPP_GOT=$("$MCPP" --version 2>/dev/null | head -1 \ - | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) - [ "$MCPP_GOT" = "$MCPP_PIN" ] || { - echo "FAIL: mcpp at $MCPP reports '$MCPP_GOT', pin is '$MCPP_PIN'"; exit 1; } + # Pinned to .xlings.json, shared implementation. A bare + # `xlings install mcpp` resolved 0.0.105 here on a cold cache — below + # the index floor, which makes every descriptor read return nothing. + MCPP=$(bash "$GITHUB_WORKSPACE/.github/tools/install_pinned_mcpp.sh" "$GITHUB_WORKSPACE") echo "MCPP=$MCPP" >> "$GITHUB_ENV" echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV" diff --git a/.github/tools/install_pinned_mcpp.sh b/.github/tools/install_pinned_mcpp.sh new file mode 100755 index 00000000..f3d2723c --- /dev/null +++ b/.github/tools/install_pinned_mcpp.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +# install_pinned_mcpp.sh — install the bootstrap mcpp that .xlings.json pins, +# and print the resolved binary's path on stdout. +# +# WHY THIS EXISTS. Six places bootstrapped mcpp with `xlings install mcpp -y`. +# Passing the package name is what breaks it: with an explicit name xlings +# resolves "newest in this runner's index copy" and the workspace pin never +# applies. On 2026-07-26 CI jobs were running 0.0.102, 0.0.105 and 0.0.107 +# against an index whose floor is 0.0.108. Warm caches hid it; the moment one +# went cold the floor check made EVERY descriptor read return nothing, every +# dependency fell back to its legacy derived address, and the build died on +# `mcpplibs.cmdline@0.0.1` — an error naming neither the version nor the floor. +# +# WHAT THIS DOES NOT DO. It does not parse the pin to decide what to install, +# does not refresh the index, and does not go looking for the binary on disk. +# `.xlings.json` is xlings' own file and xlings already does all three: +# +# $ xlings install # no package name +# [xlings] 'mcpp@0.0.108' not in current index; refreshing index... +# Packages to install (1): +# ◆ xim:mcpp@0.0.108 +# +# An earlier version of this script re-implemented every one of those steps by +# hand — grep the pin, `xlings update` on miss, `find` the install directory. +# That is the same mistake the fix it supports is about: re-deriving what the +# owner of the information will tell you. It also cost two bugs (a hardcoded +# per-platform layout, and a `find` that exits non-zero on one unreadable +# directory under `set -eo pipefail`). +# +# `-u` activates the version just installed, so the shim resolves to it; that +# is the piece a plain `install` leaves alone, and the reason CI could install +# one version and then run another. +# +# Usage: MCPP=$(bash .github/tools/install_pinned_mcpp.sh) +# MCPP=$(bash .github/tools/install_pinned_mcpp.sh /path/to/repo) +# +# stdout is ONLY the binary path; all diagnostics go to stderr. +set -euo pipefail + +REPO_DIR="${1:-$(pwd)}" +[ -f "$REPO_DIR/.xlings.json" ] || { + echo "FAIL: no .xlings.json at $REPO_DIR" >&2; exit 1; } + +case "$(uname -s)" in + MINGW*|MSYS*|CYGWIN*) XL_HOME="${USERPROFILE:-$HOME}"; EXE=".exe" ;; + *) XL_HOME="$HOME"; EXE="" ;; +esac + +# The workspace pin is read from the CWD, so run from the repo regardless of +# where the caller happens to be (the Windows legs cd into an unpack dir and +# never return). +cd "$REPO_DIR" + +# Address xlings by path, not through PATH. The callers export +# `$USERPROFILE/.xlings/subos/default/bin` into PATH before invoking this, and +# `xlings.exe --version` works in that shell — but this script runs as a child +# bash, and MSYS re-derives PATH from the Windows environment on startup, which +# drops that mixed-separator entry (`C:\Users\x/.xlings/...`) and leaves +# `xlings.exe: command not found`. This is the same well-known location every +# caller already hardcodes as XLINGS_BIN, so it adds no new layout knowledge. +XL="$XL_HOME/.xlings/subos/default/bin/xlings${EXE}" +[ -x "$XL" ] || XL=$(command -v "xlings${EXE}" 2>/dev/null || true) +[ -n "$XL" ] && [ -x "$XL" ] || { + echo "FAIL: no xlings at $XL_HOME/.xlings/subos/default/bin nor on PATH" >&2 + exit 1; } + +"$XL" install -y -u -g >&2 + +MCPP="$XL_HOME/.xlings/subos/default/bin/mcpp${EXE}" +[ -x "$MCPP" ] || { echo "FAIL: no mcpp at $MCPP after install" >&2; exit 1; } + +# Assert the outcome rather than trust it. This is the ONLY place the pin is +# parsed, and only to check what xlings actually activated — a guard against +# the silent drift this script exists to end, not a second resolution path. +# +# The `|| true` on both captures is load-bearing under `set -eo pipefail`: a +# `| grep` that matches nothing exits 1, which would kill the script silently — +# CI would get a bare non-zero and no diagnostic, exactly the failure mode this +# whole change is meant to stop. A miss has to reach the check below, which can +# name what it expected and what it found. +PIN=$(grep -oE '"mcpp"[[:space:]]*:[[:space:]]*"[^"]+"' "$REPO_DIR/.xlings.json" \ + | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true) +GOT=$("$MCPP" --version 2>/dev/null | head -1 \ + | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true) +# Exact comparison: a substring match would let a pin of 0.0.10 be satisfied by +# a 0.0.109 binary. +[ -n "$PIN" ] && [ "$GOT" = "$PIN" ] || { + echo "FAIL: .xlings.json pins '${PIN:-?}' but the active mcpp is '${GOT:-?}'" >&2 + exit 1; } +echo "bootstrap mcpp: $MCPP ($GOT)" >&2 + +echo "$MCPP" diff --git a/.github/workflows/cross-build-test.yml b/.github/workflows/cross-build-test.yml index be5c73f1..38773400 100644 --- a/.github/workflows/cross-build-test.yml +++ b/.github/workflows/cross-build-test.yml @@ -120,9 +120,15 @@ jobs: find "$HOME/.xlings" -name '.xlings-index-cache.json' -delete 2>/dev/null || true xlings config --mirror GLOBAL 2>/dev/null || true xlings update -y 2>/dev/null || xlings update 2>/dev/null || true - xlings install mcpp -y + # MCPP_BOOT is what actually runs the bootstrap build below, so it — + # not just MCPP — has to be the pinned binary. It used to be the shim + # in subos/default/bin, which resolves to whatever version xvm has + # selected; pinning only MCPP would have looked right and changed + # nothing. + MCPP_BOOT=$(bash "$GITHUB_WORKSPACE/.github/tools/install_pinned_mcpp.sh" "$GITHUB_WORKSPACE") + echo "MCPP=$MCPP_BOOT" >> "$GITHUB_ENV" echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV" - echo "MCPP_BOOT=$HOME/.xlings/subos/default/bin/mcpp" >> "$GITHUB_ENV" + echo "MCPP_BOOT=$MCPP_BOOT" >> "$GITHUB_ENV" - name: Self-host build (bootstrap mcpp -> fresh host mcpp) run: | @@ -247,9 +253,15 @@ jobs: find "$HOME/.xlings" -name '.xlings-index-cache.json' -delete 2>/dev/null || true xlings config --mirror GLOBAL 2>/dev/null || true xlings update -y 2>/dev/null || xlings update 2>/dev/null || true - xlings install mcpp -y + # MCPP_BOOT is what actually runs the bootstrap build below, so it — + # not just MCPP — has to be the pinned binary. It used to be the shim + # in subos/default/bin, which resolves to whatever version xvm has + # selected; pinning only MCPP would have looked right and changed + # nothing. + MCPP_BOOT=$(bash "$GITHUB_WORKSPACE/.github/tools/install_pinned_mcpp.sh" "$GITHUB_WORKSPACE") + echo "MCPP=$MCPP_BOOT" >> "$GITHUB_ENV" echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV" - echo "MCPP_BOOT=$HOME/.xlings/subos/default/bin/mcpp" >> "$GITHUB_ENV" + echo "MCPP_BOOT=$MCPP_BOOT" >> "$GITHUB_ENV" - name: Self-host build (fresh host mcpp) run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bda4c578..9507f471 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -107,10 +107,10 @@ jobs: fi export PATH="$HOME/.xlings/subos/default/bin:$PATH" xlings --version - xlings install mcpp -y - MCPP="$HOME/.xlings/subos/default/bin/mcpp" - test -x "$MCPP" - "$MCPP" --version + # Pinned to .xlings.json — a bare `xlings install mcpp` resolves + # "newest in this runner's index copy" and put 0.0.105 (below the + # index floor) into this job. See .github/tools/install_pinned_mcpp.sh. + MCPP=$(bash "$GITHUB_WORKSPACE/.github/tools/install_pinned_mcpp.sh" "$GITHUB_WORKSPACE") echo "MCPP=$MCPP" >> "$GITHUB_ENV" echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV" @@ -296,18 +296,22 @@ jobs: run: | xlings config --mirror GLOBAL 2>/dev/null || true xlings update -y 2>/dev/null || xlings update 2>/dev/null || true - xlings install mcpp -y - mcpp --version + MCPP=$(bash "$GITHUB_WORKSPACE/.github/tools/install_pinned_mcpp.sh" "$GITHUB_WORKSPACE") + echo "MCPP=$MCPP" >> "$GITHUB_ENV" + "$MCPP" --version - name: Cross-build mcpp -> aarch64-linux-musl (this release's source) run: | export MCPP_VENDORED_XLINGS="$XLINGS_BIN" - mcpp self config --mirror GLOBAL 2>/dev/null || true + # "$MCPP", not a bare `mcpp`: the bare form runs the PATH shim, which + # resolves to whatever version xvm has selected — so pinning the + # bootstrap would have looked correct and changed nothing here. + "$MCPP" self config --mirror GLOBAL 2>/dev/null || true # The published bootstrap mcpp predates aarch64 cross-build support # (the feature landed after the last release), so it resolves the # x86_64 host musl-gcc for an aarch64 target. Two-stage instead: build # THIS release's x86_64 mcpp first, then cross-build aarch64 with it. - mcpp build --target x86_64-linux-musl + "$MCPP" build --target x86_64-linux-musl FRESH=$(find target/x86_64-linux-musl -type f -name mcpp | head -1) test -n "$FRESH" "$FRESH" build --target aarch64-linux-musl @@ -429,10 +433,10 @@ jobs: fi export PATH="$HOME/.xlings/subos/default/bin:$PATH" xlings --version - xlings install mcpp -y - MCPP="$HOME/.xlings/subos/default/bin/mcpp" - test -x "$MCPP" - "$MCPP" --version + # Pinned to .xlings.json — a bare `xlings install mcpp` resolves + # "newest in this runner's index copy" and put 0.0.105 (below the + # index floor) into this job. See .github/tools/install_pinned_mcpp.sh. + MCPP=$(bash "$GITHUB_WORKSPACE/.github/tools/install_pinned_mcpp.sh" "$GITHUB_WORKSPACE") echo "MCPP=$MCPP" >> "$GITHUB_ENV" echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV" @@ -601,6 +605,10 @@ jobs: XLINGS_NON_INTERACTIVE: '1' XLINGS_VERSION: '0.4.69' run: | + # Captured before the `cd` below, in POSIX form: this step never + # returns to the workspace, and GITHUB_WORKSPACE is a backslash + # Windows path that git-bash tools mangle. + REPO_DIR="$(pwd)" WORK=$(mktemp -d) zipfile="xlings-${XLINGS_VERSION}-windows-x86_64.zip" curl -fsSL -o "${WORK}/${zipfile}" \ @@ -611,13 +619,9 @@ jobs: export PATH="$USERPROFILE/.xlings/subos/default/bin:$PATH" echo "$USERPROFILE/.xlings/subos/default/bin" >> "$GITHUB_PATH" xlings.exe --version - xlings.exe install mcpp -y - MCPP=$(find "$USERPROFILE/.xlings" -name "mcpp.exe" -path "*/bin/*" 2>/dev/null | head -1) - if [ -z "$MCPP" ]; then - MCPP=$(find "$USERPROFILE/.xlings" -name "mcpp" -path "*/bin/*" 2>/dev/null | head -1) - fi - test -n "$MCPP" || { echo "FAIL: mcpp not found after xlings install"; exit 1; } - "$MCPP" --version + # Pinned + version-scoped lookup. The old `find | head -1` returned + # whichever version the directory walk reached first. + MCPP=$(bash "$REPO_DIR/.github/tools/install_pinned_mcpp.sh" "$REPO_DIR") echo "MCPP=$MCPP" >> "$GITHUB_ENV" XLINGS_BIN=$(cygpath -w "$USERPROFILE/.xlings/subos/default/bin/xlings.exe") echo "XLINGS_BIN=$XLINGS_BIN" >> "$GITHUB_ENV"