-
Notifications
You must be signed in to change notification settings - Fork 5
257 lines (222 loc) · 10.5 KB
/
ci-windows.yml
File metadata and controls
257 lines (222 loc) · 10.5 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
name: ci-windows
# Windows CI for mcpp — same flow as Linux (ci-linux.yml) and macOS (ci-macos.yml):
# xlings install mcpp → self-host build → E2E → smoke → package
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
concurrency:
group: ci-windows-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-test:
name: build + test (windows x64, self-host)
runs-on: windows-latest
timeout-minutes: 45
env:
MCPP_HOME: C:\Users\runneradmin\.mcpp
steps:
- uses: actions/checkout@v4
- name: Cache mcpp sandbox
uses: actions/cache@v4
with:
path: ~\.mcpp
key: mcpp-sandbox-${{ runner.os }}-${{ hashFiles('mcpp.toml', '.xlings.json') }}
restore-keys: |
mcpp-sandbox-${{ runner.os }}-
- name: Cache xlings
uses: actions/cache@v4
with:
path: ~\.xlings
key: xlings-${{ runner.os }}-v2-${{ hashFiles('.xlings.json') }}
restore-keys: |
xlings-${{ runner.os }}-v2-
- name: Bootstrap mcpp via xlings
shell: bash
env:
XLINGS_NON_INTERACTIVE: '1'
XLINGS_VERSION: '0.4.30'
run: |
WORK=$(mktemp -d)
zipfile="xlings-${XLINGS_VERSION}-windows-x86_64.zip"
curl -fsSL -o "${WORK}/${zipfile}" \
"https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/${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
xlings.exe install mcpp -y
echo "=== Searching for mcpp binary ==="
find "$USERPROFILE/.xlings" -name "mcpp.exe" -o -name "mcpp" 2>/dev/null | head -10
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; }
echo "Found mcpp at: $MCPP"
"$MCPP" --version
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
XLINGS_BIN=$(cygpath -w "$USERPROFILE/.xlings/subos/default/bin/xlings.exe")
echo "XLINGS_BIN=$XLINGS_BIN" >> "$GITHUB_ENV"
- name: Build mcpp from source (self-host)
shell: bash
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
"$MCPP" build
MCPP_SELF=$(find target -name "mcpp.exe" -path "*/bin/*" | head -1)
test -n "$MCPP_SELF" || { echo "FAIL: no mcpp.exe"; exit 1; }
MCPP_SELF=$(cd "$(dirname "$MCPP_SELF")" && pwd)/$(basename "$MCPP_SELF")
echo "Self-hosted binary: $MCPP_SELF"
"$MCPP_SELF" --version
echo "MCPP_SELF=$MCPP_SELF" >> "$GITHUB_ENV"
- name: Unit + integration tests via mcpp test
shell: bash
run: |
export MCPP_VENDORED_XLINGS=$(cygpath -w "$USERPROFILE/.xlings/subos/default/bin/xlings.exe")
"$MCPP_SELF" test
# Regression test for the Windows first-run "press Enter to advance" hang.
# Launches mcpp with an OPEN, EMPTY, never-closing stdin pipe. Without
# seal_stdin's Windows fix, any grandchild that reads stdin would inherit
# our pipe and block forever — caught by the timeout below. With the fix,
# every subprocess stdin is redirected from NUL → no possibility of hang.
- name: "Regression: mcpp survives open-empty-stdin (Windows hang fix)"
shell: pwsh
timeout-minutes: 15
env:
MCPP_VENDORED_XLINGS: ${{ env.XLINGS_BIN }}
run: |
$ErrorActionPreference = 'Stop'
# MCPP_SELF was set in a bash step as an MSYS-style path
# (e.g. /d/a/mcpp/...). PowerShell can't exec that — convert it
# to a native Windows path via the git-bash cygpath that ships
# on the runner.
$mcppExe = (& 'C:\Program Files\Git\usr\bin\cygpath.exe' -w $env:MCPP_SELF).Trim()
Write-Host "Resolved MCPP_SELF (Windows form): $mcppExe"
if (-not (Test-Path $mcppExe)) {
throw "MCPP_SELF after cygpath not found: $mcppExe"
}
$tmp = Join-Path $env:RUNNER_TEMP ("stdin-hang-test-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $tmp | Out-Null
Set-Location $tmp
& $mcppExe new hello_stdin
Set-Location hello_stdin
function Invoke-McppWithOpenStdin {
param([string]$McppPath, [string]$McppArgs, [int]$TimeoutSeconds = 300)
$psi = [System.Diagnostics.ProcessStartInfo]::new()
$psi.FileName = $McppPath
$psi.Arguments = $McppArgs
$psi.WorkingDirectory = (Get-Location).Path
$psi.UseShellExecute = $false
$psi.RedirectStandardInput = $true # parent holds child's stdin
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
# By default the child inherits the parent's env (we did not
# touch $psi.Environment) so MCPP_VENDORED_XLINGS / PATH / etc.
# propagate.
$p = [System.Diagnostics.Process]::Start($psi)
# Async-drain stdout/stderr so a full output buffer doesn't
# itself deadlock the child (separate failure mode from the
# stdin hang we're testing).
$stdoutTask = $p.StandardOutput.ReadToEndAsync()
$stderrTask = $p.StandardError.ReadToEndAsync()
# NEVER write or close $p.StandardInput — the pipe stays open
# and empty for the lifetime of the child. Any grandchild that
# reads stdin will block on this pipe → caught by WaitForExit.
if (-not $p.WaitForExit($TimeoutSeconds * 1000)) {
try { $p.Kill($true) } catch {}
Write-Host "----- captured stdout -----"
Write-Host $stdoutTask.Result
Write-Host "----- captured stderr -----"
Write-Host $stderrTask.Result
throw "REGRESSION: 'mcpp $McppArgs' HUNG with open-empty stdin after ${TimeoutSeconds}s. The Windows seal_stdin fix is not effective."
}
Write-Host "----- stdout -----"
Write-Host $stdoutTask.Result
Write-Host "----- stderr -----"
Write-Host $stderrTask.Result
if ($p.ExitCode -ne 0) {
throw "'mcpp $McppArgs' exited with code $($p.ExitCode) (no hang, but failed)."
}
}
Write-Host '=== T1: mcpp --version (sanity, fast path) ==='
Invoke-McppWithOpenStdin -McppPath $mcppExe -McppArgs '--version' -TimeoutSeconds 30
Write-Host '=== T2: mcpp build (full bootstrap + toolchain + dep resolve + compile) ==='
Invoke-McppWithOpenStdin -McppPath $mcppExe -McppArgs 'build' -TimeoutSeconds 600
Write-Host '=== T3: mcpp run (post-build run path) ==='
Invoke-McppWithOpenStdin -McppPath $mcppExe -McppArgs 'run' -TimeoutSeconds 120
Write-Host 'SUCCESS: mcpp completes with open-empty stdin → Windows seal_stdin fix verified.'
- name: E2E suite
shell: bash
# See ci-linux.yml — fail-fast on hung tests instead of burning the
# whole job budget. Per-test 600s timeout lives in run_all.sh.
timeout-minutes: 25
run: |
export MCPP="$MCPP_SELF"
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
export MCPP_E2E_TOOLCHAIN_MIRROR=GLOBAL
"$MCPP_SELF" self config --mirror GLOBAL
"$MCPP_SELF" toolchain default llvm@20.1.7
bash tests/e2e/run_all.sh
- name: "Toolchain: LLVM — mcpp new → run"
shell: bash
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
TMP=$(mktemp -d)
cd "$TMP"
"$MCPP_SELF" new hello_win
cd hello_win
"$MCPP_SELF" run
- name: "Toolchain: LLVM — build mcpp (self-host)"
shell: bash
run: |
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
cp "$MCPP_SELF" /tmp/mcpp-fresh.exe
MCPP=/tmp/mcpp-fresh.exe
"$MCPP" toolchain default llvm@20.1.7
"$MCPP" clean --bmi-cache
"$MCPP" build 2>&1 | tee build.log; grep -q "Resolved llvm@20.1.7" build.log
- name: Package Windows release zip
id: package
shell: bash
run: |
VERSION=$(awk -F '"' '/^version[[:space:]]*=/{print $2; exit}' mcpp.toml)
WRAPPER="mcpp-${VERSION}-windows-x86_64"
ZIPNAME="${WRAPPER}.zip"
MCPP_BIN=$(find target -name "mcpp.exe" -path "*/bin/*" | head -1)
test -n "$MCPP_BIN" || { echo "FAIL: no mcpp.exe in target/"; exit 1; }
STAGING=$(mktemp -d)
mkdir -p "$STAGING/$WRAPPER/bin" "$STAGING/$WRAPPER/registry/bin"
cp "$MCPP_BIN" "$STAGING/$WRAPPER/bin/mcpp.exe"
printf '@echo off\r\n"%%~dp0bin\\mcpp.exe" %%*\r\n' > "$STAGING/$WRAPPER/mcpp.bat"
cp README.md "$STAGING/$WRAPPER/" 2>/dev/null || true
cp LICENSE "$STAGING/$WRAPPER/" 2>/dev/null || true
XLINGS_EXE="$USERPROFILE/.xlings/subos/default/bin/xlings.exe"
[ -f "$XLINGS_EXE" ] && cp "$XLINGS_EXE" "$STAGING/$WRAPPER/registry/bin/xlings.exe"
mkdir -p dist
(cd "$STAGING" && 7z a -tzip "$ZIPNAME" "$WRAPPER")
cp "$STAGING/$ZIPNAME" "dist/$ZIPNAME"
(cd dist && sha256sum "$ZIPNAME" > "$ZIPNAME.sha256")
echo "zipname=$ZIPNAME" >> "$GITHUB_OUTPUT"
ls -la dist/
- name: Smoke-test the packaged zip
shell: bash
run: |
ZIPNAME="${{ steps.package.outputs.zipname }}"
WRAPPER="${ZIPNAME%.zip}"
SMOKE=$(mktemp -d)
(cd "$SMOKE" && unzip -q "$GITHUB_WORKSPACE/dist/$ZIPNAME")
"$SMOKE/$WRAPPER/bin/mcpp.exe" --version
test -f "$SMOKE/$WRAPPER/registry/bin/xlings.exe"
test -f "$SMOKE/$WRAPPER/mcpp.bat"
echo "Smoke-test passed"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: mcpp-windows-x86_64
path: |
dist/*.zip
dist/*.sha256