-
-
Notifications
You must be signed in to change notification settings - Fork 102
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
Implements pre-releases #249
Conversation
It now accepts unbalanced versions numbers, for example: - 1.0 == 1.0.0.0 - 1.0 < 1.0.1 - 1.0.2 > 1.0 The tilde operator was matching invalid version numbers, because we only checked that the version number started with the loose requirement, not that it matched the version number segment. For example "~> 0.1.0" matched "0.10.0" which was invalid. Added some tests from @straight-shoota for Helpers::Versions and some more tests for the lower Helpers::NaturalSort module.
Helpers::NaturalSort wasn't a real natural sort algorithm, it was sorting segments partitioned by non alphanumeric chars. There is now a single Versions object responsible for sorting and comparing version numbers. Introduces support for prerelease versions, defined as a version number that contains an ASCII letter. They're correctly compared using a natural sort algorithm.
A version number is considered a prerelease when it has at least one letter. For example `1.0.alpha` and `2.0.0-pre10` are valid prereleases for release versions `1.0` and `2.0.0` respectively. Prereleases will never be selected until a `shard.yml` specifically requires one. For example `version: 1.0-alpha`. The approximate operator is recommended since it will allow upgrading to the next prereleases and eventually to the release. For example `version: ~> 1.0.0.pre` can be updated to `1.0.0.rc` then `1.0.0` but not `1.1.0.beta`.
eb1f1e2
to
7121263
Compare
Also happens to fix #183 where unbalanced version numbers since 1.0 and 1.0.0 are now considered the same. |
|
||
when />=(.+)/ | ||
ver = $1.strip | ||
versions.select { |v| compare(v, ver) <= 0 } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor detail: If you switch the argument positions of v
and ver
, the comparator lines up nicely with the restriction >=
.
end | ||
|
||
def test_sort | ||
100.times do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why so many randomized iterations? The comparisons between items always stay the same, so this doesn't provide any additional coverage.
You don't get any more complete result than iterating through the sorted list and asserting that every item compares lower than its successors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original NaturalSort had a bug where the initial order influenced the output ordering. Adding randomization catched it. 100 is a magic number, chances of not catching the issue is veeery low, and this has no impact on speed test (fast).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It just has some smell to use randomized brute force specs 😄
Hm, I was already working on a patch for this 🤔 That's why I assigned #184 to myself. Both result and key implementation are pretty similar in your approach, so 👍 |
Sorry for duplicating work. I went overboard on some free time I had 🙇 |
@@ -146,10 +146,19 @@ module Shards | |||
end | |||
end | |||
|
|||
def self.prerelease?(str) | |||
str.each_char.any?(&.ascii_letter?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hyphens are often also used to determine pre-releases. Rubygems considers any version with a letter or hyphen as pre-release.
This obviously only makes a difference when the pre-release tag doesn't contain any letters (like 0.1-1
), which is probably pretty rare.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it really? Sigh. 0.1.1-1
looks like a build number that supersedes a 0.1.1
release, it doesn't look like a pre-release to me.
Shall we really support that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rubygems doesn't look for a hyphen, only for a letter:
https://github.com/rubygems/rubygems/blob/master/lib/rubygems/version.rb#L290-L297
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take a look at the constructor. Internally it actually replaces a hyphen with .pre.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this needs to be supported. But rubygems made extra effort to achieve this, so maybe they have a good reason?
Anyway, it's really trivial to implement here, so why not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's really trivial to implement here
Actually, it's not, because dashes and dots are equivalent, so 1.0.0-1
== 1.0.0.1
. I must use the same trick than rubygems and replace -
with a .pre.
so 1.0.0-1
becomes 1.0.0.pre.1
to be considered a prerelease, and thus older to 1.0.0
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The replace leads to oddities, like 0.0.1-alpha > 0.0.1.beta
because we parse 0.0.1-alpha
as 0.0.1.pre.alpha
but don't transform 0.0.1.beta
.
We should probably check the partition result (dot or dash) in addition to the segment, but... I honestly, I'm tired, here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't currently support semver perfectly then we don't have to support it perfectly in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw, n.n.n-n
notation is used by homebrew, where last part denotes revision, far from being pre-release.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Sija This is pretty common with any package distribution. Crystal's apt and rpm packages also use this release + iteration format.
But in semver, everything following a hyphen makes a pre-release. We can't fit both. But I agree with @RX14.
So every version containing a letter is considered a pre-release.
module Shards | ||
class VersionsTest < Minitest::Test | ||
def test_compare | ||
# a is older than b: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would't use older and newer here. Version comparison is not temporal.
I added a few tests, and also ignore semver metadata. I failed to add support for prereleases made of only numbers. Still, it's better than the current one, and there shouldn't any regression since the overall algorithm is the same. |
@@ -2,7 +2,15 @@ | |||
|
|||
## UNRELEASED | |||
|
|||
Features: | |||
- Experimental support for prereleases. Add a letter to a version number to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it really just experimental?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean experimental as in "works on my computer, not tested in the wild".
@@ -146,10 +146,19 @@ module Shards | |||
end | |||
end | |||
|
|||
def self.prerelease?(str) | |||
str.each_char.any?(&.ascii_letter?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Sija This is pretty common with any package distribution. Crystal's apt and rpm packages also use this release + iteration format.
But in semver, everything following a hyphen makes a pre-release. We can't fit both. But I agree with @RX14.
So every version containing a letter is considered a pre-release.
Thanks, @ysbaddaden 👍 |
You're welcome —even if it took 2 years. |
…natural-sort Implements pre-releases
I wanted to fix the issue found by tests in #248 but actually refactored
Helper::NaturalSort
andHelper::Versions
as a singleVersions
module, responsible for comparing and sorting version numbers correctly:1.0.0 == 1.0
and1.0.1 > 1.0)
;~> 0.1.0
matched0.10.0
.Supporting pre-releases in install/update commands was then just extracting pre-release versions correctly, and not filtering them out unless a requirement specifically requires one.
closes #184
fixes #183