ci: harden installer canary and add pre-merge Bash 3.2 gate

Merged
JE
3mo ago
+221-1

Follow-on to the #558/#568 installer incident and the #569 canary: the canary had no Windows coverage, no alerting, and pre-merge CI had no real Bash 3.2 coverage.

Canary (installer-smoke.yml)

  • Push paths now include scripts/install.ps1 — the raw URL serves main, so merge == deploy for the PowerShell installer too.
  • Windows job runs the README's documented irm | iex shape under both Windows PowerShell 5.1 (the stock interpreter, exercising install.ps1's 5.1-specific branches) and pwsh 7, in a fail-fast-off matrix. Fetch and execution both happen inside the matrix interpreter (GitHub Actions doesn't evaluate expressions in steps[*].shell, so a pwsh launcher invokes it explicitly). Writes are contained under a scratch USERPROFILE/BASECAMP_BIN_DIR in RUNNER_TEMP; ?sha= cache-busts the raw URL (it varies its cache key on unknown query params). No basecamp.com endpoint exists for install.ps1, so the raw URL is the documented shape.
  • Notify job: on any leg's failure, files or updates an "Installer canary failure" issue (exact-title dedupe, best-effort gh calls, ::error:: annotation — same idiom as release.yml's skills-sync alert). Skipped entirely on green runs.

Pre-merge (test.yml): installer-bash32

Plain steps on macos-latest, not bats — bats 1.11 execs env bash at every layer, so the interpreter isn't pinnable through it. Asserts /bin/bash is exactly 3.2, syntax-checks install.sh, then runs it piped with curl off PATH and asserts the guard's clean failure message with no unbound-variable errors (the #558 regression class). Path-filtered to scripts/install.sh, e2e/installer.bats, and test.yml itself, so the job proves itself on this PR. Skipped filters still report job success, so it's safe to mark as a required check later (branch-protection change, out of repo).

Verification

Branch dispatches run the branch's workflow file against the published main installer — that validates the Windows matrix and notify mechanics, which is the correct scope for a post-deploy canary (PR-head script changes are covered pre-merge by bats + the new 3.2 gate, and post-merge by the push-triggered canary).

  • Green baseline dispatch: all four legs pass, notify skipped — run 30305890057.
  • Notify drill: temporarily broken Linux assert → notify filed #574 with per-leg results (closed as drill) — run 30306010077.
  • Post-revert dispatch: green again, notify skipped — run 30306162446.
  • bin/ci green (actionlint + zizmor included).

The workflow-level BASECAMP_NO_KEYRING stays in place for now: the published v0.7.2 binary probes the keyring eagerly on startup and would hang the macOS leg without it. The follow-up PR (lazy store construction + installer belts) removes it.

Refs #558 #568 #569


Summary by cubic

Hardens the installer canary with Windows coverage and alerting, and adds a pre-merge Bash 3.2 gate with a full offline install to catch regressions early.

  • New Features

    • Canary: trigger on scripts/install.ps1.
    • Canary: Windows matrix (PowerShell 5.1 and pwsh 7) running the documented irm | iex, with isolated HOME/bin and ?sha= cache-busting.
    • Canary: notify job files/updates an "Installer canary failure" issue on any leg failure; skipped on green.
    • Pre-merge: installer-bash32 (PRs only, path-filtered) on macos-latest.
    • Pre-merge: assert /bin/bash is 3.2, bash -n on install.sh, piped run fails cleanly without curl (no unbound vars).
    • Pre-merge: full offline install under Bash 3.2 using a fixture curl and local release + checksums; assert expected output and that the installed stub runs.
  • Bug Fixes

    • Permissions: grant pull-requests: read so dorny/paths-filter can enumerate changed files.