Skip to content

Commit 212e6c5

Browse files
authored
ci: add fresh-install workflow for first-time user experience (#63)
* ci: add fresh-install workflow for first-time user experience Validates the released mcpp binary via xlings on all platforms: - Linux: xlings install mcpp → mcpp build (self) → mcpp new → mcpp run + install LLVM → mcpp new → mcpp run (continue-on-error) - macOS: xlings install mcpp → mcpp build → mcpp new → mcpp run - Windows: xlings install mcpp → mcpp build → mcpp new → mcpp run No caches — simulates a clean machine. Catches issues like incomplete sysroot, stale cfg paths, missing xpkg dependencies. * ci: add Windows LLVM smoke test + ci-fresh-install workflow Main CI changes: - ci-windows.yml: add explicit "Toolchain: LLVM — mcpp new → build → run" smoke test (previously only soft fallback via || true) - All three main CIs (ci.yml, ci-macos.yml, ci-windows.yml) no longer have continue-on-error fresh user tests (removed in PR #62) Toolchain coverage after this change: Linux: gcc@16.1.0 ✓ musl-gcc@15.1.0 ✓ llvm@20.1.7 ✓ macOS: llvm@20.1.7 ✓ (comprehensive module tests) Windows: llvm@20.1.7 ✓ (explicit smoke test) New workflow: - ci-fresh-install.yml: tests released mcpp via xlings on clean machines (no caches). Validates real first-time user experience separately from PR code testing. * fix: ci.yml — install musl-gcc before setting default The musl-gcc test step assumed the toolchain was already cached. After cache clears, `toolchain default gcc@15.1.0-musl` fails with "not installed". Add explicit install before setting default. * ci: toolchain smoke tests build mcpp itself, not hello world Each toolchain now builds mcpp from source (self-host) instead of a trivial hello-world project. This validates that the toolchain can compile a real C++23 modules codebase. Coverage: Linux: gcc@16.1.0 builds mcpp ✓ musl-gcc@15.1.0 builds mcpp ✓ llvm@20.1.7 builds mcpp ✓ macOS: llvm@20.1.7 builds mcpp ✓ (default, in self-host smoke) Windows: llvm@20.1.7 builds mcpp ✓ (new step) * ci: each platform's every toolchain builds mcpp itself Merge redundant self-host smoke steps into unified toolchain steps. Each supported toolchain per platform does: clean → build mcpp → verify. Linux: gcc@16.1.0 (+ test), musl-gcc@15.1.0, llvm@20.1.7 macOS: llvm@20.1.7 Windows: llvm@20.1.7 * ci: complete CI architecture — dev CI + release validation CI Two-tier CI design: Tier 1 — PR/dev CI (ci.yml, ci-macos.yml, ci-windows.yml): Runs on every PR. Builds mcpp from source, runs tests + e2e, then verifies each platform's supported toolchains can build mcpp: Linux: gcc@16.1.0 (+test), musl-gcc@15.1.0, llvm@20.1.7 macOS: llvm@20.1.7 Windows: llvm@20.1.7 Tier 2 — Release validation CI (ci-fresh-install.yml): Manual trigger + daily schedule. Tests released mcpp via xlings on clean machines (no caches). For each platform, every supported toolchain: new → run (basic project) + build mcpp (self-host). Linux: gcc, musl-gcc, llvm — new+run + build mcpp macOS: llvm — new+run + build mcpp Windows: llvm — new+run + build mcpp ci-fresh-install no longer runs on PRs (it tests released mcpp, not PR code). Moved to workflow_dispatch + daily cron. * ci: temporarily enable ci-fresh-install on PR for validation * ci: ci-fresh-install — fix GCC version, remove PR trigger - GCC: don't set toolchain default (GCC is already default after xlings install mcpp), fixes "gcc@ is not installed" error - Remove pull_request trigger: ci-fresh-install tests released mcpp (not PR code), so it should not block PRs. Runs on push to main, workflow_dispatch, and daily schedule only. * fix: save mcpp binary before clean in toolchain smoke tests mcpp clean deletes target/ which contains the freshly-built binary. Copy it to /tmp before running clean so it survives. Also: clear Windows BMI cache (--bmi-cache) to avoid MSVC version mismatch errors from stale std.pcm. * fix: Windows packaging — find mcpp.exe from target/ instead of stale $MCPP_SELF * fix: CI toolchain tests actually use correct toolchain + assertions Review findings addressed: 1. Linux musl/LLVM steps were NOT actually using those toolchains (mcpp.toml [toolchain].default overrides global default). Fix: - musl: use `mcpp build --target x86_64-linux-musl` - LLVM: sed-override mcpp.toml before build - All steps: grep assertion on "Resolved <toolchain>" output 2. Windows: remove `|| true` on critical config/default commands that silently swallowed failures. 3. Windows: add explicit `mcpp new hello → mcpp run` smoke test (covers first-run user path that E2E skips due to missing fresh-sandbox capability). 4. All toolchain build steps: assert via grep that the expected toolchain was actually resolved. * fix: CI — tee to file instead of /dev/stderr (Windows compat), precise grep - Replace `tee /dev/stderr` with `tee build.log` (Git Bash on Windows doesn't have /dev/stderr) - All grep assertions read from build.log file - musl grep: "musl" → "Resolved gcc@15.1.0-musl" (more precise)
1 parent 3f9a369 commit 212e6c5

4 files changed

Lines changed: 204 additions & 40 deletions

File tree

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
name: ci-fresh-install
2+
3+
# Fresh install CI — validates the released mcpp binary via xlings.
4+
# Simulates a real first-time user on a clean machine (no caches).
5+
#
6+
# For each platform, tests every supported toolchain:
7+
# 1. mcpp new hello → mcpp run (basic project)
8+
# 2. mcpp build (build mcpp itself from source)
9+
#
10+
# This workflow tests released mcpp, not PR code.
11+
# It does NOT run on PRs — only on push to main, manual trigger, and daily schedule.
12+
# Failures here mean the released mcpp has issues, not that the PR is broken.
13+
14+
on:
15+
push:
16+
branches: [ main ]
17+
workflow_dispatch:
18+
schedule:
19+
# Run daily at 06:00 UTC to catch issues from xlings/runner updates
20+
- cron: '0 6 * * *'
21+
22+
concurrency:
23+
group: ci-fresh-install
24+
cancel-in-progress: true
25+
26+
jobs:
27+
# ──────────────────────────────────────────────────────────────────
28+
# Linux: gcc@16.1.0, musl-gcc@15.1.0, llvm@20.1.7
29+
# ──────────────────────────────────────────────────────────────────
30+
linux-fresh:
31+
name: Linux fresh install
32+
runs-on: ubuntu-24.04
33+
timeout-minutes: 60
34+
steps:
35+
- uses: actions/checkout@v4
36+
37+
- name: Install xlings + mcpp
38+
env:
39+
XLINGS_NON_INTERACTIVE: '1'
40+
run: |
41+
curl -fsSL https://github.com/d2learn/xlings/releases/download/v0.4.30/xlings-0.4.30-linux-x86_64.tar.gz | tar -xzf - -C /tmp
42+
/tmp/xlings-0.4.30-linux-x86_64/subos/default/bin/xlings self install
43+
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
44+
xlings install mcpp -y
45+
echo "$HOME/.xlings/subos/default/bin" >> "$GITHUB_PATH"
46+
mcpp --version
47+
48+
- name: "GCC: mcpp new → run"
49+
run: |
50+
cd "$(mktemp -d)"
51+
mcpp new hello_gcc
52+
cd hello_gcc
53+
mcpp run
54+
55+
- name: "GCC: build mcpp"
56+
run: |
57+
mcpp clean
58+
mcpp build
59+
60+
- name: "musl-gcc: mcpp new → run"
61+
run: |
62+
mcpp toolchain install gcc 15.1.0-musl
63+
mcpp toolchain default gcc@15.1.0-musl
64+
cd "$(mktemp -d)"
65+
mcpp new hello_musl
66+
cd hello_musl
67+
mcpp run
68+
69+
- name: "musl-gcc: build mcpp"
70+
run: |
71+
mcpp toolchain default gcc@15.1.0-musl
72+
mcpp clean
73+
mcpp build
74+
75+
- name: "LLVM: mcpp new → run"
76+
run: |
77+
mcpp self config --mirror GLOBAL
78+
mcpp toolchain install llvm 20.1.7
79+
mcpp toolchain default llvm@20.1.7
80+
cd "$(mktemp -d)"
81+
mcpp new hello_llvm
82+
cd hello_llvm
83+
mcpp run
84+
85+
- name: "LLVM: build mcpp"
86+
run: |
87+
mcpp toolchain default llvm@20.1.7
88+
mcpp clean
89+
mcpp build
90+
91+
# ──────────────────────────────────────────────────────────────────
92+
# macOS: llvm@20.1.7
93+
# ──────────────────────────────────────────────────────────────────
94+
macos-fresh:
95+
name: macOS fresh install
96+
runs-on: macos-15
97+
timeout-minutes: 30
98+
steps:
99+
- uses: actions/checkout@v4
100+
101+
- name: Install xlings + mcpp
102+
env:
103+
XLINGS_NON_INTERACTIVE: '1'
104+
run: |
105+
curl -fsSL https://github.com/d2learn/xlings/releases/download/v0.4.30/xlings-0.4.30-macosx-arm64.tar.gz | tar -xzf - -C /tmp
106+
/tmp/xlings-0.4.30-macosx-arm64/subos/default/bin/xlings self install
107+
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
108+
xlings install mcpp -y
109+
echo "$HOME/.xlings/subos/default/bin" >> "$GITHUB_PATH"
110+
mcpp --version
111+
112+
- name: "LLVM: mcpp new → run"
113+
run: |
114+
cd "$(mktemp -d)"
115+
mcpp new hello_mac
116+
cd hello_mac
117+
mcpp run
118+
119+
- name: "LLVM: build mcpp"
120+
run: |
121+
mcpp clean
122+
mcpp build
123+
124+
# ──────────────────────────────────────────────────────────────────
125+
# Windows: llvm@20.1.7 + MSVC STL
126+
# ──────────────────────────────────────────────────────────────────
127+
windows-fresh:
128+
name: Windows fresh install
129+
runs-on: windows-latest
130+
timeout-minutes: 30
131+
steps:
132+
- uses: actions/checkout@v4
133+
134+
- name: Install xlings + mcpp
135+
shell: pwsh
136+
env:
137+
XLINGS_NON_INTERACTIVE: '1'
138+
run: |
139+
Invoke-WebRequest -Uri "https://github.com/d2learn/xlings/releases/download/v0.4.30/xlings-0.4.30-windows-x86_64.zip" -OutFile "$env:TEMP\xlings.zip"
140+
Expand-Archive -Path "$env:TEMP\xlings.zip" -DestinationPath "$env:TEMP\xlings-extract" -Force
141+
& "$env:TEMP\xlings-extract\xlings-0.4.30-windows-x86_64\subos\default\bin\xlings.exe" self install
142+
$xlingsbin = "$env:USERPROFILE\.xlings\subos\default\bin"
143+
$env:PATH = "$xlingsbin;$env:PATH"
144+
xlings install mcpp -y
145+
$xlingsbin | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
146+
mcpp --version
147+
148+
- name: "LLVM: mcpp new → run"
149+
shell: pwsh
150+
run: |
151+
$tmp = New-TemporaryFile | ForEach-Object { Remove-Item $_; New-Item -ItemType Directory -Path $_ }
152+
Set-Location $tmp
153+
mcpp new hello_win
154+
Set-Location hello_win
155+
mcpp run
156+
157+
- name: "LLVM: build mcpp"
158+
shell: pwsh
159+
run: |
160+
mcpp clean
161+
mcpp build

.github/workflows/ci-macos.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -306,14 +306,14 @@ jobs:
306306
"$MCPP" toolchain default llvm@20.1.7
307307
bash tests/e2e/run_all.sh
308308
309-
- name: Self-host smoke (freshly-built mcpp builds itself again)
309+
- name: "Toolchain: LLVM — build mcpp (self-host)"
310310
run: |
311311
MCPP=$(find target -path "*/bin/mcpp" | head -1)
312312
MCPP=$(cd "$(dirname "$MCPP")" && pwd)/$(basename "$MCPP")
313313
test -x "$MCPP"
314-
export PATH="$(dirname "$MCPP"):$PATH"
314+
cp "$MCPP" /tmp/mcpp-fresh
315+
MCPP=/tmp/mcpp-fresh
316+
"$MCPP" toolchain default llvm@20.1.7
317+
"$MCPP" clean
315318
"$MCPP" build
316319
"$MCPP" --version
317-
echo ":: Self-host smoke PASS"
318-
319-
# Fresh user experience test moved to ci-fresh-install.yml (PR #63)

.github/workflows/ci-windows.yml

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,29 @@ jobs:
9696
export MCPP="$MCPP_SELF"
9797
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
9898
export MCPP_E2E_TOOLCHAIN_MIRROR=GLOBAL
99-
"$MCPP_SELF" self config --mirror GLOBAL 2>/dev/null || true
100-
"$MCPP_SELF" toolchain default llvm@20.1.7 2>/dev/null || true
99+
"$MCPP_SELF" self config --mirror GLOBAL
100+
"$MCPP_SELF" toolchain default llvm@20.1.7
101101
bash tests/e2e/run_all.sh
102102
103-
- name: Self-host smoke (freshly-built mcpp builds itself again)
103+
- name: "Toolchain: LLVM — mcpp new → run"
104104
shell: bash
105105
run: |
106106
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
107-
"$MCPP_SELF" build
108-
"$MCPP_SELF" --version
109-
echo ":: Self-host smoke PASS"
107+
TMP=$(mktemp -d)
108+
cd "$TMP"
109+
"$MCPP_SELF" new hello_win
110+
cd hello_win
111+
"$MCPP_SELF" run
110112
111-
# Fresh user experience test moved to ci-fresh-install.yml (PR #63)
113+
- name: "Toolchain: LLVM — build mcpp (self-host)"
114+
shell: bash
115+
run: |
116+
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
117+
cp "$MCPP_SELF" /tmp/mcpp-fresh.exe
118+
MCPP=/tmp/mcpp-fresh.exe
119+
"$MCPP" toolchain default llvm@20.1.7
120+
"$MCPP" clean --bmi-cache
121+
"$MCPP" build 2>&1 | tee build.log; grep -q "Resolved llvm@20.1.7" build.log
112122
113123
- name: Package Windows release zip
114124
id: package
@@ -118,9 +128,12 @@ jobs:
118128
WRAPPER="mcpp-${VERSION}-windows-x86_64"
119129
ZIPNAME="${WRAPPER}.zip"
120130
131+
MCPP_BIN=$(find target -name "mcpp.exe" -path "*/bin/*" | head -1)
132+
test -n "$MCPP_BIN" || { echo "FAIL: no mcpp.exe in target/"; exit 1; }
133+
121134
STAGING=$(mktemp -d)
122135
mkdir -p "$STAGING/$WRAPPER/bin" "$STAGING/$WRAPPER/registry/bin"
123-
cp "$MCPP_SELF" "$STAGING/$WRAPPER/bin/mcpp.exe"
136+
cp "$MCPP_BIN" "$STAGING/$WRAPPER/bin/mcpp.exe"
124137
printf '@echo off\r\n"%%~dp0bin\\mcpp.exe" %%*\r\n' > "$STAGING/$WRAPPER/mcpp.bat"
125138
cp README.md "$STAGING/$WRAPPER/" 2>/dev/null || true
126139
cp LICENSE "$STAGING/$WRAPPER/" 2>/dev/null || true

.github/workflows/ci.yml

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -128,39 +128,29 @@ jobs:
128128
"$MCPP" toolchain default gcc@16.1.0
129129
bash tests/e2e/run_all.sh
130130
131-
- name: Self-host smoke (freshly-built mcpp builds itself again)
131+
- name: Save freshly-built mcpp for toolchain tests
132132
run: |
133133
MCPP=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
134-
"$MCPP" build
135-
"$MCPP" test
134+
cp "$MCPP" /tmp/mcpp-fresh
135+
echo "MCPP=/tmp/mcpp-fresh" >> "$GITHUB_ENV"
136136
137-
- name: "Toolchain: GCC — mcpp new → build → run"
137+
- name: "Toolchain: GCC — build mcpp + test"
138138
run: |
139-
MCPP=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
140-
"$MCPP" toolchain default gcc@16.1.0
141-
TMP=$(mktemp -d)
142-
cd "$TMP"
143-
"$MCPP" new hello_gcc
144-
cd hello_gcc
145-
"$MCPP" run
139+
"$MCPP" clean
140+
"$MCPP" build 2>&1 | tee build.log; grep -q "Resolved gcc@16.1.0" build.log
141+
"$MCPP" test
146142
147-
- name: "Toolchain: musl-gcc — mcpp new → build → run"
143+
- name: "Toolchain: musl-gcc — build mcpp (--target)"
148144
run: |
149-
MCPP=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
150-
"$MCPP" toolchain default gcc@15.1.0-musl
151-
TMP=$(mktemp -d)
152-
cd "$TMP"
153-
"$MCPP" new hello_musl
154-
cd hello_musl
155-
"$MCPP" run
145+
"$MCPP" clean
146+
"$MCPP" build --target x86_64-linux-musl 2>&1 | tee build.log; grep -q "Resolved gcc@15.1.0-musl" build.log
156147
157-
- name: "Toolchain: LLVM — install + mcpp new → build → run"
148+
- name: "Toolchain: LLVM — build mcpp"
158149
run: |
159-
MCPP=$(realpath "$(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2)")
160150
"$MCPP" toolchain install llvm 20.1.7
161-
"$MCPP" toolchain default llvm@20.1.7
162-
TMP=$(mktemp -d)
163-
cd "$TMP"
164-
"$MCPP" new hello_llvm
165-
cd hello_llvm
166-
"$MCPP" run
151+
# Override project toolchain to use LLVM for this build
152+
sed -i 's/^default = "gcc@16.1.0"/default = "llvm@20.1.7"/' mcpp.toml
153+
"$MCPP" clean
154+
"$MCPP" build 2>&1 | tee build.log; grep -q "Resolved llvm@20.1.7" build.log
155+
# Restore
156+
sed -i 's/^default = "llvm@20.1.7"/default = "gcc@16.1.0"/' mcpp.toml

0 commit comments

Comments
 (0)