Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
368 changes: 368 additions & 0 deletions .agents/docs/2026-08-04-ci-fresh-install-two-defects.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .github/actions/bootstrap-mcpp/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/setup-macos-llvm/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
160 changes: 160 additions & 0 deletions .github/tools/install_released_mcpp.sh
Original file line number Diff line number Diff line change
@@ -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 <version> [repo_dir]
# stdout: the resolved binary path; diagnostics go to stderr.
set -euo pipefail

VER="${1:?usage: install_released_mcpp.sh <version> [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"
2 changes: 1 addition & 1 deletion .github/workflows/bootstrap-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
96 changes: 64 additions & 32 deletions .github/workflows/ci-fresh-install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 15minmerge the bump PR (openxlings/xim-pkgindex) and re-run"
echo "::error::the published index artifact never tracked $VER within 20mincheck 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' }}
Expand All @@ -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:"
Expand Down Expand Up @@ -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
Expand All @@ -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)"
Expand Down Expand Up @@ -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:"
Expand Down Expand Up @@ -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: |
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-linux-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/cross-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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}" \
Expand Down Expand Up @@ -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}" \
Expand Down
Loading
Loading