AGENTS.md told contributors CI ran the same checks as `make lint`, and the Go job ran only gofmt and vet. A change failing the documented Go lint gate passed all three jobs. Running the linter as-is is not an option: the tree has ~296 findings today, which is why this half of `make lint` was never enforced. Blocking every PR on a cleanup nobody has scheduled gets the gate deleted again, so CI runs with --new-from-merge-base and only the lines a branch touches have to be clean. The count can then only fall. golangci-lint is built from source at a pinned version rather than downloaded. A released binary refuses to run against a Go newer than the one it was built with, and go.mod here tracks Go closely enough that the current release already fails that way on 1.26.4. .golangci.yml declared version 2 while still using v1's issues.exclude-rules key. Current golangci-lint ignores it, so the "allow repeated strings and unchecked cleanup errors in tests" exclusions silently did not apply — 16 findings in test files that the config says to skip. Moved to linters.exclusions, which `golangci-lint config verify` accepts. The four lines this surfaced in scantrigger are fixed rather than excluded: its repeated status codes and messages are now named constants, so one condition cannot end up worded two ways. Also drops the workflow token to contents:read and stops persisting credentials in the three checkouts, neither of which any job needs. Reported by CodeRabbit and Codex review on #479.
160 lines
5.0 KiB
YAML
160 lines
5.0 KiB
YAML
name: CI
|
|
|
|
on:
|
|
pull_request:
|
|
push:
|
|
branches:
|
|
- main
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: ci-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
GOPROXY: https://proxy.golang.org,direct
|
|
GOPRIVATE: github.com/Silo-Server/*
|
|
GONOSUMDB: github.com/Silo-Server/*
|
|
# Pinned so a lint gate cannot change its mind between two runs of the same
|
|
# commit. Built from source below rather than downloaded: a released binary
|
|
# refuses to run against a Go version newer than the one it was built with,
|
|
# and go.mod tracks Go closely enough that this repo is regularly ahead.
|
|
GOLANGCI_LINT_VERSION: v2.12.2
|
|
|
|
# The default token is read-write. Nothing here needs to write, and a token
|
|
# that cannot push is one fewer thing a compromised dependency can reach.
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
go:
|
|
name: Go
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
with:
|
|
# golangci-lint needs the merge base to tell this branch's lines from
|
|
# the ones it inherited.
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
|
|
# github.com/h2non/bimg binds libvips through cgo and pkg-config, so
|
|
# nothing under ./... compiles without the headers. The Dockerfile
|
|
# installs the same package in its build stage.
|
|
- name: Install libvips
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y --no-install-recommends libvips-dev
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@v6
|
|
with:
|
|
go-version-file: go.mod
|
|
cache: true
|
|
|
|
# cmd/silo embeds the built frontend, so nothing under ./... compiles
|
|
# without web/dist. The Go jobs never serve it, so a placeholder is
|
|
# enough; the Docker workflow builds the real bundle.
|
|
- name: Stub the embedded frontend bundle
|
|
run: make embed-stub
|
|
|
|
- name: Build
|
|
run: go build ./...
|
|
|
|
- name: gofmt
|
|
run: |
|
|
unformatted="$(gofmt -l .)"
|
|
if [ -n "$unformatted" ]; then
|
|
echo "::error::gofmt is required on:"
|
|
echo "$unformatted"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Vet
|
|
run: go vet ./...
|
|
|
|
# Scoped to the lines this branch touched. The repo does not pass a full
|
|
# golangci-lint run today — there are a few hundred pre-existing findings,
|
|
# which is why the Go half of `make lint` has never been enforced — and
|
|
# blocking every PR on a cleanup nobody has done would just get the gate
|
|
# removed again. New and changed lines have to be clean, so the count only
|
|
# falls from here.
|
|
- name: Install golangci-lint
|
|
run: go install "github.com/golangci/golangci-lint/v2/cmd/golangci-lint@${GOLANGCI_LINT_VERSION}"
|
|
|
|
- name: Lint changed lines
|
|
env:
|
|
# A PR carries its target branch; a push to main compares against
|
|
# main's own history, which leaves the merge base at HEAD and lints
|
|
# nothing new. Read through the environment rather than interpolated
|
|
# into the script.
|
|
BASE_REF: ${{ github.base_ref || github.event.repository.default_branch }}
|
|
run: |
|
|
git fetch --no-tags origin "+refs/heads/${BASE_REF}:refs/remotes/origin/${BASE_REF}"
|
|
golangci-lint run --new-from-merge-base="origin/${BASE_REF}" ./...
|
|
|
|
# Runs the settings-contract gate among everything else: the embedded
|
|
# manifest must parse, satisfy its own schema, hold every structural
|
|
# invariant, and agree with the live settings registry on keys and
|
|
# defaults. Without this job those tests exist but never run.
|
|
- name: Test
|
|
run: make test-go
|
|
|
|
web:
|
|
name: Web
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: web
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
with:
|
|
persist-credentials: false
|
|
|
|
# The pnpm version comes from web/package.json's packageManager field —
|
|
# there is no package.json at the repo root, and `defaults.run` does not
|
|
# apply to an action's own inputs.
|
|
- name: Set up pnpm
|
|
uses: pnpm/action-setup@v4
|
|
with:
|
|
package_json_file: web/package.json
|
|
|
|
- name: Set up Node
|
|
uses: actions/setup-node@v5
|
|
with:
|
|
node-version: 22
|
|
cache: pnpm
|
|
cache-dependency-path: web/pnpm-lock.yaml
|
|
|
|
- name: Install
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Lint
|
|
run: pnpm run lint
|
|
|
|
- name: Format check
|
|
run: pnpm run format:check
|
|
|
|
- name: Typecheck and build
|
|
run: pnpm run build
|
|
|
|
# Includes the appearance-cache ownership tests, which are the regression
|
|
# guard for cross-account leaks in the localStorage warm start.
|
|
- name: Test
|
|
working-directory: .
|
|
run: make test-web
|
|
|
|
docs:
|
|
name: Docs hygiene
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Verify no local paths leaked into committed docs
|
|
run: make verify-local-paths
|