Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): correct --prerelease use and add --as-prerelease #647

Merged
merged 5 commits into from
Apr 27, 2024

Conversation

bernardcooke53
Copy link
Contributor

@bernardcooke53 bernardcooke53 commented Jul 19, 2023

Prior to this change, --prerelease performed the role that --as-prerelease now does, which was an unintentional breaking change to the CLI compared to v7.

--prerelease now forces the next version to increment the prerelease revision, which makes it consistent with --patch, --minor and --major, while --as-prerelease allows for the usual next version to be converted to a prerelease befoer it is applied to the project

Resolves: #639

@css-optivoy
Copy link
Contributor

I'm no expert, but something feel a little off about this logic to me.

This example from the PR seems to violate SemVer:

if 0.2.1-rc.1 already exists as a previous version, and the latest version is 0.2.1, invoking semantic-release version --prerelease --prerelease-token "rc" will produce 0.2.1-rc.2

Albeit the section that follows it warns about that.

I haven't tested this, but I feel like PSR v7, when given --prerelease, would first apply its automatic version bump logic to determine the next version, and then apply the prerelease tag.
I.e. 0.2.1-rc.1 would become 0.2.2-rc.1 if only patch changes had happened since 0.2.1.

The other thing I'm having a hard time wrapping my head around is the --prerelease/--as-prerelease duality. I had to read through the PR a couple of times to understand what they each do, and why we might need them.

My understanding is that --as-prerelease is there primarily to allow us to go from e.g. 0.1.1 directly to 0.2.0-rc.1 via --minor --as-prerelease. And this is in turn because we can't use --minor --prerelease for that since all the bump flags are mutually exclusive.

Now, the behaviour I would expect is something closer to this:

  1. --prerelease forces the next prerelease version. Either by bumping the prerelease version if the latest version is a prerelease (1.2.3-pre.1 -> 1.2.3-pre.2), or by bumping the version, and then tagging that as a prerelease (1.2.3 -> 1.2.4-pre.1).
  2. The other bump flags, --major, --minor and --patch, can be combined with --prerelease to force a manual bump to a greater prerelease, so e.g. --major --prerelease would give you 1.8.9 -> 2.0.0-pre.1.

This should largely preserve existing behaviour, and avoid the above potential issues.

How well this works with the new multi-branch versioning I can't really say, as I unfortunately have no experience with such a setup.

@bernardcooke53
Copy link
Contributor Author

Thanks for taking a look @css-optivoy. It's kind of gnarly logic, I admit. Let me try to respond thoroughly to each of your comments:

This example from the PR seems to violate SemVer:

if 0.2.1-rc.1 already exists as a previous version, and the latest version is 0.2.1, invoking semantic-release version --prerelease --prerelease-token "rc" will produce 0.2.1-rc.2

Albeit the section that follows it warns about that.

I don't think it violates SemVer, it's just that it's possible to shoot yourself in the foot if you're developing on a branch that's out-of-date compared to your main/latest release. SemVer doesn't mandate that your project versions are released monotonically, only that according to the precedence rules certain versions are "higher" than others. I can release v1.0.1, v2.0.0 and then v1.1.0 in that order, if need be - it's just everyone looking for "latest" by semver will get v2.0.0 instead of v1.1.0.

Let me try to clarify the example (and then update the docs):

If you release normal versions from main and prereleases from feat/* branches and you last released 0.2.0, suppose you do the following:

  • create feat/A and feat/B off of main
  • develop a fix on feat/A, do a prerelease to get 0.2.1-rc.1
  • merge feat/A into main, and do a release to get 0.2.1
  • commit a fix to feat/B and try to do a prerelease

Now, from this prerelease, the commit history from the point of view of HEAD on feat/B only knows of 0.2.0 as the last major version - so a patch increment + prerelease would result in 0.2.1-rc.1. Obviously that tag already exists so there's two choices:

  • either error out/don't release, indicating that 0.2.1-rc.1 is already released, or
  • recognise that 0.2.1-rc.1 is already released and increment to 0.2.1-rc.2

Having to deal with multiple branches makes this a pain to get right, however it's not possible to naively search the repository tags and pick the latest version from those to base the next version off of - if you pick up a branch from 3 years ago, you'll be prereleasing for the incremented version with a wildly stale codebase. It would prevent trying to support multiple versions.

I was/am still considering whether the tag-search for prereleases leading up to the same normal version is the "correct" thing to do. It would be much easier to take the stance of "use build metadata for this instead of prerelease tags", which is honestly a more robust way to go about it, but that would in turn ask for a change in versioning scheme from (I imagine) a lot of users.


I haven't tested this, but I feel like PSR v7, when given --prerelease, would first apply its automatic version bump logic to determine the next version, and then apply the prerelease tag.
I.e. 0.2.1-rc.1 would become 0.2.2-rc.1 if only patch changes had happened since 0.2.1.

This is technically true, but it was influenced by the default for --prerelease-patch rather than --prerelease directly. In my opinion, if you're using flags on the command-line to alter the behaviour of the next version from what the commits would otherwise indicate, the most important thing is having the level of control to decide what your next version is.

For example, if I want to run an ad-hoc prerelease from main, I can use --as-prerelease to calculate my next version from the commits that need releasing, and then ensure I publish a prerelease version. If I want to force a minor prerelease, I can use --minor --as-prerelease to reach that version. Through combination of --prerelease/--patch/--minor/--major I can force the increment of any of the version digits that I need to, and through --as-prerelease I can ensure I construct a prerelease either if I need to release a new version (with no other forcing flags) or intentionally along with a forced increment.

What could potentially be missing here is a --minimum-bump="none|patch|minor|major" flag, with "bump by at least x level" semantics.

I think the usage you suggested with --prerelease and --patch --prerelease has a similar risk of confusion as --prerelease vs --as-prerelease imo - the behaviour of the flag would change depending on the other flags provided, and your point 1 imposes an explicit patch bump that can't be disabled (something maybe a --minimum-bump flag could mitigate). --as-prerelease is new functionality and I intended it to be there for more of that granular control over the next version.

Definitely open to renaming it if you have a clearer/more explicit name suggestion!

@bernardcooke53 bernardcooke53 marked this pull request as draft August 10, 2023 18:28
@github-actions github-actions bot added the stale label Mar 25, 2024
@codejedi365 codejedi365 added confirmed Prevent from becoming stale and removed stale labels Mar 29, 2024
@codejedi365 codejedi365 removed the confirmed Prevent from becoming stale label Mar 30, 2024
@codejedi365 codejedi365 self-assigned this Mar 30, 2024
@codejedi365 codejedi365 force-pushed the fix-prerelease-cli-options branch 2 times, most recently from 5aa391d to 6c86d51 Compare April 20, 2024 16:36
bernardcooke53 and others added 5 commits April 27, 2024 11:29
Prior to this change, `--prerelease` performed the role of converting whichever forced
version into a prerelease version declaration, which was an unintentional breaking
change to the CLI compared to v7.

`--prerelease` now forces the next version to increment the prerelease revision,
which makes it consistent with `--patch`, `--minor` and `--major`. Temporarily disabled
the ability to force a prerelease.
…sion to be a prerelease

Prior to this change, `--prerelease` performed the role that `--as-prerelease` now does,
which was an unintentional breaking change to the CLI compared to v7.

`--prerelease` is used to force the next version to increment the prerelease revision,
which makes it consistent with `--patch`, `--minor` and `--major`, while `--as-prerelease`
forces for the next version to be converted to a prerelease version type before it is
applied to the project regardless of the bump level.
@codejedi365 codejedi365 marked this pull request as ready for review April 27, 2024 15:46
@codejedi365 codejedi365 merged commit 2acb5ac into master Apr 27, 2024
7 checks passed
@codejedi365 codejedi365 deleted the fix-prerelease-cli-options branch April 27, 2024 15:57
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.

semantic-release version --prerelease does not force a prerelease version bump.
3 participants