Continuous Integration gehört zu meinen Lieblingsthemen. Daher gibt es eine kurze Serie zu diesem Thema. In diesem Teil gibt es nur die theoretischen Grundlagen, damit jedem die Vorteile der später vorgestellten Software verständlich sind.
Continuous Integration:
Integration nennt man den Prozeß, wenn diverse Komponenten einer Software zusammengeführt werden. In der klassischen Softwaretechnik passiert dies häufig vor dem Release weswegen zu diesem vor allem die Fehler auffallen, die in der Kommunikation zwischen den Komponenten auftreten. Daneben ist auch ein typisches Problem, dass die Entwicklungsumgebung von dem Zielsystem abweicht und dadurch unvorhergesehene Fehler auftreten. Wie man sich nun vorstellen kann verwandelt sich die Entwicklungsabteilung zu einem Ameisenhaufen. Alle rennen wild umher, um die Informationen zu sammeln, die zur erfolgreichen Integration notwendig sind. Zum Teil herrscht eine richtige Angst vor dem Integrationszeitpunkt, was den Prozeß nicht vereinfacht und wie wir alle wissen, werden ungeliebte Aufgaben gerne aufgeschoben.
Um diesem Problem zu begegnen gibt es die Continuous Integration (auf deutsch: kontinuierliche Integration). Hierbei wird während der gesamten Laufzeit eines Projekts integriert. Das Motto könnte man mit „geteiltes Leid ist halbes Leid“ umschreiben. Denn wenn man den Prozess in mehrere Tausend kleine Teile zerbricht, so verfliegt die Angst und der endgültige Integrationszeitpunkt lockt den Entwicklern nur ein müdes Lächeln hervor. Damit sich der Aufwand aber nicht exorbitant vergrößert nutzt man eine Software, die diese Integration durchführt und Fehler den entsprechenden Personen meldet. Das sind die CI-Server wie bspw CruiseControl oder Hudson.
Trigger:
Die CI-Server werden üblicherweise an das vorhanden Versionsverwaltungssystem angebunden. Svn ist hier in Unternehmen aktuell sehr beliebt, git oder Mercurial sind aber auf dem Vormarsch. Die CI-Maschine nimmt sich also den Quelltext und führt eine Integration durch. Hierzu muss der Buildprozess automatisierbar sein. Ein automatisierter Buildprozess hat neben der Benutzung durch den CI-Server den Vorteil, dass auch neue Kollegen ohne großes Vorwissen ein lauffähiges System aufsetzen können. Weiter kann dieser Prozess angepasst werden und somit zu einem Releasebuild führen. Abhängig von der Infrastruktur kann man sogar noch ein automatisiertes Deployment anknüpfen. So könnte man von jedem System, dass eine Grundkonfiguration mitbringt ein „One-Click-Deployment“ durchführen – das ist richtig cool. Zurück zum Buildprozeß. Dieser ist automatisiert und wird vom CI-Server genutzt. Es gibt hier 4 verschiedene Trigger, um einen Build anzustoßen.
1. Manuell durch einen Benutzer:
Ein Benutzer klickt auf dem CI-Server das entsprechende Projekt an und dieser rennt los. Und man erhält eine aktuelle Integration. Vorteil ist, dass ein Entwickler einen Builderhält, sobald er diesen benötigt. Nachteil ist ganz klar, dass so die Integration schleifen gelassen werden kann und dann wieder der Pre-CI-Server Zustand eintritt.
2. Zeitlicher Trigger:
Hier ist die Zeit der Verursacher. Jedem Entwickler und Nutzer von Beta-Software sind die sogenannten nightly Builds ein Begriff. Um bestimmte Uhrzeiten wird ein Build gestartet. Vorteil ist hier ganz klar, dass man aufwändige Prozesse in die Nacht verlagern kann zu einer Zeit, in der kein Entwickler tätig ist. Bei verteilten Teams kann man einen Zeitpunkt wählen, an dem wenige Entwickler tätig sind. Das Bauen einer Dokumentation oder Gesamt-Integrationstest sind Kandidaten, die man am besten zeitlich triggert.
3. Versionsverwaltungstrigger:
Das ist der Trigger, den ich immer präferieren würde. Der CI-Server schaut in der Versionskontrolle nach, ob Updates vorhanden sind. Ist dies der Fall wird ein Build angestoßen. Somit hat man mit einer relativ kurzen Latenz (bspw 5 Minuten) eine aktuelle Version des Projekts. Zu beachten ist hierbei, dass sinnvollerweise nur Tests genutzt werden, die in kurzer Zeit durchgeführt werden, da alle paar Minuten ein Build durchgeführt wird.
4. Abhängigkeitstrigger:
Ein Projekt A nutzt ein Projekt B. Wenn in Projekt B eine Änderung durchgeführt wurde und damit Projekt B neugebaut werden muss, hat dies Auswirkungen auf Projekt A. Nachdem B gebaut wurde gibt es einen Rebuild von A, da die Änderungen bei A zu einem Defekt führen könnten. Dies ein ganz normales Verhalten und man sollte die Abhängigkeiten durch ein Abhängigkeitsverwaltungssystem (Dependency Management) organisieren. Bei PHP kann man dazu Pear nutzen, bei Java ist ivy beliebt.
Ich persönlich würde eine Kombination aus 1-4 empfehlen. Ein Releasebuild kann manuell getriggert werden. Normale Builds durch Versionskontrolle und Zeit. Nach einer Änderung in einem genutzten Projekt sollte natürlich alle abhängigen neu gebaut werden.
Continuous everything:
Ein wichtiger Punkt für einen CI-Server sind die Möglichkeiten einige Dinge zentral durchführen zu können. Besonders interessant sind hier Testing und Inspection. Das bedeutet Unittests und weitere Test werden an einem Punkt durchgeführt und so kann eine Gesamttestabdeckung sehr einfach erstellt werden. Inspection ist auch ein wichtiges Thema bei großen Projekten. Da sich alle Entwickler an einen gemeinsamen Coding Style halten sollten, muss dies auch überprüft werden. Hier ist bsp. die Tabs oder Space Einrückung ein heißes Thema. Wie man sich vorstellen kann würde eine Reformatierung von Space auf Tabs eine Differenz in jeder Zeile darstellen. Dadurch würde die Versionskontrolle einiger Vorteile beraubt. Man möchte schließlich sehen, was wirklich geändert wurde.
Interessant ist auch die Continuous Database Integration. Hier wird bei jeder Integration auch die Datenbank neu gebaut. Dadurch kann ein Testsystem schnell aufgesetzt werden. Schließlich möchte man Änderungen an der Datenbank lokal testen ohne Angst zu haben die zentrale Datenbank der Kollegen zu zerstören. Man kann dies sogar soweit treiben, dass man eine Basisdatenbank nutzt und Migrationsskripte auf diese anwendet, um dann zu sehen, ob die neue Version hiermit zusammenspielt. Hier ist es einfach notwendig entsprechende SQL-Skripte zu definieren, diese in der Versionsverwaltung aubzulegen und Targets für den Buildprozess zu definieren.
2 Themen sind noch offen, zum einen Continuous Deployment und zum anderen Contiuous Feedback. Deployment ist eigentlich fast selbst erklärend. Es ist sinnvoll einen Testserver bereitzustellen, auf dem der CI-Server immer die aktuelle Version deployed. Dieser sollte nah an dem eigentlichen Release-System sein, denn damit kann man sicher gehen, dass das spätere Deployment zum Tag X nicht schief geht. Auch ist es wunderbar, wenn man dem Kunden so einen immer aktuellen Stand zum „Spielen“ geben kann. Der Projektleiter kann sich hier auch erfreuen und es ist natürlich für den Entwickler immer eine Freude etwas vollständiges zu sehen.
Das Feedback ist eine super Sache für die gesamte Entwicklungsabteilung. Man kann sich abhängig vom Fehler entsprechende Feedback Varianten vorstellen. Bspw ist die Lavalampe oder ein Ambientdevice eine nette Sache. So kann jeder sehen, wie der aktuelle Zustand des Builds ist. Daneben sind Infos per Mail eigentlich Standard. Hier sieht man den Vorteil der Versionskontrolle. Man kann bei einem fehlschlagenden Build die Entwickler informieren, die Änderungen seit dem letzten stabilen Build eingecheckt haben. Bei kritischen Projekten kann man weiter auch noch Infos per SMS versenden. So kann der Projektleiter informiert werden, wenn ein Build fehlschlägt. Insbesondere vor dem Release ist eine solche Funktion sinnvoll. Mein Buildserver nutzt ein IM-Plugin und informiert mich per Jabber über den akuellen Zustand. Anbindung an Twitter und Co sind inzwischen auch nicht unüblich. So sollte eigentlich jeder Entwickler informiert sein und das über einen Weg, der ihm persönlich am genehmsten ist. Mail Spam mit Meldungen wie „Build stable“ sollte so nicht vorkommen.
Meine Wunschinfrastruktur wäre ein XFD (extreme Feedback Device), das den aktuellen Zustand angibt. Hierfür eine Lampe oder ein Monitor. Fehlgeschlagene Builds werden per Mail versendet. Daneben gibt es einen Jabber-Konferenzraum oder ein IRC-Channel in dem der CI-Bot eine Nachricht bei jedem Build absetzt – unabhängig vom Status.
Im Teil 2 werde ich einen Buildserver vorstellen und eine Anbindung zu PHP beschreiben.