Skip to content

fix(version-dispatch): keep prereleases in their rc cycle#86

Merged
exo-mv merged 1 commit into
masterfrom
egor/prerelease-keeps-rc
May 19, 2026
Merged

fix(version-dispatch): keep prereleases in their rc cycle#86
exo-mv merged 1 commit into
masterfrom
egor/prerelease-keeps-rc

Conversation

@exo-egor

Copy link
Copy Markdown
Contributor

Summary

Caught by exodus-hydra#16379's preview comment:

```
@exodus/headless | major | 5.0.0-rc.96 | 5.0.0 ← wrong
```

`semver.inc('5.0.0-rc.96', 'major')` returns `'5.0.0'` — semver's "drop the prerelease, ship the release" semantics. That's the wrong policy when a `refactor!:` lands on a package that's been sitting in rc for 96 iterations; an unrelated breaking commit shouldn't accidentally promote a long-lived rc to stable.

Fix

Detect prerelease state from the current version (presence of `-`) and downgrade the bump level to `prerelease` so the rc counter advances instead:

Current Bump Before After
`5.0.0-rc.96` major `5.0.0` `5.0.0-rc.97`
`5.0.0-rc.96` minor `5.0.0` `5.0.0-rc.97`
`5.0.0-rc.96` patch `5.0.0-rc.97` `5.0.0-rc.97` (unchanged)
`5.0.0` major `6.0.0` `6.0.0` (unchanged — stable path untouched)

Applied symmetrically to both:

  • `preview.nextVersion` (the preview comment table)
  • `versionPackagesExplicit` (the actual release bump)

The team must explicitly promote an rc to stable via a separate workflow when they're ready.

Consumer pins

The `isMajorBump` check in `versionPackagesExplicit` still gates the consumer-pin walker. An rc.X → rc.X+1 bump doesn't move the major number, so consumer pins like `^5.0.0-rc.0` keep resolving via the workspace symlink. No churn.

Tests

193/193 passing (was 181, +12):

  • `preview.spec.ts`: 6 `.each` rows covering rc.4 → rc.5 across major/minor/patch, rc.96 → rc.97, and `1.0.0-alpha.0` → `1.0.0-alpha.1`.
  • `version-packages.spec.ts` "prerelease handling" block: 5 `.each` rows for the rc counter bump on disk, plus a no-consumer-pin-walk regression and a confirmation that stable versions stay on the original `semver.inc` path.

A commit-driven \`major\`/\`minor\`/\`patch\` bump on a package whose
current version is itself a prerelease (\`5.0.0-rc.96\`) used to
return \`semver.inc\`'s raw output — for \`major\`, that's \`5.0.0\`,
i.e. drop the rc and ship the long-awaited stable release. That's
almost certainly the wrong policy when a \`refactor!:\` lands on a
package that's been sitting in rc for 96 iterations.

Detect prerelease state from the current version (presence of \`-\`)
and downgrade the bump level to \`prerelease\` so the rc counter
advances instead (\`5.0.0-rc.96\` → \`5.0.0-rc.97\`). Stable
versions are unchanged.

Caught by exodus-hydra#16379's preview comment:
  @exodus/headless | major | 5.0.0-rc.96 | 5.0.0 ← wrong

Now reads:
  @exodus/headless | major | 5.0.0-rc.96 | 5.0.0-rc.97 ← correct

The team must explicitly promote an rc to stable via a separate
workflow when they're ready.

Both the preview's \`nextVersion\` and \`versionPackagesExplicit\`'s
real bump path apply the same downgrade. The \`isMajorBump\` check
(consumer-pin walker) keeps the workspace symlink resolving — an
rc → next-rc bump doesn't move the major, so no consumer pins are
touched.

193/193 tests passing (was 181, +12 across preview.spec and
version-packages.spec — covering rc.X → rc.X+1, no consumer pin
walk, and the stable-version path staying on the original semver.inc).
@exo-egor exo-egor requested a review from exo-mv May 19, 2026 19:17
@exo-mv exo-mv requested a review from sparten11740 May 19, 2026 19:19
@exo-egor

Copy link
Copy Markdown
Contributor Author

E2E verified in actions-playground

Run 26119756534b3958ad4, against this PR's branch (@egor/prerelease-keeps-rc, SHA cbe0e9d).

Fixture state at run time:

  • @exodus/gadgets at 5.0.0-rc.96 (prerelease — exercises the new path)
  • @exodus/batcave at 4.1.0 (stable — control)
  • bumps input: {"@exodus/gadgets":"major","@exodus/batcave":"patch"}

Action logs:

Bumping @exodus/gadgets: 5.0.0-rc.96 → 5.0.0-rc.97 (requested major; downgraded to prerelease because current is a prerelease) in features/gadgets
Bumped @exodus/gadgets@5.0.0-rc.97 (was 5.0.0-rc.96)
Bumping @exodus/batcave: 4.1.0 → 4.1.1 (patch) in libraries/batcave
Bumped @exodus/batcave@4.1.1 (was 4.1.0)

Release PR produced: actions-playground#709chore: release @exodus/gadgets@5.0.0-rc.97,@exodus/batcave@4.1.1.

Confirms:

  • Prerelease major bump → rc counter advances (rc.96rc.97), no stable promotion.
  • Stable patch bump → unchanged semver.inc semantics (4.1.04.1.1).
  • The "downgraded to prerelease" notice fires only on the prerelease entry, as expected.

// prerelease counter rather than dropping the rc — a `feat!:` commit
// shouldn't accidentally promote a long-lived rc to a stable release.
// The caller can promote to stable via a separate workflow when ready.
const effectiveBump = isPrerelease(current) ? 'prerelease' : (bump as ReleaseType)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: dedupe

@exo-mv exo-mv merged commit 9962a24 into master May 19, 2026
2 checks passed
@exo-mv exo-mv deleted the egor/prerelease-keeps-rc branch May 19, 2026 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants