Skip to content

Detect version range usage in project or transitive dependencies#206

Open
slawekjaranowski wants to merge 2 commits intoapache:masterfrom
slawekjaranowski:version-range
Open

Detect version range usage in project or transitive dependencies#206
slawekjaranowski wants to merge 2 commits intoapache:masterfrom
slawekjaranowski:version-range

Conversation

@slawekjaranowski
Copy link
Member

@slawekjaranowski slawekjaranowski commented Mar 21, 2026

Summary

This PR adds version-range awareness to the artifact plugin to improve reproducibility checks.

What changed

  • Added RangesUtil to detect direct and transitive dependencies that use version ranges (including LATEST / RELEASE).
  • Integrated version-range validation into artifact:check-buildplan.
  • Added rebuild hints to generated .buildinfo files via:
    • mvn.rebuild-args=-Dmaven.session.versionFilter=...
  • Added integration tests for:
    • fail-on-range scenario,
    • non-fail scenario (failOnNonReproducible=false),
    • dependencyManagement override scenario.

Following this checklist to help us incorporate your
contribution quickly and easily:

  • Your pull request should address just one issue, without pulling in other changes.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Each commit in the pull request should have a meaningful subject line and body.
    Note that commits might be squashed by a maintainer on merge.
  • Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied.
    This may not always be possible but is a best-practice.
  • Run mvn verify to make sure basic checks pass.
    A more thorough check will be performed on your pull request automatically.
  • You have run the integration tests successfully (mvn -Prun-its verify).

If your pull request is about ~20 lines of code you don't need to sign an
Individual Contributor License Agreement if you are unsure
please ask on the developers list.

To make clear that you license your contribution under
the Apache License Version 2.0, January 2004
you have to acknowledge this by using the following check-box.

@slawekjaranowski slawekjaranowski self-assigned this Mar 21, 2026
@slawekjaranowski slawekjaranowski added the enhancement New feature or request label Mar 21, 2026
@slawekjaranowski slawekjaranowski linked an issue Mar 24, 2026 that may be closed by this pull request
@hboutemy
Copy link
Member

hboutemy commented Mar 24, 2026

For clarity: what do you do when a range is detected? In which goals?

@slawekjaranowski
Copy link
Member Author

slawekjaranowski commented Mar 24, 2026

For clarity: what do you do when a range is detected? In which goals?

According to new option failVersionRange build will fail artifact:compare by default

@hboutemy
Copy link
Member

ok, now I understand the intent (= what should be in the PR description)

I love detecting version ranges, I'm torn on failing artifact:compare: I know that warnings will be ignored...
failing is crude, not really actionable (message Please fix them to have a fixed version for better reproducibility is too late on a release), for the first time, rebuilding will manage to reproduce...

I would not hesitate on artifact:check-build-plan, which is something that happens early, when people try to improve their build

I know that I'll have to de-activate artifact:compare failure in Reproducible Central
what I don't know is "how to report that a rebuild was done now that was successfully matching reference build, BUT it was a lucky non-permanent rebuild"

I'll probably have to accept this PR: I love the warning that I'll get when disabling

what I'd like after this PR is to see if we can have:

  • a message of artifact:compare in the .compare output file (like "fragile dependency version resolution from range")
  • the same message in artifact:check-build-plan

@slawekjaranowski
Copy link
Member Author

a message of artifact:compare in the .compare output file (like "fragile dependency version resolution from range")

ok, we can add a message to .buildcompare
As I see it is a property file ....

  • should we add new key (which name) or comment like for diffoscope
  • should we add static text that build contains dependencies with range or list all dependencies with range and path?

@slawekjaranowski
Copy link
Member Author

Next idea warn on compare add info to .buildcompare and not break a build
List dependencies with range in check-buildplan and break according to existing option failOnNonReproducible

@hboutemy
Copy link
Member

hboutemy commented Mar 25, 2026

I'm thinking at this very concrete example: https://github.com/jvm-repo-rebuild/reproducible-central/blob/master/content/io/cucumber/gherkin/README.md

Where they use ranges, and:

  • sometimes I was lucky to rebuild early enough to get the same resolved version, like 39.0.0
  • sometimes I rebuilt too late, and I can't force Maven resolution to NOT use newer versions, like 37.0.1 and 38.0.0 (I added a manual comment in diffoscope output)

in this case, when another version is resolved that the initial build, it has an impact on .class content
But often it does not impact .class

the more I write, the more I think we're not describing the same "Reproducible Builds" semantics
It could sometimes impact CycloneDX files, because they list dependencies. Or it will impact .war or shade output or anything that really copies the dependencies.

But in general, version ranges that give "un-stable over time" resolution is not impacting "Reproducible Builds" in terms of getting the same output
And even if it impacts output, I'm sure that by configuring an extension that locks a little bit resolution, we could force the build to get the same output = what Reproducible Builds is about = checking that nothing has been cheated in the output

that being said, what to put in .buildcompare, I confess I don't know: in fact, I don't know how to describe the issue in plain text even in this PR

what would describe the gherkin case? Perhaps even in .buildinfo output, as this resolution result is part of build info

@slawekjaranowski
Copy link
Member Author

@cstamas are there some properties which reduce ranges resolving ?

@cstamas
Copy link
Member

cstamas commented Mar 25, 2026

Yes, in Resolver 2/Maven 4. Those may be possible in Maven 3.10.x as well.
https://github.com/apache/maven/blob/master/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java#L346-L366

These are global ones (affecting whole system/session). I want to make platform support too, but I am currently derailed.

@slawekjaranowski
Copy link
Member Author

@hboutemy we will can filter range in Maven 4.x, 3.10.x so we can add instruction to buildinfo 😄
in mvn.rebuild-args

@slawekjaranowski slawekjaranowski marked this pull request as draft March 25, 2026 23:34
@slawekjaranowski slawekjaranowski marked this pull request as draft March 25, 2026 23:34
@hboutemy
Copy link
Member

hboutemy commented Mar 26, 2026

so we can add instruction to buildinfo

buildspec, yes: that will be nice (sometimes, for example, we inject some detailed JDK version, or other unusual data that do not really make the build not reproducible per-se, but just require more env details: see https://github.com/search?q=repo%3Ajvm-repo-rebuild%2Freproducible-central%20%22extracted%20from%3A%22&type=code )

that will be great

@slawekjaranowski slawekjaranowski marked this pull request as ready for review March 26, 2026 20:46
@slawekjaranowski
Copy link
Member Author

@hboutemy next proposition, PR description also updated

Copy link
Member

@hboutemy hboutemy left a comment

Choose a reason for hiding this comment

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

I love the result: proposed changes are only reader proofing

assert buildLog.contains('[ERROR] Version with range found in dependencies, project can be non-reproducible')
assert buildLog.contains(' - Dependency io.cucumber:messages:jar:32.2.0 (compile) via io.cucumber:gherkin:jar:38.0.0 is referenced with a range version [32.0.0,33.0.0)')
assert buildLog.contains(' - Dependency commons-io:commons-io:jar:2.21.0 (compile) is referenced with a range version [2.20.0,)')
assert buildLog.contains(' - Dependency commons-collections:commons-collections:jar:LATEST (compile) is referenced with a range version LATEST')
Copy link
Member

Choose a reason for hiding this comment

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

should probably also assert that commons-lang3 dependency is not flagged

Map<DependencyNode, String> versionRangeDependencies =
rangesUtil.findVersionRangeDependencies(session, project);
if (!versionRangeDependencies.isEmpty()) {
String message = "Version with range found in dependencies, project can be non-reproducible"
Copy link
Member

Choose a reason for hiding this comment

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

Version => Version specification
(this is the opportunity to clarify that in POM, this is a version specification)
can be => may
project => build

}
result.put(
node,
"Dependency " + node.getDependency() + path + " is referenced with a range version "
Copy link
Member

Choose a reason for hiding this comment

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

is referenced with => has been resolved from
range version => version range

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

detect version range usage, even if rebuild is ok

3 participants