Builds the input-log fixture inside its cases #179
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: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| permissions: | |
| contents: read | |
| jobs: | |
| gate: | |
| name: Full quality gate | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| # ADR-0005: database-backed tests are ordinary tests in the ordinary | |
| # suite, against a real Postgres server - no tag skips them here either. | |
| # Credentials match docker-compose.yml's local `db` service so the PG* | |
| # env below is an override mechanism, not a setup step. | |
| services: | |
| postgres: | |
| image: postgres:17 | |
| env: | |
| POSTGRES_PASSWORD: postgres | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 10 | |
| env: | |
| PGHOST: localhost | |
| PGPORT: 5432 | |
| PGUSER: postgres | |
| PGPASSWORD: postgres | |
| PGDATABASE: statifier_persistence_test | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Single source of truth for the toolchain: a version duplicated into | |
| # this file drifts the first time mise.toml moves. mise itself is not | |
| # used to provision CI - it would build Erlang from source. | |
| - name: Read the toolchain out of mise.toml | |
| id: toolchain | |
| run: | | |
| set -euo pipefail | |
| erlang="$(sed -n 's/^erlang *= *"\(.*\)".*$/\1/p' mise.toml | head -1)" | |
| elixir="$(sed -n 's/^elixir *= *"\(.*\)".*$/\1/p' mise.toml | head -1)" | |
| test -n "$erlang" || { echo "no erlang version in mise.toml" >&2; exit 1; } | |
| test -n "$elixir" || { echo "no elixir version in mise.toml" >&2; exit 1; } | |
| echo "erlang=$erlang" >> "$GITHUB_OUTPUT" | |
| echo "elixir=$elixir" >> "$GITHUB_OUTPUT" | |
| - uses: erlef/setup-beam@v1 | |
| with: | |
| otp-version: ${{ steps.toolchain.outputs.erlang }} | |
| elixir-version: ${{ steps.toolchain.outputs.elixir }} | |
| # _build carries the Dialyzer PLT | |
| # (_build/dev/dialyxir_erlang-*_elixir-*_deps-dev.plt), so this cache is | |
| # the PLT cache too. statifier is a Hex dependency whose resolved | |
| # version mix.lock pins, so the hash key moves whenever the engine pin | |
| # moves. mix.exs swaps that for a path dep when STATIFIER_PATH is set, | |
| # but CI never sets it, so the Hex pin in mix.lock is exactly what gets | |
| # built. The restore-keys fallback means a lockfile bump rebuilds the | |
| # PLT incrementally from the previous one rather than from scratch. | |
| # | |
| # Known tradeoff, the same one statifier-ex and statifier-ui accept: a | |
| # warm _build means the compile stage is incremental, so | |
| # warnings_as_errors only sees files this push actually recompiles. | |
| # Deleting the cache is how to get a from-scratch compile when one is | |
| # wanted. | |
| - name: Cache deps and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| deps | |
| _build | |
| key: mix-${{ runner.os }}-otp${{ steps.toolchain.outputs.erlang }}-ex${{ steps.toolchain.outputs.elixir }}-${{ hashFiles('mix.lock') }} | |
| restore-keys: | | |
| mix-${{ runner.os }}-otp${{ steps.toolchain.outputs.erlang }}-ex${{ steps.toolchain.outputs.elixir }}- | |
| - name: Fetch dependencies | |
| run: mix deps.get | |
| # The gate is the whole job. It formats, compiles with warnings as | |
| # errors, runs credo --strict, dialyzer, the deps audit, and the full | |
| # suite with coverage, printing detail under any failing stage - so a | |
| # red run is diagnosable from this log alone. Three stages report as | |
| # skipped and always will: doctor, gettext, and sobelow, none installed | |
| # here. | |
| # | |
| # Same reasoning as the toolchain step, applied to the gate itself: the | |
| # command is read out of .claude/wurk.json rather than written here, so | |
| # the gate CI runs and the gate an agent must have green before | |
| # committing are one definition with one place to change it. A gate.full | |
| # that grows a flag is picked up on the next run, with nothing to | |
| # remember. It is read and executed inside the step rather than | |
| # interpolated into `run:`, so a repository file never reaches the shell | |
| # as expression text. | |
| - name: Full quality gate | |
| run: | | |
| set -euo pipefail | |
| mapfile -t cmd < <(jq -er '.gate.full[]' .claude/wurk.json) | |
| test "${#cmd[@]}" -gt 0 || { echo "gate.full is empty" >&2; exit 1; } | |
| echo "gate command (.claude/wurk.json gate.full): ${cmd[*]}" | |
| "${cmd[@]}" |