Repository navigation
ci: support releases from maintenance branches - #420
Conversation
Release Draft now opens the release pull request against the branch it runs on, and refuses a major bump anywhere but main. Release Publish also runs on maintenance/* branches. It publishes to npm as `latest` only when the version is higher than the current `latest`, and as `latest-<major>` otherwise, so a 3.x patch cannot pull `latest` back from 4.x. The GitHub release is marked Latest on the same rule. Also harden Release Draft: pin actions/checkout to a commit, stop persisting git credentials, pass the new version to shell steps through the environment, and limit the release app token to contents and pull requests. Zizmor now scans maintenance/* branches too. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PR SummaryMedium Risk Overview Enhances the publishing workflow with concurrency serialization and dynamic dist-tag resolution. When publishing, the workflow compares the version against npm's current Reviewed by Cursor Bugbot for commit c689cad. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Concurrent main and maintenance releases can race and incorrectly roll back npm’s latest tag.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Enables independent releases from maintenance branches while preserving the newest npm and GitHub release as Latest.
Changes:
- Targets release drafts and publishes to maintenance branches.
- Selects npm dist-tags and GitHub Latest status by version.
- Hardens release workflows and expands Zizmor coverage.
| File | Description |
|---|---|
.github/workflows/release-draft.yml |
Targets the selected branch and hardens release drafting. |
.github/workflows/release-publish.yml |
Publishes maintenance releases with version-aware tags. |
.github/workflows/zizmor.yml |
Scans maintenance branches. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
A release from main and one from a maintenance branch could run at the same time, both read the old npm `latest`, and both choose `latest`. If the maintenance publish finished last, `latest` would roll back below the new major. Put the publish job in one shared, non-cancelling concurrency group so each run resolves its tag after the previous publish completes. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Drop a tooling prefix from two comments and say plainly what each limitation is. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Why
Apps that stay on version 3 of this package need to keep getting fixes after version 4 ships. Version 4 will include breaking changes, so not every app will upgrade straight away. Today the release workflows only release from main, and main will soon hold version 4, so a 3.x fix would have no way out. Once this lands, a 3.x fix can merge into a maintenance branch and be released from there, without moving the default
npm installor the "Latest" GitHub release back to 3.x.Programme
This serves the version 4 release. The plan keeps 3.x on a
maintenance/3.xbranch; it is internal and cannot be linked. The approach follows the release workflows on the maintenance branch of the Rokt iOS SDK distribution (ROKT/rokt-sdk-iosmaintenance/4.x) and of two other SDK repositories. What this package adds is handling for npm'slatesttag.What changes
Before: Release Publish ran only on main. It always published to npm as
latestand marked the GitHub release Latest. Release Draft relied on an implicit default for the branch its release pull request targets.After:
maintenance/3.xprepares a release intomaintenance/3.x. It refuses amajorbump on any branch other than main.VERSIONchanges on amaintenance/*branch. It publishes to npm aslatestonly when the new version is higher than npm's currentlatest, and aslatest-<major>otherwise, for examplelatest-3. The GitHub release is marked Latest by the same rule. A 3.x patch released before 4.0.0 ships therefore still becomeslatest; after 4.0.0 it becomeslatest-3. Publishes run one at a time across branches, so each one reads npm'slatestafter the previous publish has finished.actions/checkoutis pinned to a commit and no longer keeps git credentials, the new version reaches shell steps through the environment, and the release GitHub App token is limited to contents and pull requests. Zizmor now also scansmaintenance/*branches.No documentation or changelog moved: the changelog is written by Release Draft at release time, and the release steps are not documented in this repository. The new process is under Rollout.
Start reading at the
Resolve npm dist-tagstep inrelease-publish.yml. Left alone on purpose: the changelog action stays on@main, so the fix under Linked work reaches this repository without a bump, and the workflow-wideGITHUB_TOKENpermissions are unchanged, because narrowing them cannot be tested without a real release.Linked work
Related: ROKT/rokt-workflows#67 fixed the changelog action so 3.x release notes are based on the previous 3.x release. It merged on 2026-09-29, and this repository references the action at
@main, so Release Draft already uses the fix.Rollout
Path: merging changes CI only; nothing is published, and the next release from main behaves as today (
lateston npm, marked Latest on GitHub). After the merge, in this order:maintenance/*to the branch ruleset main uses, so the branch requires reviews and checks and cannot be force-pushed or deleted. This comes first because a direct push that changesVERSIONon an unprotected maintenance branch would publish to npm without review. (Planned.)maintenance/3.xfrom the merge commit before any version 4 change reaches main. Each branch runs its own copy of these workflows, so the branch must contain them. (Planned.)Feature flags: none.
Turning it off: revert this pull request on main and on
maintenance/3.x; the next run uses the old workflows. A version already published to npm cannot be recalled. A wrong npm tag can be moved in seconds by a package maintainer withnpm dist-tag add react-native-mparticle@<version> latest.What we watch: after every release,
npm view react-native-mparticle dist-tagsand the Latest badge on the GitHub releases page. Todaylatestis 3.4.0. After 4.0.0,latestmust be a 4.x version andlatest-3the newest 3.x;lateston a 3.x version after that means move the tag back and revert this change.Risks
latestback from 4.x; prevented by comparing the new version with npm's currentlatestbefore publishing, a rule checked locally on five version pairs; we would see it innpm view react-native-mparticle dist-tags.maintenance/3.xpublishes a release nobody reviewed; prevented by adding the branch to the ruleset before the branch is cut (Rollout step 1); we would see a release on npm with no merged release pull request behind it.latest, and the one that finishes last wins it; prevented by running the publish job in one shared, non-cancelling concurrency group (raised by the automated Copilot review); a third release started at the same time cancels the waiting one, which shows as a cancelled run that can be re-run.bash -e; we would see a failed Release Publish run with nothing published.sort -Vorders a pre-release such as4.0.0-beta.1above4.0.0; not addressed, because this package does not publish pre-releases; we would see a wrong tag on the first pre-release.Risk class: higher — a wrong npm tag or Latest flag changes what every new
npm install react-native-mparticlegets until a maintainer moves it back, and a published version cannot be recalled.Who
Written by: an automated coding agent (Claude Code), at an SDK engineer's request, following their plan to keep 3.x on a maintenance branch after version 4.
Code reviewed before opening: no human reviewer. Trunk's pre-commit checks passed on each commit.
Design reviewed before opening: the requesting engineer approved the plan on 2026-09-29, including the
latest-3tag and fixing the changelog action upstream rather than here.Decision this implements: that plan approval on 2026-09-29; the record is internal and cannot be linked.
Checked: on 2026-09-29,
trunk checkon the three workflow files (actionlint, checkov, yamllint and prettier) passed. zizmor 1.30.1, the version CI runs, was run before and after, with online audits: see the note below. The npm tag rule was run locally on five version pairs. CI passed on the latest commit: Trunk Check, the zizmor scan, JS Tests, Android unit tests and lint, and the Android and iOS sample app builds. After opening, the automated Copilot review raised one finding, the publish race under Risks, which the second commit fixes; Cursor Bugbot found no issues.Not checked: a real Release Draft or Release Publish run, because both open release pull requests or publish, and the maintenance branch does not exist yet. The JavaScript and Android test suites were not run locally, because nothing outside
.github/workflowschanged; CI ran them on this pull request instead (see Checked).Size
Hand-written: 47 lines added and 4 removed, in 3 files.
Generated: none.
Notes for reviewers
npm tag rule, run locally:
latestlatestlatest-3latestlatest-3latest3.xandv3cannot be npm tags, because npm reads them as version ranges; checked with the semver library bundled with npm.zizmor 1.30.1. With the regular persona that CI uses,
release-draft.ymlwent from 6 findings on main to 1. The one left is the@mainreference to the changelog action, kept on purpose.release-publish.ymlandzizmor.ymlhave none. With the strictest (auditor) persona, the total went from 17 to 12, with no new finding.Files.
.github/workflows/release-draft.yml,.github/workflows/release-publish.yml,.github/workflows/zizmor.yml.🤖 Generated with Claude Code