Versionierung mit Bitbucket und Scriptrunner

Tobias Janssen

Backend Software-Developer und GIT Enthusiast bei traperto GmbH

Mit diesem Blogartikel möchte ich zeigen, wie moderne Entwicklungsteams mit aktuellen Tools ihre Entwicklungen automatisiert versionieren können.
Es gibt unterschiedliche Arten von Entwicklungsprozessen via GIT. Wir benutzen zum einen GIT Flow und zum anderen das Atlassian Produkt Bitbucket – Mehr dazu weiter unten.

Dieser Blogartikel richtet sich daher an fortgeschrittene Softwareentwickler mit ähnlichen Tools und Entwicklungsverfahren.

Wie bereits angesprochen arbeiten wir nach dem GIT Flow Verfahren – mehr zu GIT Flow folgt. Es gibt auch noch weitere wie z.B. Trunk Based Development.
Beides gilt der Entwicklung, Strukturierung & Veröffentlichung von Softwareständen.

GIT bietet die Möglichkeit an, Entwicklungsstände (Commits) zu benennen (taggen). Ein Tag ist eine Referenz zu einem bestimmten Entwicklungsstand, also eine lesbare Bezeichnung eines Commits.

Ein Tag wird wie folgt erstellt:

$git tag v1.0.0

Die erstellten Tags sind auch im GIT Log nachvollziehbar:

* ae5c5a8 (tag: v1.0.1, Branch-7, Branch-6, Branch-5) A lot of more Changes
| * de20bd8 (Branch-B) Changes Branch B
|/
| * 4283bc1 (Branch-A) Changes Branch a
|/
* de4d379 (tag: v1.0.0) Init

Wie in dem Snippet zu sehen referenziert der tag v1.0.1 auf den Commit ae5c5a8

So kann beispielsweise Kunde A mit der Software Version v1.0.0 arbeiten und Kunde B mit Version v2.1.13.
Für die Versionen kann folgende Syntax verwendet werden:

v<major>.<minor>.<patch>

Viele Entwicklerteams verwenden bei ihrer Entwicklung hilfreiche Tools wie Atlassian Bitbucket. Bitbucket ermöglicht eine Art Qualitätskontrolle der Softwareentwicklung. So werden unter anderem Änderungen in Branches durch Pull Requests (Code-Reviews) behandelt, die von einem Dritten durchgeführt werden. Mehr dazu hier:
https://www.atlassian.com/de/software/bitbucket

Was hat es jetzt mit Bitbucket, Git Flow und Tags auf sich?

Git Flow arbeitet mit dem Prinzip: Master ist untouchable und lauffähig, aber bei jeder Änderung auf dem Master wird eine neue Version erzeugt. Im Falle des GIT Flows sind das Änderungen durch Hotfixes oder Release Branches.
Wird also ein Hotfix oder der Release Branch in den Master zurückgeführt soll eine neue Version erstellt werden.
Natürlich kann eine Version jederzeit händisch erstellt werden, das ist jedoch wieder mit Aufwand und Fehleranfälligkeiten (falsche Version verwendet oder Tag an einem falschen Commit erstellt) verbunden. Wie kann das automatisiert werden? Wir könnten zum einen GIT Hooks verwenden oder Bitbucket um eine Automatisierung erweitern.

Zunächst einmal ein paar Gegebenheiten.

  • es sollten GIT Conventions verwendet werden. Z.b. den Namen von Branches. Ein Hotfix Prefix im Branchnamen lässt schnell darauf schließen wofür die Änderungen sind
  • Wir verwenden Bitbucket
  • Das Plugin Scriptrunner ist installiert

Scriptrunner erlaubt es eigene Scripts für bestimmte Automatismen in Jira Produkte zu installieren. https://www.adaptavist.com/products/atlassian-apps/scriptrunner-for-bitbucket
Die Scripte werden in der Programmiersprache Groovy programmiert.

Was genau soll dieses Script erledigen?

  • Gibt es Änderungen auf dem Master?
  • Wenn Ja, stammen die Änderungen von einem Branch des Namen „Hotfix“ beinhaltet?
  • Wie ist der aktuelle Tag?
  • Zähle den Tag um eine Patch Version hoch.

Wir erstellen im Scriptrunner Context von den Bitbucket Plugins ein neuen „Custom Event Handler“, welcher dann verwendet wird, sobald Änderungen durch ein Pull Request gemerged werden.

Gibt es Änderungen auf dem Master?

if (event.getRefChanges().any { it.ref.id.startsWith("refs/heads/master") })
{
    //your Code!
}

Kommt die Änderungen von einem Branch der Hotfix im Namen als Prefix trägt?

def sourceBranchName = event.getPullRequest().getFromRef().getId();
if (sourceBranchName.contains("hotfix")) {
    //your Code!
}

Wie ist die aktuelle Version?

//Wie ist die aktuellste Version?
String currentTag = gitCommandBuilderFactory.builder(event.getRepository())
    .command("describe")
    .argument("--abbrev=0")
    .argument("--tags")
    .build(new StringCommandOutputHandler())
    .call()
 
 
//Die Syntax der Version
def pattern = ~/(v\d+\.\d+\.)(\d+)/
 
def matcher = currentTag =~ pattern
matcher.find()

Zähle den Tag um ein Patch höher

String majorMinorLevel = matcher.group(1)
int patchLevel = matcher.group(2).toInteger() + 1
String newTag = majorMinorLevel + patchLevel.toString()

Und erstelle einen neuen Tag

gitCommandBuilderFactory.builder(event.getRepository())
    .command("tag")
    .argument(newTag)
    .build(new StringCommandOutputHandler())
    .call()

So wurde vollautomatisch die Version nach einem Hotfix hochgezählt.
Dieser Artikel dient als eine Art Inspiration, um anderen modernen Entwicklungsteams zu zeigen, was alles mit ihren Tools möglich ist und wie mit diesen der Entwickleralltag vereinfacht werden kann.