Contents

Standard-version gitlab integration (java,pom.xml)

Standard-version is a utility using semver and CHANGELOG.md generation powered by Conventional Commits. In this tutorial I will explain how I use standard-version to generate CHANGELOG.md, update pom.xml revision version and create a git tag and run it only when a MR is merged to master by a user and not a bot. Once MR is merged to master then standard-version will do the following:

  1. Retrieve the current version of your repository by looking at packageFiles, falling back to the last git tag.
  2. bump the version in bumpFiles based on your commits.
  3. Generates a CHANGELOG/md based on your commits (uses conventional-changelog under the hood).
  4. Creates a new tag with the new version number.

My current Setup:

  • Self hosted gitlab server
  • Gitlab-runner as “shell” executor in a host with NodeJS and standard-version already installed. (npm i -g standard-version)
  • A Java Application repo containing the following files in the root:

standard-version config

.versionrc file to define the packageFiles to read the current revision version from pom properties-revision and bumpFiles filename and updater module to bump the version.

.versionrc file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
   "packageFiles": [
        {
          "filename": "pom.xml",
          "type": "pom",
          "updater": "pomupdater.js"
        }
      ],
   "bumpFiles": [
        {
          "filename": "pom.xml",
          "type": "pom",
          "updater": "pomupdater.js"
        }
      ]
}

pomupdater.js is the javascript file that is using a regex and read the revision version from properties. pomupdater.js file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
function versionTagRegex (contents) {
    return RegExp(`^\\s+<properties>[\\s\\S]*?(\\s+<revision>([\\d\\.]+)<\/revision>)[\\s\\S]*?<\/properties>`, 'm') 
 }
 
 module.exports.readVersion = function (contents) {
     const matches = versionTagRegex(contents).exec(contents)
     if (matches === null) {
         throw new Error('Failed to read the <revision> tag in your pom file - is it present?')
         
     }
     console.log(matches[2]);
     return matches[2]
 }

module.exports.writeVersion = function (contents, version) {
    //  Find the version tag, set the new version in it.
    return contents.replace(versionTagRegex(contents), (match) => {
        //  Replace the inner part of the version tag with the new version.
       return match.replace(/revision>[^,]+<\/revision/, `revision>${version}<\/revision`)
    })
}

Sample pom.xml file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<project>
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.apache</groupId>
    <artifactId>apache</artifactId>
    <version>18</version>
  </parent>
  <groupId>org.apache.maven.ci</groupId>
  <artifactId>ci-parent</artifactId>
  <name>First CI Friendly</name>
  <version>1</version>
  ...
  <properties>
    <revision>1.1.6</revision>
  </properties>
</project>

Create git gitlab-ci pipeline

gitlab-ci.yml file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# If you use docker gitlab-runner standard-version docker image
#default:
#  image: detouched/standard-version

workflow:
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $GITLAB_USER_NAME != 'BOT-000'
    # != 'BOT-000' part to avoid job stuck in a loop, to run only when a user merge to master and not the bot

build-job:
  stage: build
  tags:
    - runner-host-shell

  script:
    - rm -rf /tmp/repo
    - git config user.name "BOT-000"
    - git config user.email "bot-000@georgios.sh"
    - git config --global user.email "bot-000@georgios.sh"
    - git config --global user.name "BOT-000"
    - git clone "https://oauth2:${GITLAB_KEY2}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}" /tmp/repo
    - cd /tmp/repo
    - standard-version
    - git push --follow-tags origin master 
  only:
    - master

Important: this rule “!= ‘BOT-000” to run only when merge is sone by a user and not a bot avoid job stuck in loop.

pipeline output:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Running with gitlab-runner 14.7.0 (98daeee0)
  on runner-host-shell xxxxx
Preparing the "shell" executor
00:00
Using Shell executor...
Preparing environment
00:01
Running on xxxxxx...
Getting source from Git repository
00:00
Fetching changes with git depth set to 50...
Reinitialized existing Git repository in /home/gitlab-runner/builds/xxxxxxxx/0/cicd-repos/cicd_jobs/.git/
Checking out ddaa7c21 as master...
Skipping Git submodules setup
Executing "step_script" stage of the job script

$ rm -rf /tmp/repo
$ git config user.name "BOT-000"
$ git config user.email "bot-000@georgios.sh"
$ git config --global user.email "bot-000@georgios.sh"
$ git config --global user.name "BOT-000"
$ git clone "https://oauth2:${GITLAB_KEY2}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}" /tmp/repo
Cloning into '/tmp/repo'...
warning: redirecting to https://git.aganet.gr/cicd-repos/cicd_jobs.git/
$ cd /tmp/repo
$ standard-version
0.1.6
0.1.6
✔ bumping version in pom.xml from 0.1.6 to 0.1.7
✔ outputting changes to CHANGELOG.md
✔ committing pom.xml and CHANGELOG.md
✔ tagging release v0.1.7
ℹ Run `git push --follow-tags origin master` to publish
$ git push --follow-tags origin master
warning: redirecting to https://git.xxxxxxxxx.xxx/cicd-repos/cicd_jobs.git/
To https://git.xxxxxxxx.xxx/cicd-repos/cicd_jobs
   ddaa7c2..2ba7e63  master -> master
 * [new tag]         v0.1.7 -> v0.1.7
Cleaning up project directory and file based variables
00:00
Job succeeded

Once the pipeline is complete will have an updated CHANGELOG.md, pom file revision version and a new repo tag with the new version.