Skip to content

test(cliffs): the residual is a SUPPRESSION, and the wider sweep is not worth capturing (#84) #129

test(cliffs): the residual is a SUPPRESSION, and the wider sweep is not worth capturing (#84)

test(cliffs): the residual is a SUPPRESSION, and the wider sweep is not worth capturing (#84) #129

Workflow file for this run

# The whole quality gate, run automatically. Until this file existed, `pnpm run
# verify` only ran when a human remembered to (or at deploy time, which can be
# days after the breaking merge) - see issue #54.
#
# This job deliberately runs `pnpm run verify` VERBATIM rather than re-listing
# its four phases as separate steps. `verify` is the single definition of "the
# repo is consistent"; splitting it here would create a second definition that
# can silently drift from package.json. Note it is `verify`, not `check` -
# `check` is `vp check --fix` and CI must never rewrite files.
#
# The production build runs too, in the separate `build` job below - see its
# own comment for why it is not a phase inside `verify`.
#
# What is deliberately NOT here:
# - `pnpm refs:sync`. It needs a Factorio binary and ~/GitHub/factorio-data,
# neither of which exists on a runner. `verify` is designed to pass with no
# Factorio installed and that property is what makes this workflow possible.
# - Any deploy step or credential. Cloudflare Pages does not build this repo
# (`deploy:app` uploads an already-built `dist`), so CI here is a check
# only and the job needs no secrets at all.
name: verify
on:
push:
branches: [main]
pull_request:
# So a run can be forced without an empty commit.
workflow_dispatch:
# Superseded pushes to the same PR are pointless at ~4 minutes a run, so those
# get cancelled. Pushes to `main` do NOT - `cancel-in-progress` there means a
# second merge kills the first one's run, and `main` is the branch the deploy
# ships from, so every commit on it should carry its own verdict.
#
# This is not hypothetical: on 2026-07-30 six PRs merged inside half an hour and
# the runs for `605a4cc` and `be5f592` were both cancelled by the merges that
# followed them. Neither commit is unverified in practice - each had a green
# `verify` on its own PR before the ruleset would let it merge - but "green on
# the PR" and "green as it sits on main" are different claims, and only the
# second one survives a rebase-free merge into a `main` that has since moved.
# Ruleset `EJ` has since set `strict_required_status_checks_policy: true`, which
# closes most of that gap by forcing a PR up to date before it can merge; a
# per-commit verdict on `main` is still the thing that proves it, so these runs
# stay uncancelled.
concurrency:
group: verify-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
# Minimum scope: the job only reads the tree. It writes no statuses, comments,
# packages or releases, so nothing beyond `contents: read` is granted.
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-latest
# `verify` is ~60-90s of work; 15 minutes is a hang detector, not a budget.
timeout-minutes: 15
env:
# `preview:test` shells out to wrangler (`wrangler types --check`). Keep it
# from phoning home for telemetry on a runner where nobody can answer the
# opt-in prompt.
WRANGLER_SEND_METRICS: "false"
steps:
# Third-party actions are pinned to a full commit SHA, never a moving tag.
# The trailing comment names the release each SHA is, and Renovate updates
# the SHA and that comment together - see .github/renovate.json5.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# No `version:` input on purpose. pnpm/action-setup >= 6 reads
# `devEngines.packageManager` from package.json, so the pnpm pin stays in
# exactly one place. Hard-coding it here would be a second pin to drift.
# This step must precede setup-node: `cache: pnpm` below resolves the
# store path by running pnpm, so pnpm has to be on PATH already.
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
# `.node-version` (26.5.0) is the single source of truth for the Node
# version, and this is its first real consumer - the file went from
# documentation to machinery when this workflow landed. `engines.node`
# stays a permissive floor and is deliberately NOT what CI runs.
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .node-version
cache: pnpm
# A full workspace install. Anything narrower can leave a sibling
# workspace's symlink dangling and produce fake
# `TS2307: Cannot find module 'vitest'` errors; a real full install is the
# one that reports `Scope: all 3 workspace projects`.
- run: pnpm install --frozen-lockfile
- run: pnpm run verify
# `verify` is check + type-check + tests. None of them build, so a change that
# passes all three and breaks the production build reached `main` unnoticed
# until somebody deployed - possibly days later, and landing on whoever was
# deploying rather than whoever broke it (issue #61). This job closes that.
#
# Deliberately a SEPARATE job rather than an extra phase inside `verify`:
# `deploy` already runs `pnpm build` immediately after `pnpm run verify`, so
# folding the build into `verify` would build twice on every deploy and slow
# the local gate people actually run by hand. Separate also means it runs in
# parallel with the test job instead of after it.
build:
runs-on: ubuntu-latest
# The build is ~1s of Rolldown after ~30s of setup. 15 minutes is a hang
# detector, matching the job above.
timeout-minutes: 15
steps:
# Default `fetch-depth: 1` is correct here, and that was MEASURED rather
# than assumed - #61 was filed believing the build needed deeper history.
# `scripts/buildStamp.ts` runs exactly three git commands:
# `rev-parse HEAD`, `rev-parse --short HEAD`, and `status --porcelain`.
# None of them read history, so a shallow checkout is enough and the
# earlier "the build stamp reads git history" was imprecise - it reads git
# *state*.
#
# Worth knowing rather than fixing: on a `pull_request` event checkout
# lands on the merge commit, so the stamp this job produces is a synthetic
# SHA that exists nowhere in the repo. Harmless, because CI never deploys
# its artifact - `deploy:app` builds locally and uploads that. If a CI
# build ever becomes the thing that ships, this needs revisiting.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
# This catches a build that FAILS. It does not enforce "zero warnings",
# which CLAUDE.md leans on ("anything that does appear is new and worth
# reading"). Both ways of enforcing that were considered and rejected:
#
# - Grepping the build output for /warn/i. `vp build` has no
# `--fail-on-warn` (checked against vp 0.2.6 `--help`), and this repo
# has already been burned once by grepping build output - a version
# grep returned zero because the minifier had rewritten the string.
# A false pass here is worse than no check.
# - A `build.rollupOptions.onLog` hook that throws on `warn`. Robust, but
# it would hard-fail every local build too the moment a dependency
# emits one benign warning, and dependency-sourced warnings are exactly
# what this repo has seen (zlib-asm needed two suppressions before #46
# removed it). The value is in reading a new warning, not in blocking on
# it.
#
# So warnings stay visible in this job's log and unenforced. If that ever
# needs to change, the `onLog` route is the sturdy one - not a grep.
- run: pnpm vp build