Skip to content

Commit 63de26b

Browse files
committed
docs(readme): document the release-cutting workflow
New "Cutting a release" subsection under Working in the codebase (placed adjacent to "Commit message convention" since releases are driven by the conventional-commit history). Documents the seven-step recipe used to cut v1.0.1: git cliff --bumped-version → pyproject bump + make lock → git cliff --tag → chore commit → annotated tag → push commit + tag → gh release create --notes-from-tag --verify-tag. Adds non-obvious-detail callouts for each tool flag (why --bumped-version, what make lock regenerates, why --notes-from-tag, why --verify-tag). TOC updated to include the new subsection.
1 parent bc93f9a commit 63de26b

1 file changed

Lines changed: 41 additions & 1 deletion

File tree

README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ This application provisions Lambda, API Gateway, DynamoDB, SSM Parameter Store,
3737
- [Getting started](#getting-started)[Prerequisites](#prerequisites) · [Quick start](#quick-start) · [Makefile](#makefile) · [Editor setup (VS Code)](#editor-setup-vs-code)
3838
- [Architecture](#architecture)[Lambda Powertools features](#lambda-powertools-features) · [AWS resources provisioned](#aws-resources-provisioned) · [Stack and construct composition](#stack-and-construct-composition) · [Frontend stack](#frontend-stack) · [Monitoring](#monitoring)
3939
- [Deploy the application](#deploy-the-application)[Recommended order](#recommended-order-for-ongoing-deploys) · [Different region](#deploying-to-a-different-region) · [Destroying](#destroying-a-deployment) · [Cleanup](#cleanup)
40-
- [Working in the codebase](#working-in-the-codebase)[Add a resource](#add-a-resource-to-your-application) · [Useful CDK commands](#useful-cdk-commands) · [Synthesize and validate locally](#synthesize-and-validate-locally) · [Debugging the Lambda function](#debugging-the-lambda-function) · [Fetch, tail, and filter logs](#fetch-tail-and-filter-lambda-function-logs) · [Commit message convention](#commit-message-convention) · [Documentation](#documentation)
40+
- [Working in the codebase](#working-in-the-codebase)[Add a resource](#add-a-resource-to-your-application) · [Useful CDK commands](#useful-cdk-commands) · [Synthesize and validate locally](#synthesize-and-validate-locally) · [Debugging the Lambda function](#debugging-the-lambda-function) · [Fetch, tail, and filter logs](#fetch-tail-and-filter-lambda-function-logs) · [Commit message convention](#commit-message-convention) · [Cutting a release](#cutting-a-release) · [Documentation](#documentation)
4141
- [Quality and security](#quality-and-security)[Tests](#tests) · [Linting and static analysis](#linting-and-static-analysis) · [Detecting deprecated APIs](#detecting-deprecated-apis) · [Pre-commit hooks](#pre-commit-hooks) · [Security](#security) · [CDK security checks](#cdk-security-checks) · [pyproject.toml configuration](#pyprojecttoml-configuration)
4242
- [CI/CD](#cicd)[GitHub Actions](#github-actions)
4343
- [Project dependencies](#project-dependencies)
@@ -634,6 +634,46 @@ The committed history is the input to **[git-cliff](https://github.com/orhun/git
634634

635635
Forks that want to *enforce* the convention at author time (rather than rely on the reviewer + pre-commit gates) can adopt **[Commitizen](https://github.com/commitizen-tools/commitizen)** as an interactive authoring helper — it prompts for type/scope/description and refuses to create commits that don't conform. Intentionally not wired into this project because the convention is short enough to author by hand and the existing pre-commit hooks catch the common drift sources; meaningful in larger teams where conformance otherwise erodes.
636636

637+
### Cutting a release
638+
639+
Releases follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and are driven from the conventional-commit history. The end-to-end recipe:
640+
641+
```bash
642+
# 1. Ask git-cliff what version to bump to based on commits since the last tag
643+
git cliff --bumped-version
644+
645+
# 2. Bump pyproject.toml to that version, regenerate the lockfiles
646+
sed -i '' 's/^version = ".*"/version = "X.Y.Z"/' pyproject.toml
647+
make lock
648+
649+
# 3. Regenerate the changelog (treats unreleased commits as if tagged X.Y.Z)
650+
git cliff --tag vX.Y.Z -o CHANGELOG.md
651+
652+
# 4. Commit everything in one chore commit
653+
git add pyproject.toml uv.lock CHANGELOG.md
654+
git commit -m "chore: release vX.Y.Z"
655+
656+
# 5. Tag with annotated release notes — the body shows up on the Releases page
657+
git tag -a vX.Y.Z -m "vX.Y.Z — short title
658+
659+
Longer release notes here..."
660+
661+
# 6. Push the commit and the tag
662+
git push origin main
663+
git push origin vX.Y.Z
664+
665+
# 7. Publish the GitHub Release (pulls notes from the annotated tag)
666+
gh release create vX.Y.Z --notes-from-tag --latest --verify-tag
667+
```
668+
669+
A few non-obvious details:
670+
671+
- **`git cliff --bumped-version`** reads conventional-commit types since the last tag and follows SemVer: `feat:` triggers a minor bump, `fix:` a patch, `docs`/`chore`/`ci` etc. stay at patch. Breaking changes (`!` or `BREAKING CHANGE:` footer) trigger a major bump. v1.0.1 was chosen this way — only `docs:` commits since v1.0.0, so `--bumped-version` returned `v1.0.1`.
672+
- **`make lock`** regenerates `uv.lock` and `lambda/requirements.txt` so the lockfile records the new project version. The only diff in `uv.lock` is the one-line `version =` field for this project; transitive pins do not move.
673+
- **`git cliff --tag vX.Y.Z -o CHANGELOG.md`** regenerates the *entire* `CHANGELOG.md` rather than prepending — this keeps the footer URL list (`[X.Y.Z]: .../compare/..`) consistent across releases. The cost is rewriting unchanged previous sections; in practice that's a no-op since the content is deterministic.
674+
- **`--notes-from-tag`** on `gh release create` pulls the annotated tag body into the GitHub Release object, so the same release notes are available via `git show vX.Y.Z`, the GitHub web UI, and the Releases API without duplicating them.
675+
- **`--verify-tag`** rejects the call if the tag doesn't exist on origin, catching the "forgot to `git push origin vX.Y.Z`" case before it creates an orphan Release.
676+
637677
### Documentation
638678

639679
The docs site is built by [Zensical](https://zensical.org/) (MkDocs-Material's successor, same maintainer) with the [mkdocstrings](https://mkdocstrings.github.io/) Python handler. It covers two audiences:

0 commit comments

Comments
 (0)