Who broke the build – Teil II

VS_BuildManagementIm ersten Teil der Serie „Who broke the Build“ habe ich beschrieben, was mich dazu bewogen hat ein Tool zu entwickeln, welches die Ergebnisse eines Nightly-Builds visualisiert und per Mail an alle Entwickler verschickt. In Teil zwei werde ich Euch erklären, wie genau die Applikation funktioniert und Euch ein paar ausgewählte Code Snippets dazu zeigen.

Der Build Notification Service

Im Kern ist der Build Notification Service eine .NET Konsolenapplikation, welche die TFS-Client API verwendet, um eine Verbindung zum Team Foundation Server herzustellen. Der Service verwendet das TFS-Client-Object-Model, um auf die Build Information eines TFS Team Projekts zugreifen zu können. Der Dienst liest alle beendeten Build Definitionen der letzten 24 Stunden aus und verschickt deren Ergebnis als Email im html-Format über die Exchange Web Services an alle Mitglieder einer Mailing-Liste.

Who broke the build - Mailing List

Eine Verbindung zum TFS aufbauen

Um die gewünschten Informationen des TFS Build-Systems zu erhalten, muss der „Build Service“ des Team Foundation Servers angefragt werden. Um diesen Dienst anzusprechen wird eine Instanz der TFSTeamProjectCollection Klasse benötigt, welche die GetService(Type)-Methode implementiert, über die wiederum eine Instanz von IBuildServer geladen werden kann.

// Lade Konfigurations- und Verbindungsinformationen
Config config = LoadConfigInformation();
Uri uri = new Uri(config.Url);
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(uri);
IBuildServer buildService = tfs.GetService(typeof(IBuildServer)) as IBuildServer;

Alle notwendigen Parameter, wie z.B. die Collection-Url oder der Team Projekt Name, werden aus einer Konfigurationsdatei ausgelesen und als die entsprechenden Parameter übergeben.

Build Details abfragen

Wenn der „Build Service“ des Team Foundation Server erfolgreich geladen werden konnte, lässt sich mit Hilfe einer Build Detail Spezifikation IBuildDetailSpec die Auswahl der zu ladenden Build-Ergebnisse einschränken. In der Build Detail Spezifikation wird beispielsweise angegeben in welchem Team Projekt Build Definitionen gesucht, wie viele Builds pro Definition abgefragt und wie das erwartete Ergebnis sortiert werden soll.

Besonders wichtig an dieser Stelle ist der Parameter InformationTypes der Build Detail Spezifikation. Durch diesen Parameter kann die Informationstiefe der Abfrage gesteuert werden, um nicht benötigte Information im Vorfeld auszufiltern und somit die Antwortzeit zu reduzieren. Durch das explizite Setzen von NULL, werden weder assoziierte Work Items, noch assoziierte Changesets oder Error Details über den Build Service ausgelesen, wodurch die Response-Zeit der QueryBuilds-Methode drastisch reduziert wird.

// Erstelle IBuildDetailSpec, um die Antwort der Build-Abfrage einzuschränken
// Maximal einen Build per Definition
// Sortiert nach FinishTime
// Keine zusätzlichen Informationen laden (z.B. Error Details, etc.)
// Nur Builds aus den letzten 24 Stunden
 
IBuildDetailSpec buildDetailSpec = buildService.CreateBuildDetailSpec(config.TeamProject"*");
 
buildDetailSpec.MaxBuildsPerDefinition = 1;
buildDetailSpec.QueryOrder = BuildQueryOrder.FinishTimeDescending;
buildDetailSpec.InformationTypes = null;
buildDetailSpec.MinFinishTime = DateTime.Now.AddHours(-24.0);
 
var htmlBody = string.Empty;
var buildQueryResult = buildService.QueryBuilds(buildDetailSpec);

Verarbeiten der Build Informationen

Die Ergebnisse der Abfrage werden im Array buildQueryResult.Builds vom Typ IBuildDetail hinterlegt und werden zur weiteren Verwendung der Reihe nach ausgewertet und in ein Html-Template eingefügt. Die zum Einsatz kommende Html-Vorlage ist vergleichsweise primitiv und sieht wie folgt aus:

<b>{1}</b> Start Time: End Time: <a href=“{4}“>Details…</a>   {2} {3}
<table width=“400″>
<tbody>
<tr>
<td width=“60″>
<img alt=““ src=“{0}“ width=“50″ height=“50″ />
</td>
</tr>
</tbody>
</table>

Jeder einzelne Platzhalter wird durch einen Wert aus dem Array der Build-Ergebnisse ersetzt. Diese Prozedur wird so oft durchlaufen, wie es abgeschlossene Build Definitionen im Array gibt. Wenn alle Teil-Html-Vorlagen ausgefüllt sind, werden sie in eine große Html-Vorlage zusammengefügt und anschließend als Email verschickt.

Verwenden der Exchange Web Services

Die Emails des Build Notification Service werden über die managed API der Exchange Web Services (EWS) verschickt. Die EWS bieten dafür ein relativ einfaches Objektmodell, um Nachrichten zu verschicken. Für detaillierte Informationen zu den Exchange Web Services empfiehlt es sich den Bereich „Get started with the EWS Managed API“ der MSDN genauer zu studieren.

Bevor eine erste Email versendet werden kann, muss der Exchange Service instanziiert und die entsprechenden Credentials zur Authentifizierung gesetzt werden. In diesem Beispiel kommen die Default Credentials zum Einsatz, da die Applikation innerhalb der Domain ausgeführt wird und die Nachrichten über einen on-premise Exchange Server verschickt werden.

// Initialisiere den ExchangeService und setze Default Credentials
ExchangeService eService = new ExchangeService();
eService.UseDefaultCredentials = true;

Als letzten Schritt der Initialisierung muss noch die Service URL gesetzt werden. Dafür wird in diesem Fall der AutodiscoverService geladen, um die Benutzereinstellungen des aktuellen AD-Users zu laden. Aus dessen Response kann dann eine valide URL entnommen werden, die vom EWS verwendet werden kann.

// Lade Autodiscover Service der Exchange Web Services und lade User-Einstellungen
AutodiscoverService autodiscoverService = new AutodiscoverService();
GetUserSettingsResponse response = autodiscoverService.GetUserSettings(smtpAdresse,
UserSettingName.InternalEwsUrl);
eService.Url = new Uri((string)response.Settings[UserSettingName.InternalEwsUrl]);

Der Rest ist dann straight-forward. Die Instanz des EWS wird in eine EmailMessage induziert und mit den entsprechenden Informationen angereichert. Als Email Body wird die Html-Vorlage mit allen Build Definitionen verwendet und die Empfänger werden aus der Konfiguration gelesen und angehängt.

Der Trick mit den Status Bildern

Damit der Status eines Builds durch ein Image in der Email visualisiert werden kann, wird der Status-Platzhalter aus der Html-Vorlage nur mit einer Content-Id befüllt (z.B. cid:picOK). Diese Content-ID wird kurz vor dem Verschicken der Email mit einem Mail-Attachement verknüpft, welches die gleiche Content-ID bekommt. Dadurch wird der Text aus der Html-Vorlage durch das Attachement ausgetauscht und ein Status-Bild angezeigt.

Wiederkehrender morgendlicher Task

Wie eingangs angesprochen soll den Entwicklern ein Feedback über die Build Läufe der letzten Nacht gegeben werden. Da der Build Notification Service eine Client Anwendung ist und das TFS Client Object Modell verwendet, muss die Anwendung auf einem Rechner ausgeführt werden auf dem Visual Studio installiert ist. Am einfachsten ist es einen täglichen Task im Task Scheduler anzulegen und so das Konsolenprogramm zu starten.

Transparenz belebt das Geschäft

Damit sind die Grundlagen geschaffen und es kann täglich eine Mail generiert werden, die den Status aller relevanten Nightly Builds visualisiert und an alle Entwickler verschickt. Somit verfügt jeder über den gleichen Informationsstand. Aber Mails sind geduldig und können ignoriert, verschoben oder gelöscht werden. Somit ist die Mail nur so gut wie jedes andere Informationsmedium auch – Team-Mitglieder, die sich schon immer gekümmert haben, freuen sich über die Übersicht. Die Anderen können sich nach wie vor ihre Verantwortung drücken.

Auf der Manage Agile 2013 Konferenz in Berlin hielt Uwe Henker den Vortrag „Human Factors and SCRUM“. Herr Henker beschrieb in seinem Vortrag wie Transparenz und die Visualisierung von Ergebnissen eine Verbesserung im „Gesamtsystem“ herbeiführen können. Aufbauend auf dieser These wurde der Logarithmus des Build Notification Service weiterentwickelt, um den Prozess noch transparenter zu machen. Der Dienst soll nun zusätzlich aus einem fehlgeschlagenen Build das potentiell verantwortliche Entwicklungsteam bestimmen und diese Information über die tägliche Status-Mail
verbreiten. Dabei geht es weniger darum einen Schuldigen zu finden, sondern um eine erste Anlaufstelle für die Fehlersuche zu nennen. Es soll ein erster Kümmerer gefunden werden, der auch die Lage beurteilen kann – da der Fehler schließlich irgendwo in dessen Code aufgetreten ist.

Implementieren lässt sich das relativ einfach. Der „Build Service“ muss lediglich für eine bestimmte Build Definition die Build Details abrufen.

// lade Liste der Verantwortlichen aus den verschiedenen Bereichen
// lade zusätzlichen Informationen für konkrete Build Definition
// prüfe den Build Status und suche Verwantwortlichen
IDictionary&lt;string, string&gt; map = LoadResponsibles();
IBuildDetail buildDetail = buildService.GetAllBuildDetails(buildDefinition.Uri);
if(buildDetail.Status == BuildStatus.Failed)
{
WhoBrokeTheBuild(buildDetail, tfs, map);
}

Nachdem Laden der Build Details wird der Status des Build überprüft. Im „Nicht-Gut“-Fall wird dann nach einem Kümmerer aus der Liste der möglichen Verantwortlichen gesucht. Dazu werden in der WhoBrokeTheBuild()-Methode die Build Errors der Build Definition abgefragt und ausgewertet.

// Build Errors über InformationNodeConverters laden
List errors = InformationNodeConverters.GetBuildErrors(buildDetail);
foreach (IBuildError err in builderrors)
{
// do something
// search for responsible in map
}

Der Name des Kümmerers bzw. des Teams wird nun solange gezeigt, bis der Fehler bereinigt wurde oder durch einen neuen Kümmerer (neuer Fehlerfall) ersetzt wird.

Fazit

Einige Wochen nach der Einführung der Build Status Email bleibt festzustellen, dass diese neue Transparenz zu etwas höherer Qualität geführt hat. Die Builds laufen stabiler und auftretende Fehler, werden schneller bereinigt. Allerdings ist auch festzustellen, dass sobald der Logarithmus keinen Kümmerer ausfindig machen kann, sich auch kein Verantwortlicher freiwillig meldet – und sich dadurch die Fehlerbehebung wieder verzögert.

Was also bleibt? Zum einen die Erkenntnis, dass der Algorithmus noch wesentlich besser werden muss und das die Qualität einer Software immer nur so hoch ist wie die Motivation seiner Entwickler.

Mögliche Sinnvolle Erweiterung

Zugegebenermaßen ist der Build Notification Service ein wenig „dumm“, da er im Moment nur ein einziges Team Projekt überwacht und auch nicht zwischen Teams unterscheidet. D.h. eine Fokussierung und Kanalisierung der Information ist im Moment nicht möglich. Deswegen wäre eine sinnvolle Erweiterung für die Zukunft, die Überwachung von Teams und Team Projekten konfigurierbar zu machen.

Eine weitere Erweiterung könnte die Abschaffung der klassischen Mailing-Liste sein, die jetzt in der Konfigurationsdatei hinterlegt werden muss. Wenn man den Team-Gedanken aus der ersten Erweiterung aufgreift, könnte es sich durchaus lohnen einen Mechanismus zu schreiben, der die Mitglieder aus den jeweiligen Teams ausliest, deren Mail-Adresse bestimmt und ihnen die teamrelevanten Build-Informationen zuschickt.

Weitere Ideen oder Anregungen könnt Ihr gerne im Kommentar hinterlassen. Ich würde mich freuen.

[1] dotnetpro 02/14 Andy Grothe – Sie haben Post

[2] http://msdn.microsoft.com/de-de/magazine/jj553516.aspx

[3] http://msdn.microsoft.com/en-us/library/office/jj220499(v=exchg.80).aspx

[4] http://msdn.microsoft.com/de-de/library/bb130146.aspx

 

3 Gedanken zu “Who broke the build – Teil II

  1. Hallo Karsten, wir haben eine ähnliche Anwendung geschrieben, welche nicht nur über den Status des Build, sondern auch über den Status der in dem Build ausgeführten Tests informiert. Wir senden dort eine Information über
    a) die getesteten Assemblies
    b) das verantwortliche Team für die jeweilige Assembly
    c) Anzahl Code Blöcke
    d) die gemesse Code Coverage (farblich visualsiert die Abweichung von der DoD und die Abweichung vom Maximal-Höchstwert)
    e) den Zielwert für eine Code Coverage der in der Definition of Done festgehalten wird und der für diese für diese Assembly mindestens erreicht werden sollte
    f) den Bestwert der Code Coverage der jemals gemessen wurde.
    g) das Datum seitdem die Assembly „untersucht“ wird

    Das Tool speichert diese Werte in einer eigenen kleinen Datenbank um die zusätzlichen Daten, wie Zielwert, Maximalwert nutzen zu können. Der Entwickler sieht so nicht nur, ob sein Build erfolgreich gelaufen ist, sondern auch ob er die Testabdeckung verbessern könnte.
    Viele Grüße

  2. Hi Chris. Sehr coole Idee! Wie werden diese Hinweise von den Entwicklern angenommen? Konntet Ihr dadurch Eure Code Qualität nachweislich steigern?
    Viele Grüße

    • Der Hauptkritikpunkt war der Versand dieser Information per E-Mail. Anfangs haben wir diese Informationen nach jedem Build verschickt, das haben wir sehr schnell, wie auch in deinem Beispiel, auf eine tägliche Zusammenfassung geändert. Gerade der Bestwert (der beste jemals gemessene Wert der Code Coverage) hat viele Entwickler intrinsisch motiviert diesen Bestwert zu halten oder sogar noch zu verbessern, was zumindestens dazu führte, dass mehr Unit Tests geschrieben wurden. Definition of Done als Mindestmaß ist das eine, Anreize dazu zu geben diese DoD zu übertreffen ist dann, denke ich, noch ein Stück besser.

Die Kommentarfunktion ist geschlossen.