From fc6cb0388c02c2ffd2c1c6f584362735069fec28 Mon Sep 17 00:00:00 2001 From: Imani Manyara Date: Mon, 5 Oct 2026 07:14:10 -0400 Subject: [PATCH 1/2] Fold the shipping checklist into docs/release.md The org keeps one shipping checklist outside the repositories, so the per-project copy goes. Its generic steps are already covered there; everything specific to shimkit moves to docs/release.md as a release-readiness table, the invalid-publisher history, the dispatch-on-tag republish recipe, the CI gates, the verify-attestation job, the abandoned Homebrew tap and the optional future items. The branch-protection item was still recorded as pending. An active ruleset on main requires a pull request and every CI check, so it is recorded as done, with the date it was checked. The old release.md ship table also described a bump-homebrew-tap job that release.yml no longer has; that now sits under the abandoned tap section. --- CHANGELOG.md | 17 ++++ README.md | 3 +- docs/onboarding.md | 6 +- docs/overview.md | 7 +- docs/plans/known-issues.md | 2 +- docs/plans/shipping-audit.md | 10 ++- docs/release.md | 154 +++++++++++++++++++++++++++++------ docs/shipping-checklist.md | 152 ---------------------------------- docs/validation-scope.md | 8 +- 9 files changed, 166 insertions(+), 193 deletions(-) delete mode 100644 docs/shipping-checklist.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e64f9ae..5f3eae3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,9 +28,26 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm `[tool.coverage.report] fail_under`, so local runs enforce it too. - `codecov/codecov-action` pinned to the v7.1.1 commit SHA, and it uses `CODECOV_TOKEN` when one is configured. +- The shimkit-specific release facts from `docs/shipping-checklist.md` + now live in `docs/release.md`: a release-readiness table (ruleset on + `main`, the `pypi` environment, the pending pypi.org trusted + publisher, TestPyPI, first upload), the `invalid-publisher` history + and the `gh workflow run release.yml --ref ` republish recipe, + the CI gates, the `verify-attestation` job, the abandoned Homebrew + tap and the optional future items. The branch-protection item, + recorded as pending, is corrected to done: an active ruleset on + `main` requires a pull request and every CI check (checked + 2026-10-05). Links in the README index and `docs/` point at + `docs/release.md` instead. - `codecov.yml` added: carryforward flags, a blocking project status with 1% slack, and an advisory patch status. +### Removed + +- `docs/shipping-checklist.md`. Its generic steps are covered by the + org-wide Simtabi shipping checklist, and everything specific to + shimkit moved to `docs/release.md` (see Changed). + ### Fixed - A rejected Codecov upload was silent: with `fail_ci_if_error: false` diff --git a/README.md b/README.md index 90d3315..27a9eb5 100644 --- a/README.md +++ b/README.md @@ -202,8 +202,7 @@ the full per-version release-notes index — see | Config layer, schema, examples | [`docs/configuration.md`](docs/configuration.md) | | Architecture, the load-bearing rules, how to add a new tool | [`docs/architecture.md`](docs/architecture.md) | | Onboarding: setup, the 5 rules, recipe for adding a tool, debugging | [`docs/onboarding.md`](docs/onboarding.md) | -| Cutting a release, what each CI job does | [`docs/release.md`](docs/release.md) | -| Shipping checklist (what's done vs what's pending) | [`docs/shipping-checklist.md`](docs/shipping-checklist.md) | +| Cutting a release, what each CI job does, release readiness (what's done vs pending) | [`docs/release.md`](docs/release.md) | | Validation scope: what's gated, what's deliberately out of scope | [`docs/validation-scope.md`](docs/validation-scope.md) | | Known issues + pending items (un-automatable checks, deferrals) | [`docs/plans/known-issues.md`](docs/plans/known-issues.md) | | Future additions (no-demand, naturally-extensible surface) | [`docs/plans/future-additions.md`](docs/plans/future-additions.md) | diff --git a/docs/onboarding.md b/docs/onboarding.md index 320de63..16de6e6 100644 --- a/docs/onboarding.md +++ b/docs/onboarding.md @@ -479,8 +479,8 @@ adguard-integration ← real AGH on ubuntu-latest. JSON-asserted output. adguard-mutating-integration ← real `shimkit adguard fix` inside a privileged systemd container. ``` -All must pass before merge to `main` (once branch protection is -configured — see `docs/shipping-checklist.md` row 1.8). +All must pass before merge to `main`: the ruleset on `main` requires +them (see [release.md](release.md#release-readiness), item 2). The release workflow only triggers on `v*` tags. Pushing to `main` is a no-op for releases. @@ -495,4 +495,4 @@ is a no-op for releases. - For validation-scope questions ("is this thing tested?"), see [validation-scope.md](validation-scope.md). - For "what's left to ship?" questions, see - [shipping-checklist.md](shipping-checklist.md). + [release.md](release.md#release-readiness). diff --git a/docs/overview.md b/docs/overview.md index 9e330ce..cd13799 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -84,10 +84,9 @@ Top-level utilities (not tools): productive: setup, the 5 rules with grep recipes, the canonical recipe for adding a new tool, common dev tasks, debugging guide. - **[Release process](release.md)** — cutting a new version, the CI - pipeline, what each release job does. -- **[Shipping checklist](shipping-checklist.md)** — every step from - "code ready" to "users can install", in dependency order. Tracks - what's done vs what still needs your action. + pipeline, what each release job does, and the release-readiness + table: every step from "code ready" to "users can install", in + dependency order, with what's done vs what still needs your action. - **[Validation scope](validation-scope.md)** — what's in scope for automated + manual gates, what's deliberately out of scope, and how to expand the envelope. diff --git a/docs/plans/known-issues.md b/docs/plans/known-issues.md index da5b2d4..b2666ae 100644 --- a/docs/plans/known-issues.md +++ b/docs/plans/known-issues.md @@ -5,7 +5,7 @@ for a future release. Tracked here so contributors and operators can see what's known and decide if it matters for their use case. For the release-blocking checklist see -[`shipping-checklist.md`](../shipping-checklist.md). For the broader +[`release.md`](../release.md#release-readiness). For the broader validation envelope see [`validation-scope.md`](../validation-scope.md). --- diff --git a/docs/plans/shipping-audit.md b/docs/plans/shipping-audit.md index 1241dc3..da2fd74 100644 --- a/docs/plans/shipping-audit.md +++ b/docs/plans/shipping-audit.md @@ -16,8 +16,9 @@ Cross-references: with concrete patterns but no current demand. - [`known-issues.md`](known-issues.md) — runtime checks that can't be containerised. -- [`../shipping-checklist.md`](../shipping-checklist.md) — the - release-readiness operational checklist (different from this +- [`../release.md`](../release.md#release-readiness) — the + release-readiness operational checklist (formerly + `docs/shipping-checklist.md`) (different from this audit; that file tracks "ship readiness", this file tracks "feature completeness against plans"). - [`../../.design/plans/feature-gap-analysis.md`](../../.design/plans/feature-gap-analysis.md) @@ -95,7 +96,8 @@ Source: [`docs/plans/cleanup-2026-05-14.md`](cleanup-2026-05-14.md). ## E. Shipping-checklist phases -Source: [`../shipping-checklist.md`](../shipping-checklist.md). +Source: the former `docs/shipping-checklist.md`, now the release-readiness +table in [`../release.md`](../release.md#release-readiness). | Phase | Item | Status | |-------|------|--------| @@ -103,7 +105,7 @@ Source: [`../shipping-checklist.md`](../shipping-checklist.md). | **1.8** | **Branch protection on `main`** | **⏳ pending — user action; see section G** | | 2.1-2.3 | GitHub remote setup | ✅ done | | **2.4** | **`pypi` GitHub Environment** | **⏳ pending — user action; see section G** | -| 3 | Homebrew tap | 🗑 abandoned — see [`../shipping-checklist.md`](../shipping-checklist.md) Phase 3 | +| 3 | Homebrew tap | 🗑 abandoned — see [`../release.md`](../release.md#homebrew-tap-abandoned) | | **4.1** | **PyPI account + 2FA** | **⏳ pending — user action; see section G** | | **4.2** | **Configure PyPI trusted publisher** | **⏳ pending — user action; see section G** | | 4.3 | TestPyPI (dry-run) | ⏸ optional | diff --git a/docs/release.md b/docs/release.md index 3945805..1e2faf2 100644 --- a/docs/release.md +++ b/docs/release.md @@ -1,7 +1,6 @@ # Release process -Releases are **tag-driven**. Push a `vX.Y.Z` tag, GitHub Actions does -the rest. +Releases are tag-driven: push a `vX.Y.Z` tag and GitHub Actions builds, attests and publishes it. > **Current channels (as of v0.11.0):** GitHub Release wheel + sdist > + SBOM, AND PyPI (when the trusted-publisher config below is in @@ -10,31 +9,102 @@ the rest. > next tag after the trusted-publisher prerequisites are in place. > > The Homebrew tap path and the GHCR container were removed -> earlier — Homebrew tap is in `shipping-checklist.md` Phase 5 -> if anyone wants to re-add it; the GHCR container was always -> meant as a testing artifact, not a distribution channel. +> earlier. The tap is recorded under +> [Homebrew tap (abandoned)](#homebrew-tap-abandoned) if anyone +> wants to re-add it; the GHCR container was removed in v0.2.2 +> because it was always a testing artifact, not a documented +> install method. For the one-time PyPI / npm / Docker setup (trusted publishers, environments, tokens), this repo follows the standard [PyPI Trusted Publishers](https://docs.pypi.org/trusted-publishers/) -flow. The recipe is below; ask the team if you need the org-wide -reference for cross-project conventions. +flow. The recipe is below. The generic, org-wide release gate (writing +review, security, CI hardening, supply chain, first-release steps) is +the Simtabi shipping checklist, which is kept outside this repository; +this page carries only what is specific to shimkit. -## Ship checklist +## Release readiness -Do these in dependency order: +What stands between "code is ready" and "users can `pip install +shimkit`", in dependency order. Each state below says when it was last +checked. -| # | Step | Where | +| # | Item | State | |---|------|-------| -| 1 | Claim `shimkit` on PyPI + configure trusted publisher | | -| 2 | Create `pypi` GitHub Environment | `simtabi/shimkit` Settings → Environments | -| 3 | Create `simtabi/homebrew-tap` (public repo, empty `Formula/` dir) | `gh repo create simtabi/homebrew-tap --public` | -| 4 | Create `TAP_GITHUB_TOKEN` secret (fine-grained PAT, Contents R/W on tap repo) | Settings → Secrets → Actions | -| 5 | Cut `v0.1.0` (or whatever's next) | `git tag vX.Y.Z && git push origin vX.Y.Z` | +| 1 | GitHub Release channel (wheel + sdist + SBOM) | Live. Every release from v0.5.0 onward is on the GitHub Release page. | +| 2 | Ruleset on `main` requiring a pull request and passing checks | Active (checked 2026-10-05 with `gh api repos/simtabi/shimkit/rulesets`): ruleset "default branch: pull request and passing checks", requiring every `test` matrix cell, `security`, `build`, both `smoke` cells and both `adguard` integration jobs. | +| 3 | `pypi` GitHub Environment | Exists (checked 2026-10-05 with `gh api repos/simtabi/shimkit/environments`). The OIDC token every `publish-pypi` run presents already carries `sub: repo:simtabi/shimkit:environment:pypi`. Optionally add a Required Reviewer rule for a human gate before upload. | +| 4 | PyPI account with 2FA | Pending, owner action: . | +| 5 | PyPI trusted publisher for `shimkit` | **Pending, owner action, and the one blocker left.** See [PyPI trusted-publisher setup](#pypi-trusted-publisher-setup). | +| 6 | TestPyPI rehearsal | Optional. Configure a `testpypi` trusted publisher at and a `testpypi` GitHub environment to rehearse a release on TestPyPI before real PyPI. | +| 7 | First PyPI upload | Automatic: the next tagged release after item 5 publishes to PyPI. Then verify with `pip install shimkit` (no `--index-url`), which should resolve to the latest PyPI version. | -Step 3's `bump-homebrew-tap` workflow step `continue-on-error`s, so -step 5 will succeed even if step 3 isn't done yet — the formula just -won't be auto-bumped on that release. +Critical path for the next release to reach PyPI: + +``` +3 Create the pypi GitHub Environment (done) + └─► 4 PyPI account + 2FA + └─► 5 Configure trusted publisher + └─► next tag → auto-publishes +``` + +If only the GitHub Release wheel is acceptable, **none of these are +required**: that channel works today and has shipped every release +since v0.5.0. + +### Why PyPI has never received an upload + +Every `publish-pypi` run from v0.11.0 through v0.19.0 (nine releases) +failed the token exchange with +`invalid-publisher: valid token, but no corresponding publisher`, and `https://pypi.org/pypi/shimkit/json` +answers 404 (checked 2026-10-05). The GitHub side is correct; pypi.org +has no publisher registered. + +### Publishing past tags after the publisher exists + +Failed uploads on past tags can **not** be fixed with "Re-run failed +jobs": `publish-pypi` downloads the `dist` artifact from its run's +`build` job, and those artifacts expire after 90 days (v0.11.0 to +v0.19.0 ran 2026-05-15/16). Dispatch the whole workflow on the tag +instead, which rebuilds and uploads: + +```bash +gh workflow run release.yml --repo simtabi/shimkit --ref v0.19.0 +``` + +To publish older releases retroactively, dispatch `release.yml` the +same way on each tag (`--ref vX.Y.Z`). `publish-pypi` sets +`skip-existing: true`, so a version already on PyPI is skipped rather +than failing the run. + +## Gates a release depends on + +CI (`ci.yml`) runs on macOS + Ubuntu × Python 3.10/3.11/3.12/3.13 with +the jobs `test`, `security`, `build`, `smoke`, `adguard-integration` +and `adguard-mutating-integration`. The `test` job carries ruff +(strict), mypy (strict, pydantic plugin), shellcheck, bandit (`-ll`, +fail on medium and above) and pip-audit (`--skip-editable`); at v0.12.0 +the suite was 1027 pytest cases at 85% coverage. Coverage uploads to +codecov.io (wired in v0.12.0, with the coverage badge in the README). +Dependabot opens weekly PRs for pip and GitHub Actions updates. + +Repository setup that the release path assumes is in place: the +`src/` layout with the hatchling backend, `py.typed` and the bundled +`defaults.json` (the wheel builds cleanly with `python -m build`); the +org-style docs (`README.md`, +`docs/{installation,configuration,architecture,release,tools/*}.md` +with a `docs/tools/.md` page for every shipped tool, +`CHANGELOG.md`, `CONTRIBUTING.md`, `SECURITY.md`, +`CODE_OF_CONDUCT.md`); issue and PR templates, Dependabot and +pre-commit; and the public `simtabi/shimkit` repository with `main` as +default branch, description, homepage, topics, Issues and Discussions. +Commits use the noreply identity `imanimanyara@users.noreply.github.com`; +the ID-prefixed form `19682005+imanimanyara@users.noreply.github.com` +recorded in the old checklist is no longer what the checkout is +configured with. + +What each gate validates, and what is deliberately left out, is in +[`validation-scope.md`](validation-scope.md). ## Cutting a release @@ -76,12 +146,19 @@ push tag v* │ (skip-existing: true for reruns) │ └──► github-release ────────── create the GH Release with - wheel + sdist + SBOM attached. + │ wheel + sdist + SBOM attached. + │ + ▼ + verify-attestation ────── download the released wheel + sdist + and run `gh attestation verify` + on each (wired in v0.12.0). ``` `publish-pypi` and `github-release` run in parallel after `build` — a PyPI outage doesn't block the GitHub Release upload, and vice -versa. +versa. `verify-attestation` reads the provenance back from Sigstore's +transparency log after the release exists, so a misconfigured publish +flow fails visibly instead of shipping unattested artifacts. ### PyPI trusted-publisher setup @@ -96,7 +173,7 @@ Required once per project, by a maintainer with PyPI write access: | PyPI Project Name | `shimkit` | | Owner | `simtabi` | | Repository name | `shimkit` | - | Workflow filename | `release.yml` | + | Workflow name | `release.yml` | | Environment name | `pypi` | 2. **GitHub side** — Settings → Environments → New environment @@ -110,8 +187,11 @@ Required once per project, by a maintainer with PyPI write access: If either side is missing, the workflow fails at the `publish-pypi` step with `invalid-publisher`. The `github-release` job still -succeeds, so you can fix the PyPI side at your leisure and re-run -the workflow against the same tag via `workflow_dispatch`. +succeeds, so you can fix the PyPI side at your leisure and then +dispatch the workflow against the same tag (`workflow_dispatch`, see +[Publishing past tags](#publishing-past-tags-after-the-publisher-exists)); +"Re-run failed jobs" only works while the run's `dist` artifact has +not expired. ## Verifying a release @@ -147,3 +227,31 @@ git commit -am "release: vX.Y.Z" && git tag vX.Y.Z && git push origin main vX.Y. CI does everything else. If the `pypi` environment has Required Reviewers configured, you'll get a GitHub notification asking you to approve before publish. + +## Homebrew tap (abandoned) + +Distribution through a Homebrew tap was scoped in the original release +plan but never restored after PyPI was deferred, and `release.yml` +has no tap job today. The live channels are PyPI and the GitHub +Release page. + +If a tap is wanted in future: + +1. Create `simtabi/homebrew-tap` (public repo, empty `Formula/` dir): + `gh repo create simtabi/homebrew-tap --public`. +2. Create a `TAP_GITHUB_TOKEN` secret: a fine-grained PAT scoped to + that repo with Contents read/write (Settings → Secrets → Actions). +3. Add a `bump-homebrew-tap` job to `release.yml` after + `github-release`, marked `continue-on-error` so a tap failure + never fails the release; the formula is simply not bumped on that + release. + +## Optional and future + +- Move the docs to mkdocs-material on GitHub Pages if SEO and + discoverability come to matter more than plain GitHub rendering. +- Add a `CITATION.cff` if the tool gets cited academically. + +--- + +[← Docs index](../README.md#documentation) diff --git a/docs/shipping-checklist.md b/docs/shipping-checklist.md deleted file mode 100644 index dd0979f..0000000 --- a/docs/shipping-checklist.md +++ /dev/null @@ -1,152 +0,0 @@ -# Shipping checklist - -Everything that needs to happen between "code is ready" and "users can -`pip install shimkit`". Items are listed in **dependency order** — -later items assume earlier ones are done. - -> **Status as of v0.12.0** — Eleven releases are live on the GitHub -> Release page (v0.5.0 through v0.12.0). PyPI publishing was -> restored in v0.11.0 but the first PyPI upload is blocked on the -> user-side trusted-publisher setup (Phase 4). The Homebrew tap -> path was originally planned for Phase 3 but never restored — the -> abandoned-channel rationale is in [`docs/release.md`](release.md). - -Three columns: - -- **Status** — `done` (already in place), `pending` (someone needs - to act), `optional`, or `abandoned`. -- **Owner** — `code` (already shipped in the repo), `user` (you, via - web UI or local terminal), or `ci` (the release workflow does it - automatically when triggered). -- **Item** — what needs doing, with the exact command or click path. - -For what each automated and manual gate validates (and what we -intentionally don't validate), see -[`validation-scope.md`](validation-scope.md). - ---- - -## Phase 1 · Code & repo setup - -| # | Status | Owner | Item | -|---|--------|-------|------| -| 1.1 | ✅ done | code | shimkit package built: src/ layout, hatchling backend, py.typed, bundled `defaults.json`. Wheel builds cleanly (`python -m build`). | -| 1.2 | ✅ done | code | Tests pass: 1027 pytest cases at 85% coverage. ruff strict, mypy strict + pydantic plugin, shellcheck, bandit (`-ll`, fail on medium+), pip-audit (`--skip-editable`). | -| 1.3 | ✅ done | code | CI workflow — macOS + Ubuntu × Python 3.10/3.11/3.12/3.13. Jobs: `test`, `security`, `build`, `smoke`, `adguard-integration`, `adguard-mutating-integration`. | -| 1.4 | ✅ done | code | Release workflow — `guard` → `build` (sdist + wheel + SBOM + attestation) → `publish-pypi` (restored v0.11.0) + `github-release` in parallel. | -| 1.5 | ➖ n/a | code | (Reserved.) Earlier the project shipped a container image; that path was removed in v0.2.2 because Docker was a testing artifact, not a documented install method. | -| 1.6 | ✅ done | code | Org-style docs: `README.md`, `docs/{installation,configuration,architecture,release,tools/*}.md`, `CHANGELOG.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`. | -| 1.7 | ✅ done | code | Community hygiene: ISSUE_TEMPLATE/, PULL_REQUEST_TEMPLATE, dependabot, pre-commit. | -| 1.8 | ⏳ pending | user | Configure branch protection on `main`: require `test`, `security`, `build`, `smoke` checks before merge. Repo Settings → Branches → Add rule. | - -## Phase 2 · GitHub remote - -| # | Status | Owner | Item | -|---|--------|-------|------| -| 2.1 | ✅ done | user | Repo exists at `simtabi/shimkit`. Default branch `main`. Public. | -| 2.2 | ✅ done | user | Repo metadata set: description, homepage, topics. Issues + Discussions enabled. | -| 2.3 | ✅ done | user | Local git identity scoped to noreply (`19682005+imanimanyara@users.noreply.github.com`). | -| 2.4 | ✅ done | user | **Create the `pypi` GitHub Environment.** Exists (checked 2026-10-05 with `gh api repos/simtabi/shimkit/environments`). The OIDC token every `publish-pypi` run presents already carries `sub: repo:simtabi/shimkit:environment:pypi`. Optionally add a Required Reviewer rule for a human gate before upload. | - -## Phase 3 · Homebrew tap — abandoned - -Distribution via Homebrew tap was scoped in this checklist's original -shape but never restored after the Phase 4 PyPI deferral. The current -distribution channels are PyPI (Phase 4) and the GitHub Release page. - -If a tap is desired in the future: - -1. Create `simtabi/homebrew-tap` (public repo, empty `Formula/` dir). -2. Create a `TAP_GITHUB_TOKEN` fine-grained PAT scoped to that repo - (Contents R/W). -3. Add a `bump-homebrew-tap` job to `release.yml` after - `github-release`. - -## Phase 4 · PyPI — restored in v0.11.0 - -The `publish-pypi` job exists in `release.yml` and runs in parallel -with `github-release` after `build`. First upload blocked on the -user-side trusted-publisher setup. - -| # | Status | Owner | Item | -|---|--------|-------|------| -| 4.1 | ⏳ pending | user | **Register / sign in to PyPI** and enable 2FA. . | -| 4.2 | ⏳ **pending** | **user** | **Configure the trusted publisher** for `shimkit`. → Add a new pending publisher → GitHub. | -| | | | • PyPI Project Name: `shimkit` | -| | | | • Owner: `simtabi` | -| | | | • Repository name: `shimkit` | -| | | | • Workflow name: `release.yml` | -| | | | • Environment name: `pypi` | -| 4.3 | ⏸ optional | user | (Dry-run only) Configure a `testpypi` trusted publisher at + a `testpypi` GitHub environment if you want to rehearse a release on TestPyPI before hitting real PyPI. | - -After 2.4 + 4.1 + 4.2 are done, the next tag will publish to PyPI -automatically. - -**4.2 is the one blocker left.** Every `publish-pypi` run from v0.11.0 -through v0.19.0 (nine releases) failed the token exchange with -`invalid-publisher: valid token, but no corresponding publisher`, and -`https://pypi.org/pypi/shimkit/json` answers 404 (checked 2026-10-05). -The GitHub side is correct; pypi.org has no publisher registered. - -Failed uploads on past tags can **not** be fixed with "Re-run failed -jobs": `publish-pypi` downloads the `dist` artifact from its run's -`build` job, and those artifacts expire after 90 days (v0.11.0–v0.19.0 -ran 2026-05-15/16). Dispatch the whole workflow on the tag instead, -which rebuilds and uploads: -`gh workflow run release.yml --repo simtabi/shimkit --ref v0.19.0`. - -## Phase 5 · First PyPI upload - -Prerequisites: 2.4 + 4.1 + 4.2 all done. - -| # | Status | Owner | Item | -|---|--------|-------|------| -| 5.1 | ▶︎ auto | ci | The next tagged release after 4.2 publishes to PyPI automatically. | -| 5.2 | ⏳ pending | user | Verify the upload: `pip install shimkit` (no `--index-url`) should resolve to the latest PyPI version. | -| 5.3 | ⏸ optional | user | Retroactively publish past releases by dispatching `release.yml` on each tag (`--ref vX.Y.Z`); re-running the old failed jobs no longer works once their artifacts have expired. | - -## Phase 6 · Post-release / ongoing - -| # | Status | Owner | Item | -|---|--------|-------|------| -| 6.1 | ▶︎ auto | ci | **Dependabot** opens weekly PRs for pip + GitHub Actions updates. | -| 6.2 | ▶︎ auto | ci | Subsequent releases are tag-driven and need no manual setup — bump versions, commit, tag, push. | - -## Phase 7 · Optional / future - -| # | Status | Owner | Item | -|---|--------|-------|------| -| 7.1 | ✅ done | code | Code coverage upload to codecov.io. Wired in v0.12.0; coverage badge in README. | -| 7.2 | ✅ done | code | `docs/tools/.md` for every shipped tool. | -| 7.3 | ⏸ optional | user | Move docs to mkdocs-material on GitHub Pages if SEO / discoverability matters more than raw GitHub rendering. | -| 7.4 | ⏸ optional | user | Add a `CITATION.cff` if the tool gets cited academically. | -| 7.5 | ✅ done | code | `gh attestation verify` smoke test post-release. Wired in v0.12.0. | - ---- - -## Critical path summary - -For the next release to publish to PyPI: - -``` -2.4 Create the pypi GitHub Environment (done) - └─► 4.1 PyPI account + 2FA - └─► 4.2 Configure trusted publisher - └─► next tag → auto-publishes -``` - -If only the GitHub Release wheel is acceptable, **none of these are -required** — that channel works today and has shipped 11+ releases. - ---- - -## Status cheat sheet - -| Status | Meaning | -|--------|---------| -| ✅ done | Already in place. No action needed. | -| ⏳ pending | Needs your action — see the linked command/UI path. | -| ▶︎ auto | Triggered automatically by the release workflow / Dependabot. | -| ⏸ optional | Nice-to-have, not required to ship. | -| 🗑 abandoned | Originally scoped, intentionally not pursued. | -| ⚠ blocker | Downstream items can't proceed until this is done. | diff --git a/docs/validation-scope.md b/docs/validation-scope.md index 276ead0..3418dbd 100644 --- a/docs/validation-scope.md +++ b/docs/validation-scope.md @@ -8,8 +8,8 @@ intentionally does not. Use this page to decide: - What to write in the v0.2.0+ release PR description for each sign-off check. -The companion document is [`shipping-checklist.md`](shipping-checklist.md), -which tracks **release-blocking** items in dependency order. +The companion document is [`release.md`](release.md#release-readiness), +whose release-readiness table tracks **release-blocking** items in dependency order. --- @@ -172,14 +172,14 @@ To add a new manual validation: 1. Add a numbered step to [`prompt.md`](../prompt.md) Phase 7. 2. Add a row to the sign-off criteria table. -3. Reference the new step in [`shipping-checklist.md`](shipping-checklist.md) +3. Reference the new step in [`release.md`](release.md#release-readiness) if it's release-blocking. --- ## Related -- [`shipping-checklist.md`](shipping-checklist.md) — release-blocking +- [`release.md`](release.md#release-readiness) — release-blocking items in dependency order. - [`release.md`](release.md) — cutting a new version, the CI pipeline, what each release job does. From b82cdc681c921984967d7d002dcbc834e2444dcb Mon Sep 17 00:00:00 2001 From: Imani Manyara Date: Mon, 5 Oct 2026 07:19:14 -0400 Subject: [PATCH 2/2] Release steps: reach main through a PR, push only the tag The page told readers to push main and the tag together, which bypasses the pull-request ruleset on main. --- docs/release.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/release.md b/docs/release.md index 1e2faf2..1c94a03 100644 --- a/docs/release.md +++ b/docs/release.md @@ -115,10 +115,17 @@ What each gate validates, and what is deliberately left out, is in # 2. Update CHANGELOG.md with the new entry (move [Unreleased] to [X.Y.Z]). -# 3. Commit and tag: +# 3. Commit on a branch and open a pull request; main only changes through a PR +# (its ruleset requires one, with passing checks): +git switch -c release/vX.Y.Z git commit -am "release: vX.Y.Z" -git tag vX.Y.Z -git push origin main vX.Y.Z +git push -u origin release/vX.Y.Z +gh pr create --title "Release vX.Y.Z" --body "..." + +# 4. After the PR merges, tag the merge commit and push only the tag: +git switch main && git pull --ff-only +git tag -a vX.Y.Z -m "Release vX.Y.Z" +git push origin vX.Y.Z ``` ## What `release.yml` does @@ -220,8 +227,9 @@ jq .name /tmp/sbom.json # should print "shimkit" Once the first release has gone through, every subsequent one is just: ```bash -# Bump versions, commit, tag, push: -git commit -am "release: vX.Y.Z" && git tag vX.Y.Z && git push origin main vX.Y.Z +# Bump versions and the CHANGELOG on a release branch, merge it through a PR, +# then tag the merge commit and push only the tag: +git switch main && git pull --ff-only && git tag -a vX.Y.Z -m "Release vX.Y.Z" && git push origin vX.Y.Z ``` CI does everything else. If the `pypi` environment has Required