Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ jobs:
# would switch the guards off precisely when prose changes. The job is
# cheap enough that running it redundantly alongside the matrix on code
# PRs costs less than maintaining a list of which packages are guards.
#
# The capabilities gate rides along for the same reason. The semantic
# assertion — the checked-in capabilities page is the rendering of the
# YAML — is already a unit test; the make target adds the generator's
# own `go run` entry point, which release.yml runs against the tagged
# tree, so a broken target fails a PR here rather than a release.
unit:
name: unit tests (no Docker; docs guards)
runs-on: ubuntu-latest
Expand All @@ -82,6 +88,9 @@ jobs:
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version-file: go.mod
# The gate is one `go run` and a diff; it goes first so a broken
# generator is reported even when an unrelated unit test fails.
- run: make check-capabilities
- run: make test-unit

lint:
Expand Down Expand Up @@ -213,7 +222,7 @@ jobs:
# Single required status for branch protection ("all-green" is the
# context to require). Succeeds when nothing failed — including
# docs-only PRs, where the code-gated jobs were skipped but the unit
# job (and its docs guards) still had to pass.
# job (its docs guards and the capabilities gate) still had to pass.
all-green:
if: always()
needs: [changes, unit, lint, build, test, demo]
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ jobs:
# previous workflow could have poisoned.
cache: false

# A tag cannot ship a capabilities matrix that disagrees with its YAML.
- name: Check the capabilities matrix
run: make check-capabilities

# Belt and braces on top of the ancestry gate: re-run the suite against
# the exact tree being released.
- name: Test the tagged tree
Expand Down
25 changes: 22 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ PG_DSN_LOCAL = postgres://$(PG_USER):$(PG_PASSWORD)@localhost:$(PG_PORT)/$(PG_DA
# Which corpus replay project to run (replay/<project>/project.conf).
REPLAY_PROJECT ?= buzz

.PHONY: build gen-capabilities test test-unit test-db test-supported-postgres test-aws-boundary lint setup db-up db-down demos clean demo demo-seed demo-check replay replay-refresh replay-down
.PHONY: build gen-capabilities check-capabilities test test-unit test-db test-supported-postgres test-aws-boundary lint setup db-up db-down demos clean demo demo-seed demo-check replay replay-refresh replay-down

# The first target is make's default goal: keep build here so a bare
# `make` builds the binary rather than rewriting a checked-in document.
Expand Down Expand Up @@ -54,9 +54,28 @@ lint:
golangci-lint run

# Regenerate the marked regions of docs/capabilities.md from the embedded
# matrix (pkg/capabilities/capabilities.yaml); CI fails if they drift.
# matrix (pkg/capabilities/capabilities.yaml); CI fails if they drift. The
# gate below runs the same command, so the two cannot drift apart.
GEN_CAPABILITIES = $(GO) run ./internal/cmd/gen-capabilities

gen-capabilities:
$(GO) run ./internal/cmd/gen-capabilities
$(GEN_CAPABILITIES)

# Regenerate the capabilities page and fail if regeneration changed it. Only the
# generator's own edits count, so an uncommitted edit to the hand-written prose
# outside the marker regions does not trip the gate. The target is a pure
# check: on failure — a generator error or a diff — it puts the page back as it
# was before the run, so a rerun fails the same way and `make gen-capabilities`
# is the only command that writes the page.
check-capabilities:
@before=$$(mktemp); cp docs/capabilities.md "$$before"; \
$(GEN_CAPABILITIES) || { cp "$$before" docs/capabilities.md; rm -f "$$before"; exit 1; }; \
if ! diff -u --label docs/capabilities.md --label regenerated "$$before" docs/capabilities.md; then \
cp "$$before" docs/capabilities.md; rm -f "$$before"; \
echo "docs/capabilities.md disagrees with pkg/capabilities/capabilities.yaml; run make gen-capabilities and commit the result" >&2; \
exit 1; \
fi; \
rm -f "$$before"

# Configure git hooks (relative path so worktrees work too).
setup:
Expand Down
18 changes: 12 additions & 6 deletions docs/capabilities-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ path on T3 rows.
│ go:embed │ regenerate │ committed
▼ ▼ ▼
┌───────────────────────┐ ┌─────────────────────────────────────────────────────┐
│ pkg/capabilities │ │ CI gate: regenerate, then require an empty git diff
│ pkg/capabilities │ │ CI gate: regenerate, then require a no-op rewrite
└───────────┬───────────┘ └─────────────────────────────────────────────────────┘
Expand Down Expand Up @@ -154,10 +154,16 @@ markers — the introduction, tier explanation, legend, peer comparison, refusal
and operator recipes — remains hand-written. Generated output is deterministic: source
order is display order, formatting has no timestamps, and a second generation is a no-op.

The generator lands with the YAML file, not later. A Make target runs its `go run`
entry point. CI runs that target and then fails unless `git diff --exit-code` is empty.
The test validates semantics; regenerate-and-diff proves the checked-in human page is
the rendering of the validated data.
The generator lands with the YAML file, not later. `make check-capabilities` runs its
`go run` entry point and fails unless regeneration leaves the page byte-identical; only
the generator's own edits count, so an uncommitted edit to the hand-written prose does
not trip it, and a failing run — a generator error or a diff — restores the page as it was
before the run, so the check has no side effect and `make gen-capabilities` is the one
command that writes it. The unconditional
unit job in `.github/workflows/ci.yml` runs that target beside the unit tests on code and
docs-only changes alike, and `.github/workflows/release.yml` repeats it for the tagged
tree before the test sweep. The test validates semantics; regenerate-and-diff proves the
checked-in human page is the rendering of the validated data.

The capability-statement rule still applies beyond the generated matrix. A behavior
change updates the YAML, [limitations.md](limitations.md), and the README's short
Expand Down Expand Up @@ -261,7 +267,7 @@ prerequisite for the others; the rest land independently:
and markers together, making the repository single-source on day one; *(done)*
2. add `pg-sprite capabilities`, including `--json` and the embedded binary version;
*(done)*
3. add the regenerate-and-diff CI gate to the normal pipeline; and *(pending)*
3. add the regenerate-and-diff CI gate to the normal pipeline; and *(done)*
4. add documentation and `jq` recipes for consumers. *(done)*

The generator is part of the first step rather than a cleanup step: there is never an
Expand Down
Loading