Release flow
This page describes the default pr-flow strategy. For trunk-based, see Branch strategies.
- Feature PRs merge into developType CI runs on each PR and push to develop.
- Open a release PR: develop → mainPROJECT-COMMON-AUTO-CHANGELOG-CONTROL starts.
- Confirm the versionNext version from commit types (semver-auto). Every version file is synced and committed to the PR branch.
- Write release notesSummary engine chain: your AI API → Copilot (optional) → rule-based. Posted as a PR comment.
- Update CHANGELOG.md / CHANGELOG.jsonCommitted to the PR branch with [skip ci].
- AutomergeMerge commit into main.
- Tag vX.Y.Z + GitHub ReleasePROJECT-COMMON-RELEASE-PUBLISH on the push to main.
- README version line + deploysREADME-VERSION-UPDATE runs; server and store deploy workflows run on the release branch.
Step by step
Section titled “Step by step”1. Release PR opened
Section titled “1. Release PR opened”PROJECT-COMMON-AUTO-CHANGELOG-CONTROL runs on PRs from the development branch to the release branch that are still open. A run queued behind the one that already merged the PR exits early.
2. Version confirmed
Section titled “2. Version confirmed”With semver_auto: true (the default), commits since the last release are classified:
| Commit | Bump |
|---|---|
feat: |
minor |
any standard type with ! (feat!:, fix(api)!:), or a BREAKING CHANGE: footer at the start of a body line |
major |
| anything else | patch |
Types are case-insensitive. Only the BREAKING CHANGE: footer is read from commit bodies; the rest of the body is ignored.
If the result is patch but some commits do not follow the convention, and a user AI API or Copilot is configured, the AI is asked only whether to upgrade patch to minor. It never produces a major bump. With AI off (the default) the rule result is used as is.
With semver_auto: false, every release is patch + 1 and you edit major/minor by hand in version.yml.
The new version is written to version.yml and to each type’s version file, then committed to the PR branch.
3. Release notes and CHANGELOG
Section titled “3. Release notes and CHANGELOG”changelog_manager.py builds the summary from commit subjects plus a capped git diff --stat (file-level summary, no diff body). The same text is posted as a PR comment (re-runs edit that comment) and written to CHANGELOG.json and CHANGELOG.md. The entries come from commit subjects, so the language setting does not translate them. See Summary engine.
4. Automerge
Section titled “4. Automerge”The workflow enables automerge with a merge commit, falls back to a direct merge, and retries with backoff while GitHub catches up with the freshly pushed head. An already-merged PR counts as success.
When branch protection blocks the merge
Section titled “When branch protection blocks the merge”If a standing rule on the release branch rejects the merge (for example it requires an approving review, or a required check has not passed), the run fails and says which kind of rule it was, with the options. The workflow also renames the release PR to chore(release): vX.Y.Z (PR #N) and leaves one guidance comment, so a merge you make yourself with Create a merge commit still publishes the release.
- To let the workflow merge next time, change the rule so the account that merges (the
WORKFLOW_PATowner, or the GitHub Actions bot when no PAT is set) is allowed to merge. - To merge by hand every time without a failing run, set
release_automerge: false(see below). - Branch protection and rulesets are not available on private repositories of a free plan, so this only comes up where they can be created.
Merging the release PR yourself
Section titled “Merging the release PR yourself”Automerge is on by default. To keep a human in the loop, set release_automerge: false in version.yml (or reinstall with --no-release-automerge). The workflow then skips automerge and the wait-for-merge job, renames the release PR to chore(release): vX.Y.Z (PR #N) and leaves one guidance comment.
- Merge after that workflow run has finished, because it confirms the version and renames the PR.
- Any merge method publishes the release. Create a merge commit is recommended, because it keeps the history the next release notes are built from.
- If the automatic rename fails, the comment tells you which merge message to set so the release is still published.
What each merge method does to the release:
| Merge method | Release published? | Why |
|---|---|---|
| Create a merge commit | Yes | The release gate reads chore(release): from the merge commit message, which carries the renamed PR title. |
| Squash and merge | Yes | With the default message (the commit list is included) the squash commit body carries the [skip ci] of the version-confirm commit, so GitHub skips the push workflows; PROJECT-RELEASE-PR-MERGED sees the merged release PR and dispatches PROJECT-RELEASE-PUBLISH. A title-only squash message is published by the push as usual. The next release notes can list earlier changes again, because the original commits are not part of the release branch history. |
| Rebase and merge | Yes | The last commit on the release branch is the version-confirm commit ([skip ci]), so the push workflows are skipped and PROJECT-RELEASE-PR-MERGED dispatches PROJECT-RELEASE-PUBLISH. The next release notes do not repeat the rebased commits, but only while develop has not merged the release branch back; after that merge they are listed again. |
| Merged before the run finished | Extra patch release | The PR is not renamed and the version is not confirmed yet, so the safety-net workflow publishes a patch version of its own. |
The version-confirm commit keeps [skip ci]: the bot pushes it to the open release PR, and without the marker GitHub would queue that PR’s workflows for approval. PROJECT-RELEASE-PR-MERGED listens to pull_request_target, which [skip ci] does not affect; it never checks out the PR’s code, ignores fork PRs, and its token can only read contents and dispatch workflows. The safety-net back-merge commit carries no [skip ci], so a release PR opened right after it still runs its workflows. If a release was still not published, run PROJECT-RELEASE-PUBLISH manually on the release branch (Actions > Run workflow). It is idempotent, so running it twice is safe.
Known limits:
- Only a merge commit keeps the release branch history the release notes are built from. A squash merge cannot be told apart from new work when the next notes are collected.
- Older wizard versions (0.15.1 and earlier) do not know the
release_automergekey and drop it when you reinstall with them, which turns automerge back on. Reinstall with 0.16.0 or later, or add the key again.
5. Tag and GitHub Release
Section titled “5. Tag and GitHub Release”On the push to the release branch, PROJECT-COMMON-RELEASE-PUBLISH only proceeds when the head commit message matches the release pattern chore(release):. It pushes the vX.Y.Z tag and creates the GitHub Release. The release notes are the CHANGELOG entry plus GitHub’s generated notes (contributors and compare link). Tag and Release are checked separately, so a half-finished release is completed on re-run.
It then dispatches README-VERSION-UPDATE, which rewrites the version line under the <!-- AUTO-VERSION-SECTION --> marker in README.md.
6. Deploys
Section titled “6. Deploys”Server and store deploy workflows run on pushes to the release branch.
Direct pushes to main
Section titled “Direct pushes to main”Not every change arrives through a release PR. PROJECT-COMMON-VERSION-CONTROL is a safety net for direct pushes to the release branch: it bumps the patch version, syncs the version files, adds a CHANGELOG entry built from commits since the last tag, dispatches RELEASE-PUBLISH, and merges the release branch back into the development branch so the next release PR does not conflict on version files.
It skips pushes that came from a release PR (the merge commit carries chore(release): and changes version.yml) and does not run at all in trunk-based mode.
Publishing packages
Section titled “Publishing packages”The release flow stops at the GitHub Release. It does not publish to npm, PyPI or app stores by itself (store deploys are separate Flutter workflows). To publish a package, add a workflow on the release: published event. This repository does exactly that for npm: the tag is an immutable snapshot, so what gets published does not depend on workflow timing.
A Release created with GITHUB_TOKEN does not trigger other workflows, so a release: published workflow only fires when RELEASE-PUBLISH runs with a WORKFLOW_PAT.