How to generate unique snapshots with the same version string for all subprojects?

I maintain an open source project using gradle which consists of multiple subprojects. Thanks to the help I received on a previous post, I was able to configure a uploadLocal task, which will install unique snapshots into our Maven repository.

This basically works: We expose the repo via HTTPS and users are able to specify a unique snapshot in their projects. The only annoyance is that the version string of the unique snapshots will differ slightly for every subproject for the same gradle uploadLocal invocation. Because the snapshot for every subproject is created at a slightly different time, and the timestamp is part of the unique snapshot’s version string.

Consider a typical use-case, where a user may include the open source project I maintain by putting the following lines into his build.gradle:

ext {
  smackVersion="4.2.0-rc2"
}

dependencies {
  compile "org.igniterealtime.smack:smack-android:$smackVersion"
  compile "org.igniterealtime.smack:smack-tcp:$smackVersion"
}

Now a bug in 4.2.0-rc2 is discovered by the user, reported, got fixed and, thanks to your CI system, a unique snapshot including the fix is pushed into our repo.

The problem is that the version string of smack-tcp and smack-android slightly differ. So in order for the user to test the fix, he has to change his build.gradle as follows:

ext {
  // This is now ignored.
  smackVersion="4.2.0-rc2"
}

dependencies {
  compile "org.igniterealtime.smack:smack-android:4.2.0-rc2-20170108.010349-3"
  compile "org.igniterealtime.smack:smack-tcp:4.2.0-rc2-20170108.010503-3"
}

This is not only a problem for the user, because some setups require to declare more than two dependencies. But also for me, as I can’t simply tell the user

Your issue should be fixed fixed in 4.2.0-rc2-20170108.010503-3, please test and report back.

It would be great if the only thing the user had to change was the smackVersion definition, so he would end up with

ext {
  smackVersion="4.2.0-rc2-20170108.010503-3"
}

dependencies {
  compile "org.igniterealtime.smack:smack-android:$smackVersion"
  compile "org.igniterealtime.smack:smack-tcp:$smackVersion"
}

Therefore my question is: How can I generate unique snapshots with the same version string for all subprojects?

Can’t you just generate the version in the root project and use the same value in all subprojects?

Eg:

root/build.gradle

def myVersion = "4.2.0-rc2-" + new SimpleDateFormat(...).format(new Date())
allprojects {
   version = myVersion
} 

Thanks for your suggestion. But unfortunately that doesn’t work, and even if it would, then I would have to change my release workflow towards something I don’t want.

It does not work because the string that is used to make the snapshot version unique is generated withing gradle/maven. So if myVersion is ‘1.0.0-2017-01-01’, then the resulting unique snapshot version string would be something like ‘1.0.0-2017-01-01-20170101.010503-3’.

My release workflow currently consists of two variables: version and isSnapshot. Depending on weather isSnapshot is true or false, the string ‘-SNAPSHOT’ is appended to version. Most of the time isSnapshot is set to true, only if I do a release I change it to false, commit that change, upload that release to Maven Central, and after that, open the development cycle again with another commit changing version (e.g. from ‘-beta1’ to ‘-beta2’) and setting isSnapshot to false. This wouldn’t work any more with your suggestion.

Please share the code where you generate the version. I’m sure you can do it once for all projects rather than per project

Sure: https://github.com/igniterealtime/Smack/blob/master/build.gradle
(I’ve also edited the original post to include a direct link to the line number of the uploadLocal task)

I’m still looking for a solution to this. Meanwhile I created https://github.com/gradle/gradle/issues/1772