Über unsMediaKontaktImpressum
Sebastian Böhm 30. Mai 2017

Integrierter Entwicklungsprozess mit JIRA, Bitbucket und Jenkins

Mit dem Workflow werden kleine, aber wichtige und oft vergessene Arbeitsschritte abgenommen. © nikkytok / Fotolia.com
© nikkytok / Fotolia.com

Automatisierung ist in der Industrie bereits seit Jahrzehnten Gang und Gäbe und auch in der IT gibt es z. B. bei modelgetriebenen Ansätzen mit Code-Generierung viele Möglichkeiten, wiederkehrende Codeteile maschinell erstellen zu lassen. Dennoch scheint es in dieser Hinsicht oft noch Lücken im Projektvorgehen zu geben: Zum Beispiel müssen Entwickler ihre JIRA-Tickets manuell aktuell halten, Entwicklungszweige müssen in der Versionsverwaltung manuell erstellt und wieder zurückgeführt werden oder Reviews werden teilweise per E-Mail angefragt. Dies hat oft zur Folge, dass die vereinbarten Prozesse einfach umgangen oder ignoriert werden, sodass JIRA nicht mehr den aktuellen Projektstatus abbildet oder alle Entwickler auf dem Hauptentwicklungszweig arbeiten und sich gegenseitig behindern.

Innerhalb Atlassians Portfolio gibt es bereits gute Integrationsmöglichkeiten zwischen JIRA und der Versionsverwaltung Bitbucket und auch der CI-Server Jenkins kann mit Hilfe von Plugins gut in den Entwicklungsprozess eingegliedert werden: Man kann zum Beispiel direkt aus JIRA-Tickets neue Entwicklungszweige in Bitbucket erstellen und eingecheckter Code wird automatisch vom Jenkins gebaut. Allerdings fehlen in diesem Prozess oft Kleinigkeiten, die dann doch manuell durchgeführt werden müssen, wie das direkte Navigieren zu den Code-Änderungen für einen Review oder das Zuweisen eines JIRA-Tickets an einen Reviewer. Das sind dann auch die Dinge, die den Entwicklungsprozess bremsen, wenn sie vergessen werden, z. B. erfährt der Reviewer nicht, dass er irgendwelche Code-Änderungen prüfen soll.

Dieser Artikel befasst sich mit einem einfachen Workflow, der auf JIRA, Bitbucket (Git) und Jenkins basiert und dem Entwickler so viele Arbeitsschritte wie möglich abnehmen will. Im folgenden Teil wird der Workflow für eine Feature-Entwicklung im Detail beschrieben. Das anschließende Tutorial erklärt dann Schritt für Schritt die Konfiguration der Werkzeuge und die Einrichtung der Workflows. Dafür werden Grundkenntnisse in JIRA, Jenkins und Bitbucket vorausgesetzt.

Das Ziel: Ein integrierter Workflow

Abb.1: Offenes JIRA-Ticket. © Sebastian Böhm
Abb.1: Offenes JIRA-Ticket. © Sebastian Böhm
Abb.2: Erstellen eines Feature-Branches in Bitbucket. © Sebastian Böhm
Abb.2: Erstellen eines Feature-Branches in Bitbucket. © Sebastian Böhm
Abb.3: Bestätigung des neu erstellten Branches. © Sebastian Böhm
Abb.3: Bestätigung des neu erstellten Branches. © Sebastian Böhm

Die Ausgangssituation ist ein offenes JIRA-Ticket im initialen Status TO DO, das bereits dem entsprechenden Entwickler zugeordnet ist (s. Abb.1).

Um sich bei der Entwicklung nicht ins Gehege zu kommen, macht es Sinn, dass jeder Entwickler in seinem eigenen Feature-Branch arbeitet und nur einen abgeschlossenen Stand integriert. So wird vermieden, dass zum Beispiel andere Entwickler durch einen eingecheckten (Compile-)Fehler behindert werden oder die spätere Software halbfertige Entwicklungen enthält. Um einen Feature-Branch zu erstellen, kann der Entwickler den Hyperlink Create Branch im rechten Bereich des JIRA-Tickets verwenden und wird auf den entsprechenden Dialog in Bitbucket geleitet.

In dem sich öffnenden Dialog zum Erstellen des Branches (s. Abb.2) wird neben dem Quellbrach (master) auch ein Name für den neuen Branch gesetzt. Der vorbelegte Name bietet sich an, da er bereits mit dem Schlüssel des JIRA-Tickets beginnt, was die Voraussetzung für die Verknüpfung von JIRA und Jenkins ist.

Nach der Erstellung des Branches kann er direkt ausgecheckt und bearbeitet werden. Über einen besonders hervorgehobenen Button kann der Branch sogar direkt in SourceTree (graphischer Git-Client von Atlassian) geöffnet werden, insofern es installiert ist (s. Abb.3).

Wurde eine Änderung am Code eingecheckt und gepusht, wird sie vom Jenkins erkannt und ein Build in der entsprechenden Build-Gruppe angestoßen (s. Abb.4).

Das Ergebnis des Jenkins-Builds wird nicht nur an Bitbucket geschickt, wo es auf der Informationsseite des jeweiligen Branches angezeigt wird (s. Abb.5), sondern auch als Kommentar dem entsprechenden JIRA-Ticket hinzugefügt – zusammen mit Links zur Jenkins-Build-Seite und den Commit- und Änderungsinformationen von Bitbucket (s. Abb.6).

Abb.4: Laufender Jenkins-Build. © Sebastian Böhm
Abb.4: Laufender Jenkins-Build. © Sebastian Böhm

Abb.5: Build-Ergebnisse auf der Branch-Informationen-Seite von Bitbucket. © Sebastian Böhm
Abb.5: Build-Ergebnisse auf der Branch-Informationen-Seite von Bitbucket. © Sebastian Böhm
Abb.6: Build-Ergebnisse im Kommentar des JIRA-Tickets. © Sebastian Böhm
Abb.6: Build-Ergebnisse im Kommentar des JIRA-Tickets. © Sebastian Böhm
Abb.7: Alle Commits eines JIRA-Tickets. © Sebastian Böhm
Abb.7: Alle Commits eines JIRA-Tickets. © Sebastian Böhm

Eine Gesamtübersicht aller Commits eines JIRA-Tickets erhält man nach einem Klick auf x commits im Abschnitt Development des JIRA-Tickets (s. Abb.7). Dann öffnet sich ein Popup mit Detail-Informationen wie den Commit-IDs oder den Commit-Kommentaren. Außerdem springt man mit einem Klick auf eine Commit-ID auf die jeweiligen Commit-Informationen von Bitbucket, wo die konkreten Code-Änderungen aufgeführt sind (s. Abb.8).

Ist ein Feature abgeschlossen, müssen alle zugehörigen Änderungen von einem anderen Entwickler, dem Reviewer, eingesehen und geprüft werden. Dafür wird ein sogenannter Pull Request erstellt, welcher den Review und die Integration des Feature-Branches in den Hauptzweig vereint (s. Abb.9). Ein entsprechender Hyperlink dafür ist ebenfalls im JIRA-Ticket enthalten (Klick auf branch, dann Klick auf Create Pull Request) und führt auf den Pull Request - Erstellungsdialog von Bitbucket.

Beim Erstellen des Pull Requests ist es wichtig, dass der Titel ebenfalls mit dem JIRA-Ticket-Schlüssel beginnt (ist bereits so vorbelegt). Es ist auch vordefiniert, in welchen Branch der Pull Request nach einer Freigabe integriert werden soll und der Kommentar enthält eine Liste mit allen Commits des Branches. Als Reviewer ist der definierte Standard-Reviewer ausgewählt.

Nachdem der Pull Request erstellt worden ist, wurde auch das JIRA-Ticket aktualisiert: Es ist nun im Status In Review, dem Reviewer zugewiesen und hat einen entsprechenden Kommentar (s. Abb.10).

Der Reviewer ist nun an der Reihe und muss die Änderungen im Code prüfen und entweder akzeptieren oder abweisen (s. Abb.11). Auf den Dialog zum Bearbeiten des Pull Requests kommt er ebenfalls über einen Link im JIRA-Ticket (Klick auf pull requests, Klick auf offenen Pull Request).

Abb.8: Commit-Informationen von Bitbucket. © Sebastian Böhm
Abb.8: Commit-Informationen von Bitbucket. © Sebastian Böhm
Abb.9: Pull Request - Erstellungsdialog von Bitbucket. © Sebastian Böhm
Abb.9: Pull Request - Erstellungsdialog von Bitbucket. © Sebastian Böhm
Abb.10: Kommentar im JIRA-Ticket nach einem erstellten Pull Request. © Sebastian Böhm
Abb.10: Kommentar im JIRA-Ticket nach einem erstellten Pull Request. © Sebastian Böhm
Abb.11: Bitbucket-Dialog zum Akzeptieren/Abweisen eines Pull Requests. © Sebastian Böhm
Abb.11: Bitbucket-Dialog zum Akzeptieren/Abweisen eines Pull Requests. © Sebastian Böhm
Abb.12: Kommentar im JIRA-Ticket nach einem abgewiesenen Pull Request. © Sebastian Böhm
Abb.12: Kommentar im JIRA-Ticket nach einem abgewiesenen Pull Request. © Sebastian Böhm
Abb.13: Kommentar im JIRA-Ticket nach einem akzeptierten Pull Request. © Sebastian Böhm
Abb.13: Kommentar im JIRA-Ticket nach einem akzeptierten Pull Request. © Sebastian Böhm

Angenommen, irgendetwas passt noch nicht und er weist den Pull Request ab (decline), dann wird das JIRA-Ticket zurück in den Status To Do versetzt und wieder dem ursprünglichen Entwickler zugewiesen. Außerdem wird die Begründung des Reviewers dem JIRA-Ticket als Kommentar hinzugefügt (s. Abb.12).

Nachdem die Kritik des Reviewers eingearbeitet wurde, kann der Entwickler einen neuen Pull Request erstellen. Der Reviewer ist dieses Mal zufrieden und akzeptiert den Pull Request (approve) (s. Abb.13). Das JIRA-Ticket wird nun ebenfalls wieder dem Entwickler zugewiesen, allerdings kommt er in den Zustand Done und erhält den Wert Resolved als Lösung.

Abschließend kann der Entwickler auf der Pull Request-Seite von Bitbucket den Merge, die Integration des Feature-Branches in den Hauptzweig, automatisch per Knopfdruck starten. Dabei führt Bitbucket beide Code-Stände zusammen und committet die Änderungen. Der Jenkins-Server baut dann automatisch den neuen master-Stand mit dem neuen Feature. Nur bei Konflikten im Code oder fehlerhaften Tests muss hier noch manuell nachgearbeitet werden.

Wie man gesehen hat, werden dem Entwickler einige Arbeitsschritte abgenommen und er kann sich auf seinen eigentlichen Job, die Feature-Entwicklung, konzentrieren. Wie man zu dieser Konfiguration der drei Werkzeuge JIRA, Bitbucket und Jenkins kommt, wird im folgenden Tutorial beschrieben.

Installation von JIRA und Jenkins

Für die Installationen von JIRA und Jenkins sind bereits sehr gute Anleitungen im Internet zu finden. Deshalb werden im Folgenden nur nützliche Links mit ein paar zusätzlichen Tipps erwähnt, mit denen man die beiden Server auf einer CentOS-Maschine schnell installieren kann.

Java

Der Jenkins-Server benötigt eine Java-Laufzeitumgebung der Version 7 oder höher, welche separat installiert werden muss, da die Jenkins-Installation nicht darauf prüft [1].

PostgreSQL

JIRA benötigt für den Betrieb eine Datenbank und bringt bereits eine vorkonfigurierte H2-Datenbank mit. Allerdings ist diese Datenbank nur für den Testbetrieb empfohlen. Für die Installation einer PostgreSQL-Datenbank existiert eine Anleitung [2].

Die konkreten Schritte zum Einrichten der Datenbank für JIRA werden (später) in der JIRA-Installationsanleitung im Detail beschrieben. Diese Tipps können beim Einrichten noch hilfreich sein:

  • Beim Erstellen von Datenbank-Benutzern kann zusätzlich der Parameter -P verwendet werden, damit ein Passwort definiert werden kann: createuser -interactive -P.
  • Zum Ändern des Besitzers einer Datenbank kann folgender SQL-Befehl verwendet werden: ALTER DATABASE jiradb OWNER TO jiradbuser;.

JIRA

Für die Installation von JIRA hat Atlassian erstklassige Dokumentationen bereit gestellt [3]. Hier werden nicht nur die Vorraussetzungen aufgeführt, sondern neben der Standard-Installation über den interaktiven Installer auch die manuelle Installation aus einer Archiv-Datei beschrieben. Des Weiteren gibt es detaillierte Anleitungen zum korrekten Einrichten von gängigen Datenbanken.

Installation von Jenkins

Für die Installation von Jenkins findet sich ebenfalls eine Anleitung [4]. Für den gleichzeitigen Betrieb von JIRA und Jenkins auf einer Maschine muss der Port noch geändert werden (beide verwenden standardmäßig 8080). Er kann unter CentOS in der Datei /etc/sysconfig/jenkins in der Variablen JENKINS_PORT definiert werden.

Zusätzlich benötigt man Git (yum install git) und – zumindest für dieses Tutorial – Maven (yum install maven). Beide Werkzeuge müssen im Jenkins noch konfiguriert werden (Manage Jenkins => Global Tool Configuration).

Einrichtung von Bitbucket

Abb.14: Erstellung eines Teams in Bitbucket. © Sebastian Böhm
Abb.14: Erstellung eines Teams in Bitbucket. © Sebastian Böhm
Abb.15: Erstellung eines Repositories in Bitbucket. © Sebastian Böhm
Abb.15: Erstellung eines Repositories in Bitbucket. © Sebastian Böhm
Abb.16: Auswahl der Standard-Reviewers. © Sebastian Böhm
Abb.16: Auswahl der Standard-Reviewers. © Sebastian Böhm

Für den integrierten Workflow werden zwei Bitbucket-Accounts bzw. Benutzer vorausgesetzt, ein Entwickler und ein Reviewer, da Reviews vernünftigerweise nicht von den Bearbeitern selbst durchgeführt werden dürfen.

Nach dem Einloggen in Bitbucket kann ein Entwicklungsteam über das obere Hauptmenü mit Team => Create Team erstellt werden. Neben einem beliebigen Namen muss hier eine in Bitbucket eindeutige Team ID vergeben werden. Als Team-Administrator ist automatisch der angemeldete Benutzer eingetragen. Weitere Benutzer können dem Team über ihr Benutzerkürzel hinzugefügt werden (s. Abb.14).

Im nächsten Schritt muss ein Repository für dieses Team angelegt werden. Alternativ kann auch ein bereits bestehendes Bitbucket oder Github-Repository importiert werden. Beim Erstellen des Repositories wird auch gleichzeitig ein Projekt erstellt (s. Abb.15).

In den Einstellungen des Repositories (Settings => Default Reviewer) wird schließlich einer der beiden Benutzer als Default Reviewer definiert (s. Abb.16).

Bitbucket ist nun so konfiguriert, dass beide Benutzer an dem angelegten Projekt arbeiten können. Sie können Code aus- und einchecken sowie Pull Requests bzw. Reviews erstellen und bearbeiten. Bis jetzt können diese Aktionen allerdings nur direkt in Bitbucket durchgeführt werden.

Einrichtung von JIRA

Abb.17: Basic Software Development - JIRA-Projekttyp. © Sebastian Böhm
Abb.17: Basic Software Development - JIRA-Projekttyp. © Sebastian Böhm
Abb.18: Benötigte JIRA-Benutzer. © Sebastian Böhm
Abb.18: Benötigte JIRA-Benutzer. © Sebastian Böhm

Im nächsten Schritt wird ein JIRA-Projekt angelegt und mit Bitbucket verbunden. Dies ermöglicht den Zugriff auf Bitbucket-Funktionen (z. B. Erstellung von Pull Requests) direkt aus den JIRA-Tickets heraus.

Einrichtung des Projektes

Der Dialog zum Erstellen neuer Projekte kann über die obere Menüleiste aufgerufen werden (oder bei einer frischen Installation direkt über einen Link auf der Hauptseite). Hier wird die Vorlage für Basic Software Development ausgewählt (s. Abb.17). Sie enthält bereits alle für die Softwareentwicklung notwendigen Vorgangstypen wie Verbesserungen, Neue Funktionen, Bugs sowie Aufgaben und Unteraufgaben. Der enthaltene Workflow wirkt zwar im Vergleich zum klassischen JIRA-Workflow etwas primitiv, da es zum Beispiel keine fest definierten Transitionen zwischen den einzelnen Zuständen gibt, reicht aber für die einfache Entwicklung vollkommen aus. Nach dem anschließenden Festlegen eines Projektnamens (z. B. New Idea) und des Kürzels (z. B. NI) ist das Projekt einsatzbereit.

Anschließend müssen beide Benutzer (Entwickler und Reviewer), die dem Team in Bitbucket zugewiesen wurden, auch in JIRA eingerichtet werden. Dabei ist es (später für die Synchronisation) notwendig, dass die Benutzerkürzel in Bitbucket und JIRA übereinstimmen! Außerdem werden zusätzlich zwei weitere Benutzer benötigt: Ein Benutzer, über den die automatisierten JIRA-Aktionen durchgeführt werden (z. B. Automation for JIRA) und ein Benutzer für die Aktionen des Jenkins-Server (z. B. Jenkins) (s. Abb.18).

Benutzer können über das User Management erstellt und konfiguriert werden. Bestimmte Benutzerrollen müssen nicht definiert werden, da standardmäßig alle JIRA-Benutzer an allen Projekten arbeiten können.

Verknüpfung von JIRA und Bitbucket

Für die Verknüpfung von Bitbucket und JIRA stellt Atlassian bereits eine Anleitung zur Verfügung [5]. Hier werden beide Applikationen über OAuth, ein offenes Protokoll zur sicheren API-Autorisierung, miteinander verbunden [6].

Einrichtung von Jenkins

Abb.19: Konfiguration des JIRA Pipeline Steps Plugin. © Sebastian Böhm
Abb.19: Konfiguration des JIRA Pipeline Steps Plugin. © Sebastian Böhm

Installation der Plugins

Bei einer frischen Jenkins-Installation fehlen für die Integration mit Bitbucket und JIRA noch folgende Plugins, die nachinstalliert werden müssen:

  • Mit dem Bitbucket Branch Source Plugin kann man gruppierte Build-Jobs anlegen, welche das definierte Repository nach Branches und Pull Requests durchsuchen und für jeden Zweig automatisch einen eigenen Build-Job anlegen.
  • Das Plugin JIRA Pipeline Steps stellt zahlreiche JIRA-Funktionen zur Verfügung, die im Jenkinsfile (Groovy-Skript) zum Konfigurieren der Build-Pipeline verwendet werden können.

Konfiguration der Plugins

Für die korrekte Funktionalität der JIRA Pipeline Steps muss noch eine zu verwendende JIRA-Instanz konfiguriert werden. Dafür müssen in den Jenkins-Einstellungen unter Manage Jenkins => Configure System im Abschnitt JIRA Steps die URL des JIRA-Servers und die Anmeldedaten des Benutzers Jenkins eingetragen werden. Der Name (hier LOCAL) wird bei den JIRA-Funktionen als Parameter mitgegeben, um die Ziel-Instanz der Funktion zu wählen (s. Abb.19).

Anlegen des Build-Jobs

Bevor ein Jenkins-Job angelegt werden kann, sollte noch ein einfaches Projekt erstellt werden. Die Quellcodes 1 und 2 enthalten ein Beispiel für eine einfache "Hello World"-Java-Anwendung mit Spring Boot, welche mit dem Buildtool Maven gebaut wird.

Quellcode 1: Maven-Konfigurationsdatei (/pom.xml)

<?xml version="l.0" encoding="UTF-8"?>
<project xmglns="http://maven.apache.org/POM/4.0.0" xmmlns:xsi"http://www.w3.org/200l/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.sboehm.demo</groupId>
<artifactId>new-idea</artifactld>
<version>0.0.l-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>l.5.2.RELEASE</version>
<relativePath/>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>l.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

Quellcode 2: "Hello World" - Java-Klasse (/src/main/java/org/sboehm/demo/DemoApplication.java)

package org.sboehm.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);

}

public DemoApplication() {
System.out.printIn ("Hello World! ");
}
}

Außerdem werden die Schritte der Build-Pipeline ebenfalls im Code definiert (Quellcode 3). Hier wird nicht im Detail auf den gesamten Inhalt oder die Syntax eingegangen, zusammenfassend checkt das Groovy-Skript den Quellcode aus dem später im Jenkins-Job definierten GIT-Repository aus, kompiliert ihn und kommentiert das JIRA-Ticket, welches über den Kommentar des zu bauenden Commits verknüpft ist.

Hier ist der Inhalt von stage("Comment on JIRA") interessant: Zunächst wird der Schlüssel des JIRA-Tickets aus dem Namen des zu bauenden Branches extrahiert (man geht davon aus, dass der Branchname mit dem Schlüssel beginnt). Anschließend wird der Kommentartext erstellt, welcher einen Link auf den jeweiligen Jenkins-Job und auf eine Bitbucket-Seite enthält, die die Änderungen des zu bauenden Commits darstellt. Die dafür benötigten Informationen werden aus von Jenkins definierten Variablen (z. B. BRANCH_NAME, JOB_BASE_NAME, BUILD_URL) und Objekten (currentBuild) entnommen. Schließlich wird der Kommentar über die Funktion jiraAddComment an die JIRA-Instanz mit dem Namen LOCAL geschickt.

Quellcode 3: Jenkinsfile (Pipeline-Konfiguration in Groovy)

node {

def mvnHome = tool 'Maven'

stage("Checkout") {
checkout scm
}

stage("Build") {
try {
sh "${mvnHome}/bin/mvn clean install"
currentBuild.result = "success"
} catch(exception) {
currentBuild.result = "failure"
}
}

stage("Comment on JIRA") {
def jiraIssueNumber = getJiraIssueNumber()
if (jiraIssueNumber) {
def comment = "success".equalsIgnoreCase(currentBuild.result) ? "Successfully integrated" : "Integration failed";
comment += " in [${JOB_BASE_NAME} ${BUILD_DISPLAY_NAME}|${BUILD_URL}]:"
comment += getChangesAsComment().replaceAll("BASE_COMMIT_URL", getBaseCommitUrl())
jiraAddComment site: "LOCAL", idOrKey: jiraIssueNumber, comment: comment
}
}

}

def getChangesAsComment() {
def changes = "\n"
for (changeSet in currentBuild.changeSets) {
for (item in changeSet.items) {
changes += "- *Commit ${item.commitId} by ${item.author}*\n${item.msg} ([Details|BASE_COMMIT_URL${item.commitId}])\n"
}
}
return changes
}

def getBaseCommitUrl() {
sh "git config --get remote.origin.url > .git/remote-url"
def gitRemoteUrl = readFile('.git/remote-url').trim()
return gitRemoteUrl.replaceAll("\\.git", "/commits/")
}

@NonCPS
def getJiraIssueNumber() {
def matcher = (BRANCH_NAME =~ "^([A-Za-z]+-\\d)")
return matcher ? matcher[0][1] : null
}
Abb.20: Erstellen eines Jenkins-Jobs. © Sebastian Böhm
Abb.20: Erstellen eines Jenkins-Jobs. © Sebastian Böhm
Abb.21: Konfiguration des Bitbucket Branch Source Plugins. © Sebastian Böhm
Abb.21: Konfiguration des Bitbucket Branch Source Plugins. © Sebastian Böhm
Abb.22: Übersicht des Jenkins-Jobs über alle verfügbaren Branches eines Repositories. © Sebastian Böhm
Abb.22: Übersicht des Jenkins-Jobs über alle verfügbaren Branches eines Repositories. © Sebastian Böhm

Sobald der Code für das Projekt geschrieben, committet und gepusht wurde, kann der Jenkins-Job angelegt werden. Hierfür kommt man unter dem Menüpunkt New Item in den Projekttyp-Auswahldialog, in dem man Bitbucket Team/Project auswählt (s. Abb.20). Dieser Job-Typ holt sich aus einem definierten Bitbucket-Repository alle verfügbaren Branches und baut, sobald irgendwo ein Commit getätigt wurde. Die Build-Schritte werden aus dem gerade erstellten Jenkinsfile entnommen.

Für die Konfiguration des zu verwendenden Bitbucket-Repositories müssen im Einrichtungsdialog des Jenkins-Jobs im Abschnitt Bitbucket Team/Project unter Owner die ID des Bitbucket-Teams, der Name des Repositories und natürlich die Anmeldedaten (Bitbucket-Account) angegeben werden (s. Abb.21).

Sobald die Job-Einstellung gespeichert wurden, fängt der Jenkins an zu bauen. Mit einem Klick auf den eingerichteten Job erscheint eine Liste mit allen gefundenen Repositories (man kann auch mehrere überwachen lassen) und nach einem Klick auf eines der Repositories kommt eine Liste mit allen Zweigen, in denen dann für jeden Commit ein Build angestoßen wird (s. Abb.22).

Nach jedem Build werden die Ergebnisdaten an Bitbucket geschickt, sodass man auch schon direkt auf den Infoseiten der Commits in Bitbucket erkennen kann, ob der Commit erfolgreich integriert werden konnte. Die Weitergabe der Ergebnisse an das jeweilige JIRA-Ticket erfolgt über die JIRA-Pipeline-Funktionen, die im Jenkinsfile definiert wurden. So müssen in den Job-Einstellungen hierfür keine Anpassungen mehr durchgeführt werden.

Einrichten der Automatisierungsregeln

In diesem Abschnitt werden Automatisierungsregeln eingerichtet, die bei bestimmten Aktionen in Bitbucket die entsprechenden JIRA-Tickets aktualisieren: Beim Erstellen von Pull Requests wird das JIRA-Ticket in den Status IN REVIEW versetzt, dem Reviewer zugewiesen und der Kommentar des Pull Requests auch ins JIRA-Ticket eingetragen. Beim Akzeptieren von Pull Requests wird das JIRA-Ticket als DONE markiert und wieder dem ursprünglichen Entwickler zugewiesen. Beim Ablehnen eines Pull Requests bekommt der ursprüngliche Entwickler das JIRA-Ticket im offenen Status TO DO zurück.

Automation For JIRA

Für die Automatisierungsregeln wird das Add-on Automation For JIRA [8] verwendet. Es ermöglicht es, auf verschiedene interne und externe Ereignisse zu reagieren (z. B. Erstellung eines Tickets, Ändern des Status oder der Felder eines Tickets, Aufruf eines Webhooks) und erlaubt diverse Aktionen an den Tickets (z. B. Hinzufügen von Kommentaren, Zuweisen von Bearbeiter, Versenden von E-Mail- oder Slack-Nachrichten, Erstellung neuer Tickets). Des Weiteren können diese Aktionen an bestimmte Bedingungen (z. B. an die Gruppenzugehörigkeit des Bearbeiters) gekoppelt werden.

Im folgenden Teil wird das Erstellen derjenigen Automatisierungsregel im Detail erklärt, die auf erstellte Pull Requests reagiert und den Status und Bearbeiter des betroffenen JIRA-Tickets aktualisiert sowie einen entsprechenden Kommentar hinzufügt. Anschließend werden alle Informationen tabellarisch bereitgestellt, mit denen man nach dem selben Muster auch die Regeln für das Akzeptieren und das Ablehnen von Pull Requests erstellen kann.

Webhook für erstellte Pull Requests

Abb.23: Auswahl einer Vorlage für eine neue Automatisierungsregel. © Sebastian Böhm
Abb.23: Auswahl einer Vorlage für eine neue Automatisierungsregel. © Sebastian Böhm

Die Automatisierungsregeln können in den JIRA-System-Einstellungen über den Menüpunkt Automation rules verwaltet werden. Für neue Regeln werden mehrere Vorlagen angeboten, aus denen die leere Vorlage (Blank rule) ausgewählt wird (s. Abb.23).

Auf der linken Seite des sich öffnenden Dialogs sieht man den Ablauf der Automatisierungsregel. Er besteht aus einem Auslöser (Trigger), mindestens einer Aktion (Component) und optionalen Bedingungen (Condition) (s. Abb.24). Auf der rechten Seite können Eigenschaften der Regel (z. B. Name, betroffene Projekte) bzw. ihrer einzelnen Komponenten (Trigger, Component oder Condition) bearbeitet werden. Als Name wird "PullRequest Created" definiert, die Regel soll nur für das Projekt "New Idea" gelten und von dem JIRA-Benutzer Automation for JIRA ausgeführt werden (Actor). Somit ist später in den JIRA-Tickets klar, welche Aktionen von einer Automatisierungsregel durchgeführt wurden.

Als Auslöser (Trigger) wird Incoming Webhook ausgewählt (s. Abb.25). Webhooks sind Services auf dem JIRA-Server, die es erlauben, auf Ereignisse aus anderen Systemen zu reagieren. Dafür werden sie einem Fremdsystem bekannt gemacht und dort bei einem entsprechenden Ereignis aufgerufen. Dabei können beim Aufruf zusätzliche Informationen mitgegeben werden. In diesem Fall ist das Fremdsystem Bitbucket.

Im Konfigurationsdialog Incoming webhooks wird zum Einen die Adresse des Webhooks angezeigt, die später in Bitbucket registriert wird und zum Anderen werden über einen JQL-Ausdruck (Jira Query Language) die betroffenen JIRA-Tickets selektiert. Es wird hier davon ausgegangen, dass der Titel des Pull Requests mit dem Schlüssel des betroffenen JIRA-Tickets beginnt! Hier wird mit Hilfe der smart-values auf die mitgegebenen Daten des Webhook-Requests zugegriffen, aus denen der Schlüssel mit einem regulären Ausdruck aus dem Titel extrahiert und für die Ticket-Auswahl verwendet wird:

key={{webhookData.pullrequest.title.match("([A-Z]*-\d+)")}} 

Der verwendete smart-value greift dabei auf den Pfad pullrequest => title innerhalb der in JSON formatierten Request-Daten zu. Weitere Informationen (z. B. Syntax und Funktionsumgang) zu den vom Add-on eingeführten smart-values können auf der Hilfeseite eingesehen werden, die man mit dem Link unter dem JQL-Eingabefeld erreicht (s. Abb.26).

Im nächsten Schritt (nach dem Speichern des Auslösers) wird im Regelablauf eine neue Aktion hinzugefügt (Add component) und als Vorlage Transition Issue ausgewählt. Hier wird lediglich der Zielstatus auf In Review gesetzt (s. Abb.27).

Abb.24: Allgemeine Eigenschaften einer Automatisierungsregel. © Sebastian Böhm
Abb.24: Allgemeine Eigenschaften einer Automatisierungsregel. © Sebastian Böhm
Abb.25: Auswahl eines Auslösers (Trigger). © Sebastian Böhm
Abb.25: Auswahl eines Auslösers (Trigger). © Sebastian Böhm
Abb.26: Konfiguration des Auslösers Incoming Webhook. © Sebastian Böhm
Abb.26: Konfiguration des Auslösers Incoming Webhook. © Sebastian Böhm
Abb.27: Konfiguration der Aktion zum Ändern des Ticket-Status auf In Review. © Sebastian Böhm
Abb.27: Konfiguration der Aktion zum Ändern des Ticket-Status auf In Review. © Sebastian Böhm
Abb.28: Konfiguration der Aktion zum Zuweisen des JIRA-Tickets an den Reviewer. © Sebastian Böhm
Abb.28: Konfiguration der Aktion zum Zuweisen des JIRA-Tickets an den Reviewer. © Sebastian Böhm
Abb.29: Konfiguration der Aktion zum Kommentieren des JIRA-Tickets. © Sebastian Böhm
Abb.29: Konfiguration der Aktion zum Kommentieren des JIRA-Tickets. © Sebastian Böhm

Anschließend wird eine Aktion mit der Vorlage Assign issue erstellt, welche den zuzuweisenden Benutzer mit Hilfe der smart-value

{{webhookData.pullrequest.reviewers.first.username}} 

setzt (Auswahl von Smart value in Assign the Issue to), die das Benutzerkürzel des Reviewers aus den Webhook-Request-Daten extrahiert. Diese Aktion ist der Grund dafür, dass die Benutzerkürzel in Bitbucket und JIRA synchron sein müssen (gleicher Benutzer = gleiches Kürzel) (s. Abb.28)!

Schließlich wird noch eine letzte Aktion mit der Vorlage Comment on issue erstellt, in der mit dem smart-value

{{webhookData.pullrequest.author.display_name}} 

der Name des Pull Request-Erstellers und mit

{{webhookData.pullrequest.description}}

der im Pull Request geschriebene Kommentar in das JIRA-Ticket eingetragen werden (s. Abb.29).

Abb.30: Registrierung eines Webhook in einem Bitbucket-Repository. © Sebastian Böhm
Abb.30: Registrierung eines Webhook in einem Bitbucket-Repository. © Sebastian Böhm
Abb.31: Detaillierten Informationen über Webhook-Requests in Bitbucket. © Sebastian Böhm
Abb.31: Detaillierten Informationen über Webhook-Requests in Bitbucket. © Sebastian Böhm

Mit einem letzten Speichern der Aktion und dem finalen Veröffentlichen der Regel (Publish rule) ist sie vollständig eingerichtet und kann nun in Bitbucket registriert werden.

Registrierung des Webhooks in Bitbucket

Für die Registrierung des Webhooks der Automatisierungsregel wird dessen URL benötigt, die aus dem Konfigurationsdialog des Auslösers Incoming webhook kopiert werden kann. In Bitbucket navigiert man zum Einstellungsdialog des verwendeten Repositories und kann dort unter dem Menüpunkt Webhooks einen neuen Webhook hinzufügen. Dabei wählt man als Auslöser (Trigger) nur Pull Request Created aus der angebotenen Liste aus (Radiobutton hinter Triggers auf Choose from a full list of triggers setzen). Mit einem Klick auf Save ist der Webhook aktiviert und kann bereits verwendet werden (s. Abb.30).

Wurde nun ein Webhook durch einen erstellten Pull Request aktiviert, kann man sich den Webhook-Request ansehen, indem man auf View Request des entsprechenden Webhooks im Webhooks-Einstellungsdialog klickt. Hier sieht man alle durchgeführten Anfragen und deren Details (Klick auf View Details): Neben den Antwort-Informationen vom JIRA-Server kann man auch die Request-Daten einschließlich der Payload einsehen (Link Show Request Body) (s. Abb.31). Hier ist die gesamte JSON-Strukur abgebildet, die an den Webhook gesendet wurde (einschließlich der eben verwendeten Pfade pullrequest.title, pullrequest.reviewers.first.username und pullrequest.description). Für das Erstellen eigener Automatisierungsregeln können in dieser Ansicht die Pfade zu den benötigten Informationen schnell gefunden werden.

Weitere Webhooks

Jetzt müssen noch die entsprechenden Webhooks für das Akzeptieren und Ablehnen von Pull Requests erstellt werden. Hier geht man analog mit folgenden Werten vor:

Tabelle 1: Konfigurationsdaten für alle benötigten Webhooks

Dialog Incoming Trigger Transition issue Assign issue Comment on issue Webhook in BitBucket
Feld JQL Destination Statusm Resolution (Add field to form) Assign the issue to Assignee Comment Triggers
PullRequest Created key={{webhook
Data.pullrequest.
title.match("([A-Z]*-\d+)")}}
In Review Smart value {{webhookData.
pullrequest.reviewers.
first.username}}
Pull Request created by {{webhookData.
pullrequest.author.
display_name}}:{{webhookData.pullrequest.description}}
PullRequest Approved -"- Done Done -"- {{webhookData.
pullrequest.author.
username}}
Pull Request approved by {{webhookData.
pullrequest.reviewers.
first.display_name}}
Pull Request => Approved
PullRequest Declined -"- To Do -"- -"- Pull Request declined by {{webhookData.
pullrequest.reviewers.
first.display_name}}:{{webhookData.
pullrequest.reason}}
Pull Request => Declined

Ergebnis und Perspektive

Mit dem eben eingerichteten, integrierten Workflow werden den Entwicklern nun zum Einen Abkürzungen angeboten (Aufruf von Bitbucket-Funktionen und Navigation zu Code-Änderungen direkt aus den JIRA-Tickets heraus) und zum Anderen werden ihnen kleine, aber wichtige und oft vergessene Arbeitsschritte abgenommen (automatische Zustandsänderungen und Benutzerzuweisungen der JIRA-Tickets). Der Projekt-Status bleibt also im Normalfall aktuell.

Allerdings existieren in diesem Workflow noch Freiheiten, die etwa die Automatisierung aushebeln können. Zum Beispiel können die Benutzer die Titel der Pull Requests und die Commit-Kommentare frei wählen. Wird dabei der JIRA-Ticket-Schlüssel nicht als Prefix gesetzt, so können diese Aktionen nicht dem Ticket zugewiesen werden und bleiben von den Automatisierungsregeln unbeachtet. Hier wäre eine vorherige Prüfung der Eingaben hochgradig sinnvoll.

Des Weiteren gibt es viele weitere, teilweise für den tatsächlichen produktiven Einsatz unabdingbare Erweiterungsmöglichkeiten: Ergänzung eines weiteren JIRA-Ticket-Zustandes In Test, automatisches Füllen der Start- und Endzeitpunkts der Implementierung, Erstellung von Sub-Tasks bei mehreren Reviewern, Verhalten bei fehlgeschlagenen Branch-Merges, HipChat- oder Slack-Integration uvm.

Der in diesem Tutorial erstellte Workflow dient also als Grundlage und Ideenquelle für eigene integrierte Workflows. Mit den hier vorgestellten Möglichkeiten, vor Allem mit der Konfiguration der Jenkins-Pipeline über das Jenkinsfile und dem JIRA Add-on Automation for JIRA, können aber viele Funktionen sehr individuell implementiert werden. Es macht hier auf jeden Fall Sinn, dieses umfassende Thema separat von der eigentlichen Entwicklung zu betrachten. Eine saubere Konfiguration der Werkzeuge nimmt sehr viel Zeit in Anspruch, benötigt eine genaue Auseinandersetzung mit den benötigten und gewünschten Prozessen und sollte somit nicht nur nebenbei durchgeführt werden!

Quellen
  1. Anleitung für die Java-Installation auf CentOS
  2. Installation einer PostgreSQL-Datenbank
  3. Installation von JIRA
  4. Installation von Jenkins
  5. Anleitung zur Verfügung von Bitbucket und JIRA
  6. Wikipedia: OAuth
  7. Apache: Buildtool Maven
  8. Automation For JIRA
nach Oben
Autor

Sebastian Böhm

Sebastian Böhm ist Diplom-Ingenieur der Informationstechnik und sein Hauptinteresse gilt Web-Technologien und der Verwendung von Techniken zur Minimierung der Kosten und der Komplexität in der Software-Entwicklug wie...
>> Weiterlesen
botMessage_toctoc_comments_929