Skip to content

chore(ci): release & supply-chain hardening from the v0.13.0 retro#127

Merged
Exoridus merged 2 commits into
mainfrom
chore/ci-release-hardening
Jun 13, 2026
Merged

chore(ci): release & supply-chain hardening from the v0.13.0 retro#127
Exoridus merged 2 commits into
mainfrom
chore/ci-release-hardening

Conversation

@Exoridus

Copy link
Copy Markdown
Owner

Folds in the lessons from the v0.13.0 release, where every release-tooling bug surfaced only at tag-push time on an irreversible tag. Supersedes #123 (its prettier drift is absorbed here under a real CI gate).

CI gates

  • format:check in the Lint lane. Prettier was only enforced by the pre-push hook (bypassable; merged PRs were never format-gated), so 34 files drifted and blocked the v0.13.0 tag push. This absorbs that drift and gates it going forward.
  • PR-only Release dry run lanerelease:prepare --build --skip-zip, path-filtered to release-relevant changes. Runs pack → attw → external consumers on a cheap PR. The tilemap-missing-from-pack and attw-output-format regressions that broke the release would have failed HERE.

Release correctness

  • verify:release-matrix now requires a repository field on every official package. npm publish --provenance aborts without it — the exact cause of the v0.13.0 partial publish (Core published with provenance, particles failed mid-run).
  • scripts/release/RELEASING.md: the new-package OIDC bootstrap procedure (publish placeholder + Trusted Publisher config BEFORE first release — OIDC cannot first-publish) and the npm dist-tag add → E401-under-OIDC limitation with the manual-promotion runbook.

Supply chain

  • pnpm minimumReleaseAge: 4320 (3 days) — quarantines brand-new versions on every install, local AND CI, defending against publish-then-yank attacks. Complements the Dependabot cooldown (which only governs Dependabot PRs).
  • publint (pinned, via dlx) validates the published manifests — the layer attw does not cover.

Tooling pins

Output-gating tools must not drift the way attw@latest did mid-release:

  • typescript / eslint / typescript-eslint~ (patch-only; minors can add type errors / lint rules).
  • actionlint Docker image → 1.7.12.

Style

  • array-simple for the test/scripts/config eslint blocks too (was always-[]); autofixed T[]Array<T> for non-simple types, matching src/.

Dependabot

  • Group the vitest and eslint families across patch+minor so lockstep tools move together instead of as separate skew-prone PRs.

Validation

pnpm typecheck ✓ · pnpm test 3049 passed / 10 skipped ✓ · pnpm lint:all ✓ · pnpm format:check ✓ · pnpm verify:release-matrix ✓ (incl. new repository checks) · pnpm verify:lockstep

Folds in the lessons from the v0.13.0 release, where every release-tooling
bug surfaced only at tag-push time on an irreversible tag.

CI gates:
- Add `format:check` to the Lint lane. Prettier was only enforced by the
  pre-push hook (bypassable, and merged PRs were never format-gated), so
  drift accumulated and blocked the v0.13.0 tag push. Absorbs that drift.
- Add a PR-only `Release dry run` lane (`release:prepare --build --skip-zip`,
  path-filtered to release-relevant changes): packs + attw + external
  consumers on a cheap PR. The tilemap-missing-from-pack and attw-format
  regressions would have failed HERE instead of mid-release.

Release correctness:
- verify:release-matrix now asserts every official package has a `repository`
  field — `npm publish --provenance` aborts without it, which caused the
  v0.13.0 partial publish (Core published, particles failed mid-run).
- Add scripts/release/RELEASING.md: new-package OIDC bootstrap procedure and
  the `npm dist-tag add` E401-under-OIDC limitation + manual promotion runbook.

Supply chain:
- pnpm `minimumReleaseAge: 4320` (3 days) quarantines brand-new versions on
  every install, local and CI — defends against publish-then-yank attacks.
- publint (pinned, via dlx) validates the published manifests — the layer attw
  does not cover.

Tooling pins (output-gating tools must not drift like attw@latest did):
- typescript / eslint / typescript-eslint to `~` (patch-only).
- actionlint Docker image pinned to 1.7.12.

Style consistency:
- array-simple for test/scripts/config blocks too (was always-[] there);
  autofixed T[] -> Array<T> for non-simple types. Matches src/.

Dependabot:
- Group the vitest and eslint families across patch+minor so lockstep tools
  move together instead of as separate skew-prone PRs.
The prettier pass in this PR reformatted NineSliceSprite/RepeatingSprite/
TextureRegion source, shifting the line numbers the API doc generator bakes
into the MDX sourceUrl anchors. Regenerate so docs:api:check matches.

Surfaces a real coupling: format:check (added here) and docs:api:check are
linked — reformatting an API-exported source requires a docs regen in the
same change. Both are gates and the regenerated MDX is itself prettier-clean.
@Exoridus Exoridus merged commit be552b5 into main Jun 13, 2026
13 checks passed
@Exoridus Exoridus deleted the chore/ci-release-hardening branch June 13, 2026 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant