Skip to content

feat: release tooling (version bump, changelog, tag, GitHub release) - #17

Merged
JsNcAr merged 4 commits into
mainfrom
feat/release-tooling
Oct 12, 2026
Merged

JsNcAr merged 4 commits into
mainfrom
feat/release-tooling

Conversation

@JsNcAr

@JsNcAr JsNcAr commented Oct 12, 2026

Copy link
Copy Markdown
Owner

Summary

Release tooling for projects built from this starter, while the starter itself stays at 0.1.0. Four commits, one concern each.

scripts/release.py (standard library only)

  • prepare patch|minor|major|X.Y.Z: refuses off a clean, up-to-date main or when the tag exists; runs pytest; writes the version into pyproject.toml (the only place it lives, APP_VERSION reads the installed metadata); moves the changelog's Unreleased section under the version, or renders one from Conventional Commit subjects since the last tag (security maps to a Security heading); commits chore(release): vX.Y.Z; creates an annotated tag with the notes; pushes branch and tag atomically after confirmation. --dry-run, --skip-tests, --no-push, --yes, --allow-branch.
  • Reads the project name from pyproject.toml: a renamed clone changes nothing in it.
  • Refuses while [tool.release] starter = true, which this PR sets in the starter. A clone flips it once (added to the "Adapting this starter" list).
  • After the release commit, runs poetry install --only-root so a running dev server reports the new version (editable installs write the metadata at install time).
  • Tags are created with --cleanup=verbatim: git otherwise strips the ### Heading lines of the notes as comments. The reference script this is ported from has that latent bug; the end-to-end test caught it here.
  • check-version, notes, version serve the workflow.

.github/workflows/release.yml on v* tags and manual runs: verify (tag vs pyproject.toml, notes exist; nothing installed), ci (reuses ci.yml through a new workflow_call trigger: lint, mypy, pip-audit, the suite with PostgreSQL), build (poetry build, wheel and sdist as an artifact), publish (gh release create with the changelog section as notes and the archives attached, --prerelease for a suffixed version). A manual run stops after build. No third-party release action.

Docs: docs/releasing.md (how a release is put together, choosing the version for an HTTP API, step by step, the starter and its clones, writing the notes, troubleshooting, hotfixes); setup, index, README, AGENTS and the changelog header updated. black, flake8 and mypy now cover scripts/ too.

Verification

  • tests/test_release_script.py (pure, 26 tests): bumps and pre-release ordering, pyproject.toml reading and writing (other tables untouched, marker present/absent/false), generated notes incl. breaking and skipped housekeeping, changelog splitting and insertion, and full prepare runs in a throwaway repository with a bare remote: commit, verbatim tag message, empty Unreleased, check-version pass and fail, notes, version, dry run changes nothing, the starter guard, refusals off main, with a dirty tree and when behind.
  • On this repository: prepare minor --dry-run stops with the starter message and the tree stays clean; version prints 0.1.0; check-version v0.1.0 passes.
  • Locally: black, flake8, mypy (incl. scripts/) clean; 88 pure tests pass. CI runs the full suite.
  • The release workflow cannot run on a tag in the starter by design; after merging, a manual Run workflow on main exercises verify, ci and build without publishing.

🤖 Generated with Claude Code

BrahiamSSS and others added 4 commits October 12, 2026 10:42
scripts/release.py, standard library only: `prepare patch|minor|major|X.Y.Z`
runs the suite, writes the version into pyproject.toml (the only place it
lives), moves the changelog's Unreleased section under the version (or
renders one from Conventional Commit subjects since the last tag), commits,
creates an annotated tag with the notes, and pushes branch and tag
atomically after confirmation. `check-version`, `notes` and `version` serve
the release workflow. It reads the project name from pyproject.toml, so a
renamed clone changes nothing in it, and refuses to run while
[tool.release] starter = true, which keeps the starter itself unreleased.
After the release commit it reinstalls the project so the installed metadata
(what APP_VERSION reads) carries the new version.

The annotated tag is created with --cleanup=verbatim: git otherwise drops
the "### Heading" lines of the notes as comments.

tests/test_release_script.py covers bumps, pyproject and changelog
handling, and full prepare runs in a throwaway repository, including the
starter guard and the preflight refusals.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
.github/workflows/release.yml: on a pushed v* tag, `verify` checks the tag
against pyproject.toml and that CHANGELOG.md has the section (standard
library script, nothing installed), `ci` runs the existing CI workflow
through workflow_call (lint, mypy, pip-audit, the suite with PostgreSQL),
`build` makes the wheel and sdist with the Poetry named in poetry.lock, and
`publish` creates the GitHub release with gh, the changelog section as its
notes and the archives attached, marked pre-release when the version has a
suffix. A manual run stops after build. ci.yml gains the workflow_call
trigger and checks scripts/ too; mypy covers scripts/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…E, AGENTS

docs/releasing.md: how a release is put together, choosing the version for
an HTTP API, the step by step, the starter marker and a clone's first
release, writing the notes (headings, style, the generated fallback),
changing notes later, troubleshooting, hotfixes. Setup's adapting list gains
the marker flip; the pure-test command lists the new test files; lint
commands include scripts/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
[tool.release] starter = true keeps the starter at 0.1.0: scripts/release.py
refuses to tag while it is set. The changelog header now says Unreleased
becomes the release notes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@JsNcAr
JsNcAr merged commit c52ca3d into main Oct 12, 2026
2 checks passed
@JsNcAr
JsNcAr deleted the feat/release-tooling branch October 12, 2026 15:51
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.

2 participants