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

Introduce VersionRange #330

Merged
merged 9 commits into from
Jul 23, 2024
Merged

Introduce VersionRange #330

merged 9 commits into from
Jul 23, 2024

Conversation

gastaldi
Copy link
Contributor

This introduces a predicate that can be used for comparing if given a version is contained inside a version range pattern

@gastaldi
Copy link
Contributor Author

@dmlloyd @aloubyansky I introduced an utility class to check the range introduced in quarkusio/quarkus#41908

@aloubyansky
Copy link
Member

Looks nice @gastaldi, thanks! Could we also add tests involving version qualifiers? Right now the tests are limited to "Final" versions. So, for example, if a range starts with [1.0,...] then 1.0.0.Alpha3 shouldn't be accepted.

@aloubyansky
Copy link
Member

But [1.0.a,..,] should include 1.0.0.Alpha3

@aloubyansky
Copy link
Member

We may want to test SP and redhat in the mix as well, it's a known troublesome combination for dependabot, for example.

@aloubyansky
Copy link
Member

The SP and redhat case would be more than what we currently need from this though. So shouldn't be a blocker if it appears to be problematic.

@gastaldi
Copy link
Contributor Author

@aloubyansky I added more tests, see if they make sense

assertTrue(versionRange.test("3.8.4.SP1-redhat-00001"));
assertTrue(versionRange.test("3.8.4.SP2-redhat-00001"));
assertTrue(versionRange.test("3.8.4.redhat-00002"));
assertFalse(versionRange.test("3.8.5.redhat-00003"));
Copy link
Member

Choose a reason for hiding this comment

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

Thanks, this looks good. What if you test 3.8.0.SP1-redhat-00001 for [3.8.0.redhat-00001,)?

Copy link
Contributor Author

@gastaldi gastaldi Jul 18, 2024

Choose a reason for hiding this comment

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

It fails. Not sure why.

This returns 1:

VersionScheme.MAVEN.compare("3.8.0.redhat-00001", "3.8.0.SP1-redhat-00001")

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 wonder if we shouldn't have a specific Red Hat VersionScheme for cases like these

Copy link
Member

Choose a reason for hiding this comment

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

It's ok, it's not a blocker, I was just wondering. Dependabot has the same issue. We can fix it later. It'd be good to fix it though :)

Copy link
Member

Choose a reason for hiding this comment

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

Maven resolver actually does handle it properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm, that sounds like a bug in MavenVersionScheme. WDYT @dmlloyd ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe that custom qualifiers come after predefined qualifiers (like SP) according to the Maven rules.

 $ jbang --main=org.eclipse.aether.util.version.GenericVersionScheme org.apache.maven.resolver:maven-resolver-util:1.9.6 1.redhat 1.sp
Display parameters as parsed by Maven Resolver (in canonical form and as a list of tokens) and comparison result:
1. 1.redhat -> 1.redhat; tokens: [1, redhat]
   1.redhat > 1.sp
2. 1.sp -> 1.sp; tokens: [1, 1]
$ jbang --main=org.eclipse.aether.util.version.GenericVersionScheme org.apache.maven.resolver:maven-resolver-util:1.9.6 3.8.0.redhat-00001 3.8.0.SP1-redhat-00001
Display parameters as parsed by Maven Resolver (in canonical form and as a list of tokens) and comparison result:
1. 3.8.0.redhat-00001 -> 3.8.0.redhat-00001; tokens: [3, 8, redhat, 1]
   3.8.0.redhat-00001 > 3.8.0.SP1-redhat-00001
2. 3.8.0.SP1-redhat-00001 -> 3.8.0.SP1-redhat-00001; tokens: [3, 8, 1, 1, redhat, 1]

Copy link
Collaborator

Choose a reason for hiding this comment

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

Make sure you aren't using the utilities in maven-artifact to test, because that is generally considered to be deprecated at this point (and IIUC is not used by Maven anymore).

This introduces a predicate that can be used for comparing if given a version is contained inside a version range pattern
@dmlloyd
Copy link
Collaborator

dmlloyd commented Jul 22, 2024

There are two main problems remaining that I can see.

The first problem is that of the parser. It operates by searching the same string ranges many times (using indexOf), and comparing indices to see which things come first (if they are found). It is hard to prove that such a parser is in fact correct. Normally a parser should proceed from left to right (or, rarely, from right to left) and visit each character once, making a decision at that point. In this way it is easier to verify that in some given parsing state, the successor states are all valid and make sense.

The second problem is the restriction classes, which implement a parallel variety of predicate compared to the whenXxx methods that I had you add. The point of adding these methods would be to have only one kind of predicate. For example, if I give a version range of (,1.2) then I'd want that to be exactly identical to calling whenLt("1.2"). As a side effect of this duplication, we have a couple extra classes, one of which is visible to the user.

I'd made an example commit here which you can feel free to use as you wish. This commit institutes a left-to-right parser and ensures there is only one way for the API to represent a range predicate.

One thing that this commit does not do, which we can look into if you think this would be a valid use case, is validate that a given version range is indeed valid (other than a couple very opportunistic checks). The reason for this again is that the predicate methods should behave identically to the range-parsed predicates. That is, if the parser rejects (2.0,1.0) then the predicate methods should also reject whenLt("2.0").and(whenGt("1.0")). In order for this to happen, the predicate methods would have to produce instances with a more comprehensive API (somewhat similar to your existing VersionRestriction class). I've left that off as something we could look into later, possibly. In my view, I think it would be totally valid for (2.0,1.0) to just yield () -> false (or something that behaves in an identical way), which is already what would happen if you combined the predicate methods (as they are currently defined) in an invalid way.

I hope this makes sense. Feel free to use some, all, or none of my example commit to get this over the line. Thanks!

Intoduce a left-to-right recursive-descent parser for version ranges.
Use the predicates on `VersionScheme` to compose the resultant range.
@gastaldi
Copy link
Contributor Author

@dmlloyd loved it! I've pushed your commit to my branch for your appreciation

@dmlloyd dmlloyd merged commit c29bb1c into smallrye:main Jul 23, 2024
4 checks passed
@github-actions github-actions bot added this to the 2.6.0 milestone Jul 23, 2024
@gastaldi gastaldi deleted the range branch July 23, 2024 17: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.

3 participants