ci: add analytics for installations #6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Install Smoke | |
| on: | |
| pull_request: | |
| paths: | |
| - "install.sh" | |
| - "install.ps1" | |
| - ".github/workflows/install-smoke.yml" | |
| # Direct pushes to main are a normal way to ship an installer fix here, and | |
| # without this trigger those land completely unverified — the PowerShell 5.1 | |
| # path in particular has no other gate. Scoped to main on purpose: a push to | |
| # a feature branch that also has a PR open would otherwise run this twice. | |
| # | |
| # Read this as a detector, not a gate. It starts after the commit is already | |
| # on main, so it cannot block a bad installer from being published — see the | |
| # workflow_run wiring in notify-landing.yml, which is what holds the | |
| # archcore.ai redeploy until these jobs pass. | |
| push: | |
| branches: [main] | |
| paths: | |
| - "install.sh" | |
| - "install.ps1" | |
| - ".github/workflows/install-smoke.yml" | |
| workflow_dispatch: | |
| # Second line of defence for install telemetry. Every job below runs the copy of | |
| # the installer committed to this repo, where the PostHog key is still the | |
| # `__POSTHOG_KEY__` placeholder — archcore.ai substitutes the real key only when | |
| # it syncs the script into public/ — so these runs are already inert. This makes | |
| # that inertness explicit rather than incidental, so a future step that fetches | |
| # the published installer instead cannot start reporting hundreds of phantom CI | |
| # installs before anyone notices. | |
| env: | |
| DO_NOT_TRACK: "1" | |
| jobs: | |
| windows: | |
| name: Windows ${{ matrix.shell }} (${{ matrix.mode }}) | |
| runs-on: windows-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| mode: [file, pipe] | |
| # `pwsh` is PowerShell 7 (HttpClient stack), `powershell` is Windows | |
| # PowerShell 5.1 (HttpWebRequest stack) — the version the script's | |
| # #Requires line targets. The two surface a redirect's Location header | |
| # through mutually exclusive shapes: a typed .Location property on PS7, | |
| # a string indexer on PS5.1, each throwing on the other's form under | |
| # Set-StrictMode. Testing only pwsh let a resolver that fails every | |
| # unpinned PS7 install pass CI, so both are exercised. | |
| shell: [pwsh, powershell] | |
| # `steps.*.shell` does not accept the `matrix` context (GitHub's | |
| # context-availability table only allows `matrix` on `run`, `if`, `name`, | |
| # `env`, and `defaults.run.shell` — not on a step's own `shell:` key), so | |
| # the per-step `shell: ${{ matrix.shell }}` lines that used to live below | |
| # were rejected as invalid. Setting it once here at the job level via | |
| # `defaults.run.shell` is the supported equivalent and still applies to | |
| # every `run:` step in this job. | |
| defaults: | |
| run: | |
| shell: ${{ matrix.shell }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install (file mode) | |
| if: matrix.mode == 'file' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: ${{ matrix.shell }} -NoProfile -File .\install.ps1 | |
| - name: Install (pipe mode via iex) | |
| if: matrix.mode == 'pipe' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Exercises the `irm | iex` path: script runs in-memory, $PSCommandPath | |
| # and $PSScriptRoot are empty. Catches any regressions that rely on | |
| # self-path references. | |
| run: Get-Content -Raw .\install.ps1 | Invoke-Expression | |
| - name: Assert install result | |
| run: | | |
| $bin = Join-Path $env:LOCALAPPDATA 'Programs\archcore\archcore.exe' | |
| if (-not (Test-Path $bin)) { | |
| throw "Binary not found at $bin" | |
| } | |
| & $bin --help | Out-Null | |
| if ($LASTEXITCODE -ne 0) { | |
| throw "archcore --help exited $LASTEXITCODE" | |
| } | |
| Write-Host "OK: $bin" | |
| unix: | |
| name: ${{ matrix.os }} (${{ matrix.mode }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest] | |
| mode: [file, pipe] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install (file mode) | |
| if: matrix.mode == 'file' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: bash install.sh | |
| - name: Install (pipe mode via bash) | |
| if: matrix.mode == 'pipe' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Exercises the curl-pipe path ($0 = bash, script on stdin). | |
| run: cat install.sh | bash | |
| - name: Assert install result | |
| run: | | |
| bin="$HOME/.local/bin/archcore" | |
| test -x "$bin" || { echo "Binary not found or not executable at $bin" >&2; exit 1; } | |
| "$bin" --help >/dev/null || { echo "archcore --help failed" >&2; exit 1; } | |
| echo "OK: $bin" | |
| anonymous: | |
| name: Ubuntu — no credentials | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Run the installer with no token in the environment | |
| # Every other job passes GITHUB_TOKEN, which would mask a regression | |
| # back to api.github.com: that endpoint allows 60 requests/hour per IP, | |
| # and an authenticated call sails through where an anonymous one on a | |
| # shared Actions runner address does not. Resolving the version from | |
| # the github.com redirect needs no credentials at all — this job is the | |
| # only place that actually proves it. | |
| env: | |
| GITHUB_TOKEN: "" | |
| run: bash install.sh | |
| - name: Assert install result | |
| run: | | |
| bin="$HOME/.local/bin/archcore" | |
| test -x "$bin" || { echo "Binary not found or not executable at $bin" >&2; exit 1; } | |
| "$bin" --help >/dev/null || { echo "archcore --help failed" >&2; exit 1; } | |
| echo "OK: $bin (anonymous install)" | |
| alpine: | |
| name: Alpine 3.19 (musl) | |
| runs-on: ubuntu-latest | |
| container: | |
| image: alpine:3.19 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install dependencies (bash is not default on Alpine) | |
| run: apk add --no-cache bash curl tar ca-certificates | |
| - name: Run installer | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Proves the CGO_ENABLED=0 Go binary runs on musl-libc without rebuild. | |
| run: bash install.sh | |
| - name: Assert install result | |
| run: | | |
| bin="$HOME/.local/bin/archcore" | |
| test -x "$bin" || { echo "Binary not found or not executable at $bin" >&2; exit 1; } | |
| "$bin" --help >/dev/null || { echo "archcore --help failed" >&2; exit 1; } | |
| echo "OK: $bin (musl-libc)" | |
| debian-guard: | |
| name: Debian 12 — guard fires on sh | |
| runs-on: ubuntu-latest | |
| container: | |
| image: debian:12-slim | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install curl and ca-certificates (no bash) | |
| # Intentionally do NOT install bash — we want /bin/sh = dash. | |
| run: | | |
| apt-get update | |
| apt-get install -y --no-install-recommends ca-certificates curl | |
| - name: Assert guard exits 1 when script is invoked under sh | |
| # Regression test for the "sh: Illegal option -o pipefail" class of | |
| # bugs: install.sh must detect non-bash shells and print a clear | |
| # remediation message instead of crashing mid-script on bashisms. | |
| run: | | |
| set +e | |
| output=$(sh install.sh 2>&1) | |
| exit_code=$? | |
| set -e | |
| echo "---- captured output ----" | |
| echo "$output" | |
| echo "---- exit code: $exit_code ----" | |
| if [ "$exit_code" -ne 1 ]; then | |
| echo "FAIL: expected exit code 1, got $exit_code" >&2 | |
| exit 1 | |
| fi | |
| echo "$output" | grep -qi "bash" || { | |
| echo "FAIL: expected 'bash' in output" >&2 | |
| exit 1 | |
| } | |
| echo "$output" | grep -q "install.sh | bash" || { | |
| echo "FAIL: expected remediation hint 'install.sh | bash'" >&2 | |
| exit 1 | |
| } | |
| echo "OK: guard fired with expected message" |