Description
Hello everyone, first of all thank you for your work on this project !
I have recently switched workflow to use snapshot versions built on remote, and ran into the following issue (I think it might be due to the fact that I use the github maven package registry).
The maven-metadata.xml
for the snapshot version looks like this:
<metadata modelVersion="">
<groupId>com.example</groupId>
<artifactId>artifact</artifactId>
<version>2.5.x-SNAPSHOT</version>
<versioning>
<snapshot>
<timestamp>20250522.153927</timestamp>
<buildNumber>2</buildNumber>
</snapshot>
<lastUpdated>20250521153923</lastUpdated>
<snapshotVersions>
<snapshotVersion>
<extension>jar</extension>
<value>2.5.x-20250521.153917-1</value>
<updated>20250521153920</updated>
</snapshotVersion>
<snapshotVersion>
<extension>jar.sha1</extension>
<value>2.5.x-20250521.153917-1</value>
<updated>20250521153921</updated>
</snapshotVersion>
<snapshotVersion>
<extension>jar.md5</extension>
<value>2.5.x-20250521.153917-1</value>
<updated>20250521153923</updated>
</snapshotVersion>
<snapshotVersion>
<extension>jar</extension>
<value>2.5.x-20250522.153927-2</value>
<updated>20250522153924</updated>
</snapshotVersion>
<snapshotVersion>
<extension>jar.sha1</extension>
<value>2.5.x-20250522.153927-2</value>
<updated>20250522153926</updated>
</snapshotVersion>
<snapshotVersion>
<extension>jar.md5</extension>
<value>2.5.x-20250522.153927-2</value>
<updated>20250522153927</updated>
</snapshotVersion>
</snapshotVersions>
</versioning>
</metadata>
As you can see, both snapshot builds are still present in the metadata file. With this configuration, coursier always fetches the first built JAR (effective version 2.5.x-20250521.153917-1
), whereas the mvn dependency:get
command correctly fetches the latest one (effective version 2.5.x-20250522.153927-2
), which is the expected behavior.
From my research on the topic, this is due to different snapshot version handling between maven and coursier.
Maven
As seen in the maven implementation, the buildNumber is taken into account and the computation is the following:
- removing the SNAPSHOT suffix
- adding the timestamp
- adding a dash
- adding the buildNumber
Coursier
For coursier, the logic is here. As of today, this logic is the following:
- looping on each snapshotVersion
- taking the first one that has the correct classifier and extension
Proposed solution
I would suggest mimicking the effective version computation from maven, that would result in the following code (syntax may not follow project standards, just to give a rough idea)
def mavenVersioning(
snapshotVersioning: SnapshotVersioning,
classifier: Classifier,
extension: Extension
): Option[Version] =
snapshotVersioning.buildNumber flatMap {
buildNumber =>
// Compute the latest jar expected based on timestamp and buildNumber
val latestBuildValue = s"${snapshotVersioning.version0.asString.stripSuffix("SNAPSHOT")}${snapshotVersioning.timestamp}-$buildNumber"
// Make sure it is in the list, otherwise fallback to previous computation (first snapshot version)
snapshotVersioning
.snapshotVersions
.collectFirst { case v if
(v.classifier == classifier || v.classifier == Classifier("*")) &&
(v.extension == extension || v.extension == Extension("*")) &&
(v.value0.asString == latestBuildValue) => v.value0
}
} orElse {
snapshotVersioning
.snapshotVersions
.find { v =>
(v.classifier == classifier || v.classifier == Classifier("*")) &&
(v.extension == extension || v.extension == Extension("*"))
}
.map(_.value0)
.filter(_.asString.nonEmpty)
}
This does pass mill compilation and from my testing retrieves the same version as maven from the metadata file.
I could open a PR if required and I am open for feedback.
Thank you for your time !