Manual release flow for claude-code-chat-browser. A release is a git tag plus a GitHub Release with notes. No CI publish workflow, and no wheel, npm package, or other artifact.
v0.1.0 (2026-06-18) and v0.2.0 (2026-07-30) are the shipped tags so far. The steps below apply to the next cut.
The release version is __version__ in app.py line 3. There is no pyproject.toml version field.
SECURITY.md line 5 names the latest shipped release in its supported-versions blurb, so update it when a tag ships, not on the .dev0 bumps in between. README.md points readers at that page.
Between tagged releases, master may use a .dev0 suffix (for example 0.3.0.dev0 while the next release is in flight). The shipped tag drops .dev0.
- Confirm
masteris green in GitHub Actions (.github/workflows/ci.yml). - Decide the semver bump using docs/deprecation-policy.md (pre-1.0: patch for safe fixes, minor for additive work and deprecations).
- If the release removes or renames a documented API field, confirm the two-release deprecation window is satisfied.
Do the changelog in one PR and the version bump in a second PR, or combine them on a release/vX.Y.Z branch. For v0.2.0 we used two PRs: changelog first (docs/changelog-0-2-0), then the bump (release/v0.2.0).
- Edit CHANGELOG.md (Keep a Changelog):
- Move everything under
## [Unreleased]into a new## [X.Y.Z] - YYYY-MM-DDsection with### Added,### Changed,### Deprecated,### Fixed,### Removed, or### Securityas needed. - Leave
## [Unreleased]empty. - Update the footer compare links:
[Unreleased]→vX.Y.Z...HEAD, add[X.Y.Z]→vPREVIOUS...vX.Y.Z.
- Move everything under
- On a branch from current
master, setapp.pyline 3 to the release version without.dev0(for example"0.2.0"). - Update SECURITY.md line 5 so the version inside its "(currently ...)" parenthetical is the one you are shipping. Keep the surrounding formatting and leave the table at lines 7-10 unchanged.
- From the repo root, grep for stale version strings (
.dev0suffixes, the old SECURITY.md parenthetical, or the previous shipped release):PointPREVIOUS=0.2.0 PREVIOUS_ESC=$(echo "$PREVIOUS" | sed 's/[.]/\\&/g') git grep -nE "\.dev0|\(currently |${PREVIOUS_ESC}"
PREVIOUSat the release you are replacing (novprefix). Fix any hits that should name the new version (for exampledocs/deprecation-policy.md). - Open a PR, get review, merge to
master. - Tag the merge commit (lightweight tag, same as
v0.1.0andv0.2.0):Tag format:git checkout master git pull git tag vX.Y.Z git push origin vX.Y.Z
vMAJOR.MINOR.PATCH(for examplev0.2.0). - Publish the GitHub Release after pushing the tag. From the repo root, extract the body from
CHANGELOG.md:cd "$(git rev-parse --show-toplevel)" VERSION=0.2.0 # no leading v; not the literal X.Y.Z placeholder VERSION="${VERSION#v}"
case "$VERSION" in [!0-9.]|'') echo "error: VERSION must look like 0.2.0" >&2; exit 1 ;; ..|.|.) echo "error: VERSION must look like 0.2.0" >&2; exit 1 ;; ...) echo "error: VERSION must look like 0.2.0" >&2; exit 1 ;; ..) ;; ) echo "error: VERSION must look like 0.2.0" >&2; exit 1 ;; esac case "$VERSION" in 0[0-9]..|.[0][0-9].|..[0][0-9]*) echo "error: VERSION components must not have leading zeros" >&2; exit 1 ;; esac NOTES="$(mktemp)" trap 'rm -f "$NOTES"' EXIT awk -v ver="$VERSION" ' BEGIN { heading = "## [" ver "]" } /^## [/ { if (found) exit if (index($0, heading) == 1) { found=1 } } /^[[^]]+]:/ { if (found) exit } found { print } ' CHANGELOG.md >"$NOTES" grep -v '^## [' "$NOTES" | grep -q '[^[:space:]]' || { echo "error: no CHANGELOG.md section for $VERSION" >&2; exit 1; } printf '\n\nFull changelog: https://github.com/cppalliance/claude-code-chat-browser/blob/v%s/CHANGELOG.md\n' "$VERSION" >>"$NOTES" gh release create "v${VERSION}" --title "v${VERSION}" --verify-tag --notes-file "$NOTES"
The `printf` adds the same "Full changelog" footer that `v0.1.0` and `v0.2.0` carry. On macOS, Linux, and Git Bash, `mktemp` works. Or paste the `[X.Y.Z]` section in the GitHub UI and add that footer line by hand.
## After release
1. On `master`, bump `app.py` `__version__` to the next development suffix when you start the next cycle (for example `0.3.0.dev0` after shipping `0.2.0`). Put that commit in `CHANGELOG.md` under `[Unreleased]` only when there is user-visible work; a bare dev bump can ride with the next feature PR.
2. New work goes under `## [Unreleased]` until the next release.
## References
| Topic | Location |
|-------|----------|
| Changelog format | [CHANGELOG.md](CHANGELOG.md) |
| Version bump | `app.py` line 3 |
| Supported versions blurb | `SECURITY.md` line 5 |
| Deprecation / semver | [docs/deprecation-policy.md](docs/deprecation-policy.md) |
| Security reporting | [SECURITY.md](SECURITY.md) |
| CI gates | [`.github/workflows/ci.yml`](.github/workflows/ci.yml), [CONTRIBUTING.md](CONTRIBUTING.md) |