How do I include a Gradle auto-incremented version number in my code's repo?

Viewed 9089

I have spring boot application in my github public reposity. I have used gradle as a build tool for this spring boot application. I am using jenkins as CI/CD.

I have below task in my build.gradle file which is used to auto-increment the build number so that my generated executable jar will have unique version name in the generated jar file.

task versionIncr {
    Properties props = new Properties()
    File propsFile = new File('gradle.properties')
    props.load(propsFile.newDataInputStream())
    Integer nextbuildnum = ( ((props.getProperty('artifactBuildNumber')) as BigDecimal) + 1 )
    props.setProperty('artifactBuildNumber', nextbuildnum.toString())
    props.store(propsFile.newWriter(), null)
    props.load(propsFile.newDataInputStream())
}

i am calling this task in jenkins as below.

"versionIncr bootJar docker --warning-mode=all"

this task is working perfectly. As a result of this task below is happening in jenkins server

  1. jenkins pulling the git $branch into the jenkins server workspace
  2. task =>versionIncr is getting executed and incrementing the version number and updating "gradle.properties" file in the workspace that is there in jenkins server
  3. generating executable jar file
  4. creating docker image with the newly generated executable jar file

problem:: the changes made to "gradle.properties" file are left in the jenkins server workspace and the updated version number will not gets reflected in the git hub branch. since jenkins made changes locally so when i push any changes to the github and run the jenkins job then version number in "gradle.properties" file will still remains same. I do not want to update the version number manually each time i push my commits. I want jenkins to handle the version change for me.

is there any way or gradle plugin or jenkins plugin which i can use to push the modified "gradle.properties" file from jenkins workspace back to "github" repository. Also if possible i would like to know the way of achieving using either github username/password or by using SSH.

let me know if i need to post any more information here.

Update:: Posting my build.gradle file just in case if some one is interested in how i am doing it. build.gradle

buildscript {
    repositories {
    jcenter()
    }
    dependencies {
    classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.3.RELEASE")
    classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4+"
    }
}

plugins {
    id 'org.springframework.boot' version '2.2.7.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
    id 'maven-publish'
    id 'com.palantir.docker' version '0.25.0'
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
//apply plugin: 'io.spring.gradle.dependencymanagement.DependencyManagementPlugin'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'maven-publish'
//apply plugin: 'org.jfrog.gradle.plugin.artifactory.ArtifactoryPlugin'

group 'com.javasree'
version project.properties.containsKey("releaseVersion") ? "${artifactMajorVersion}" : "${artifactMajorVersion}-${artifactBuildNumber}"
sourceCompatibility = 1.8

ext {
    springCloudVersion ='Greenwich.RELEASE'
    artifactName ='<artifact>'
    artifactory = 'http://localhost:8081/artifactory/'
    artifactoryRepo = 'gradle-lib-release'
    artifactorySnapShotRepo = 'gradle-lib-snashot'
    artifactoryRepo3pp = 'pub-gradle-remote'
    artifactoryUser = System.getProperty("user", "")
    artifactoryPassword = System.getProperty("password", "")
}

repositories {
    mavenCentral()
    maven {
    url "${artifactory}${artifactoryRepo3pp}"
    allowInsecureProtocol = true
    credentials {               // Optional resolver credentials (leave out to use anonymous resolution)
        username = "admin" // Artifactory user name
        password = "password" // Password or API Key
    }
    }
}

publishing.publications {
    maven(MavenPublication) {
    artifact bootJar
//      groupId 'gatewayengine'
//      artifactId artifactName
//      version '1.0-SNAPSHOT'
    from components.java
    }
}

publishing.repositories {
    maven {
    allowInsecureProtocol = true
    credentials {
        username = "admin" // Artifactory user name
        password = "password" // Password or API Key
    }
    if(project.version.endsWith('-SNAPSHOT')) {
        url "${artifactory}${artifactorySnapShotRepo}"
    } else {
        url "${artifactory}${artifactoryRepo}"
    }
    }
}

dependencyManagement {
    imports {
    mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    //mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
    }
}

docker {
    name "localhost:5000/${project.name}:${project.version}"
    files tasks.bootJar.outputs
    //tag 'localhost:5000/${project.name}:${project.version}'
    dockerfile file('Dockerfile')
    //buildArgs([HOST_APP_JAR_LOC: 'version'])
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web',
        'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:2.2.2.RELEASE',
        'org.springframework.cloud:spring-cloud-starter-netflix-zuul:2.2.2.RELEASE'
}




task versionIncr {
    Properties props = new Properties()
    File propsFile = new File('gradle.properties')
    props.load(propsFile.newDataInputStream())
    Integer nextbuildnum = ( ((props.getProperty('artifactBuildNumber')) as BigDecimal) + 1 )
    props.setProperty('artifactBuildNumber', nextbuildnum.toString())
    props.store(propsFile.newWriter(), null)
    props.load(propsFile.newDataInputStream())
}
3 Answers

I have previously solved your proposed issue in 2 separate ways. Firstly, by using a Gradle plugin, similar to the nebula-release plugin @sghill linked above.

However, that plugin worked by counting all the commits for a patch version, configured major and minor via a Gradle extension and appended metadata information, e.g. branch name and whether it was dirty or not. That seemed too complex a workflow for my needs and was not useful for projects that didn't use Gradle. For your case, however, it's a quick off the shelf solution.

In my case, all I needed were unique version numbers automatically tagged upon a PR being merged into develop or master, and unique version numbers for each commit on a branch. To do so, I did use Git tags and wrote a script for it.

The 3 cases for versioning were:

  • a new, uninitialised repo => generate a new version.json file with a default branch (master, but can be changed per repo, a major and a minor version to configure those bumps)
  • any commit merged into the default branch generates a new version and tags it. If the major or minor versions in version.json have been changed, a major or minor bump occurs and the patch version is reset to 0.
  • unique versions on branches: the output of git describe and the branch name, e.g. 0.1.0-x-branch-name where x is the number of commits ahead of the default branch.

You can find it here and the docker container here

As for configuring Jenkins to have write access to the repo, have you followed the instructions here? This is what I've been successfully doing in all of my repos: Git push using jenkins credentials from declarative pipeline

The nebula.release Gradle plugin is able to auto-increment versions by using git tags. To get started:

plugins {
    id 'nebula.release' version '15.0.0' // or current latest
}

The plugin adds several tasks for the type of release and options for which version component to increment. By default it will increment the minor version. Here's an example of how versions will be incremented on a single branch:

$ ./gradlew final                       # releases 0.1.0; tags repo v0.1.0
$ ./gradlew final                       # releases 0.2.0; tags repo v0.2.0
$ ./gradlew final -Prelease.scope=minor # releases 0.2.1; tags repo v0.2.1

Since the version is incremented after finding the previous git tag, the starting version can be adjusted by manually creating a git tag on a recent commit.

Caveats:

  1. Jenkins needs write access to the repo for pushing back the tag
  2. The plugin assumes everything in the repo will be released with the same version
  3. It strongly prefers semver-like versions

If this doesn't meet your use-case, there are many more versioning plugins to choose from on the Gradle Plugin Portal.

Version numbers are barely auto-increment, but build numbers are.

Jenkins exposes it's own auto-increment ${BUILD_NUMBER} per project.

With System.getenv("BUILD_NUMBER") it can be obtained at build-time:

// $BUILD_NUMBER only exists when building with Jenkins
project.ext.set('BUILD_NUMBER', System.getenv("BUILD_NUMBER") ?: "0")

Yesterday with Google Cloud Build, I've also defined a BUILD_NUMBER;
since I need to to differ in between manual builds and automatic builds.
... different builders may provided difference environment variables.

Jenkins usually builds when a branch has changed. Therefore one can update version.properties files through Git (a local pre-commit hook can increment).
In case you really want to increment the version on every commit to Github.

Related