Give the seed a CI of its own

Merged
JE
2mo ago
+307-66

This repo has had no CI of its own: actions/* run live in consumers at @main, and seed/** is copied into new SDKs, with nothing checking either before merge. This is the minimal version of the self-CI proposal: one workflow, permissions: {} at the top, every action SHA-pinned.

What runs

actions job — over actions/ and this repo's own workflows:

  • zizmor (1.30.0, default policy: no findings tolerated)
  • shellcheck on every *.sh and test stub under actions/
  • bats on every *.bats under actions/ (today: the release-orchestrate suite from #9)

seed job — over the templates:

  • hack/render-seed.sh renders seed/ into .rendered/ the way prompts/seed-sdk.md describes (fill {{.Var}}, strip .tmpl, copy the rest verbatim) with fixture values for exactly the placeholder set AGENTS.md documents. Anything left unrendered fails the job: an undocumented placeholder, a pipeline the fixture does not know, or a placeholder in a non-.tmpl file (which would ship as literal text).
  • actionlint (v1.7.12, shellcheck included) on the rendered workflows, with the rendered tree as the project root so uses: ./.github/workflows/<reusable>.yml resolves against the seed's own files.
  • zizmor --min-severity high on the rendered workflows under hack/zizmor-seed.yml, which records the exceptions the templates carry today (@vN tags accepted via a ref-pin policy; cache-poisoning and excessive-permissions ignored for the four release templates that trip them) and why: they predate basecamp-sdk's SHA-pinned, zizmor-clean release workflows and each entry goes when its template is re-extracted from there.

.github/dependabot.yml keeps the pins moving (github-actions for / and /actions/*).

What the smoke found on its first run

release-go, release-kotlin, release-ruby, release-swift and release-typescript all call ./.github/workflows/smithy-verify.yml, a reusable workflow the seed never shipped — an SDK seeded from these templates has release workflows that fail to load. seed/.github/workflows/smithy-verify.yml.tmpl (reusable workflow wrapping basecamp/sdk/actions/smithy-verify) fills that in. hey-sdk, fizzy-sdk and basecamp-sdk each wrote their own long ago.

Also: the three templates that wrote >> $GITHUB_OUTPUT unquoted now quote it (actionlint SC2086), and smithy-verify takes its input through env:, pins setup-java, and calls the Smithy CLI by path instead of appending to GITHUB_PATH — the last three being what the actions job needs to be green, all behaviour-preserving.

Deliberately not here

  • A languages.json manifest with a consistency check across the machine surfaces (recon suggested it). Nothing would read it yet, and until the Rust templates land every surface lists the same five languages; it earns its keep when a sixth language touches all of them at once, so it is queued for that PR rather than added as an empty contract now.
  • make -n over the rendered Makefile: make -n still executes $(MAKE) -C go … lines, and the scaffold has no sub-Makefiles to recurse into.
  • Runner lockfile audits and the Smithy plugin's Gradle tests (#8 covers the former's current state); conformance fixture schema validation (basecamp-sdk runs the equivalent). Each is a small follow-on once this is in.

Stacking

Based on #9 and carrying #10's commits so the actions job is green from the first run; GitHub retargets to main when #9 merges, and #10's commits drop out on rebase once it merges. This PR's own change is the last commit.

Gates

Everything above run locally: render clean (76 templates, 6 verbatim files), actionlint clean, zizmor clean on actions/ + .github/workflows and on the rendered templates under the seed policy, shellcheck clean, bats 26/26. The workflow itself runs on this PR.