HotFix automation in Maven projects using TeamCity
This post will describe how to set up HotFix automation in Maven projects using Teamcity.
To make a HotFix there is usually a lot of manual work involved:
Create a branch for the release you want to push HotFix to
Fix bug in release
Change bugfix version in release branch
Roll out version bugfix tag
Items 1,3,4 can be automated.
Before we move on to the topic, I want to touch on an important and complex topic - versioning software. Briefly about Semver can be understood in this screenshot.
In Trunk-Based Development, you need to create your own branch for each release. All changes (hotfix) within this release are committed to this branch.
As part of this post, we will automate the following things:
Teamcity server and agent. You can raise your local Teamcity server and agent with docker-compose
Where you have a Teamcity agent, java, maven, git must be installed
Let's create the "Automation Maven Hotfix" project in Teamcity and create 4 tasks there.
CI Build (CI build)
Create branch for release
Maven increment bugfix (Change bugfix version))
Maven release (Creating a new release)
Project screenshot:
General Settings
In all tasks, you must check the box "Clean build: Delete all files in the checkout directory before the build“Because when this checkbox was missing, I got errors.
We create a single VCS. VCS features circled in red.
Typically, VCSs use the HTTPS scheme. IN branch specification: it is indicated to watch all brunches and all tags:
+:refs/heads/*
+:refs/tags/*
You need to create 4 Configuration Parameters.
BRANCH_FOR_INCREMENT
TAG_FROM_VERSION
TEAM_USER
TEAM_USER_EMAIL
The value field in BRANCH_FOR_INCREMENT and TAG_FROM_VERSION must be left empty.
You need to upload/add a private key. All tasks except CI Build require a private key.
In each task, except for CI Build, in the Build Features section, you need to connect a private key.
Example for Maven release
CI Build**.
In a task CI Build just one step mvn clean test
Maven release
In a task Maven release 2 steps. The first step is checking what the brunch is master. If brunch is not master, then the task falls.
BRANCH=$(git branch | grep * | cut -d ' ' -f2)
echo "$BRANCH"
if [[ "$BRANCH" != "master" ]]; then
echo 'Branch is not master';
echo 'Aborting';
exit 1;
fi
The second step is standard mvn release:prepare with option --batch mode
Create branch for release
To create a hotfix for a release, you need to create a branch. This is what a task does Create branch for release. She has 2 steps.
The first step checks that the brunch is not master, and the second checks that the version in the file pom.xml did not contain a word SNAPSHOT
BRANCH=$(git branch | grep * | cut -d ' ' -f2)
echo "$BRANCH"
if [[ "$BRANCH" == "master" ]]; then
echo 'Branch is master';
echo 'Aborting';
exit 1;
fi
echo "Get version package from pom.xml"
version=`python -c "import xml.etree.ElementTree as ET; print(ET.parse(open('pom.xml')).getroot().find('{http://maven.apache.org/POM/4.0.0}version').text)"`
echo "Check SNAPSHOT"
if [[ $version == "*SNAPSHOT*" ]]; then
echo "******************* W A R N I N G *************************"
echo "************ You are create branch for SNAPSHOTS ******************"
echo "***********************************************************"
exit 1
fi
The second step changes the connection scheme in developerConnection from HTTPS to GIT.
# Здесь получаем developerConnection из файла pom.xml
developerConnection=$(xmllint -xpath "/*[local-name() = 'project' ]//*[local-name() = 'developerConnection']/text()" pom.xml | sed 's|scm:git:ssh://||')
echo developerConnection
echo $developerConnection
# Здесь меняем / на : в URL для git_remote_url
git_remote_url=$(echo $developerConnection| sed 's/gitlab.com//gitlab.com:/g')
echo git_remote_url
echo $git_remote_url
git remote set-url origin $git_remote_url
# Если вы не используете ввстроенную возможность Teamcity получения user и email из ~/.gitconfig, то можно указать их здесь
echo 'git config user.name %TEAM_USER%'
git config user.name %TEAM_USER%
echo 'git config user.email %TEAM_USER_EMAIL%'
git config user.email %TEAM_USER_EMAIL%
# Здесь получаем версию из файла pom.xml
echo "Get version package from pom.xml"
version=`python -c "import xml.etree.ElementTree as ET; print(ET.parse(open('pom.xml')).getroot().find('{http://maven.apache.org/POM/4.0.0}version').text)"`
echo $version
# Почему-то без fetch выдавало ошибку.
git fetch
if [ `git branch -a | egrep "${version}$"` ]
then
echo "Branch exists"
exit 1
fi
# Создаем бранч той версии, который был в файле pom.xml
echo "Create branch"
git checkout -b $version
# Чистый git всегда предлагает настроить политику отправки.
git config --global push.default simple
# Пушим в ветку совпадающую с версией в pom.xml
echo "Push release branch"
git push --set-upstream origin $version
Maven increment bugfix
The task consists of 6 parts. It could have been refactored, but it still works.
The first step is to check that the brunch is not master. If brunch master the cart falls.
BRANCH=$(git branch | grep * | cut -d ' ' -f2)
echo "$BRANCH"
if [[ "$BRANCH" == "master" ]]; then
echo 'Branch is master';
echo 'Aborting';
exit 1;
fi
# Здесь получаем версию из файла pom.xml
echo "Get version package from pom.xml"
BRANCH=`python -c "import xml.etree.ElementTree as ET; print(ET.parse(open('pom.xml')).getroot().find('{http://maven.apache.org/POM/4.0.0}version').text)"`
# Приходится делать checkout на нужный бранч.
# Иначе git status показывает detached к нужному бранчу.
# Нужно чтобы git status показывал просто бранч
git checkout $BRANCH
# Экспортируем переменную bash в переменную Teamcity для дальнейшего использования.
echo "##teamcity[setParameter name='BRANCH_FOR_INCREMENT' value='$BRANCH']"
The second Maven step is to change the bugfix version in the pom.xml file.
The fourth step changes the developerConnection connection scheme from HTTPS to GIT.
And pushes changes to the branch specified in the Teamcity variable %BRANCH_FOR_INCREMENT%
# Здесь получаем developerConnection из файла pom.xml
developerConnection=$(xmllint -xpath "/*[local-name() = 'project' ]//*[local-name() = 'developerConnection']/text()" pom.xml | sed 's|scm:git:ssh://||')
echo developerConnection
# Здесь меняем / на : в URL для git_remote_url
git_remote_url=$(echo $developerConnection| sed 's/gitlab.com//gitlab.com:/g')
echo git_remote_url
echo $git_remote_url
git remote set-url origin $git_remote_url
# Если вы не используете ввстроенную возможность Teamcity получения user и email из ~/.gitconfig, то можно указать их здесь
echo 'git config user.name %TEAM_USER%'
git config user.name %TEAM_USER%
echo 'git config user.email %TEAM_USER_EMAIL%'
git config user.email %TEAM_USER_EMAIL%
echo 'git add .'
git add .
echo 'git commit -m "Increment bugfix"'
git commit -m "Increment bugfix"
git push --set-upstream origin %BRANCH_FOR_INCREMENT%
Fifth step get from file pom.xml version and install it in teamcity variable TAG_FROM_VERSION. Note that the version from the file pom.xml without a v in front. A tag based on this version is already with the letter v at the beginning.
echo "Get version package from pom.xml"
VERSION_AFTER_CHANGE=`python -c "import xml.etree.ElementTree as ET; print(ET.parse(open('pom.xml')).getroot().find('{http://maven.apache.org/POM/4.0.0}version').text)"`
echo $VERSION_AFTER_CHANGE
echo "##teamcity[setParameter name='TAG_FROM_VERSION' value='v$VERSION_AFTER_CHANGE']"
Sixth step - tagging bugfix versions. This is done using Maven with the desired option Goal.