Preferred way to build a release

What is the preferred way to build a release in gradle? For those using Gradle over Maven, what is the process you are using for building a release?

I have been using Maven for years now and have begun to appreciate the strength and simplicity of gradle build script. I have written a gradle build script that builds my project exactly the same as with maven. Though I am missing the release process.
With maven release plugin the release is a two step process.

mvn release:prepare
mvn release:perform

It creates the git tag and two git commits for the release:

063f3d36b [maven-release-plugin] prepare for next development iteration
92d35d70f (tag: v1.0.0) [maven-release-plugin] prepare release v1.0.0

There is an gradle release plugin.

https://github.com/researchgate/gradle-release

$ gradle release -Prelease.useAutomaticVersion=true -Prelease.releaseVersion=1.0.0 -Prelease.newVersion=1.1.0-SNAPSHOT

Also an gradle git plugin that seems to have similar release feautures
https://github.com/ajoberstar/gradle-git

$ release -Prelease.scope=major -Prelease.stage=final

From what I see there is either using one of these gradle plugins to perform the release build, or do it manually.
$ sed -i 's/1.0.0-SNAPSHOT/1.0.0/g' build.gradle
$ git add build.gradle
$ git commit -m "Gradle Release v1.0.0"
$ git push
$ gradle build
$ git tag -a v1.0.0 -m "Gradle Release Build v1.0.0"
$ git push --tags
$ sed -i 's/1.0.0/1.1.0-SNAPSHOT/g' build.gradle
$ git add build.gradle
$ git commit -m "Gradle Release next development"
$ git push

This is a lot of work to do when we need to build a release.

Or perhaps have version always at 1.0.0-SNAPSHOT and not commit changes for version. Just change the version and build and create git tag for the release build. However we need some way to keep track on the previous version to increment, perhaps on a git tag.

$ gradle build -Prelease=true -PreleaseVersion=1.0.0

in build.gradle

if (release) {
    version = releaseVersion
}
1 Like

I have been looking into the build scripts for Gradle itself. Many interesting examples.

One file caught my eyes:

What kind of plugin creates this file?

There is also a version.txt, I wonder what the purpose of it is. The version mentioned there is the next-next release (twice over).

How does gradle use these to actually release? Tempted to borrow the versioning.gradle for my project.

Hello,

I come from Maven too and miss release process very much. Eventually, I choose to use https://github.com/researchgate/gradle-release

For me it is complete equivalent to maven release process.
You keep project version in gradle.properties file (inside the project). E.g. version=1.0.1-SNAPSHOT.
When you need to perform release, just do ./gradlew release and it will ask you about releasing version (and update it) and then about new version (100% the same as maven).

Usually, the only customization you need to do is attaching custom tasks to release cycle. In my case (mostly OSS projects) it’s bintray upload: afterReleaseBuild.dependsOn = [bintrayUpload]

You can see complete project example here: https://github.com/xvik/dropwizard-guicey/blob/master/build.gradle#L121

Another moment I missed after maven was “maven install” for local snapshots deployment. To fulfill this need I wrote https://github.com/xvik/gradle-java-lib-plugin. It also configures required jars (src, javadoc) and adds maven xml and properties inside main jar (as maven did). You don’t need to use plugin if you don’t want to, everything is described here https://github.com/xvik/gradle-java-lib-plugin#boilerplate-plugin-removes so you can just re-use some parts of this configuration.

Hello,
I think version should not be set in project file (pom.xml or build.gradle), because during development version is meaningless. So I set version on command line only for release task (gradlew release -Pversion=x.y.z). My release task checks if property version is set. My steps for building release are the opposite to yours. I tag version in Git, build server then grabs that tag and runs gradle task. Version property is set according to tag name. So only manual thing I have to do is to find right commit and tag it by version number. The rest is done by build server automatically.

Hi Filip, if you tag the version yourself before the CI runs, then it means you create the tag before building, so you may end up tagging a commit that doesn’t CI-build (even if it does build locally). Have you ever had any problems like this?

Also, some projects do need the version at runtime, so you need it during development when you test this kind of feature. How do you deal with these?

Hi Joffrey, I tag the commit after it was successfully built by CI, or after it was manually tested. So technically it is new build, but the only change should be version number. If I wanto to, I can tag one commit with different versions - for example first time I tag it version-1.0.0-RC3 and after all manual tests are successfully done I tag the same commit as final version-1.0.0.
In case I tag some version and CI fails on that tag (because of some bug leading into unreproducible build), I remove the version tag from repository and I tag fixed commit with the same version number (but it should be rare occurrence).

I use fixed string, for example “devel”, during development. This is what I have in one of my Gradle tasks:

file("$buildDir/web/static/version.txt").text = (project.version == 'unspecified' ? 'devel' : project.version)

If it is needed there can be commit number or timestamp in version number during development. I don’t use “production” version number scheme for development, because I think these development version numbers are fabricated - often version 1.2.5-SNAPSHOT became 1.3.0 and so on. I think Maven’s “-SNAPSHOT” is design mistake :slight_smile:

3 Likes

I agree with you in this respect, I never quite understood how people could know in advance what the next version would be when releasing a given version.
In my projects, I currently don’t even try to change anything and just leave the latest version I released in the build.gradle, but this feels wrong and I’m trying to find a clean solution to this.

The fixed string idea is a good idea, and maybe we simply don’t need more than that.
However, I was thinking about using a convention that’s the opposite of maven: rather than guessing the next version and expressing somehow that it’s not ready (“snapshot”), we could keep the latest released version, but with a marker saying that we added stuff to it since the release.
For example, after releasing “1.2.0”, we set the version to something like “1.2.0-NEXT”, or “1.2.0-NEXT.1234”, 1234 being some CI build number.

Probably you can use gradle-git-version plugin. It assembles version number from last released version (from git tag name), git commit ID and „dirty“ suffix if needed. But I don’t know if this plugin can be used with current Gradle.

1 Like

You could also look at gradle-jgitver-plugin which is a bit more configurable.

I am beginning to lean towards Automaric Versioning. I have been contributing on a SemVer release tool that was mainly developed for Jenkins X, but can be used as a standalone tool.
It uses the base version defined in a build automation file, and git tags to determine the next release version.

It looks similar to what the jgitver does, but works first by reading a base version from a build automation file such as those from Makefile, Automake, CMake, Maven, Gradle, Python and NPM.
You still need need release bump commits when changing the major/minor version. It automatically calculates the next patch version from the base version and git tags.
The reason I settled on this one is because many of our projects do not use Gradle, but CMake.

jx-release-version doesn’t look like it has support for Gradle (there’s an outstanding request) so why is it suggested here?

Currently it does not have support for Gradle, but I am working on it. I will add a PR with Gradle support in a week or two.

during development version is meaningless

This is not true for libraries, because it needs to be checked locally that the consumers are not affected by installing to local Maven repo (publishToMavenLocal).

Now for the actual release process, I’m in the same boat as everyone else here. The manual process is ok for a few projects, but if you are trying to release dozens of projects, some interdependent, you can’t keep it all in your head which dependency to update and what to release.

I saw some blog posts that make it sound like using Git tags solve the versioning problem. BS! The biggest problem with using Git tags is that they are tied to commits, and have no concept of branches. Thus, if I’ve perfect working code that I just released as v1.0.0 by creating such a tag on a release branch, my dev branch also sees the same tag. If I create tag v1.1.0-alpha on the dev branch, the release branch will see it and it’ll screw up the versioning of the release branch.

I’m still looking.

I think it’s perfectly fine that the tags are global. The library versions are also global. You don’t want to have v1.1.0-alpha from the dev branch and another version v1.1.0-alpha from the release branch.