Skip to content

feat(versioning): opt-in pre-1.0 bump rules - #1379

Open
BryanFRD wants to merge 4 commits into
mainfrom
feat/pre-major-bump-rules
Open

BryanFRD wants to merge 4 commits into
mainfrom
feat/pre-major-bump-rules

Conversation

@BryanFRD

@BryanFRD BryanFRD commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Adds an opt-in preMajor setting (workspace, overridable per package) so a semver project below 1.0.0 can bump like release-please's bump-minor-pre-major + bump-patch-for-minor-pre-major:

"workspace": { "preMajor": { "breaking": "minor", "feat": "patch" } }
  • Applies only to semver while the current major is 0. From 1.0.0 on it has no effect; leaving 0.x stays ferrflow release --force-version 1.0.0.
  • Each key is optional (minor or patch). Unset means today's behaviour, so existing configs are unchanged.
  • The remap happens in compute_plan, so check, why and cascades show the adjusted bump. Cascaded bumps are not remapped twice.
  • zerover is untouched.

Not changed: the standalone ferrflow changelog, status, version_diff and api_check bump displays, which compute bumps outside the release plan.

Tests

  • From 0.5.0: feat! → 0.6.0, feat → 0.5.1, fix → 0.5.1; from 1.0.0 nothing changes; unset keeps feat! → 1.0.0
  • single-key rules, zerover unaffected, v-prefixed and unparseable versions
  • parsing in JSON (both spellings), TOML, JSON5; invalid level rejected; package over workspace
  • not reported as an unknown key, a typo inside it is
  • end-to-end compute_plan: 0.5.0 plans 0.6.0, 1.0.0 plans 2.0.0

Schema, npm/types/config.d.ts, README and the en/fr config docs are updated. Versioned docs (v0 to v6) are not.

Also carries #1380 (merged into this branch): the release-please migrator maps extra-files and both pre-major flags, the latter to preMajor.

Closes #1376
Closes #1375

Add workspace and per-package preMajor ({ breaking, feat }, each minor or patch). While the current major is 0 on a semver project, a breaking change and a feat take the configured bump instead. No effect from 1.0.0 on; unset keeps the current behaviour.

Closes #1376

Signed-off-by: BryanFRD <bryanferrando59@gmail.com>
@BryanFRD
BryanFRD enabled auto-merge (squash) November 9, 2026 14:48
Signed-off-by: BryanFRD <bryanferrando59@gmail.com>
* fix(migrate): map release-please extra-files and pre-major flags

Read extra-files (generic, toml on Cargo.toml, json $.version) and
report anything else under "Review manually". Map bump-minor-pre-major
to zerover with a warning, warn on bump-patch-for-minor-pre-major,
disable autoMergeReleases, enable updateLockfiles for lockfile
manifests, fall back to the directory name for the root package, and
write only non-default keys in the migrate output.

Closes #1375

Signed-off-by: BryanFRD <bryanferrando59@gmail.com>

* fix(migrate): drop stale release-type warning and test config round-trip

The no-file-mapping warning now fires only when a package ends up with no versioned files. Migrated configs of every source are reloaded through the config loader and compared.

Signed-off-by: BryanFRD <bryanferrando59@gmail.com>

* fix(migrate): map release-please pre-major flags to preMajor

bump-minor-pre-major sets preMajor.breaking: minor and bump-patch-for-minor-pre-major sets preMajor.feat: patch, each on its own. Serialise preMajor in camelCase.

Signed-off-by: BryanFRD <bryanferrando59@gmail.com>

---------

Signed-off-by: BryanFRD <bryanferrando59@gmail.com>

@ferrfleet-interne ferrfleet-interne Bot left a comment

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.

Changes requested. preMajor is applied to a package's own commits but not to bumps that arrive through the dependency cascade, so a 0.x package can still be released as 1.0.0.

Blocking

  • src/monorepo/run/cascade.rs:106: cascaded bumps bypass preMajor. apply_pre_major is only called at plan.rs:400, on the bump derived from the package's own commits. resolve_cascaded (cascade.rs:105-106) passes the propagated bump straight to compute_next_version.
    • Scenario: workspace preMajor {breaking: minor}. core is at 1.2.0 and gets feat!, so it bumps Major. app is at 0.3.0 and dependsOn core with propagate: same. cascade_round hands app a Major and app is released as 1.0.0, which is what preMajor is meant to prevent.
    • It also affects a package with its own commits: its held bump is the remapped Minor, the incoming Major wins in cascade_round (graph.rs:52), and it is recomputed as 1.0.0.
    • The plan.rs test only covers direct commits, so this path is untested.
    • Suggested fix: in resolve_cascaded, remap before computing: let bump = apply_pre_major(pkg.effective_pre_major(&config.workspace), strategy, &current_version, bump); ahead of compute_next_version. Add a cascade test with a 1.x upstream breaking change and a 0.x dependent carrying preMajor.
    • graph --impact (cascade_round) will still show the raw propagated level. That is acceptable since it previews membership, not versions, but worth a note.

Formal verification

The formal verification step failed and returned no result, so nothing was verified by it.

…cade

A 0.x package that only moved because a dependency took a breaking change still released as 1.0.0. Both the post-loop cascade and the raise of a package's own plan now remap the incoming bump.

Signed-off-by: BryanFRD <bryanferrando59@gmail.com>
@BryanFRD

BryanFRD commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in bf73a16. preMajor is now applied on both cascade paths, not just to a package's own commits:

  • resolve_cascaded (post-loop cascade): the propagated bump is remapped before compute_next_version, as suggested.
  • apply_cascade_upgrades (a package with its own plan raised by a dependency): raised_version remaps too, and a raise that flattens to the bump the package already holds is skipped instead of being recorded.

Tests: a 0.3.0 dependent of a 1.2.0 package taking a breaking change goes to 0.4.0 through the cascade, a 0.3.0 package with its own patch is raised to 0.4.0, and one already at minor stays at 0.4.0. All three fail with the remaps removed.

graph --impact still shows the raw propagated level, as noted. The settled cascade state also still carries the raw level to further dependents, so a 1.x package downstream of a remapped 0.x one sees the original bump. Changing that means resolving versions inside the fixpoint, which I'd keep out of this PR.

@ferrfleet-interne ferrfleet-interne Bot left a comment

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.

Changes requested. The preMajor remap is correct where it is wired in (plan, cascade, upgrade raises) and well tested, but it is applied per call site. The why and impact previews still compute the next version without it, so they disagree with what release does.

Blocking

  • src/monorepo/run/why/mod.rs:442: ferrflow why ignores preMajor for dependency-triggered bumps. decide() passes the raw cascaded bump to compute_next_version. With workspace.preMajor.breaking = minor, a 0.3.0 dependent of a package taking a major is reported as going to 1.0.0 by why, while release (cascade.rs:105) writes 0.4.0. bump_leaves_version_unchanged (line 359) has the same gap.
    Fix: call apply_pre_major(pkg.effective_pre_major(&config.workspace), strategy, &current, bump) before compute_next_version in both places. Better, add one helper (for example PackageConfig::next_version(workspace, strategy, current, bump)) and route plan.rs, cascade.rs, upgrade.rs, why/mod.rs and impact.rs through it so the rule cannot be missed at a call site.

To fix

  • major, src/monorepo/impact.rs:68: Versions::next calls compute_next_version with the propagated bump directly, so the graph impact report shows 0.x packages going to 1.0.0 under a breaking seed even when preMajor keeps them in 0.x. Apply apply_pre_major with pkg.effective_pre_major(&self.config.workspace) and the current version first, or use the shared helper above.

Suggestions

  • src/config/migrate/release_please.rs:143: extra-files mapping, default pruning in prune.rs, autoMergeReleases: false and the cwd-based default package name are independent of the pre-1.0 bump rules. They look correct, but splitting them out would make each easier to review and revert.

Formal verification

The formal verification step failed and returned no result, so nothing is verified there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant