5

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())
}
Ghost Rider
  • 688
  • 3
  • 17
  • 38

3 Answers3

4

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

afterburner
  • 2,552
  • 13
  • 21
  • your answer might solve my problem But i am a new bee and i don't know how to right one such descriptive file for jenkins. I always use the UI options to set my repo and goals like how building and publishing jar to artefacts. your answer looks promising but how do i convert all my jenkins configurations into a file where i can add the suggested script for pushing the files to github. Please help – Ghost Rider May 19 '20 at 08:17
  • That's a more involved answer than I can give in this comment, because I don't know all the steps and stages you've configured through the UI. However, the gist of it is, you do so by having a `Jenkinsfile` inside your repo, and when you create a jenkins job for the repo, you specify configuration from `Jenkinsfile`. Essentially, all the configuration from the UI should be directly translatable into the declarative pipeline syntax. Have a look at this getting started page and if you have questions, let me know: https://www.jenkins.io/doc/book/pipeline/getting-started/ – afterburner May 19 '20 at 09:26
2

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.

sghill
  • 546
  • 1
  • 5
  • 8
  • thank you for the answer. I will try this today and will let you know. But how do i give full permission to jenkins for my github repo?? should i configure the user and give permission in github? can i just create ssh key in my jenkins machine and add it in github? is this the way i am supposed to give access or is there any other way?. – Ghost Rider May 15 '20 at 12:22
  • There are options depending on what you want to do. See [Machine Users](https://developer.github.com/v3/guides/managing-deploy-keys/#machine-users) for a common pattern of granting automation access to multiple repositories. – sghill May 15 '20 at 16:25
  • sorry for the delay. the above plugin is for versioning right. my build script that i am currently looking for is for development purpose as in there will be multiple push to the repo and multiple builds per day. I do not want to use the tag option for development purpose. I will use tags when i release the build for QA / RELEASE. I can use the suggested plugin for version auto incrementing. – Ghost Rider May 16 '20 at 09:41
  • but, my real challenge is not versioning. challenge is how do i push the changes made by jenkins back to my github repo. As stated in my previous comment, since i want to use tag based pushing for my QA/RELEASE but not for my development build as i will end up creating hundreds of tags. I tried using shell script to check for modified files and add them by saying "git push" but that is not happening as it is not able to authenticate the user( even though i am able to push change in the same jenkins machine using CLI) – Ghost Rider May 16 '20 at 09:46
  • > I do not want to use the tag option for development purpose. I will use tags when i release the build for QA / RELEASE. Consider having two jobs: 1. Runs `./gradlew devSnapshot` when branches are updated, which does not create a tag 2. Release job that runs `./gradlew -Prelease.useLastTag=true final` when new tags are created. You could use GitHub releases to create a tag. In this setup, Jenkins does not need write access. > even though i am able to push change in the same jenkins machine using CLI Some things to check: the user, ssh key, the repo's clone uri. – sghill May 16 '20 at 17:59
  • the last point where you have mentioned of using CLI to push the changes is where i am stuck. I have tried pushing using the normal git commands in "execute shell" command after building the project. But for some reason it is not pushing the changes to GITHUB. wasn't sure how to configure it. I have created SSH key of jenkins machine and added to GitHub repo and it is having both "read and write" access. – Ghost Rider May 18 '20 at 08:21
2

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.

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • agree that we can use "BUILD_NUMBER" of jenkins, but lets say my build had failed for 5 times and success on 6th build then my generated build jar will have 6 as it's build. this may cause confusing as if some one did some thing to the remaining 1.2....5 build as if they are missing. so i am happy with the current approach of auto incrementing with Gradle task. But, my REAL PROBLEM is NOT AUTO INCREMENTING the version. but commit/push the auto incremented version number back to git hub. I do not want to use TAG's as this script is for Dev environment. – Ghost Rider May 17 '20 at 06:53
  • the branch will not change in my case. people will create separate/private branches for each change and will create PR(Pull Request). After care full code review this PR will be merged to Develop branch. Build will be triggered always for "develop" branch only. so for each merge of PR I would like to release the jar with new version number automatically. so as stated above i request is "how do i push back the modified version number back to GITHUB" – Ghost Rider May 17 '20 at 06:58