igneum/tools/ci/fixtures/bash-body-ok.ps1
igneum-labs a5533177d3 CI: bash bodies in PowerShell jobs pass bash -n (the lost-quote class)
Twice on 5 October 2026 a PowerShell job script carried a bash body inside a string, a quote was lost on the way
through PowerShell, and bash refused the body: pc1-cpu-prove.ps1 (first version) reported exit 0 having done
nothing, the 0.3.10 installer job failed in 4 s. tools/amd-prove/check-job-bash.sh covered only its own here-string.

tools/ci/bash-body-check.sh reads every *.ps1 under relay/playbooks/ and tools/, finds each bash body however it is
handed over (bash -c "...", bash -lc '...', bash -c $var, a + concatenation in parentheses, the Start-Process argument
list, a here-string written to a file that is later run with bash), unescapes it the way PowerShell would (backtick
escapes and "" in double-quoted strings, '' in single-quoted strings, here-strings verbatim; $var left as-is, a $(...)
subexpression replaced by ${PS_SUBEXPR}), and runs bash -n on it. One line per body with the file line of the error.
A body it sees but cannot read is "unextractable body" and fails too: a skip would be a hole in the class check.
bash 3.2 compatible; python3 for the extractor.

--self-test runs three fixtures under tools/ci/fixtures/: the correct shapes (8 bodies, must pass), the lost quotes
(the awk apostrophe, a dropped closing quote in a literal and in a variable; must fail with the line), and three
unreadable bodies (must fail). Wired into ci.yml next to the copied-sources check, self-test first. The current tree:
7 inline bodies in 3 playbooks, all parse. packaging/README-ship.md: the job-script rule (body to a file, bash <file>).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 21:38:41 +00:00

53 lines
3.7 KiB
PowerShell

# Fixture for tools/ci/bash-body-check.sh --self-test: every way a job script hands bash a body, all of them
# correct. The check must report 8 bodies ok and exit 0. Never run; a stand-in for a job script.
$ErrorActionPreference = 'Continue'
$distro = 'Ubuntu-24.04'
$job = Join-Path $env:TEMP 'igneum-fixture'
$pkgW = '/mnt/c/igneum-fixture/pkg'
$FIXTURES = 'block-56-transfers-3shards block-78-increment'
function WslPath($p) { '/mnt/c' + ($p.Substring(2) -replace '\\', '/') }
# 1. a double-quoted here-string written to a file and run with bash (the pc1-cpu-prove.ps1 shape, fixed version)
$bash = @"
set -uo pipefail
export PATH="`$HOME/.cargo/bin:`$PATH"; [ -f "`$HOME/.cargo/env" ] && . "`$HOME/.cargo/env"
stamp() { date -u +%Y-%m-%dT%H:%M:%SZ; }
PKG='$pkgW'; DEST="`$HOME/igneum-prove-cpu"
echo "RESULT wsl `$(stamp) cores `$(nproc) ram_total_mb `$(free -m | awk '/Mem:/ {print `$2}')"
for FX in $FIXTURES; do
awk -v fx="`$FX" '/Maximum resident set size/ {r=`$NF} END {print "RESULT " fx " max_rss_kb=" r}' "`$DEST/`$FX-time.txt"
echo "RESULT `$FX prove end `$(stamp) exit `${PIPESTATUS[0]}"
done
"@
$bashFile = Join-Path $job 'cpu-prove.sh'
[IO.File]::WriteAllText($bashFile, ($bash -replace "`r`n", "`n"), (New-Object System.Text.UTF8Encoding $false))
$chk = (& wsl.exe -d $distro -u root -- bash -n (WslPath $bashFile) 2>&1); if ($LASTEXITCODE -ne 0) { "RESULT syntax FAILED: $chk"; exit 1 }
& wsl.exe -d $distro -u root -- bash (WslPath $bashFile) 2>&1 | ForEach-Object { "$_" }
# 2. a single-quoted literal after -lc (the wsl-setup.ps1 shape)
& wsl.exe -d $distro -- bash -lc 'id; uname -r; nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null || echo "no GPU visible inside WSL"' 2>&1 | ForEach-Object { "$_" }
# 3. a double-quoted string in a variable, interpolation left as-is (the prover-setup.ps1 shape)
$linuxDir = '/mnt/c/igneum-prove/igneum-prove-wsl2'
$cmd = "echo igneum | sudo -S -v 2>/dev/null; sudo -n true || echo 'sudo still asks for a password'; cd $linuxDir && bash ./setup-wsl.sh"
& wsl.exe -d $distro -u igneum -- bash -lc $cmd 2>&1 | ForEach-Object { "$_" }
# 4. single-quoted pieces joined with + over three lines (the wsl-setup.ps1 shape)
$mk = 'id igneum >/dev/null 2>&1 || (useradd -m -s /bin/bash igneum && echo igneum:igneum | chpasswd); ' +
'echo "igneum ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/igneum && chmod 440 /etc/sudoers.d/igneum; ' +
'printf "[user]\ndefault=igneum\n" > /etc/wsl.conf; id igneum'
& wsl.exe -d $distro -u root -- bash -c $mk 2>&1 | ForEach-Object { "$_" }
# 5. a concatenation in parentheses with a variable in the middle (the shard-test.ps1 shape)
& wsl.exe -d $distro -u igneum -- bash -lc ("sudo -n true 2>/dev/null || echo 'sudo asks for a password'; cd " + $linuxDir + " && bash ./setup-wsl.sh") 2>&1 | ForEach-Object { "$_" }
# 6. the Start-Process argument list, -c as its own quoted argument
$p = Start-Process -FilePath 'wsl.exe' -ArgumentList @('-d', $distro, '--', 'bash', '-c', 'pkill -f igneum-prove-host; pkill -f prove-shard.sh; true') -NoNewWindow -PassThru
# 7. a single-quoted here-string piped to Set-Content, the file then run by a derived path
$body = @'
set -euo pipefail
cd "$HOME/igneum" && ./igneum-miner --help | sed 's/^/RESULT help /'
'@
$sh = Join-Path $job 'body.sh'
$body | Set-Content -Path $sh -Encoding ascii
$shW = WslPath $sh
& wsl.exe -d $distro -- bash $shW 2>&1 | ForEach-Object { "$_" }
# 8. -ec with a doubled quote and backtick escapes in a double-quoted string
& wsl.exe -d $distro -- bash -ec "echo ""started""; printf '%s`n' `"done`"" 2>&1 | ForEach-Object { "$_" }
# not bodies: a script file by path, and bash -n on a file
& wsl.exe -d $distro -u igneum -- bash /mnt/c/igneum-prove/igneum-prove-wsl2/prove-block.sh fixture core 2>&1 | ForEach-Object { "$_" }
exit 0