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

fix: incorrect results from diff sometimes with prerelease versions #546

Merged
merged 7 commits into from Apr 13, 2023

Conversation

tjenkinson
Copy link
Contributor

@tjenkinson tjenkinson commented Apr 11, 2023

The following inputs previously gave the incorrect result based on my understanding:

  • 0.0.2-1 => 0.0.3 was prepatch when it should be patch, as it's doing a patch operation that goes from the first to the second. Fixed with first commit.
  • 0.0.0-1 => 0.0.0 was prerelease when it should be major. I think 0.0.0-1 => 0.0.0 should be consistent with 1.0.0-1 => 1.0.0. Fixed with second commit. This makes it more consistent but not sure if it's actually what people would expect. See below.
  • 1.0.0-1 => 2.0.0-1 was premajor but should be major because the biggest change is the major version. Fixed with third commit.

I'm actually not convinced the behaviour for the second point is correct. For any version change where the stable part remains the same and the prerelease gets removed, npm version patch would give that result. So should the diff always be patch for these cases?

I.e 1.1.0-pre => 1.1.0 is achievable with npm version patch and npm version minor. I personally would have expected the diff between those to be patch, just like it would be if it was 1.1.1-pre => 1.1.1

`0.0.2-1` => `0.0.3` should be `patch` not `prepatch`
I.e. `1.0.0-1` => `2.0.0-1` should be `major` not `premajor`, because the biggest change is the major version
@tjenkinson tjenkinson marked this pull request as ready for review April 11, 2023 22:37
@tjenkinson tjenkinson requested a review from a team as a code owner April 11, 2023 22:37
@tjenkinson tjenkinson requested a review from fritzy April 11, 2023 22:37
functions/diff.js Outdated Show resolved Hide resolved
@wraithgar
Copy link
Member

Yeah I think the pre* diff results are very counterintuitive. They are trying to leverage the operations that .inc would do, but diff is not currently directional. Your PR attempts to make the comparison directional which is the part that would need to be discussed.

functions/diff.js Outdated Show resolved Hide resolved
@wraithgar wraithgar changed the title Fix incorrect result from diff sometimes with prerelease versions fix: incorrect results from diff sometimes with prerelease versions Apr 12, 2023
@wraithgar wraithgar requested a review from nlf April 12, 2023 22:08
@wraithgar
Copy link
Member

Gonna kind of spam those test lines w/ comments sorry for the noise but this deserves a good paper trail

['0.0.2-1', '0.0.2', 'patch'],
['0.0.2-1', '0.0.3', 'patch'],
Copy link
Member

@wraithgar wraithgar Apr 12, 2023

Choose a reason for hiding this comment

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

The difference is actually two .inc patches, but that's still a patch difference. A good test

> semver.inc('0.0.2-1', 'patch')
'0.0.2'
> semver.inc('0.0.2', 'patch')
'0.0.3'

['0.0.2-1', '0.0.2', 'patch'],
['0.0.2-1', '0.0.3', 'patch'],
['0.0.2-1', '0.1.0', 'minor'],
Copy link
Member

Choose a reason for hiding this comment

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

> semver.inc('0.0.2-1', 'minor')
'0.1.0'

['0.0.2-1', '0.0.2', 'patch'],
['0.0.2-1', '0.0.3', 'patch'],
['0.0.2-1', '0.1.0', 'minor'],
['0.0.2-1', '1.0.0', 'major'],
Copy link
Member

Choose a reason for hiding this comment

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

> semver.inc('0.0.2-1', 'major')
'1.0.0'

@@ -19,10 +19,19 @@ test('diff versions test', (t) => {
['1.1.0', '1.1.0-pre', 'minor'],
['1.1.0-pre-1', '1.1.0-pre-2', 'prerelease'],
['1.0.0', '1.0.0', null],
['1.0.0-1', '1.0.0-1', null],
Copy link
Member

Choose a reason for hiding this comment

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

> semver.eq('1.0.0-1', '1.0.0-1')
true

['0.1.0-1', '0.1.0', 'minor'],
['1.0.0-1', '1.0.0', 'major'],
['0.0.0-1', '0.0.0', 'prerelease'],
['1.0.1-1', '1.0.1', 'patch'],
Copy link
Member

Choose a reason for hiding this comment

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

> semver.inc('1.0.1-1', 'patch')
'1.0.1'

['0.1.0-1', '0.1.0', 'minor'],
['1.0.0-1', '1.0.0', 'major'],
['0.0.0-1', '0.0.0', 'prerelease'],
['1.0.1-1', '1.0.1', 'patch'],
['0.0.0-1', '0.0.0', 'major'],
Copy link
Member

Choose a reason for hiding this comment

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

> semver.inc('0.0.0-1', 'major')
'0.0.0'

['0.0.0-1', '0.0.0', 'prerelease'],
['1.0.1-1', '1.0.1', 'patch'],
['0.0.0-1', '0.0.0', 'major'],
['1.0.0-1', '2.0.0', 'major'],
Copy link
Member

Choose a reason for hiding this comment

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

Again this is two major .inc operations but that's still a major diff

> semver.inc('1.0.0-1', 'major')
'1.0.0'
> semver.inc('1.0.0', 'major')
'2.0.0'

test/functions/diff.js Outdated Show resolved Hide resolved
@wraithgar
Copy link
Member

1.0.0-1 => 2.0.0-1 was premajor but should be major because the biggest change is the major version. Fixed with third commit.

This still seems like premajor as per my comment on the test fixture:

> semver.inc('1.0.0', 'premajor')
'2.0.0-0'

@tjenkinson
Copy link
Contributor Author

Hmm yeh prerelease to prerelease on a different version is a bit confusing. Maybe prerelease to prerelease should also always be pre* ?

@tjenkinson
Copy link
Contributor Author

Is the definition of pre* meant to be ‘going to a prerelease version’? If that’s the case those 3 tests should be pre*

@wraithgar
Copy link
Member

When using .inc any directive that begins with pre leaves you in a prerelease version. pre does seem to always indicate that the new version is a prerelease.

@tjenkinson
Copy link
Contributor Author

Cool. Ok I pushed a change to make it do that

Comment on lines +40 to +51
if (lowVersion.patch) {
// anything higher than a patch bump would result in the wrong version
return 'patch'
}

if (lowVersion.minor) {
// anything higher than a minor bump would result in the wrong version
return 'minor'
}

// bumping major/minor/patch all have same result
return 'major'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Related to the last part of the PR description, this would make more sense to me if this was all replaced with return 'patch'

The way npm version behaves with major and minor bumps when the current version is a prerelease and parts are 0 is a bit confusing to me

Specifically I don't understand why there is a 0 special case here and here

Copy link
Member

Choose a reason for hiding this comment

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

special case for inc minor

The comments tell the story there:

   // If this is a pre-minor version, bump up to the same minor version.
   // Otherwise increment minor.

A preminor version is a version whose patch is 0 and has a prerelease identifier. Bumping a minor from that is a matter of dropping the prerelease identifier.

special case for inc major

Same as before, only we are looking at minor and patch. A premajor version is a version whose minor and patch are 0 and has a prerelease identifier. Bumping a major from that is a matter of dropping the prerelease identifier.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess it's a bit strange that you can go from 1.0.1-0 to 1.1.0 with npm version minor but you can't go from 1.0.0-0 to 1.1.0 because the command works differently for that case.

I still don't really get why 0 is special 🤷. Seems a bit inconsistent

Copy link
Member

@wraithgar wraithgar Apr 13, 2023

Choose a reason for hiding this comment

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

It does appear inconsistent but that is due to the nature of what 1.0.0-0 is. It's the prerelease for a semver major version. Dropping that prerelease is a major, there is no smaller increment you can make outside of increasing the prerelease identifier.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right yes that makes sense. The thing that's confusing I think is that 0.1.0 to 1.0.0 is concretely major, but I'm not sure going from 1.0.0-0 to 1.0.0 could be classed as a major change, it's just going prerelease to stable. Maybe patch/minor/major doesn't really make sense as a thing when going prerelease to stable 🤷

I think the difference between 1.0.1-0 and 1.0.1 has the same weight

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It does make sense if the result from diff when we're going from prerelease to stable release is meant to represent the diff between the previous stable version and the new one 💡, but that's a step I wouldn't expect the diff to be doing internally

['1.0.1-1', '1.0.1', 'patch'],
['0.0.0-1', '0.0.0', 'major'],
['1.0.0-1', '2.0.0', 'major'],
['1.0.0-1', '2.0.0-1', 'premajor'],
Copy link
Member

@wraithgar wraithgar Apr 13, 2023

Choose a reason for hiding this comment

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

> semver.inc('1.0.0-1', 'premajor')
'2.0.0-0'

['0.0.0-1', '0.0.0', 'major'],
['1.0.0-1', '2.0.0', 'major'],
['1.0.0-1', '2.0.0-1', 'premajor'],
['1.0.0-1', '1.1.0-1', 'preminor'],
Copy link
Member

Choose a reason for hiding this comment

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

> semver.inc('1.0.0-1', 'preminor')
'1.1.0-0'

['1.0.0-1', '2.0.0', 'major'],
['1.0.0-1', '2.0.0-1', 'premajor'],
['1.0.0-1', '1.1.0-1', 'preminor'],
['1.0.0-1', '1.0.1-1', 'prepatch'],
Copy link
Member

Choose a reason for hiding this comment

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

> semver.inc('1.0.0-1', 'prepatch')
'1.0.1-0'

Copy link
Member

@wraithgar wraithgar left a comment

Choose a reason for hiding this comment

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

Gonna let @nlf review this before landing

@wraithgar
Copy link
Member

diff.js
const semver = require('.')
for (const level of ['major', 'premajor', 'minor', 'preminor', 'patch', 'prepatch', 'prerelease']) {
  console.log(`### ${level}`)
  for (const base of ['1.0.0', '1.0.0-pre', '1.2.0', '1.2.0-pre', '1.2.3', '1.2.3-pre', '1.2.3-0']) {
    console.group(`- ${base}`)
    const inc = semver.inc(base, level)
    const diff = semver.diff(base, inc)
    console.log(`inc: ${inc}`)
    console.log(`diff: ${diff}`)
    console.groupEnd(`- ${base}`)
  }
}

major

  • 1.0.0
    inc: 2.0.0
    diff: major
  • 1.0.0-pre
    inc: 1.0.0
    diff: major
  • 1.2.0
    inc: 2.0.0
    diff: major
  • 1.2.0-pre
    inc: 2.0.0
    diff: major
  • 1.2.3
    inc: 2.0.0
    diff: major
  • 1.2.3-pre
    inc: 2.0.0
    diff: major
  • 1.2.3-0
    inc: 2.0.0
    diff: major

premajor

  • 1.0.0
    inc: 2.0.0-0
    diff: premajor
  • 1.0.0-pre
    inc: 2.0.0-0
    diff: premajor
  • 1.2.0
    inc: 2.0.0-0
    diff: premajor
  • 1.2.0-pre
    inc: 2.0.0-0
    diff: premajor
  • 1.2.3
    inc: 2.0.0-0
    diff: premajor
  • 1.2.3-pre
    inc: 2.0.0-0
    diff: premajor
  • 1.2.3-0
    inc: 2.0.0-0
    diff: premajor

minor

  • 1.0.0
    inc: 1.1.0
    diff: minor
  • 1.0.0-pre
    inc: 1.0.0
    diff: major
  • 1.2.0
    inc: 1.3.0
    diff: minor
  • 1.2.0-pre
    inc: 1.2.0
    diff: minor
  • 1.2.3
    inc: 1.3.0
    diff: minor
  • 1.2.3-pre
    inc: 1.3.0
    diff: minor
  • 1.2.3-0
    inc: 1.3.0
    diff: minor

preminor

  • 1.0.0
    inc: 1.1.0-0
    diff: preminor
  • 1.0.0-pre
    inc: 1.1.0-0
    diff: preminor
  • 1.2.0
    inc: 1.3.0-0
    diff: preminor
  • 1.2.0-pre
    inc: 1.3.0-0
    diff: preminor
  • 1.2.3
    inc: 1.3.0-0
    diff: preminor
  • 1.2.3-pre
    inc: 1.3.0-0
    diff: preminor
  • 1.2.3-0
    inc: 1.3.0-0
    diff: preminor

patch

  • 1.0.0
    inc: 1.0.1
    diff: patch
  • 1.0.0-pre
    inc: 1.0.0
    diff: major
  • 1.2.0
    inc: 1.2.1
    diff: patch
  • 1.2.0-pre
    inc: 1.2.0
    diff: minor
  • 1.2.3
    inc: 1.2.4
    diff: patch
  • 1.2.3-pre
    inc: 1.2.3
    diff: patch
  • 1.2.3-0
    inc: 1.2.3
    diff: patch

prepatch

  • 1.0.0
    inc: 1.0.1-0
    diff: prepatch
  • 1.0.0-pre
    inc: 1.0.1-0
    diff: prepatch
  • 1.2.0
    inc: 1.2.1-0
    diff: prepatch
  • 1.2.0-pre
    inc: 1.2.1-0
    diff: prepatch
  • 1.2.3
    inc: 1.2.4-0
    diff: prepatch
  • 1.2.3-pre
    inc: 1.2.4-0
    diff: prepatch
  • 1.2.3-0
    inc: 1.2.4-0
    diff: prepatch

prerelease

  • 1.0.0
    inc: 1.0.1-0
    diff: prepatch
  • 1.0.0-pre
    inc: 1.0.0-pre.0
    diff: prerelease
  • 1.2.0
    inc: 1.2.1-0
    diff: prepatch
  • 1.2.0-pre
    inc: 1.2.0-pre.0
    diff: prerelease
  • 1.2.3
    inc: 1.2.4-0
    diff: prepatch
  • 1.2.3-pre
    inc: 1.2.3-pre.0
    diff: prerelease
  • 1.2.3-0
    inc: 1.2.3-1
    diff: prerelease

@wraithgar
Copy link
Member

I'm happy to land this now. Any problems that still exist are going to be with inc and not diff, now that we've shown that the results match (prepatch/prerelease subtleties notwithstanding).

Copy link
Contributor

@nlf nlf left a comment

Choose a reason for hiding this comment

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

this is so great. thank you @tjenkinson!

@wraithgar wraithgar merged commit fc2f3df into npm:main Apr 13, 2023
24 checks passed
@github-actions github-actions bot mentioned this pull request Apr 13, 2023
@tjenkinson
Copy link
Contributor Author

thanks!

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.

None yet

3 participants