Skip to content

Releasing

The step-by-step release runbook. For the why and the shape of the branch flow, see Branching & releases; this page is the exact steps.

Run pnpm run cut-release -- X.Y.Z. The rest is automatic. You can also cut a release from GitHub Actions by running Cut Release on the develop branch with the same X.Y.Z version.

Normal release flow

  1. Make sure the intended changes are merged to develop.
  2. From a clean checkout at the latest origin/develop, run:
git checkout develop
git pull --ff-only origin develop
pnpm run cut-release -- X.Y.Z
  1. Review the generated release/X.Y.Z -> main PR.
  2. Merge the release PR to main with a merge commit.
  3. Let the Back Merge Main to Develop workflow open or update sync/main-to-develop.
  4. Merge the sync PR to develop with a merge commit.

Releases are cut from develop, never from main, so the release branch is grounded in the same state that was tested on staging. The post-release sync must be a real merge commit so release commits propagate back to develop and the branches do not silently diverge. Incident context: PR #976.

GitHub Actions entry point

Use Actions -> Cut Release -> Run workflow on develop. Inputs:

  • version: semver without a build suffix, for example 1.1.13.
  • dry_run: validates and prints the planned branch, version bump and PR without pushing anything.

The workflow wraps scripts/cut-release.ts, configures the GitHub Actions bot identity, and creates the same release PR as the local script.

Versioning

Release versions use semver: MAJOR.MINOR.PATCH. The version of record lives in both Flutter app pubspecs, which the release script writes as <version>+1:

  • apps/perci-platform-members/pubspec.yaml
  • apps/perci-platform-clinicians/pubspec.yaml

The +1 build number is intentionally stable for web releases. The release version must be strictly greater than the version currently on main; the release guard rejects downgrades and stale version bumps.

QA fixes

During QA, fixes land on develop first. Forward-port the same fix to the open release/X.Y.Z branch so the release contains the reviewed change and develop stays the source of truth.

Hotfixes

Use a hotfix when a fix must reach production before the next normal release. The rules for main PRs (branch prefix, version bump) are in Branching & releases.

For urgent production incidents:

  1. Branch from main as hotfix/PPL-NNNN.
  2. Keep the fix minimal.
  3. Open a PR to main.
  4. Merge with a merge commit after review and required checks pass.
  5. Let the automatic sync/main-to-develop PR carry the hotfix back to develop.

For non-urgent hotfixes, prefer fixing develop first:

  1. Branch from develop and merge the fix to develop through the normal PR path.
  2. Branch from main as hotfix/PPL-NNNN.
  3. Cherry-pick the minimal fix commit from develop onto the hotfix branch.
  4. Open the hotfix PR to main and merge it with a merge commit.
  5. Let the automatic sync/main-to-develop PR reconcile the production merge back to develop.

Before cherry-picking from develop, check the fix does not depend on unreleased develop-only changes. If it does, make a smaller hotfix directly from main instead.

Required repository settings

main keeps its protections and requires:

  • Pull requests; no direct pushes.
  • Required status check: Release PR Guard.
  • Merge commits allowed for release/*, hotfix/* and sync PRs.
  • Squash remains the default for ordinary PRs to develop.
  • PRs to main must come from release/*, hotfix/* or sync/main-to-develop*. The versioned enforcement lives in scripts/release-pr-guard.ts; if GitHub branch rulesets are available, mirror the same source-branch allow-list in the main ruleset.

Troubleshooting

release must be cut from the exact origin/develop commit

git checkout develop
git pull --ff-only origin develop
pnpm run cut-release -- X.Y.Z

version must be greater

Pick the next semver above the version currently on both app pubspecs. Do not reuse a version that has already shipped.

PR branch appears to be grounded in main

Close the bad release PR and re-run pnpm run cut-release -- X.Y.Z from the latest origin/develop.

.gitignore files were added

Remove generated build output from the release branch. Common examples are Flutter Linux generated files under apps/*/linux/**, .dart_tool, and build/.

mcp/*/package-lock.json

Remove the npm lockfile. MCP packages use pnpm, and the root pnpm-lock.yaml is the lockfile source of truth.

Back-merge PR has conflicts

Check out sync/main-to-develop, merge origin/main, resolve conflicts, then push the branch. Keep the PR as a merge commit into develop.