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:
- Retrieve the current version of your repository by looking at
packageFiles
, falling back to the last git tag
.
bump
the version in bumpFiles
based on your commits.
- Generates a CHANGELOG/md based on your commits (uses conventional-changelog under the hood).
- 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.