Repository navigation
Expand file tree
/
Copy pathaction.yml
More file actions
294 lines (283 loc) · 15.3 KB
/
Copy pathaction.yml
File metadata and controls
294 lines (283 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
name: bootstrap-mcpp
description: >
Restore the shared CI cache lineage (mcpp sandbox and xlings) and bootstrap
a released mcpp via xlings. Exports MCPP and XLINGS_BIN.
The caches are RESTORED here and never saved. One job per host writes them,
the build job of .github/workflows/build.yml, and only on a push to main
(rule R3 of .agents/docs/2026-10-02-pr-ci-acceleration-and-the-toolchain-
specification-design.md). Before that rule every job that used this action
saved the same key on success: thirty-four to forty-two saves per pull
request against a 10 GB repository limit, parallel saves of one key racing
each other, and the main lineage evicted within forty minutes (measured
2026-10-01). The keys are outputs so that the one writer saves exactly what
was restored.
`target/` is no longer cached. A restored `target/` made no build
incremental: on an exact hit ninja still ran 830 of 830 edges, while the
caches themselves were up to 3.3 GB each.
inputs:
xlings-version:
description: xlings release to bootstrap from
required: false
# Must equal `pinned::kXlingsVersion` in src/provider/xlings/xlings.cppm — enforced by
# .github/tools/check_version_pins.sh, not by this comment. (The previous
# comment here listed the files to keep in lock-step, and that list was
# already incomplete; that is why the check exists.)
#
# Hard floor: 0.4.69. The index's SPEC-001 short-name migration put two
# packages named `lua` in one repo (`compat:lua` and `mcpplibs.capi:lua`,
# both pulled in transitively by mcpplibs.xpkg). Before 0.4.69
# (openxlings/xlings#381) a repo keyed its table by the bare
# `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.10.17.1'
outputs:
sandbox-key:
description: the exact key of the mcpp sandbox cache
value: ${{ steps.sandbox.outputs.cache-primary-key }}
sandbox-hit:
description: "'true' when the sandbox was restored by its exact key"
value: ${{ steps.sandbox.outputs.cache-hit }}
xlings-key:
description: the exact key of the xlings cache
value: ${{ steps.xlings.outputs.cache-primary-key }}
xlings-hit:
description: "'true' when xlings was restored by its exact key"
value: ${{ steps.xlings.outputs.cache-hit }}
runs:
using: composite
steps:
# NOTE: the "-ci-" segment keeps this lineage disjoint from release.yml's
# "-release-" caches. A bare "mcpp-sandbox-<os>-" restore prefix used to
# match the release sandbox too, silently swapping in a differently
# populated registry (issue #120).
- name: Restore the mcpp sandbox
id: sandbox
uses: actions/cache/restore@v4
with:
path: ~/.mcpp
# `runner.arch` IS PART OF EVERY KEY, AND WAS NOT.
#
# These caches hold INSTALLED BINARIES — `~/.xlings`, `~/.mcpp`, and
# `target/`. Keyed on `runner.os` alone, `Linux` names two machines, and
# `ubuntu-24.04-arm` restored what `ubuntu-24.04` had stored. Measured
# the first time this repo scheduled an arm Linux runner:
#
# Cache hit for: xlings-Linux-v2-xl2026.8.17.2-…
# …/bin/xlings: cannot execute binary file: Exec format error
#
# AND IT SURVIVED THE FIX ONE LAYER DOWN. Reading `uname -m` to pick
# the right tarball is correct and was not enough: the download never
# happened, because a cache from the other architecture was already
# there. A key that does not name the machine is a key that lets one
# machine answer for another.
#
# The xlings version is part of the LINEAGE, not just the key: mcpp
# vendors xlings into ~/.mcpp/registry/bin once at `self init` and
# never revisits it (acquire_xlings_binary returns early when the file
# exists). With the version only in the key, restore-keys would hand a
# sandbox built by an OLDER xlings to a newer bootstrap, and the
# sandbox — which is what actually resolves dependencies — would
# silently stay behind (observed: a 0.4.30 sandbox surviving under a
# 0.4.69 bootstrap for weeks).
# ci.yml is part of the key because it names the toolchains the build
# job installs before it saves (`prewarm`): a sandbox saved under an
# unchanged key would never gain one added there.
key: mcpp-sandbox-${{ runner.os }}-${{ runner.arch }}-ci-xl${{ inputs.xlings-version }}-${{ hashFiles('mcpp.toml', '.xlings.json', '.github/workflows/ci.yml') }}
restore-keys: |
mcpp-sandbox-${{ runner.os }}-${{ runner.arch }}-ci-xl${{ inputs.xlings-version }}-
- name: Restore xlings
id: xlings
uses: actions/cache/restore@v4
with:
path: ~/.xlings
key: xlings-${{ runner.os }}-${{ runner.arch }}-v2-xl${{ inputs.xlings-version }}-${{ hashFiles('.xlings.json') }}
restore-keys: |
xlings-${{ runner.os }}-${{ runner.arch }}-v2-xl${{ inputs.xlings-version }}-
- name: Bootstrap mcpp via xlings (unix)
if: runner.os != 'Windows'
shell: bash
env:
XLINGS_NON_INTERACTIVE: '1'
XLINGS_VERSION: ${{ inputs.xlings-version }}
run: |
REPO_DIR="$(pwd)"
# Always install the pinned version — the cache may hold an older
# xlings whose sysroot/packages are incompatible.
# THE ARCHITECTURE IS READ, NOT ASSUMED. This branched on the OS
# alone and named `linux-x86_64` for every non-Darwin host, which is
# invisible until a Linux runner is not x86_64. Measured on
# `ubuntu-24.04-arm`, the first time this repo scheduled one:
#
# .../xlings-2026.8.17.2-linux-x86_64/subos/default/bin/xlings:
# cannot execute binary file: Exec format error
# Process completed with exit code 126
#
# xlings has published `linux-aarch64` all along; nothing here asked
# for it. `ci-aarch64-fresh-install.yml` never hit this because it
# bootstraps through `quick_install.sh`, which does read the arch.
case "$(uname -s)" in
Darwin) tarball="xlings-${XLINGS_VERSION}-macosx-arm64.tar.gz" ;;
*)
case "$(uname -m)" in
aarch64|arm64) xa=aarch64 ;;
*) xa=x86_64 ;;
esac
tarball="xlings-${XLINGS_VERSION}-linux-${xa}.tar.gz" ;;
esac
# FAST PATH: the xlings cache already holds the pinned version, so the
# tarball fetch + extract + `self install` are skipped. Without this
# guard every job of every CI run paid the download and the extract,
# measured at 5 to 30 seconds per job on Linux and ~30 seconds on
# Windows, across thirty jobs per run — about half the bootstrap-mcpp
# step on Windows, more the 7 seconds the unix leg pays. The cache key
# is `xl$VER` already; the check is the one case this guard would
# otherwise miss: a stale `xlings` cache from BEFORE the pin was
# bumped (a partial restore-key match hands the same OS/ARCH cache
# back, but with the previous release), or a binary that no longer runs
# because its dynamic loader is gone.
XL_BIN_PATH="$HOME/.xlings/subos/default/bin/xlings"
if [ -x "$XL_BIN_PATH" ]; then
xl_ver="$("$XL_BIN_PATH" --version 2>/dev/null | head -1 || true)"
if [ -n "$xl_ver" ] && echo "$xl_ver" | grep -qF "$XLINGS_VERSION"; then
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
echo "$HOME/.xlings/subos/default/bin" >> "$GITHUB_PATH"
"$XL_BIN_PATH" --version
MCPP=$(bash "$REPO_DIR/.github/tools/install_pinned_mcpp.sh" "$REPO_DIR")
echo "system xlings: $("$XL_BIN_PATH" --version 2>/dev/null | head -1)"
if [ -x "$HOME/.mcpp/registry/bin/xlings" ]; then
echo "sandbox xlings: $("$HOME/.mcpp/registry/bin/xlings" --version 2>/dev/null | head -1)"
else
echo "sandbox xlings: (not initialised yet)"
fi
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
echo "XLINGS_BIN=$XL_BIN_PATH" >> "$GITHUB_ENV"
exit 0
fi
fi
WORK=$(mktemp -d)
# Retried and verified — see .github/tools/fetch_release.sh. A bare curl
# here was the single largest source of unexplained CI red on this repo
# (`curl: (52) Empty reply from server`).
bash "$REPO_DIR/.github/tools/fetch_release.sh" \
"https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${tarball}" \
"${WORK}/${tarball}"
tar -xzf "${WORK}/${tarball}" -C "${WORK}"
# THE TWO LINUX TARBALLS DO NOT HAVE THE SAME SHAPE, and this line
# named one of them. Measured on the 2026.8.17.2 assets:
#
# linux-x86_64 → subos/default/bin/xlings (513 entries)
# linux-aarch64 → bin/xlings (494 entries)
#
# so fetching the right architecture — the fix immediately above — got
# as far as `No such file or directory`, exit 127.
#
# FOUND RATHER THAN ASSUMED, AND THE SEARCH IS BOUNDED. `-maxdepth 5`
# keeps it from wandering into the payload tree, and an empty result is
# a hard error: a bootstrap that silently continues without xlings fails
# forty lines later on something that reads like a different problem.
#
# CHECKED BEFORE CHANGING THE THREE HOSTS THAT ALREADY WORKED. On
# x86_64 this now resolves `bin/xlings` where the line before named
# `subos/default/bin/xlings` — and those are the same file:
#
# subos/default/bin/xlings -> ../../../bin/xlings (symlink)
# md5 94b8e2f2… for both
#
# A find that quietly moved macOS and Windows onto a different binary
# would have been a worse defect than the one being fixed.
XL_BOOT="$(find "${WORK}/${tarball%.tar.gz}" -maxdepth 5 -type f \
-name xlings -perm -u+x | head -1)"
[ -n "$XL_BOOT" ] || {
echo "::error::no xlings executable inside ${tarball}"
find "${WORK}/${tarball%.tar.gz}" -maxdepth 3 | head -20
exit 1
}
echo "bootstrap xlings: ${XL_BOOT#${WORK}/}"
"$XL_BOOT" self install
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`. 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
# ~/.mcpp is restored from cache by prefix. So it can lag the system
# one by a lot, and it — not the system binary — is what resolves
# dependencies. Print both; a mismatch is worth seeing in the log.
echo "system xlings: $(xlings --version 2>/dev/null | head -1)"
if [ -x "$HOME/.mcpp/registry/bin/xlings" ]; then
echo "sandbox xlings: $("$HOME/.mcpp/registry/bin/xlings" --version 2>/dev/null | head -1)"
else
echo "sandbox xlings: (not initialised yet)"
fi
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV"
- name: Bootstrap mcpp via xlings (windows)
if: runner.os == 'Windows'
shell: bash
env:
XLINGS_NON_INTERACTIVE: '1'
XLINGS_VERSION: ${{ inputs.xlings-version }}
run: |
REPO_DIR="$(pwd)"
# FAST PATH: see the unix leg for the reasoning. The cost on Windows is
# larger (the zip is bigger and the runner's network path to
# github.com is slower), measured at ~30 s per job.
#
# The fast path addresses xlings by ABSOLUTE PATH, not by bare
# `xlings.exe`. The cold path runs `xlings self install` first, which
# writes the dir into Windows PATH via `[Environment]::SetEnvironmentVariable`;
# without that step a bare `xlings.exe` call depends on the bash
# export PATH, which Git Bash re-derives from Windows on every child
# shell and drops the mixed-separator entry. install_pinned_mcpp.sh
# carries the same note.
XL_BIN_PATH="$USERPROFILE/.xlings/subos/default/bin/xlings.exe"
if [ -x "$XL_BIN_PATH" ]; then
xl_ver="$("$XL_BIN_PATH" --version 2>/dev/null | head -1 || true)"
if [ -n "$xl_ver" ] && echo "$xl_ver" | grep -qF "$XLINGS_VERSION"; then
export PATH="$USERPROFILE/.xlings/subos/default/bin:$PATH"
echo "$USERPROFILE/.xlings/subos/default/bin" >> "$GITHUB_PATH"
"$XL_BIN_PATH" --version
MCPP=$(bash "$REPO_DIR/.github/tools/install_pinned_mcpp.sh" "$REPO_DIR")
echo "system xlings: $("$XL_BIN_PATH" --version 2>/dev/null | head -1)"
if [ -x "$USERPROFILE/.mcpp/registry/bin/xlings.exe" ]; then
echo "sandbox xlings: $("$USERPROFILE/.mcpp/registry/bin/xlings.exe" --version 2>/dev/null | head -1)"
else
echo "sandbox xlings: (not initialised yet)"
fi
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
echo "XLINGS_BIN=$(cygpath -w "$XL_BIN_PATH")" >> "$GITHUB_ENV"
exit 0
fi
fi
WORK=$(mktemp -d)
zipfile="xlings-${XLINGS_VERSION}-windows-x86_64.zip"
# Same helper as the unix leg. This is the leg that kept failing, and a
# fix applied to only one of them is a fix half the CI does not get.
bash "$REPO_DIR/.github/tools/fetch_release.sh" \
"https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${zipfile}" \
"${WORK}/${zipfile}"
cd "${WORK}"
unzip -q "${zipfile}"
"$WORK/xlings-${XLINGS_VERSION}-windows-x86_64/subos/default/bin/xlings.exe" self install
export PATH="$USERPROFILE/.xlings/subos/default/bin:$PATH"
echo "$USERPROFILE/.xlings/subos/default/bin" >> "$GITHUB_PATH"
xlings.exe --version
# 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
echo "sandbox xlings: $("$USERPROFILE/.mcpp/registry/bin/xlings.exe" --version 2>/dev/null | head -1)"
else
echo "sandbox xlings: (not initialised yet)"
fi
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
echo "XLINGS_BIN=$(cygpath -w "$USERPROFILE/.xlings/subos/default/bin/xlings.exe")" >> "$GITHUB_ENV"
# Precise key on src/ + manifest so a no-source-change run lands on a full
# hit; layered restore-keys let partial hits keep BMI/dyndep state for a
# proper incremental build.