Innovations-Nummer +49 (0)7304 / 803 0

Blog-n-Roll:
Wissen,
das rockt!

ALM

2016.01.31

Päckchen packen mit NuGet, TFS 2015 und Build vNext. Teil 2: Pakete erstellen
By admin / 31 January / ALM, Entwickler / 0 comm.

Funktionalität automatisiert für andere Anwendungen bereitstellen Anmerkung: Dies ist die für Visual Studio / TFS 2015 und Build vNext überarbeitete Version unseres Artikels auf entwickler.de. Es hat sich Einiges getan: Das neue Buildsystem vereinfacht das zentrale Erstellen und Bereitstellen von NuGet-Paketen erheblich, und die NuGet-Integration in Visual Studio Team Services bietet einen einfachen weg, eigene […]

Funktionalität automatisiert für andere Anwendungen bereitstellen

Anmerkung: Dies ist die für Visual Studio / TFS 2015 und Build vNext überarbeitete Version unseres Artikels auf entwickler.de. Es hat sich Einiges getan: Das neue Buildsystem vereinfacht das zentrale Erstellen und Bereitstellen von NuGet-Paketen erheblich, und die NuGet-Integration in Visual Studio Team Services bietet einen einfachen weg, eigene Paket-Feeds in der Cloud zu hosten. Viel Spaß!

Der Open Source-Paketmanager NuGet hat sich in der .NET-Welt zur Standardmethode einwickelt, um Funktionalität zwischen mehreren Projekten zu teilen oder öffentlich bereitzustellen – fast jeder Programmierer hat schon ein NuGet-Paket in sein Projekt eingebunden. Doch NuGet kann mehr: Es bietet die Möglichkeit, wichtige Teile des Entwicklungsprozesses innerhalb der eigenen Organisation weiter zu verbessern und zu automatisieren.


Blogserie


 

Pakete selbst erstellen

Im ersten Teil unserer kleinen Artikelserie zum Thema NuGet haben wir gesehen, wie einfach es ist, öffentlich verfügbare Pakete in eigene Projekte einzubinden. Auch der Erstellen eigener Pakete ist nicht sehr schwierig: Ein simples Kommandozeilentool (nuget.exe) ist alles, was man dafür braucht. Nuget.exe ist zum Download auf der NuGet-Website erhältlich.

image

Es empfiehlt sich, das Tool in einem Ordner auf der lokalen Festplatte abzulegen und die Windows-PATH-Umgebungsvariable mit diesem Installationspfad zu ergänzen, damit ein einfacher Zugriff auf nuget.exe von der Kommandozeile aus möglich ist.

In unserem Demo-Szenario soll eine Komponente eines Projekts – eine schockierend einfache Bibliothek namens HelloLib – als NuGet-Paket bereitgestellt werden. Dieses Paket wollen wir dann in der kleinen Hello World-Konsolenanwendung verwenden, die im ersten Teil der Artikelserie vorgestellt wurde.

image

Um ein nun Paket aus diesem Visual Studio-Projekt zu erzeugen reicht der folgende simple Befehl in einer DOS-Box:

nuget pack HelloLib.csproj

image

Das war’s auch schon! Im Projektverzeichnis findet sich das frisch erstellte NuGet-Paket:

image

Diese Methode erzeugt allerdings nur ein sehr einfaches Paket, das schlicht den Build-Output des Projektes enthält – es kann nichts weiter konfiguriert werden.

Auch beschwert sich NuGet darüber, das bestimmte Informationen (wie Autor und Beschreibung) nicht angegeben wurden, die für die späteren Nutzer des Paketes wichtig sind. Aber dieses Problem ist schnell gelöst: Die Definition des gewünschten Paketinhalts und von allen Paketattributen kann über ein Konfigurationsfile mit der Endung .nuspec erfolgen. Erfreulicherweise kann der nuget-Befehl auch gleich das Konfigurationsfile erzeugen, wenn der Parameter spec übergeben wird:

nuget spec HelloLib.csproj

image

Die frisch erzeugte Datei sieht dann so aus:

image

Was in der jungfräulichen HelloLib.nuspec-Datei sofort auffällt sind die eingebetteten Variablen, wie zum Beispiel $id$ und $version$. Dabei handelt es sich um Replacement Tokens. Beim Erstellen eines Packages mit dem oben beschriebenen Befehl wird die .nuspec-Datei herangezogen und die Variablen darin werden durch Informationen aus dem Projekt ersetzt. Das klappt jedoch leider nicht für alle Replacement Tokens. Manche Werte, wie beispielsweise Autor und Beschreibung müssen entweder in der .nuspec-Datei fest eintragen oder an den nuget pack-Befehl als Argument übergeben werden:

nuget pack HelloLib.csproj -p author=Uwe;description=”Hello Lib”

Mittels des .nuspec-Files kann der Inhalt des Pakets genau gesteuert werden, um beispielsweite weitere Dateien und Abhängigkeiten mit einzubinden. Da die .nuspec-Datei eine Projekt-Konfigurationsdatei ist sollte diese in das Projekt mit aufgenommen werden. Eine genaue Beschreibung aller Konfigurationsmöglichkeiten findet sich in der NuGet-Dokumentation.

Vorerst arbeiten wir mit einer einfachen, angepassten Version von HelloLib.nuspec:

image

Zusätzlich zu den Metadaten haben wir jetzt auch noch den eigentlichen Inhalt des Pakets explizit angegeben – das File HelloLib.dll soll in das Paket – und zwar in den lib-Ordner, der dafür sorgt dass die DLL später als Referenz in das Zielprojekt eingefügt wird.

Verpacken wir das Projekt jetzt neu, wird die Konfigurationsdatei automatisch angezogen und die darin definierten Metadaten in das Paket übernommen.

image

Pakete inspizieren

Soweit, so gut. Aber was genau befindet sich den nun im eben erzeugten Paket? Bei der Arbeit mit NuGet ist es immer wieder sinnvoll, den Inhalt von Paketen inspizieren zu können. Das ist auch mit Windows-Bordmitteln kein Problem, denn NuGet-Paketdateien sind in Wirklichkeit nichts anderes als getarnte ZIP-Archive!  Einfach die Erweiterung .zip an den Dateinamen anhängen – und schon können Pakete mit dem Windows-Explorer geöffnet werden. Es zeigt sich: Ein Paket besteht aus der eigentlichen Nutzlast und zusätzlichen Konfigurationsdateien.

image

Eine elegantere Alternative bietet jedoch das frei erhältliche Tool NuGet Package Explorer. Damit werden Paketinhalte und Metadaten wunderbar übersichtlich dargestellt. Der Package Explorer kann übrigens auch – als Alternative zu nuget.exe – zum Erstellen von Paketen verwendet werden.

image


Pakete lokal automatisch bauen

Jetzt sind wir in der Lage, Pakete über die Kommandozeile erstellen zu lassen. Für etwas mehr Komfort wäre es wünschenswert, wenn das Paket bei jedem Build automatisch gebaut würde, oder? Ich höre Zustimmung…

Zu diesem Zweck bietet es sich an, den PostBuild-Event im Projekt einsprechend anzupassen (Menü Project/HelloLib Properties…) und dort die den nuget pack-Befehl auszuführen:

image

Übrigens: Wir übergeben hier auch gleich eine festeingestellte Versionsnummer – die “teuflische” 9999.99.99.99 – als Zeichen dafür, dass wir das Paket lokal gebaut haben. “Echte” Versionsnummern lassen wir nur für Pakete zu, die auf dem Buildserver mit Team Build sozusagen offiziell erstellt werden – dazu aber gleich mehr.

Kurz F5 gedrückt, und unser Paket wird auch prompt automatisch erzeugt. Was will man mehr? Einiges, aber dazu kommen wir später.

image

Pakete zentral bauen mit Team Build 2015

Noch mit Visual Studio / Team Foundation Server 2013 war die Erstellung von NuGet-Paketen auf einem Build Server nur mit relativ hohem Aufwand nötig: Es waren trickreiche Eingriffe in den MS Build-Code der Projektdatei nötig – machbar, aber fehleranfällig und unkomfortabel. In der oben bereits erwähnten Vorgängerversion dieses Artikels werden die “schmutzigen” Details beschrieben.

Mit Einführung des neuen Buildsystems in Team Foundation Server 2015 hat sich die Vorgehensweise erfreulicherweise grundlegend geändert. Team Build 2015 setzt auf modulare Skripte (Build Steps), mit denen ein individueller Buildvorgang orchestriert werden kann. Für die Wiederherstellung, Erstellung und Veröffentlichung von NuGet-Paketen während des Builds stehen allein drei verschiedene Build Steps zur Verfügung, die beim Entwurf einer Builddefinition hinzugefügt werden können:

  • NuGet Installer dient der Wiederherstellung von fehlenden NuGet Paketen während des Builds – also dem oben beschriebenen Package Restore. Übrigens: Es ist weiterhin möglich, Pakete während der Ausführung von MS Build im Build Step Visual Studio Build wiederherzustellen – dort findet sich die Option Restore NuGet Packages. Die explizite Ausführung eines Package Restore vor dem eigentlichen MS Build ist jedoch meines Erachtens vorteilhafter, weil das Debugging leichter fällt. Zudem gibt es einige Pakete, die mit der Wiederherstellung während des MS Build-Laufs Probleme bekommen, da sie diesen selbst modifizieren.
  • NuGet Packager erstellt während des Buildvorgangs Pakete durch Ausführung von nuget pack. Der Clou ist die Option Use Build number to version package. Diese übergibt den Bezeichner des aktuellen Builds als Parameter und erzeugt damit versionierte Pakete.
  • NuGet Publisher bietet schließlich die Möglichkeit, die erstellten Pakete auf einen Paketserver hochzuladen und damit anderen Entwicklern zur Verfügung zu stellen.

image

Um aus unserer kleinen Beispielbibliothek ein Paket zu bauen, muss einfach nur ein NuGet Installer und ein NuGet Packager-Build Step in die Builddefinition eingefügt werden. Die Konfiguration ist denkbar einfach – Der NuGet Installer erledigt den Package Restore und muss vorerst gar nicht konfiguriert werden. image

Für den NuGet Packager genügt der Ausgabepfad für das fertige Paket – in unserem Fall ein Share namens MyPackages. Außerdem kann wie oben schon erwähnt angegeben werden, ob das Paket automatisch mit der Buildnummer versioniert werden soll.

image

Damit alles klappt, sind jetzt noch zwei kleine Anpassungen nötig. Die automatische Versionierung über die Buildnummer verlangt ein ganz spezielles Format, das im Tab General der Builddefinition angepasst werden kann:

$(BuildDefinitionName)_$(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r)

image

Da wir unser Paket im Build Step NuGet Packager bauen wollen, müssen wir verhindern, dass der Erstellungsprozess noch an anderer Stelle im Build angeworfen wird – nämlich im Post Build Event unserer Projektdatei, der ja ausschließlich dazu dient, das Paket lokal zu bauen. Wir unterdrücken die Ausführung im Team Build kurzerhand, indem wir im Build Step Visual Studio Build einen leeren Parameter übergeben:

image

Das geht natürlich auch eleganter – wenn noch andere Schritte in das PostBuild-Event sollten, die auch im Team Build ausgeführt werden müssen ist ein solches Vorgehen sogar etwas zu brachial – in diesem Fall könnten wir beispielsweise ein eigenes Flag setzten.

Und wo wir gerade so schön dabei sind, entfernen wir auch noch das Häkchen bei Restore NuGet Packages im selben Dialog – dieses MS Build-basierte Package Restore ist unnötig, da wir ja einen eigenen Team Build Step dafür eingefügt haben.

image

Und natürlich müssen wir auch angeben, was genau wir bauen wollen (das hätten wir doch fast vergessen…):

image

Schließlich verändern wir noch die Reihenfolge, in der die Schritte abgearbeitet werden, denn zuerst müssen natürlich alle Pakete wiederhergestellt werden, dann wird gebaut, danach ein mysteriöser Schritt namens “Index Sources & Publish Symbols” durchgeführt von dem später noch die Rede sein wird, und dann wird endlich das NuGet-Paket erstellt.

image

Schnell die Builddefinition abspeichern und mit Queue Build… den Buildprozess starten.

image

Violà!  Alle Schritte laufen wie erwartet durch. Zunächst wird das log4net-Package heruntergeladen und hinzugefügt, dann die Solution gebaut und schließlich verpackt.


Achtung, Falle: Package Sources spezifizieren in Team Build vNext

In der aktuellen Version von Visual Studio ist es noch nicht möglich, im NuGet Installer Build Step eigene Paketserver anzugeben! In Visual Studio Team Services existiert diese Option aber bereits und wird deshalb sehr wahrscheinlich bald auch für die On Premise-Version von TFS nachgerüstet.

Es existiert aber ein Workaround: Einfach auf dem Build Server im Verzeichnis C:Users{BuildService}AppDataRoamingNuGet eine weitere NuGet.config Datei anlegen (wobei {BuildService} für den Account steht, unter dem der Build ausgeführt wird).

Die NuGet-Server einfach in die NuGet.config mit aufnehmen:
<?xml version=”1.0″ encoding=”utf-8″?>
<configuration>
<packageSources>
<add key=”NuGet official package source” value=”
https://nuget.org/api/v2/” />
<add key=”My Package Server” value=”
http://tfs-2015-1:9000/nuget” />
</packageSources>
</configuration>


Das fertige Paket findet sich auf dem konfigurierten Share – versioniert mit der Buildnummer zur sofortigen Verwendung durch beliebige Konsumenten.

image

Wechseln wir also in die Hello World-Konsolenapplikation aus dem ersten Teil dieser Artikelserie. Im schon bekannten NuGet Package Manager lassen sich auf der Einstellungsseite neue Paketserver eintragen:

image

Wir können jetzt den Share eintragen, auf dem wir im Team Build das eben gebaute Paket veröffentlicht haben.

image

Und schon ist unser Paket bereit, in das aktuelle Projekt eingebunden zu werden.

image

image

image

Jetzt können wir die Funktionalität der HelloLib-Bibliothek im eigenen Code verwenden.

image

Fazit und Ausblick

“Weit gekommen wir sind!” würde Yoda an dieser Stelle sagen. Aber das ist noch nicht alles: Im dritten Teil der Serie kümmern wir uns darum, unsere Pakete sicher und effizient an potentielle Konsumenten zu verteilen – ein simples Share reicht uns dafür leider nicht aus. Schalten Sie auch nächstes Mal wieder ein, wenn es heißt “Ja, wir packen das!”.

Nach oben