Skip to content

Automate the release flow to one command and two clicks - #7

Merged
Sukitly merged 2 commits into
mainfrom
feat/automated-release-flow
Aug 24, 2026
Merged

Sukitly merged 2 commits into
mainfrom
feat/automated-release-flow

Conversation

@Sukitly

@Sukitly Sukitly commented Aug 24, 2026

Copy link
Copy Markdown
Owner

Summary

Automates the release flow to one command and two clicks, modeled on the glotctl release pattern and adapted to this repository's conventions (Python entry points, SHA-pinned actions, the approval-gated release environment):

scripts/prepare-release.py patch|minor|major   <- one command
   clean/up-to-date main checks, next-version computation,
   Cargo.toml + Cargo.lock + README pinned-link bump,
   release/v<version> branch, pull request
        v
merge the release PR                            <- click 1
        v
create-release-tag.yml (new)
   branch name must match the Cargo version; tags the merge commit
   with the dedicated RELEASE_TOKEN credential; deletes the branch
        v
release.yml: build x4 -> assemble -> attest -> draft (unchanged)
        v
approve the release environment                 <- click 2
        v
publish job (new): re-verifies the six-asset contract
   against the draft, then publishes it

Changes

  • scripts/prepare-release.py (new): release PR preparation with fail-fast preconditions (on main, clean tree, in sync with origin, branch/tag not taken). Interactive confirmation; --yes for scripted use.
  • .github/workflows/create-release-tag.yml (new): triggered by merged release/* pull requests. Validates exact SemVer and the branch/version match, tags idempotently, and cleans up the branch. Uses RELEASE_TOKEN because tags pushed with GITHUB_TOKEN do not trigger the tag-driven release workflow.
  • .github/workflows/release.yml: the release environment gate moves from the draft job to a new publish job. Approval is now the publication decision; the publish job verifies the release is still a draft and carries exactly the six contracted assets before flipping it live with --latest. Draft creation no longer waits for approval (drafts are not user-visible and assets stay mutable until publication).
  • docs/releasing.md: rewritten for the new flow, including the one-time RELEASE_TOKEN setup, optional pre-approval draft inspection, and the manual tagging fallback.
  • CONTRIBUTING.md: workflow description updated.

Security posture

  • The publication boundary is unchanged in substance: one human approval stands between automation and an immutable published release, and it now gates the actual publication rather than draft creation.
  • RELEASE_TOKEN is a fine-grained PAT scoped to this repository with Contents read/write only (already configured as a repository secret).
  • The tag workflow runs only for merged pull requests from release/* branches, tags exactly the merge commit, and refuses a branch/version mismatch.

Checks run locally

  • python3 -m py_compile scripts/prepare-release.py; --help smoke.
  • YAML parse of both workflows.
  • scripts/check-doc-links.py.
  • No Rust changes. This PR touches release.yml, so the release rehearsal path (four native archive builds) runs on this PR as validation.

First real exercise of the full path will be the next release (v0.2.1 or v0.3.0).

scripts/prepare-release.py opens the version-bump pull request from a
release/v<version> branch after verifying a clean, up-to-date main; it
updates Cargo.toml, Cargo.lock, and the pinned installer links in the
README. Merging that pull request triggers create-release-tag.yml,
which validates the branch/version match and tags the merge commit
with the dedicated RELEASE_TOKEN credential (GITHUB_TOKEN tag pushes
do not trigger the release workflow). The release environment gate
moves from the draft job to a new publish job: approval is now the
publication decision, and the publish job re-verifies the six-asset
contract before flipping the draft live. docs/releasing.md documents
the new flow, the one-time token setup, and the manual fallback.
…aken versions

The publish job now re-derives the facts approval relies on inside the
gate: it downloads every draft asset, checks SHA256SUMS, and verifies
each artifact attestation, because draft assets stay mutable between
draft creation and approval. docs/releasing.md discloses that a tag
push drafts the release unattended and the environment gates
publication only. create-release-tag treats an existing tag as a safe
no-op only when it already points at the merge commit; a tag pointing
elsewhere fails with guidance to bump again, and the branch/version
mismatch error explains the reserved release/ prefix, which is now
documented in CONTRIBUTING.md. prepare-release.py fronts every content
and tool check (including gh auth) before the first write, rolls the
three release files back on a write-phase failure, and prints exact
recovery commands when the pull request cannot be created after the
push; its version arithmetic and [package]-anchored rewriting moved to
scripts/_lib/release_version.py so the writer agrees with the
structured readers in the workflows. tests/release_prepare_contract.rs
covers the refusal paths, the rollback, the post-push recovery state,
and the pure version logic. The dead url output in the draft job is
removed.
@Sukitly
Sukitly merged commit 29f8854 into main Aug 24, 2026
12 checks passed
@Sukitly
Sukitly deleted the feat/automated-release-flow branch August 24, 2026 10:40
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