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
268 changes: 268 additions & 0 deletions .agents/docs/2026-10-17-upgrade-continuity-design.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion .agents/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ superseded_by: 2026-09-07-....md # when status is superseded
---
```

332 records.
333 records.

## By subject

Expand All @@ -31,6 +31,7 @@ Records that declare one. Everything else is listed by date below.

### design

- [升级的连续性:打包修订不在使用路径上替换载荷、替换失败不拆掉可用状态、home 中的悬空记录自愈、真实的升级 lane](2026-10-17-upgrade-continuity-design.md) — active
- [一处来源,一种含义:工作空间的传递语义、生成输出、模块角色与首次运行(#786、#785、#778、#790)](2026-10-10-one-source-one-meaning-design.md) — landed
- [2026.10.5.3 发布方案:32 位 x86 的架构词汇、资源编译器的识别与增量(#776 后续)](2026-10-06-windows-x86-arch-vocabulary-and-rc-follow-ups-design.md) — active
- [下一个版本的发布方案:标准库模块、原生 MSVC LTO 与导出发现、共享库的链接配置、Windows 参数引号(#768 后续、#770、#771)](2026-10-05-std-module-pair-msvc-lto-and-export-discovery-design.md) — active
Expand Down Expand Up @@ -129,6 +130,7 @@ Records that declare one. Everything else is listed by date below.

### 2026-10

- [升级的连续性:打包修订不在使用路径上替换载荷、替换失败不拆掉可用状态、home 中的悬空记录自愈、真实的升级 lane](2026-10-17-upgrade-continuity-design.md) — active
- [mcpp 对 xlings 的依赖:Provider 端口、归属与位置、上游复用、系统级安装、payload 不可变、文件系统能力与长路径(#809)](2026-10-16-xlings-provider-port-and-shared-store-design.md) — active
- [构建依赖的一条管线:配置与产物两轴、包的实例、库接口、兼容系统与工作空间收尾(#797)](2026-10-11-build-dependencies-one-pipeline-design.md) — active
- [一处来源,一种含义:工作空间的传递语义、生成输出、模块角色与首次运行(#786、#785、#778、#790)](2026-10-10-one-source-one-meaning-design.md) — landed
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/bootstrap-mcpp/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,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.10.16.2'
default: '2026.10.17.1'

outputs:
sandbox-key:
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.10.16.2'
default: '2026.10.17.1'
image:
description: >
The runner label the job runs on (macos-15, xcode-27). It is part of the
Expand Down
197 changes: 197 additions & 0 deletions .github/tools/upgrade_lane.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#!/usr/bin/env bash
# upgrade_lane.sh <variant> <new-mcpp> <new-xlings> -- a home the previous
# release made keeps working under this commit's mcpp (design 2026-10-17 D9).
#
# Every other lane starts from an empty home, so the installed packaging
# revision always equals the index's and an upgrade is never executed. A user
# upgraded to 2026.10.16.3 and every `mcpp run` failed from then on; nothing
# in CI could see it. This lane builds the home with the PREVIOUS RELEASE --
# the newest published release that is not this commit's version, read from
# GitHub, so it moves forward with every release -- and then runs this
# commit's mcpp in it, laid out as a release (bin/mcpp, registry/bin/xlings).
#
# variant:
# plain the previous release installs from today's index.
# snapshot the previous release installs from the xim index of 2026-10-07
# (xim-pkgindex ebf1fbb): payloads genuinely of older packaging
# revisions (llvm revision 0, gcc 16.1.0 before -r1).
# orphan every payload recorded at another revision, and `c++` active at
# gcc's version whose registration is gone (a reported home).
#
# Asserted, every variant: the first `mcpp new && mcpp run` of the new mcpp
# succeeds and downloads nothing; a second run likewise; `mcpp self doctor`
# in the project installs nothing and names the toolchain the build uses.
# snapshot: `mcpp index update` names the newer packagings, the run after it
# still downloads nothing, and `mcpp toolchain list` offers the new one.
# orphan: `mcpp toolchain install` replaces the payload, and the dangling
# record is reported as repaired instead of refusing the replacement.
set -uo pipefail
VARIANT="${1:?variant}"
NEW_MCPP="${2:?new mcpp binary}"
NEW_XLINGS="${3:?new xlings binary}"
SNAPSHOT=ebf1fbb3417ec513b923279044433887b3af8850
TC=llvm@23.1.3

unset MCPP_VENDORED_XLINGS MCPP MCPP_FRESH MCPP_BOOT XLINGS_HOME XLINGS_PROJECT_DIR XLINGS_ACTIVE_SUBOS
W="${RUNNER_TEMP:-/tmp}/upgrade-lane"
rm -rf "$W"; mkdir -p "$W"
export MCPP_HOME="$W/home"
REG="$MCPP_HOME/registry"
PY=$(command -v python3 || command -v python)
FAILED=0
fail() { echo "::error::upgrade lane ($VARIANT): $*"; FAILED=1; }

case "$(uname -s)-$(uname -m)" in
Linux-x86_64) PLAT=linux-x86_64; EXT=tar.gz; EXE= ;;
Linux-aarch64) PLAT=linux-aarch64; EXT=tar.gz; EXE= ;;
Darwin-arm64) PLAT=macosx-arm64; EXT=tar.gz; EXE= ;;
MINGW*|MSYS*) PLAT=windows-x86_64; EXT=zip; EXE=.exe ;;
*) echo "::error::unknown platform $(uname -a)"; exit 1 ;;
esac
IS_LINUX=0; [[ $PLAT == linux-* ]] && IS_LINUX=1

# ── the previous release ───────────────────────────────────────────────
CURRENT=$(sed -n 's/^version *= *"\([^"]*\)".*/\1/p' mcpp.toml | head -1)
OLD_VER=$(curl -fsSL ${GH_TOKEN:+-H "Authorization: Bearer $GH_TOKEN"} \
"https://api.github.com/repos/mcpp-community/mcpp/releases?per_page=20" \
| "$PY" -c '
import json, sys
current = sys.argv[1]
for r in json.load(sys.stdin):
v = r["tag_name"].lstrip("v")
if not r["draft"] and not r["prerelease"] and v != current:
print(v); break
' "$CURRENT")
case "$OLD_VER" in ''|*[!0-9.]*) echo "::error::no previous release resolved (got '$OLD_VER')"; exit 1 ;; esac
echo "previous release: $OLD_VER; this commit: $CURRENT ($PLAT, variant $VARIANT)"

name="mcpp-$OLD_VER-$PLAT.$EXT"
bash .github/tools/fetch_release.sh \
"https://github.com/mcpp-community/mcpp/releases/download/v$OLD_VER/$name" "$W/$name" || exit 1
mkdir -p "$W/x"
if [[ $EXT == zip ]]; then
"$PY" -c "import zipfile,sys; zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])" "$W/$name" "$W/x"
else
tar xzf "$W/$name" -C "$W/x"
fi
mv "$W/x/mcpp-$OLD_VER-$PLAT" "$W/old"
OLD="$W/old/bin/mcpp$EXE"
mkdir -p "$W/new/bin" "$W/new/registry/bin"
cp "$NEW_MCPP" "$W/new/bin/mcpp$EXE"
cp "$NEW_XLINGS" "$W/new/registry/bin/xlings$EXE"
chmod +x "$OLD" "$W/old/registry/bin/xlings$EXE" "$W/new/bin/mcpp$EXE" "$W/new/registry/bin/xlings$EXE" 2>/dev/null
NEW="$W/new/bin/mcpp$EXE"
"$OLD" --version; "$NEW" --version

# run <label> <dir> <bin> args...: the log is kept; returns the exit code.
run() {
local label=$1 dir=$2 bin=$3; shift 3
echo "::group::$label: (cd $dir && mcpp $*)"
(cd "$dir" && "$bin" "$@") > "$W/$label.log" 2>&1
local rc=$?
sed -e 's/\x1b\[[0-9;]*[A-Za-z]//g' "$W/$label.log" | tr '\r' '\n' | grep -v '^\s*Planning ' | tail -60
echo "--> rc=$rc"
echo "::endgroup::"
return $rc
}
downloads() { grep -o "Downloading [^ ]*" "$W/$1.log" | sort -u | tr '\n' ' '; }
new_project() { # <dir> <bin>
(cd "$W" && "$2" new "$1" > /dev/null 2>&1) || mkdir -p "$W/$1"
printf '\n[toolchain]\ndefault = "%s"\n' "$TC" >> "$W/$1/mcpp.toml"
}

# ── the previous release builds the home ───────────────────────────────
run old-env "$W" "$OLD" self env || fail "the previous release does not start"
if [ "$VARIANT" = snapshot ]; then
git clone -q --filter=blob:none https://github.com/openxlings/xim-pkgindex "$W/xpi" \
&& git -C "$W/xpi" checkout -q "$SNAPSHOT" || { echo "::error::cannot fetch the snapshot index"; exit 1; }
idx="$REG/data/xim-pkgindex"
[ -d "$idx" ] || idx=$(find "$MCPP_HOME" -type d -name xim-pkgindex | head -1)
find "$idx" -mindepth 1 -maxdepth 1 ! -name .xlings-index-version -exec rm -rf {} +
(cd "$W/xpi" && git archive HEAD) | tar x -C "$idx"
echo "llvm.lua revisions now: $(grep -o 'revision = [0-9]*' "$idx/pkgs/l/llvm.lua" | sort -u | tr '\n' ' ')"
fi
run old-tc "$W" "$OLD" toolchain install "$TC" || fail "the previous release cannot install $TC"
[ "$VARIANT" = orphan ] && { run old-tc-gcc "$W" "$OLD" toolchain install gcc@16.1.0 || fail "the previous release cannot install gcc"; }
new_project p-old "$OLD"
run old-run "$W/p-old" "$OLD" run || fail "the previous release cannot run a new project"

if [ "$VARIANT" = orphan ]; then
"$PY" - "$REG" <<'PY' || fail "could not shape the orphan home"
import glob, json, os, sys
reg = sys.argv[1]
for f in glob.glob(os.path.join(reg, "data", "xpkgs", "xim-x-*", "*", ".xpkg-install.json")):
d = json.load(open(f)); r = d.get("revision", 0)
d["revision"] = 1 if r == 0 else r - 1
json.dump(d, open(f, "w"), indent=2)
wsf = os.path.join(reg, "subos", "default", ".xlings.json")
wsdoc = json.load(open(wsf)); ws = wsdoc["workspace"]
dbf = os.path.join(reg, "data", "versions.json")
src = json.load(open(dbf))["versions"] if os.path.exists(dbf) else json.load(open(os.path.join(reg, ".xlings.json")))["versions"]
owners = {k: (v.get("bindingGroup") or {}).get("provider") for k, v in src.get("c++", {}).get("versions", {}).items()}
keys = [k for k, p in owners.items() if p and "gcc" in p]
if not keys: sys.exit("no gcc-owned c++ registration: %r" % owners)
key = keys[0]
entry = ws.get("c++")
if isinstance(entry, dict): entry["active"] = key
else: ws["c++"] = key
json.dump(wsdoc, open(wsf, "w"))
for path in (dbf, os.path.join(reg, ".xlings.json")):
if not os.path.exists(path): continue
doc = json.load(open(path)); c = doc.get("versions", {}).get("c++", {})
c.get("versions", {}).pop(key, None)
for edges in c.get("bindings", {}).values(): edges.pop(key, None)
json.dump(doc, open(path, "w"))
print("c++ active at %s, its registration removed" % key)
PY
fi

# ── this commit's mcpp in that home ────────────────────────────────────
if [ "$VARIANT" = snapshot ]; then
run new-run-on-old-index "$W/p-old" "$NEW" run || fail "the old project does not run on the old index"
[ -z "$(downloads new-run-on-old-index)" ] || fail "the run on the old index downloaded: $(downloads new-run-on-old-index)"
run new-index-update "$W" "$NEW" index update || fail "index update failed"
grep -q "newer packagings" "$W/new-index-update.log" \
|| fail "index update does not name the newer packagings of installed payloads"
fi
new_project p-new "$NEW"
run new-run1 "$W/p-new" "$NEW" run || fail "the first run of the new mcpp failed"
[ -z "$(downloads new-run1)" ] || fail "the first run downloaded: $(downloads new-run1)"
# A payload replaced names its package (`Updating llvm@23.1.3 packaging
# revision 0 → 1`); the vendored xlings update is not one.
grep -Eq "^\s*(Updating|Reinstalling) [^ ]+@" "$W/new-run1.log" && fail "the first run replaced a payload"
run new-run2 "$W/p-new" "$NEW" run || fail "the second run failed"
[ -z "$(downloads new-run2)" ] || fail "the second run downloaded: $(downloads new-run2)"
run new-run-old-project "$W/p-old" "$NEW" run || fail "the project the previous release made does not run"
[ -z "$(downloads new-run-old-project)" ] || fail "the old project's run downloaded: $(downloads new-run-old-project)"

run new-doctor "$W/p-new" "$NEW" self doctor
[ -z "$(downloads new-doctor)" ] || fail "doctor downloaded: $(downloads new-doctor)"
grep -q "what a build here uses" "$W/new-doctor.log" || fail "doctor does not name the toolchain the build uses"

if [ "$VARIANT" = snapshot ]; then
run new-toolchain-list "$W" "$NEW" toolchain list || fail "toolchain list failed"
grep -q "packaging revision .* available" "$W/new-toolchain-list.log" \
|| fail "toolchain list does not offer the newer packaging"
fi
if [ "$VARIANT" = orphan ]; then
run new-provision "$W" "$NEW" toolchain install "$TC" || fail "toolchain install could not replace the payload"
grep -q "installed asset registration missing" "$W/new-provision.log" \
&& fail "the replacement was refused by the dangling record"
grep -qi "deactivated\|dangling" "$W/new-provision.log" \
|| fail "the repaired record is not reported"
run new-run-after-provision "$W/p-new" "$NEW" run || fail "the run after the replacement failed"
fi

{
echo "### upgrade lane: $PLAT / $VARIANT ($OLD_VER → $CURRENT)"
echo
for l in "$W"/*.log; do
b=$(basename "$l" .log)
echo "- \`$b\`: downloads [$(downloads "$b")]"
done
} >> "${GITHUB_STEP_SUMMARY:-/dev/null}"
mkdir -p "${GITHUB_WORKSPACE:-.}/upgrade-lane-logs"
cp "$W"/*.log "${GITHUB_WORKSPACE:-.}/upgrade-lane-logs/" 2>/dev/null || true
[ "$FAILED" = 0 ] && echo "PASS: upgrade lane ($VARIANT)"
exit "$FAILED"
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.10.16.2'
XLINGS_VERSION: '2026.10.17.1'
steps:
- uses: actions/checkout@v4

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci-bootstrap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
os: [ubuntu-24.04, macos-15, windows-2025]
env:
XLINGS_NON_INTERACTIVE: '1'
XLINGS_VERSION: '2026.10.16.2'
XLINGS_VERSION: '2026.10.17.1'
steps:
- uses: actions/checkout@v4

Expand All @@ -50,7 +50,7 @@ jobs:
shell: bash
run: |
set -euo pipefail
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.16.2
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.17.1
echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH"
echo "$HOME/.xlings/bin" >> "$GITHUB_PATH"

Expand Down Expand Up @@ -124,7 +124,7 @@ jobs:
shell: bash
run: |
set -euo pipefail
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.16.2
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.17.1
echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH"

- name: Install the newest release and build the tree with it (mcpp build --strict)
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci-fresh-install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ jobs:
env:
XLINGS_NON_INTERACTIVE: '1'
run: |
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.16.2
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.17.1
echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH"

- name: Install mcpp and config mirror
Expand Down Expand Up @@ -315,7 +315,7 @@ jobs:

- name: Install xlings + mcpp
run: |
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.16.2
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.17.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 Down Expand Up @@ -464,7 +464,7 @@ 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.10.16.2
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.17.1
echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH"

- name: Install mcpp and config mirror
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 @@ -414,7 +414,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.10.16.2
curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh | bash -s v2026.10.17.1
export PATH="$HOME/.xlings/subos/current/bin:$PATH"
xlings update
xlings install mcpp -y -g
Expand Down
64 changes: 64 additions & 0 deletions .github/workflows/ci-upgrade.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: ci-upgrade

# A home the previous release made keeps working under this commit's mcpp
# (design .agents/docs/2026-10-17-upgrade-continuity-design.md, D9).
#
# Every other lane starts from an empty MCPP_HOME, so the packaging revision a
# payload records always equals the index's, and an upgrade is never executed.
# 2026.10.16.3 replaced every payload of an older revision inside the user's
# next `mcpp run`, and on a home with one dangling record failed every run;
# nothing in CI could see it. Here the PREVIOUS RELEASE (the newest published
# one that is not this commit's version, read from GitHub at run time, so the
# lane moves forward with every release) builds the home, and this commit's
# binary, laid out as a release with the xlings it pins, runs in it.
# .github/tools/upgrade_lane.sh holds the steps and the assertions.
#
# Called by ci.yml after each host's build, with the binary build.yml produced
# (use-built-mcpp, rule R1 of the 2026-10-02 CI record).

on:
workflow_call:

jobs:
upgrade:
name: upgrade from the previous release (${{ matrix.host }}, ${{ matrix.variant }})
strategy:
fail-fast: false
matrix:
include:
- { host: linux-x86_64, runs-on: ubuntu-24.04, variant: plain }
- { host: linux-x86_64, runs-on: ubuntu-24.04, variant: snapshot }
- { host: linux-x86_64, runs-on: ubuntu-24.04, variant: orphan }
- { host: linux-aarch64, runs-on: ubuntu-24.04-arm, variant: plain }
- { host: macos-arm64, runs-on: macos-15, variant: plain }
- { host: windows-x86_64, runs-on: windows-latest, variant: plain }
runs-on: ${{ matrix.runs-on }}
timeout-minutes: 60
steps:
- uses: actions/checkout@v4

- if: ${{ matrix.host != 'macos-arm64' }}
uses: ./.github/actions/bootstrap-mcpp
- if: ${{ matrix.host == 'macos-arm64' }}
uses: ./.github/actions/setup-macos-llvm
with:
image: macos-15

- uses: ./.github/actions/use-built-mcpp
with:
host: ${{ matrix.host }}

- name: The previous release's home runs this commit's mcpp without downloading or failing (${{ matrix.variant }})
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
test -x "$MCPP_VENDORED_XLINGS"
bash .github/tools/upgrade_lane.sh "${{ matrix.variant }}" "$MCPP_FRESH" "$MCPP_VENDORED_XLINGS"

- uses: actions/upload-artifact@v4
if: always()
with:
name: upgrade-lane-${{ matrix.host }}-${{ matrix.variant }}
path: upgrade-lane-logs/
if-no-files-found: ignore
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,12 @@ jobs:
needs: [build-linux, build-linux-arm, build-macos, build-windows]
uses: ./.github/workflows/ci-target-matrix.yml

# A home the previous release made, run by this commit's mcpp (D9 of the
# 2026-10-17 upgrade-continuity design).
upgrade:
needs: [build-linux, build-linux-arm, build-macos, build-windows]
uses: ./.github/workflows/ci-upgrade.yml

# Pull requests and dispatch only, as when it was a workflow of its own.
openkal:
needs: [build-linux, build-linux-arm, build-macos, build-windows]
Expand Down
Loading
Loading