Eine wiederverwendbare Build-Umgebung für kleine und große Projekte

Wieviele Softwareentwickler interessieren sich für Buildskripte (Maven, Gradle, Ant, …)? Oder für Buildinfrastruktur, Build-Jobs auf dem CI-Server? Oder für saubere Git-Historien mit korrekten Namen, E-Mail-Adressen und Verweisen auf den Issue-Tracker in den Commits? Aus unserer Erfahrung will sich kaum jemand mit diesen Themen eingehend beschäftigen. Trotzdem bleiben diese ungeliebten, aber notwendigen Themen im Projekt oft doch an den Entwicklern hängen.
Insofern ergibt sich – insbesondere wenn man mehrere Projekte durchführt – schnell der Wunsch nach Zentralisierung, Vereinheitlichung und Wiederverwendung. Im Sinne eines ganzheitlichen Ansatzes wird also etwas gesucht, was
- Projekte schnell aufsetzen lässt (Buildsystem, Entwicklungsumgebung, CI-Server + Jobs und weitere Infrastruktur),
- Out-of-the-Box für die eigene Infrastruktur vorkonfiguriert ist (Authentisierung, Autorisierung, SSL, SSO, Artefaktrepository, …) und
- Anpassungen an die projektspezifischen Gegebenheiten ohne viel Copy-&-Paste erlaubt.
Als Grundgerüst empfiehlt sich die Wahl eines geeigneten Buildsystems, welches erweiterbar ist und insbesondere eine zentrale Vorkonfiguration ermöglicht. Dieses soll dann die Grundlage für den Build in der IDE und im CI-Server bilden, auch wenn diese beiden Szenarien im Detail oft sehr unterschiedlich sind.
Kern unseres Ansatzes ist das Konstrukt "gebit-build", welches eine aus unserer Sicht sinnvolle Vorauswahl und Vorkonfiguration von Werkzeugen für den Entwicklungsprozess beinhaltet. Primäres Ziel ist es, weniger Aufwand in Technik und Infrastruktur zu investieren und dafür mehr Zeit für die Fachlichkeit zu haben.
Warum Maven als Integrationskomponente?
Bei der Entwicklung von gebit-build wurde ein ausgereiftes und flexibel erweiterbares Buildwerkzeug gesucht. Zur Auswahl standen eigentlich nur Maven und Gradle. Das Gradle-Tooling in der Entwicklungsumgebung Eclipse stand damals noch in den Kinderschuhen. Insbesondere gab es noch überhaupt keine Unterstützung für Web-Projekte, was schließlich den Ausschlag für Maven gegeben hat. Für einige Details wäre Gradle ähnlich gut geeignet; eine zentral bereitgestellte Vorkonfiguration eines Builds ist damit jedoch noch nicht möglich.
Bestandteile von gebit-build
gebit-build ist nichts, was man herunterladen oder installieren kann. Es hat auch keinen scharf eingegrenzten Scope, ganz im Gegenteil. Die beste Beschreibung ist vielleicht diese: gebit-build ist alles, was dem Entwickler hilft, sich auf die Entwicklung der Software selbst zu konzentrieren.
Im aktuellen Stand fassen wir folgendes zu gebit-build zusammen:
- Vorkonfigurierte Eclipse-IDE mit allen benötigten Plugins,
- Installer für Installation der Eclipse-IDE samt Projekt-Checkout,
- vorkonfigurierter Maven-Build
- toolunterstützter Entwicklungsworkflow ähnlich gitflow,
- (halb-)automatisiert aufgesetzter CI-Server (Jenkins),
- automatisiert aufgesetzte CI-Jobs,
- Signierung von Artefakten,
- Deployment in Artefaktrepository,
- Sicherstellung der Einhaltung von Richtlinien (Code Qualität, Formatierung, git commits) und
- sinnvolle Vorgaben für die Struktur und Benennung von Projekten/Modulen/Artefakten.
Dabei setzt gebit-build konsequent auf Open-Source-Werkzeuge, um die Anpassbarkeit an eigene Bedürfnisse sicherzustellen. Es gibt nur wenige Ausnahmen, wie bspw. der Issue-Tracker, der sich jedoch durch eine offene API in das Gesamtkonstrukt einbinden lässt. Im Folgenden werden die Bestandteile vorgestellt und ihr Zusammenspiel verdeutlicht.
Ein Archetype für jeden Fall
"Das Aufsetzen eines neuen Projekts ist rein technisch anspruchsvoll. Eine Struktur muss festgelegt, das Repository organisiert, der Build-Server eingerichtet werden. Jedes der Tools bringt eine eigene Konfiguration mit, die vom Team durchdrungen, beherrscht und angewendet werden muss."
Für das Bootstrapping eines neuen Projektes bietet gebit-build einige sog. "Maven Archetypes" (Vorlagen). Damit reicht eine kurze Maven-Kommandozeile, um bspw. ein Multi-Module-Projekt aufzusetzen oder ein neues Modul zu einem bestehenden Projekt hinzuzufügen. Die Vorlagen können Platzhalter wie z. B. Projektname, Projekt-ID im Issue-Tracker, Nexus- oder git-Repository enthalten, welche dann beim Ausführen interaktiv belegt werden und so die gewünschte Projektkonfiguration ergeben. Das mühsame händische Aufsetzen und Konfigurieren entfällt hiermit. Selbst das Grundgerüst für eine projektspezifische Eclipse-IDE und die automatische Konfiguration von Jenkins-Jobs wird durch das gebit-build Projekt-Archetype erledigt.
Oomph oder Onboarding auf einfache Art
"Wer kennt das nicht? Seitenweises Lesen von meist nicht mehr ganz aktueller Dokumentation, um die Entwicklungsumgebung zum Laufen zu bringen. Für neue Projekte schon schwierig, für neu in bestehende Projekte kommende Entwickler in vielen Fällen eine unlösbare Aufgabe, die allzuoft in von Kollegen kopierten Entwicklungsumgebungen resultiert."
Eclipse bietet bereits ein mächtiges Werkzeug für diesen Zweck, den konfigurierbaren Eclipse-Installer Oomph. Viele Entwickler kennen Oomph als Eclipse-eigenen Installer, doch die wenigsten wissen, dass mit Hilfe von Oomph auch die Provisionierung einer eigenen projektspezifischen IDE möglich ist [1]. Das Setup definiert dabei
- produktspezifische Einstellungen wie die Eclipse-Version oder die zu verwendenden Plugins sowie
- projektspezifische Einstellungen wie die zu klonenden Repositories oder Code-Formatter-Regeln.
Leider ist das Oomph-Setup bisher nur rudimentär dokumentiert und zudem initial aufwändig zu erstellen.
gebit-build vereinfacht den Umgang mit Oomph. Es wird ein zentral gepflegtes Produkt mit allen unterstützten Eclipse-Versionen inkl. aller gebit-build Basisplugins zur Verfügung gestellt. Updates daran werden also automatisch an alle ausgerollt.
Im projektspezifischen Teil werden alle zusätzlichen, für das Projekt benötigten Plugins und Einstellungen gepflegt. Das projektspezifische Oomph-Setup entsteht automatisch beim Erzeugen des Projekts per Maven-Archetypes und kann anschließend auf die speziellen Projektbedürfnisse justiert werden. So lässt sich sehr einfach eine eigene Eclipse-Distribution für das Projekt provisionieren, aktuell halten und versionieren.
Eclipse Oomph hält die Entwicklungsumgebung über die gesamte Projektlaufzeit automatisch aktuell, da Änderungen getrackt und lokal bei jedem Entwickler umgesetzt werden. Dies umfasst:
- Plugins auf Stand zu halten,
- den automatischen Import von neuen Projekten in den Workspace,
- Einstellungen zur Code-Formatierung und Source-Code-Validierung,
- die Definition dynamischer Working-Sets zur Workspace-Organisation sowie
- die projekt- und branchspezifische Kennzeichnung, um die Arbeit von Entwicklern in verschiedenen Projekten oder mit unterschiedlichen Branches zu erleichtern.
Aufgerufen wird der Oomph-Installer üblicherweise mit einer Redirection-URL, hinter der sich das Verzeichnis verbirgt, das das eigene Setup enthält. Dies kann ein lokales Verzeichnis sein, sinnvoller ist jedoch eine URL in einem Repository.
eclipse-inst.exe -vmargs "-Doomph.redirection.setups=index:/-> myowngit.mycompany.de/myproject/setupdir/"
Für gebit-build wurde der Oomph-Installer soweit angepasst, dass die für den Entwickler verfügbaren Projekte und deren Branches direkt aus dem gitlab ausgelesen werden, so dass das Angeben der Redirection-URL entfällt. Hierbei werden auch die Berechtigungen des jeweiligen Entwicklers berücksichtigt, d. h. jeder bekommt nur die Projekte zu sehen, auf die er zugreifen kann.
gitflow – Versionen und Branches im Griff
"Der gitflow-Prozess ist eine sinnvolle Art, im Projekt zu arbeiten. Das Erstellen von Branches für Features ist mit git kein Problem mehr. Doch der ganze Prozess erfordert noch viele manuelle Schritte, z. B. beim Mergen auf den Master, das einiges Know-how im Umgang mit git erfordern."
Mit git als Versionsverwaltung sind unterschiedlichste Workflows und Arbeitsweisen umsetzbar. Diese Flexibilität möchte man sich einerseits zu Nutzen machen, um die bestmögliche Arbeitsweise für das Projekt zu ermöglichen. Andererseits sollen sich auch alle Projektmiglieder an die gewählte Vorgehensweise halten, was bspw. den Inhalt von Commit-Messages angeht, die Namensgebung von Branches und vor allem den Umgang mit Rebase und Merges von Feature-Branches. Bei größeren Entwicklerteams lohnt es sich, die typischen Arbeitsschritte projektspezifisch zu automatisieren. Als Beispiel könnten im Projekt folgende Vorgaben definiert sein:
- Feature-Branches sollen während der Entwicklung immer per Rebase auf den aktuellen Stand vom master gebracht, also nicht gemergt werden und
- nach Fertigstellung soll ein Feature-Branch immer per non-fast-forward Commit in den master-Branch gemergt werden.
Dann sind das eine ganze Menge auszuführender Schritte:
- Überprüfung des lokalen Repositories auf lokale Änderungen,
- ggf. committen,
- git pull,
- Überprüfung, ob alles noch kompiliert (und ggf. funktioniert),
- in den Master-Branch wechseln,
- git merge --no-ff [feature-branch],
- git push und
- alten Feature-Branch löschen.
All das lässt sich auch mit einem Maven-Aufruf automatisieren:
mvn flow:feature-finish
Der Aufruf nutzt eine für gebit-build angepasste Variante des gitflow-Plugins für Maven [2]. Als Erweiterung zum Original-gitflow-Prozess wird hier u. a. mehr als nur ein Zielbranch unterstützt, damit mehrere Produktversionen in Maintenance-Branches parallel gepflegt werden können.
Wenn dann auch noch automatisch der Jenkins-Job für den Feature-Branch abgeräumt wird, hat man sich eine Menge Arbeit gespart.
Jenkins oder: wie Builds gesät werden
"Für ein Projekt braucht man ja nicht nur einen einzigen Build-Job, sondern man möchte gern unterschiedliche Jobs für Nightly-, Integrations- und Release-Builds pflegen. Werden zudem Erweiterungen, z. B. für unterschiedliche Kunden eines Produkts, separat gebaut und Jobs für verschiedene Versionen und Feature-Branches vorgehalten, so verliert man schnell den Überblick. Eine kleine Änderung wirkt sich im schlimmsten Fall auf alle Build-Jobs aus, die manuell angepasst werden."
Zum Aufsetzen eines Buildservers stellt gebit-build einen vorkonfigurierten Jenkins-Docker-Container bereit, der zunächst einmal einen einzigen "Seed-Creator"-Job enthält. Die alleinige Aufgabe dieses Jobs ist es, mit Hilfe einiger Benutzereingaben einen weiteren sog. Seed-Job für das gewünschte Projekt zu erstellen. Ein Seed-Job ist ein Job, der weitere Jobs mit Hilfe einer Job-Beschreibungssprache, der Jenkins Job-DSL, erzeugt [3]. Der Seed-Job erzeugt dann also alle gewünschten konkreten Jobs für das Projekt.
Grundidee dieses zweistufigen Prozesses ist, dass für ein Projekt in der Regel mehr als ein Build-Job angelegt werden muss, so gibt es bspw. Nightly-, Integration- und Release-Jobs, und das nicht nur für einen Branch, sondern meist auch für alle Maintenance- und Feature-Branches. Für deren Erstellung und Aktualisierung ist der Seed-Job verantwortlich. Der benötigt allerdings die Information, für welches Projekt überhaupt Jobs erstellt werden sollen (Repository, Namensgebung, etc.). Und genau für diese Parametrisierung ist der Seed-Creator-Job da. Er ist das von gebit-build bereitgestellte, universelle Werkzeug, um mit Hilfe einiger weniger Parameter alle möglichen Arten von projektspezifischen Seed-Jobs anlegen zu können.
Der Seed-Job erstellt Maven-Jobs, die die typischen Bedürfnisse abdecken. Er schaut im Projektrepository, aber auch nach weiteren Parametrisierungen und nach Job-DSL-Skripten, die die Joberstellung beeinflussen können und so vom Standard abweichende Jobs ermöglichen. Vorgaben dazu werden bereits durch den Maven-Archetype bereitgestellt.
Listing 1: Die in Groovy implementierten Job-DSL-Beispiele zeigen das Hinzufügen eines SCM-Triggers und die Konfiguration des Docker-Plugins für einen Jenkins-Job.
/**
* Add a trigger to poll SCM regularly to trigger new builds.
*Alternatively a commit hook will trigger the build, too.
* @param ignorePostCommitHooksFlag true to ignore post
* commit hooks for triggering (false by default).
*/
protected void addChangeTrigger(boolean ignorePostCommitHooksFlag = false) {
job.with {
triggers {
scmTrigger {
scmpoll_spec('H * * * *')
ignorePostCommitHooks(ignorePostCommitHooksFlag)
}
}
}
}
/**
* Configure docker for the build. If a label is configured require it for the job
* and configure the docker plugin.
*/
@Override
protected void configureDocker() {
super.configureDocker()
if (jobProperties.dockerLabels?.trim()) {
job.label(jobProperties.dockerLabels)
}

Damit mehrere Build-Jobs parallel laufen können, müssen diese entkoppelt sein, insbesondere im Hinblick auf verwendete Ressourcen, bspw. Datenbanken im Test. Natürlich könnte man hier mit parametrisierten Ports o. ä. arbeiten, doch ist dies weder praktikabel noch skaliert es gut.
Besser ist es, jedem Build-Job seine privaten Ressourcen zur Verfügung zu stellen. Dazu wird der eigentliche Build in einen Docker-Container, den sogenannten Jenkins Docker-Slave, ausgelagert. Das Docker-Image, das der gebit-build-Jenkins mitliefert, ist bereits vorkonfiguriert, kann aber auch als Basis für projektspezifische Build-Slaves dienen.
Kontrolle ist besser
"Die git-Historie sollte immer vollständig sein. Außerdem sollte es eine Nachverfolgbarkeit geben, warum welche Artefakte erstellt wurden."
Ein saubere, lesbare git-Historie ist für ein Projekt von großem Wert. So lässt sich sehr einfach nachvollziehen, wer wann welche Artefakte angelegt oder geändert hat, aber auch eine Zuordnung von Artefakten zur Spezifikation ist mit gebit-build möglich.
Zunächst bedient sich gebit-build diverser Commit-Hooks, um eine saubere Historie im Repository sicherzustellen. So werden unter anderem folgende Eigenschaften eines Commits geprüft:
- Vollständiger Name des Committers angegeben?
- Issue-ID angegeben?
- Commit-Message angegeben?
Zusätzlich ist das Repository für ein Projekt bereits so vorkonfiguriert, dass ein Forced-Push nur für Feature-Branches, nicht aber für den Master und Maintenance-Branches erlaubt ist. Dazu ist ein geeigneter Repository-Manager für git notwendig, bspw. gitlab, wie es gebit-build verwendet. Die projektübergreifend einheitliche Benennung und Ablage von Branches im git ist eine weitere Grundvoraussetzung, um solch globale Einstellungen für alle Projekte einfach vornehmen zu können.
Doch wie stellt man die Nachverfolgbarkeit von Artefakten sicher? Am einfachsten durch die Anbindung des Issue-Trackers an das Repository. Entsprechende Plugins tracken aufgrund der in den Commits angegebenen Issue-IDs alle Commits zu einem Issue und visualisieren diese an jedem Issue inklusive direktem Link auf die Commits.
gebit-build geht noch einen Schritt weiter und bindet zudem das eigene Requirementswerkzeug an den Issue-Tracker, mit dem Anforderungsdokumente im JIRA gespiegelt werden können und Statusupdates im JIRA optional wieder in das Requirementswerkzeug zurückgeführt werden.
Kommunikation, Dokumentation, Reporting

Zur Kommunikation wird bei gebit-build aktuell Mattermost eingesetzt, ein webbasiertes Tool (ähnlich Slack), das on-premise selbst gehostet werden kann. Eingebunden ist Mattermost in die Build-Umgebung durch die Möglichkeit, Jenkins-Nachrichten nicht nur per Mail zu verschicken, sondern direkt in entsprechende Mattermost-Channels zu pushen, bspw. im Fall eines fehlgeschlagenen Builds oder eines neuen Releases.
Zu den essentiellen Artefakten einer Softwareentwicklung gehören nicht nur Code-Artefakte, sondern auch Dokumentation. Für einige dieser Dokumente bietet gebit-build ebenfalls Unterstützung. So lassen sich im Wiki (hier Confluence) Release-Notes für eine Softwareversion automatisch aus den dazugehörenden Issues erzeugen. Ein weiteres Beispiel ist die automatisch generierte Maven-Site mit Javadoc, Maven-Build-Dokumentation, Lizenzreports, etc., die versioniert für alle Branches eines Softwareprojekts erzeugt werden kann.
Rechteverwaltung
"Auf ein Projekt haben nur bestimmte Mitglieder Zugriff. Für jedes Werkzeug Listen von Mitgliedern oder Gruppen zu pflegen, ist mühselig und fehleranfällig."
In Softwareentwicklungsprojekten werden meist die gleichen oder ähnliche Rollen eingenommen, die sich jeweils in unterschiedlichen Zugriffsberechtigungen in den verwendeten Werkzeugen widerspiegeln. Es gibt in der Regel mindestens
- "Projekt-Admins", die verwaltende Tätigkeiten durchführen können, wie z. B. neue Benutzer ins Team aufnehmen, direkten Zugriff auf die Docker-Umgebung der Build-Jobs haben oder notfalls die git-Historie umschreiben können (forced push).
- "Entwickler", die Issues abarbeiten, ins git pushen, Wiki-Dokumentation schreiben, etc.
- "Benutzer", die Issues nur erstellen und kommentieren, aber nicht lösen dürfen und nur lesenden Zugriff auf Dokumentation und ggf. Quellcode haben.
Oftmals gibt es noch weitere Rollen, wie z. B. Entwickler auf Kundenseite oder Tester, für die wiederum leicht andere Berechtigungen gelten.
Damit all das nicht jedes Mal wieder projektspezifisch neu erfunden und individuell konfiguriert werden muss, verwendet gebit-build ein zentrales Active Directory (AD), in dem sämtliche Benutzer gepflegt werden. Für jedes Projekt wird eine (Unter-)OU (Organizational Unit) angelegt, in der die Projektrollen "project-admins", "project-devs", "project-users", etc. als AD-Gruppen angelegt werden. Die project-admins erhalten Verwaltungszugriff auf die eigene OU, so dass sie die Gruppenzugehörigkeiten selbst pflegen können. Die Gruppen folgen einer Benennungskonvention, wodurch sie projektübergreifend einheitlich sind. Die dezentrale Verwaltung durch die project-admins erspart Verzögerungen, die durch den Kommunikationsaufwand einer zentralen Verwaltung entstünden.
Die Projektrollen werden in allen beteiligten Werkzeugen (JIRA, gitlab, Mattermost, …) an Berechtigungen gekoppelt. Damit ist gewährleistet, dass ein neuer Mitarbeiter, der z. B. in eine "project-devs"-Gruppe gesteckt wird, automatisch Zugriff auf die nötigen Ressourcen in allen Werkzeugen erhält.
Vorkonfigurierter Maven Build
"Der Umgang mit Maven ist nicht ganz einfach. Oft ist man als Entwickler gezwungen, immer und immer wieder die gleichen Konfigurationen vorzunehmen. Die pom-Dateien werden schnell unübersichtlich und bei Änderungen müssen oft sehr viele Dateien angefasst werden."
Nachdem mit Maven-Archetypes bereits das Grundgerüst des Projektes inkl. CI-Job und Eclipse-IDE aufgesetzt wurde, stellt sich noch die Frage nach dem eigentlichen "Build". Maven ist zwar mächtig, aber auch komplex und die XML-Wüste an Konfiguration für die vielen Plugins will nicht für jedes Projekt neu erfunden werden. Letztendlich geht es immer wieder um das Gleiche:
- Projektlayout,
- Übersetzen von (Java-) Code,
- Erstellen von jars, wars, ears, rpm-Files, Docker-Images, etc.,
- Signieren der Artefakte,
- Ausführen von Unit-Tests,
- Ausführen von Integrations- oder Akzeptanztests,
- Aufzeichnen der Testabdeckung,
- Durchführen von statischer Code-Analyse,
- Erstellen von Dokumentation und Reports und
- Veröffentlichen der Artefakte.
Die Funktionsweise von Maven erlaubt es normalerweise leider nicht, solche Plugin-Konfigurationen einmal zu definieren und an verschiedenen Stellen zu verwenden. Es gibt jedoch eine Maven-Erweiterung, welche genau dies ermöglicht, die Maven Tiles [4,5]. Damit ist es möglich, in einem generischen gebit-build "parent-pom" einzelne Konfigurationsschnipsel ("Tiles") vorzudefinieren, die projektspezifisch referenziert, optional parametrisiert und ergänzt werden. Ein Tile ist dabei nichts weiter als ein Maven-Modul mit einer pom.xml mit dem packaging-Typ "tile" und einer zusätzlichen Datei tile.xml. Diese enthält dann die Konfiguration, die per Referenzierung überall eingebettet werden kann. Inhaltlich wird eine Teilmenge der pom.xml unterstützt, bspw. <properties>, <build>, <dependencies>, <reporting>, <profiles>.
Im Projekt wird dann lediglich gebit-build als parent-pom eingetragen, und in den einzelnen Maven-Modulen des Projekts wird definiert, welche Tiles zum Einsatz kommen sollen. Die Tiles sind auch komponierbar, so dass sich größere wiederverwendbare Einheiten bilden lassen.
Die folgende Auflisting zeigt einen Auszug der Optionen in gebit-build, die derzeit per Tiles konfiguriert werden können. Manche Tools, wie z. B. Checkstyle oder Spotbugs benötigen weitere Konfiguration, die nicht per pom.xml (bzw. tile.xml) bereitgestellt werden kann. In dem Fall stellt gebit-build eine sinnvolle Default-Konfiguration bereit, die an vordefinierter Stelle im Projekt überschrieben werden kann.
Übersicht über die bereitgestellten Tiles
Packaging
Jar, War, Ear, OSGi-Bundle, Source-Packaging, Signieren, Assembly, Wildfly-Distribution
Statische Analyse
Checkstyle, Spotbugs/Findbugs, Maven-Enforcer (java-version, banned-dependencies, no-snapshot-dependencies, managed-dependencies, license, …)
Testing
Junit, FitNesse, Integrity, Coverage (JaCoCo)
Reporting
Javadoc, License, Maven-Site
Verschiedenes
Vaadin (compile, compile-theme, update-widgetset, …)
Alle Tiles sind so an die Maven-Lifecycle-Phasen gebunden, damit sie miteinander harmonieren. Die in manchen Tiles definierten Properties können jeweils bei der Verwendung angepasst werden. Einige Tiles sind per default nur in einem bestimmten Maven-Profil aktiv.
Listing 2: Tile zum Signieren mit Hilfe eines Signierservers. Signiert wird per default lediglich, wenn das "codesign"-Profil aktiv ist, z. B. auf dem Buildserver.
<?xml version="1.0" encoding="UTF-8"?>
<project>
<properties>
<eclipse.jarsigner.version>1.2.0-gebit2</eclipse.jarsigner.version>
<codesign.url>https://sign.domain.example</codesign.url>
<codesign.serverId>${project.repository.releases}</codesign.serverId>
<jarsigner.url>${codesign.url}/jar-signing-service</jarsigner.url>
<jarsigner.skip>true</jarsigner.skip>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.cbi</groupId>
<artifactId>eclipse-jarsigner-plugin</artifactId>
<version>${eclipse.jarsigner.version}</version>
<executions>
<execution>
<id>sign</id>
<goals>
<goal>sign</goal>
</goals>
<phase>package</phase>
<configuration>
<signerUrl>${jarsigner.url}</signerUrl>
<skip>${jarsigner.skip}</skip>
<serverId>${codesign.serverId}</serverId>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>codesign</id>
<properties>
<jarsigner.skip>false</jarsigner.skip>
</properties>
</profile>
</profiles>
</project>
Listing 3: Verwendung des Signier-Tiles (s. Listing 3) in einer pom. Es ist nur noch die Referenz auf das Tile notwendig und ggf. die Belegung von Variablen.
<?xml version="1.0" encoding="UTF-8"?>
<project>
<tiles>
<tile>de.gebit.build.tile:enforce:@version.build@</tile>
<tile>de.gebit.build.tile:enforce-maven:@version.build@</tile>
<tile>de.gebit.build.tile:enforce-managed-deps:@version.build@</tile>
<tile>de.gebit.build.tile:enforce-module-in-parent:@version.build@</tile>
<tile>de.gebit.build.tile:javac:@version.build@</tile>
<tile>de.gebit.build.tile:encoding:@version.build@</tile>
<tile>de.gebit.build.tile:checkstyle:@version.build@</tile>
<tile>de.gebit.build.tile:findbugs:@version.build@</tile>
</tiles>
</project>
Listing 4: Ein Composite-Tile, das mehrere Konfigurationsschnipsel zusammenfasst. Der Platzhalter @version.build@ wird durch den Inhalt der Property version.build ersetzt. Es kann auch explizit eine Versionsnummer oder ein Versionsbereich angegeben werden.
<build>
<plugins>
<plugin>
<groupId>io.repaint.maven</groupId>
<artifactId>tiles-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<tiles>
<tile>
de.gebit.build.tile:jarsigner:@version.build@
</tile>
</tiles>
</configuration>
</plugin>
</plugins>
</build>
Fazit
Seit Anfang 2016 wurden für die Entwicklung von gebit-build inklusive Support für die Projekte bis Stand Januar 2019 ungefähr 5 Personenjahre aufgewendet. Dabei wurden viele existierende Werkzeuge angepasst, erweitert und optimiert, bspw. eine Reihe von Maven-Plugins, die Eclipse-Maven-Integration (m2e), eine Reihe von Jenkins-Plugins oder die JIRA-GitLab-Integration. All diese Änderungen sind entweder "upstream" eingepflegt worden oder stehen frei zur Verfügung [6].
Ebenfalls entstanden ist in der Zeit die dazugehörige Infrastruktur, wie die Docker-basierte Ausführungsumgebung für Jenkins und Build-Slaves sowie deren Umstellung auf eine private, erweiterbare Kubernetes-Cloud.
Produktiv verwendet wird gebit-build seit Mitte 2016 von mittlerweile weit über 100 Entwicklern in unzähligen kleinen und einer Reihe von großen Projekten. Auch wenn den Entwicklern nicht vollständig Kenntnisse über Maven abgenommen werden, so bietet das parent-pom mit den vielen Tiles ein gutes Grundgerüst, mit dem viel XML-Copy-&-Paste vermieden und viel Aufwand gespart wird. Das Aufsetzen und Pflegen einer eigenen IDE ist kein unmögliches Unterfangen mehr. Die Basis-IDE wird bereits als Oomph-Setup vorgegeben. Projektspezifische Details können relativ einfach als Delta dazu hinterlegt werden. Genauso wird beim Aufsetzen von Jenkins-Jobs verfahren. Die Verwendung von Maven als gitflow-Frontend umschifft viele Fehlerquellen beim Umgang mit Featurebranches und Releases.
Alles in Allem bleibt den Entwicklerinnen und Entwicklern mehr Zeit für Design, Implementierung und Testen. Ablenkung durch periphere Probleme wird ihnen größtenteils erspart. Voraussetzung für den Zeitgewinn ist eine stabile Infrastruktur, denn je mehr automatisiert wird, desto öfter werden Builds gestartet, wird released, archiviert, signiert, etc. Mit gebit-build haben wir solch eine Infrastruktur zur Verfügung gestellt, die große Akzeptanz gefunden hat.
Aufgrund der guten Erfahrungen, die wir im Projektbetrieb mit gebit-build gemacht haben, können wir – trotz der zunächst hoch erscheinenden initialen Aufwände – auch anderen Softwarehäusern empfehlen, eine entsprechende Infrastruktur aufzusetzen und zu betreiben. Die Investition amortisiert sich schnell durch die in den Projekten gesparten Aufwände und vermiedenen Reibungsverluste. Als Bonus werden Entwickler glücklich gemacht, indem sie sich kaum noch um das Buildsystem kümmern müssen.
- Eclipse Oomph
- Github: GEBIT/gitflow-maven-plugin
- Github: jenkinsci/job-dsl-plugin
- Github: maven-tiles
- Github: GEBIT/maven-tiles
- Github: GEBIT
Neuen Kommentar schreiben