One generator rather than one per repo. The point of the contract is that four codebases agree on keys, types, scopes and defaults, and four independently written generators would be four chances to disagree. Go and TypeScript land in this repo; Kotlin and Swift are written into the sibling client checkouts, skipped with a note when they are not present so a server-only developer can still run it. Output is sorted by key so an unrelated manifest edit does not produce spurious diffs. The Kotlin output is the interesting one: it generates the DeviceSettings allowlist Android maintained by hand, plus the BOOLEAN_KEYS/INT_KEYS/ DOUBLE_KEYS classification it kept as a *second* hand-maintained table that had to agree with the first. Both are manifest questions now, so the whole class of "wrote a local key to the server" and "flushed a value the store could not parse" bugs stops being possible by construction. The TypeScript output carries the full definition table — labels, controls, enum members, bounds — so web/src/lib/settingsManifest.ts can be deleted rather than kept in sync: it declared 17 definitions against the contract's 49, with its own two-scope model that does not match the contract's five. make verify-settings-bindings fails when the committed output disagrees with the manifest, wired into CI, so a manifest change cannot merge leaving every client reading stale keys. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
165 lines
5.3 KiB
YAML
165 lines
5.3 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}" ./...
|
|
|
|
# A manifest change that does not regenerate leaves every client reading
|
|
# stale keys, which the contract exists to prevent.
|
|
- name: Verify settings bindings are current
|
|
run: make verify-settings-bindings
|
|
|
|
# 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
|