📜 ⬆️ ⬇️

Versioning build artifacts in Gradle using git tag, brunch and commit names

With the move from SVN to GIT and gitlab (plus the move from Jenkins to Gitlab-CI, but its use is also mentioned), the question arose of versioning the resulting application artifacts.

In SVN there was a familiar revision number, monotonically increasing with each commit. It was convenient to add it to the version number, and this solved most of the problems. But git certainly provides a lot of goodies, and it was worthwhile to convince the management and the whole team to transfer the project to it ...
But I had to rebuild the process of versioning the resulting assembly artifacts.

As a result, we stopped at a very good Gradle plugin github.com/nemerosa/versioning , I am going to tell you about its use.

')

Problem


Our Gradle application has been used for a long time, and for SVN, we simply used the knee-down function, inherited, written directly in the file build.gradle. Fortunately, among other merits of Gradle, it is possible to mention that this is an excellent Groovy language, and it does not limit you in writing the build logic - connect the necessary libraries from the Java world, and go ahead, at least rewrite the entire application in one file!

However, you understand the destructiveness of this approach? If the logic of getting the version number takes more than 5-10 lines, and also if we are to crush our crutch for any reason, then it will simply be impossible to maintain it very soon ...

You can see such “solutions” for manual parsing, for example, in the Jenkins for Android article on a clean system and without UI or Via thorns to an assembly where you are invited to manually call git describe and parse the output with regular expressions ...

I wanted something simpler, more reliable and initially working.

Our workflow and Wishlist


In our application, a couple of jar files, 3 war artifacts, 3 RPM of them are included, and at the end of the Docker an image of the application with RPMs installed, which after automatic testing is immediately sent to gitlab-ci to a private repository.

In general, with the transition to git / gitlab, we follow the logic inherited from the standard github flow with a few changes, which means for versioning:


Suggested solution: Gradle plugin net.nemerosa: versioning


Looking around at the available gradle plugins, I found this wonderful option: github.com/nemerosa/versioning

His documentation is immediately captivating - everything is simple, logical and understandable for what has been done.
Plus, the semantic separation of release, feature

Let's try in business


So connect to the project is very simple, follow the instructions:
plugins { id 'net.nemerosa.versioning' version '2.4.0' } 


Everything, in most cases, you can already use the version in your build scripts further, where it is needed:
 version = versioning.info.full 


Well, or more to the point, say in the name of the war artifact:
 war { archiveName = "portal-api##${versioning.info.full}.war" } 


After building from feature-1 brunch, we will get a file with the following name: portal-api ## feature-1.3e46dc.war (in the example, Tomcat-style naming is used). Options for setting and parsing values ​​for more interesting situations will be discussed further.

Immediately available 2 tasks:
versionDisplay - showing information and versions and displays on the console. Very handy in debugging and versionFile - creating the build / version.properties file with ready-made variables, for importing into bash scripts externally:

 > ./gradlew versionDisplay :versionDisplay [version] scm = git [version] branch = release/0.3 [version] branchType = release [version] branchId = release-0.3 [version] commit = da50c50567073d3d3a7756829926a9590f2644c6 [version] full = release-0.3-da50c50 [version] base = 0.3 [version] build = da50c50 [version] display = 0.3.0 


 > ./gradlew versionFile > cat build/version.properties VERSION_BUILD=da50c50 VERSION_BRANCH=release/0.3 VERSION_BASE=0.3 VERSION_BRANCHID=release-0.3 VERSION_BRANCHTYPE=release VERSION_COMMIT=da50c50567073d3d3a7756829926a9590f2644c6 VERSION_DISPLAY=0.3.0 VERSION_FULL=release-0.3-da50c50 VERSION_SCM=git 


just fine

Custom logic parsing versions


Just want to note that there are many options like parsit names, process prefixes, suffixes, interpret versions. There is also support for SVN by the way. In general, you are in the customization section.

However, there is not without a spoon of tar . At the time when I started to use it, the documentation looked different .
Yes, you can set your own closure how to interpret the name of the branch (for example, 'release / 1' is considered release, and 'qa / 0.1' otherwise):
 versioning { branchParser = { String branch, String separator = '/' -> int pos = branch.indexOf(separator) if (pos > 0) { new BranchInfo( type: branch.substring(0, pos), base: branch.substring(pos + 1)) } else { new BranchInfo(type: branch, base: '') } } } 


It's all great, but we want a tag instead of a branch, if it exists!?
I did not want to abandon this idea. Of course, we wrote down a temporary workout, but the author created a request to make the parsing logic more general: github.com/nemerosa/versioning/issues/32

Damien Coraboeuf , the author of this plugin, was very responsive, and promptly correcting a couple of small things promptly.
In general, as is often the case, I proposed to implement myself, what I propose.
I followed his advice - quickly bungled pull request .

Now, after its adoption, we get the SCM commit information object (SVN or GIT) and are free to choose the way we build the version. For example, the same code as above can be implemented like this:
 versioning { releaseParser = { scmInfo, separator = '/' -> List<String> part = scmInfo.tag.split(separator) + '' new net.nemerosa.versioning.ReleaseInfo(type: part[0], base: part[1]) } } 


The same goes for the full closure.

What does this give us? Well, for example, as described in the requirements, we use this to take the name of the branch in one case, and the name of the tag in the other, and not limited to the string representation of the name of the branch. With us it now looks something like this:
 versioning{ full = { scmInfo -> // Tag name, or '_branch_name_' if it is not 'master' (scmInfo.tag ?: ( 'master' == scmInfo.branch ? '' : "_${scmInfo.branch}_." ) + scmInfo.abbreviated).toLowerCase().replaceAll(/[^a-z0-9-_\.]/, '_') } } 

Lower case is for use in Docker image tags.

As I mentioned, the options are not limited to this, we also control the dirty suffix, and add the build time to the same object using the Groovy meta-magic ...

Integration with CI


Well, since I undertook to talk about convenient integration, I should immediately pay attention to one pitfall, about which I also stumbled. And in this plugin have already taken care!

The specified code worked fine, was tested, commited. But the very first push and build on CI brought a strange result - the name of the brunch was something like HEAD.

In fact, the reason is simple, if we see what the builder does, he does not collect a branch, but a specific commit. At the time of assembly, in the same thread may already be others. Therefore, it always makes a checkout by the name of the commit hash. Thus, we get the git repository in the detached head state.

As I, running ahead, have already said, this situation is normal and most work like this, but in this plugin you just need to register one line, with the name of the external variable or variables from which you need to take the real name of the branch, for gitlab-ci I just needed add:
 branchEnv = ['CI_BUILD_REF_NAME'] 


In Jenkins, such variables were also added for a long time by the request of JENKINS-30252 . So, if you want to support both systems at once, you can simply write:
 branchEnv = ['CI_BUILD_REF_NAME', 'GIT_LOCAL_BRANCH'] 


I hope you will be more comfortable working with the versions in gradle. Yes, and in every possible way I recommend putting bugs or writing rekvesta to the author - he answers them very promptly. Good coding!

Source: https://habr.com/ru/post/321668/


All Articles