Maintenance Branches
After a new major ships, the previous one usually still has users. Maintenance branches keep those lines releasable: when a release crosses a major boundary, a branch is created from the previous release — and fixes pushed to it go through the same pull request flow and release from that branch, without disturbing the new major.
The full lifecycle on an example:
- The project is released as
1.1.0. A breaking change lands and the release pull request ships2.0.0— the action creates thev1branch from thev1.1.0tag. - A
fix: ...commit is pushed tov1— the action opens a release pull request targetingv1with the1.1.1bump. - The pull request is squash-merged —
1.1.1is tagged and published from the maintenance branch:- the package goes to the
release-1.xnpm dist-tag,lateststays on2.0.0; - the GitHub release is created without the Latest mark.
- the package goes to the
Enable maintenance branch creation in the config:
{ "project": ["@simple-release/pnpm#PnpmWorkspacesProject", { "mode": "fixed" }], "maintenanceBranch": { "enabled": true }}And let the release workflow react to pushes to the maintenance branches:
name: Releaseon: issue_comment: types: [created, deleted] push: branches: - main - 'v*'For an independent monorepo the branches are named per package — pkg-name@1 — so use the '*@*' pattern instead of 'v*'.
Instead of the config, creation can also be enabled per run with the maintenance-branch action input.
How branches are created
Section titled “How branches are created”The branch is created by the release workflow when the released version crosses a major boundary — for example 1.1.0 → 2.0.0, but not 2.0.0 → 2.1.0. It is created from the previous release tag and named after the previous major with the tag prefix: v1 from v1.1.0. In independent monorepos each package gets its own branch with its tag prefix: pkg-name@1 from pkg-name@1.1.0 — only for packages that actually crossed a major.
An existing branch is never overwritten — recreate it deliberately with the force option if needed.
| Option | Description |
|---|---|
enabled | Enable maintenance branch creation. Defaults to false. |
force | Recreate the branch if it already exists. |
branchPrefix | Prefix for the branch name, when it should differ from the tag prefix. Defaults to the tag prefix. |
Releasing from a maintenance branch
Section titled “Releasing from a maintenance branch”Nothing special to learn: the flow on a maintenance branch is the same as on main. Push conventional commits, get a release pull request targeting the branch, squash-merge it — the release runs from the branch with the right base version.
What differs is the “latest” handling, automatically:
- the npm package is published under the
release-N.xdist-tag of its major —latestkeeps pointing at the newest line; - the GitHub release is not marked as Latest.
Several maintenance lines can live side by side — v1 and v2 while main is on 3.x — each releasing independently under its own release-1.x and release-2.x tags.