Last updated July 12, 2026, Version 0.34
This page is the single source of truth for release managers cutting a stable
release, a patch release, or a pre-release of GatherPress. The tag-driven
mechanics are automated by.github/workflows/release.yml; this
doc covers the full release train around them β what to do before the tag,
what the tag automates, and what must happen after β based on how the
0.34.0 release actually shipped. Automation of the remaining manual steps is
tracked in #1921.
Branch model
developis the trunk. All feature and fix PRs target it, every PR is
squash-merged (branch protection enforces linear history and signed
commits), and every PR carries either a.github/changelog/entry file or
theSkip Changeloglabel.mainreflects the released state. Only release-train PRs target it:
the developβmain release merge, patch release branches, and changelog
parity syncs. Release merges into main use merge commits β never squash a developβmain PR (squashing 1,000+ commits into one guarantees
the branches permanently diverge). If main’s protection has
“Require linear history” enabled, it must be unchecked for the release
merge to land.- Fixes are born on
develop. Patch releases receive them as
cherry-picks (see Patch release flow); nothing
original should be written on a patch branch unless the bug doesn’t exist
on develop. - GatherPress Alpha is versioned in lockstep. It refuses to run when its
version differs from core’s, so every core release (stable or patch) needs
a matching version sync and release in
gatherpress-alpha.
What gets automated
Pushing a tag of the form X.Y.Z (stable) or X.Y.Z-alpha.N / -beta.N /-rc.N (pre-release) triggers release.yml. The workflow:
| Tag pattern | Distro zip | GitHub Release entry | Changelog body source | wp.org deploy |
|---|---|---|---|---|
0.34.0 | gatherpress.0.34.0.zip | Release (latest) | Rolled-up [0.34.0] section, committed back to CHANGELOG.md via auto-PR | Yes |
0.34.0-alpha.1 | gatherpress.0.34.0-alpha.1.zip | Pre-Release | Rolled-up [0.34.0-alpha.1] section computed in an ephemeral checkout (no commit) | No |
0.34.0-beta.1 / -rc.1 | Same shape as alpha | Pre-Release | Same shape as alpha | No |
The distro zip’s outer filename carries the version; the inner layout is
always gatherpress/... so it installs cleanly under the right slug.
Stable tags are cut from main; pre-release tags are cut from develop.
Version bump generation
Version bumps are never hand-edited. The generator (currently thewp gatherpress develop generate_version --version=X.Y.Z WP-CLI command in
the gatherpress-develop
plugin; being ported into .github/scripts by
#1827) needs a
credits entry for the target version in gatherpress-develop’sdata/credits.php first, then writes:
gatherpress.phpVersion:header,package.jsonversion,readme.txtStable tag:, the version badge inREADME.md, and the regeneratedincludes/data/credits.php.- The GatherPress Alpha
Version:header in the sibling../gatherpress-alphacheckout. SECURITY.mdsupported-versions tables in both plugins.
After running it, refresh the lockfile on the host: npm i --package-lock-only. README.md, readme.txt, includes/data/credits.php,
and SECURITY.md are generated output β never edit them by hand.
Pre-release flow
Use case. You want testers to be able to download a build of the
in-progress release and see what’s queued for it, without touching wp.org.
Prepare the version PRs (one release train, three PRs, in merge order):
credits-X.Y.Z-suffix.Nβ gatherpress-developmain: add the credits
entry.version-X.Y.Z-suffix.Nβ gatherpress-alphamain: the synced version
header (Skip Changeloglabel). Opened by that repo’s Version Bump
workflow β core’s Version Bump workflow dispatches it automatically when
theGATHERPRESS_ALPHA_TOKENsecret is configured, and otherwise prints
thegh workflow runone-liner in its run summary.version-X.Y.Z-suffix.Nβ coredevelop: the generated bump
(Skip Changeloglabel). Opened by core’s Version Bump workflow.
After the core release workflow finishes, its alpha-handoff job likewise
dispatches (or documents, without the token) the matching gatherpress-alpha
release β alpha’s workflow refuses to cut a release until its version PR has
merged, so triggering it early fails loudly rather than shipping a mismatch.
Cut it:
git checkout develop
git pull origin develop
git tag 0.34.0-alpha.1
git push origin 0.34.0-alpha.1
What the workflow does:
- Detects the tag is a pre-release (the
-alpha./-beta./-rc.suffix). - Builds
gatherpress.0.34.0-alpha.1.zipvianpm run plugin-zip. - Runs the changelog rollup in an ephemeral working copy and extracts the resulting
[0.34.0-alpha.1]section as the release body. The changes never get committed anywhere β they evaporate when the job ends. - Creates a GitHub Pre-Release with the zip attached and the rolled-up body. The Pre-Release is not marked as the latest release.
.github/changelog/*entries are left in place in the repository so the eventual stable release still has them.- Skips the wp.org deploy entirely.
Testers downloading the pre-release zip see the same changelog body they’d see at stable release time β minus any further entries that land between now and then.
Verify after the workflow lands:
- GitHub Releases page shows the new tag with a “Pre-release” badge.
- The release body matches the queued
.github/changelog/entries. - The attached zip downloads as
gatherpress.X.Y.Z-alpha.N.zipand unzips with agatherpress/top-level directory. - wp.org listing at https://wordpress.org/plugins/gatherpress/ is unchanged.
- Tag and release GatherPress Alpha at the same version (its own tag-driven
release.yml, GitHub-only).
Stable release flow
Use case. Cutting a real release that ships to end users via wp.org.
1. Pre-flight
- [ ] All issues in the release’s milestone are closed or moved.
- [ ] Queued
.github/changelog/entries read correctly (skim, fix anything off). - [ ] Credits entry for
X.Y.Zadded in gatherpress-develop (data/credits.php) β by convention an exact copy of the latest pre-release entry, roster corrections applied there first. - [ ]
generate_version --version=X.Y.Zrun +npm i --package-lock-only; version PRs opened and merged in order: gatherpress-develop credits, alpha sync, coreversion-X.Y.Zβ develop.
2. Release merge (develop β main)
main lags develop by a whole cycle and carries release-line commits of its
own, so this is a real merge, not a fast-forward:
git checkout develop && git pull origin develop
git checkout -b version-X.Y.Z # reuse the name after its develop PR merged
git merge origin/main # expect conflicts; resolve ALL in develop's favor
git diff origin/develop # MUST be empty β tree identical to develop
git push -u origin version-X.Y.Z
gh pr create --base main --title "Release X.Y.Z"
Expected conflicts are version strings (gatherpress.php, package.json,package-lock.json, includes/data/credits.php) and generated files β
always take develop’s side. The empty-diff check is the safety net: the
release ships exactly what develop has.
Merge the PR with a merge commit (toggle “Require linear history” off on
main first if enabled). Never squash it.
3. Tag it
git checkout main && git pull origin main
git tag X.Y.Z
git push origin X.Y.Z
What the workflow does:
- Detects the tag is stable (no
-alpha./-beta./-rc.suffix). - Aggregates every entry file in
.github/changelog/into a new## [X.Y.Z] - YYYY-MM-DDsection at the top ofCHANGELOG.md, appending[#NNNN]PR references, and deletes the entry files (dotfiles like.gitkeepsurvive, which is why the directory persists in git). - Commits that rollup to a new
release/X.Y.Zbranch and opens an auto-PR back todevelop(with theSkip Changeloglabel). - Builds
gatherpress.X.Y.Z.zip. - Creates a GitHub Release with the zip attached, marked as latest, with the
[X.Y.Z]section as the body. - Deploys to wordpress.org via the
10up/action-wordpress-plugin-deployaction using theSVN_USERNAME/SVN_PASSWORDsecrets.
4. After the tag β closing the loop
Every one of these is required; skipping any of them bites the next release:
- [ ] Confirm the wp.org release. wp.org emails committers a release
confirmation link; the new version is not live in the plugin directory
until a committer clicks it. - [ ] Confirm the
release/X.Y.Zrollup PR auto-merged into develop.
The workflow creates its commit via the API (GitHub-signed) and enables
auto-merge (squash), so it lands on its own once checks pass β this brings
the rolled-upCHANGELOG.mdto develop and removes the consumed entry
files. If it’s still open, see Troubleshooting. - [ ] Sync the rollup state to main (changelog parity): the auto-PR only
targets develop, so cherry-pick develop’s rollup squash commit onto a
branch off main and PR it (Sync X.Y.Z changelog rollup state to main).
Without this, the next patch release cut from main re-rolls every
already-released entry into its changelog. - [ ] Release GatherPress Alpha: merge its
version-X.Y.Zsync PR if
not already done, then let the core release workflow’salpha-handoffjob
dispatch alpha’s release (automatic with theGATHERPRESS_ALPHA_TOKEN
secret; otherwise run thegh workflow run release.ymlone-liner from the
job summary β a manualgit tag X.Y.Z && git push origin X.Y.Zon alpha’s
main still works too). Merge alpha’s own rollup PR afterward. - [ ] Bring the demo data in line with the new version: follow the
“Preparing demo-data for a new version of GatherPress” steps in the
gatherpress-demo-data README
so the Playground demo content matches the release. - [ ] Delete the spent branches:
version-X.Y.Z(core and alpha),credits-X.Y.Z(gatherpress-develop). Therelease/X.Y.Zbranches go
when their PRs merge.
Verify:
- GitHub Releases page shows the new tag as “Latest release” with the zip attached.
- wp.org listing at https://wordpress.org/plugins/gatherpress/ shows the new version (after the confirmation click; the directory can lag a few minutes).
developandmainboth have the[X.Y.Z]CHANGELOG section and an empty.github/changelog/.
Patch release flow
Use case. Shipping X.Y.1 for the released line while develop has moved
on to the next minor.
- Fixes land on develop first via normal PRs (with changelog entry
files), milestonedX.Y.1. Only fix directly on the patch branch when
the bug doesn’t exist on develop anymore. - Cut the patch branch from main β this is why the changelog parity
step above matters; main must start with a clean.github/changelog/:git checkout main && git pull origin main git checkout -b version-X.Y.1 git cherry-pick -x <sha-from-develop> [...]The-xrecords the source commit; the cherry-picks bring their
changelog entry files along. - Version bump on the patch branch: credits entry (copy the
X.Y.0
entry forward, appending any new contributors) +generate_version --version=X.Y.1+ lockfile refresh. - PR
version-X.Y.1β main, merge, tagX.Y.1on main. The workflow
ships it exactly like a stable release (it is one), and opensrelease/X.Y.1β develop. - Close the loop exactly as in the stable flow: merge the rollup PR
into develop promptly β because the fixes originated on develop, the
rollup deletes those same entry files there, so the next minor’s
changelog won’t re-list them β then parity-sync main, release alpha atX.Y.1, delete spent branches.
Because every patch commit on main is a content-identical cherry-pick of a
develop commit, the next minor’s developβmain release merge auto-resolves
everything except the usual version-string conflicts.
Troubleshooting
“Rolled-up CHANGELOG.md does not contain a [X.Y.Z] section”
The rollup job failed. Almost always means .github/changelog/
was empty at tag time. Confirm by checking out the tag locally and looking:
git checkout 0.34.0
ls .github/changelog/
If it’s truly empty, the release shouldn’t go out β there’s nothing to ship.
If there are entries but the rollup still failed, run the same command
locally to reproduce:
vendor/bin/changelogger write \
--use-version=0.34.0 \
--release-date="$(date +%Y-%m-%d)" \
--add-pr-num \
--deduplicate=-1 \
--yes
The changelog contains entries from the previous release
The tag was cut from a main that never got the previous release’s parity
sync, so already-released entry files were still present and got rolled up
again. Fix main first (cherry-pick the previous rollup commit onto main),
delete the bad tag and GitHub Release, and re-tag.
The release merge PR can’t be merged with a merge commit
main‘s branch protection has “Require linear history” enabled β GitHub
reports “Merge commits are not allowed on this repository” even when the
repo-level merge settings allow them, and a direct push of a merge commit is
rejected with “This branch must not contain merge commits.” Uncheck
Require linear history in the main protection rule for the merge, and
re-enable afterward if that’s the standing policy.
The rollup auto-PR didn’t merge on its own
The workflow signs its commit via the API and enables auto-merge, so the PR
normally lands once checks pass. If it’s stuck, merge it manually with
squash (gh pr merge --squash, --admin if protection complains). Do not
close the PR: unmerged, it leaves consumed entry files on develop and the
next release double-rolls them β the release workflow now refuses to run a
stable tag while a release/* PR is open, so an ignored rollup PR blocks
the next release rather than corrupting its changelog.
wp.org deploy failed
Usually means the SVN_USERNAME / SVN_PASSWORD secrets are stale or the
SVN repo state diverged. Quickest recovery:
- Manually run
npm run plugin-ziplocally on the tag’s commit. - Upload the zip to https://wordpress.org/plugins/developers/add/ as the new version, or use
svnto push it through the wp.org SVN flow manually. - File an issue to rotate the secrets if that’s the root cause.
The GitHub Release stays correct regardless β it’s already attached the zip
and the release body. Note that if the SVN tag already exists, the deploy
action exits successfully without pushing anything (“Version X.Y.Z β¦
was already published”) β a re-run after a partial deploy silently no-ops
with a green check. Clean up the bad SVN tag manually before re-running.
Auto-PR for the changelog rollup didn’t open
The rollup job failed late (after the rollup commit pushed but
before gh pr create ran). Check the job log; if the branchrelease/X.Y.Z exists on origin with the rollup commit, open the PR
manually:
gh pr create \
--base develop \
--head release/0.34.0 \
--title "Roll up changelog for 0.34.0" \
--body "Automated rollup of .github/changelog/* entries." \
--label "Skip Changelog"
I tagged the wrong commit
Delete the tag locally and on origin, then re-tag:
git tag -d 0.34.0
git push --delete origin 0.34.0
# fix up the branch, then re-tag
git tag 0.34.0
git push origin 0.34.0
If the release workflow already ran against the bad tag, also:
- Delete the GitHub Release entry (it’ll auto-recreate on the new tag push).
- Close the bogus auto-PR and delete its
release/X.Y.Zbranch. - Revert the wp.org deploy if it shipped β contact wp.org plugin team if you can’t.
Secrets and permissions required
The workflow uses these secrets from the repo / org settings:
SVN_USERNAMEβ wp.org SVN username (used by the wp.org deploy step only).SVN_PASSWORDβ wp.org SVN password (used by the wp.org deploy step only).GITHUB_TOKENβ auto-provisioned; the workflow declares least-privilege scopes per job (contents: writefor the rollup commit + Release creation,pull-requests: writefor the auto-PR).
If the permissions: block on release.yml is ever loosened, double-check
that no job ends up with broader scopes than it needs.
Versioning conventions
- Stable:
0.34.0,0.35.0,1.0.0. Three numeric components, no suffix. These ship to wp.org and are tagged onmain. - Patch:
0.34.1. Same shape as stable; cut from aversion-X.Y.1branch offmainper the patch flow. - Alpha:
0.34.0-alpha.1,0.34.0-alpha.2. Use for early in-cycle builds; tagged ondevelop. - Beta:
0.34.0-beta.1. Use for feature-complete in-cycle builds where the team is still smoke-testing; tagged ondevelop. - Release candidate:
0.34.0-rc.1. Use for “we believe this is shippable, last call for showstoppers”; tagged ondevelop.
The pre-release suffix matches the SemVer spec β anything outside -alpha. / -beta. / -rc. won’t be recognized by the workflow’s classifier and will be treated as stable. Don’t get creative with the suffix.