Automatisierte NuGet-Paketerstellung


Kohl, du wolltest die Versammlung vermitteln
Und mit ihnen feuriges Hallo
Nugetom, vergiss nicht,
ein Paket einzupacken !


Reservieren Sie sofort, dass wir uns in diesem Artikel auf den Microsoft .NET-Technologie-Stack konzentrieren.

Es kommt häufig vor, dass eine Teilmenge von Projekten in verschiedenen Lösungen verwendet wird.

In der Regel kümmern sich Programmierer, die in einem benachbarten Projekt etwas Nützliches entdeckt haben, zunächst nicht darum - sie erstellen den Ordner lib (dll, Assemblys usw.) und platzieren die kompilierten Assemblys aus der ursprünglichen Lösung dort. Mit der Zeit wird klar, dass dies nicht die bequemste Option ist, und hier ist der Grund:

  • Die ursprüngliche Lösung beginnt sich in ihre eigene Richtung zu entwickeln, ohne die „Verbraucher“ zu berücksichtigen: Neue Abhängigkeiten werden hinzugefügt, .NET-Versionen werden aktualisiert usw. "Witze";
  • Selbst wenn sie an „Konsumenten“ denken, vergessen sie, ihre Assemblys zu aktualisieren, wenn ein kritisches Update herauskommt oder nur eine neue Version, und dann wird alles schlimmer, wenn es mehr als eine Assembly gibt und es einige Abhängigkeiten zwischen ihnen gibt - bei der Aktualisierung einer Assembly treten Probleme auf Ausführungszeit, wie Eine andere Assembly hat möglicherweise die falsche Version.
  • Die ursprüngliche Lösung wird nicht mehr entwickelt.

Ответом на все эти неприятности может служить вынесение проектов в отдельное решение и создание NuGet-пакета, включающего общие сборки, и смена парадигмы развития этих сборок. По большому счёту, всё это можно сделать и без NuGet, но удовольствия в этом гораздо меньше.Как сделать так, чтобы NuGet-пакет собирался сам автоматически вместе с компиляцией проекта на сервере построения и включал все необходимые свистелки и гуделки — об этом и будет наш рассказ.

Изготовление NuGet-пакетов


Das Erstellen von NuGet-Paketen ist recht einfach. Der gesamte allgemeine theoretische Teil ist zugänglich und allgemein verständlich. Mit Paketen können verschiedene Inhalte gepackt werden, nicht nur kompilierte Assemblys, sondern auch das Debuggen von Symbolen, Bildern usw. Ressourcen und sogar Quellcode.

In dieser Beschreibung beschränken wir uns auf die dringlichste Frage der Verpackung kompilierter Baugruppen.

Vorbereitung des ersten NuGet-Pakets


Um die automatische Erstellung von NuGet-Paketen auf dem Build-Server einzurichten, müssen Sie die erste Version des Pakets "kochen". Der einfachste und verständlichste Weg, ein Paket zu erstellen, ist die Verwendung einer NuSpec-Datei , die beschreibt, wie das Paket aussehen wird. Sie können diese NuSpec-Datei auf verschiedene Arten erhalten:

  • Nehmen Sie das Beispiel eines anderen und beheben Sie es.
  • Generieren Sie mit dem Dienstprogramm NuGet.exe (Befehl „NuGet.exe spec“).
  • Erstellen Sie ein neues Paket oder öffnen Sie ein vorhandenes fremdes Paket mit dem NuGet Package Explorer- GUI-Dienstprogramm . Korrigieren und speichern Sie es mit dem Befehl "Metadaten speichern unter ...".

Grundsätzlich ist es möglich, die gesamte Erstellung einer NuSpec-Datei in der GUI abzuschließen. Es ist jedoch immer noch hilfreich zu verstehen, wie NuSpec funktioniert.

Eine unserer NuSpec-Dateien mit Abkürzungen sieht beispielsweise folgendermaßen aus:

Inhalt der NuSpec-Datei
NewPlatform.Flexberry.ORM2.1.0-alpha1Flexberry ORMNew Platform LtdFlexberry ORM package.
      ...
    Copyright New Platform Ltd 2015Flexberry ORM


Hier einige Erklärungen zu einigen Abschnitten:

  • ID muss im gemeinsamen Namespace aller Pakete eindeutig sein, um Kollisionen zu vermeiden. Jemand gibt den Namen des Unternehmens im Namen des Pakets an , dann den Namen des Projekts und eines bestimmten Produkts, aber jemand kümmert sich nicht darum .
  • Zu den Versionen: Die Verwendung der Prinzipien der semantischen Versionierung wird als gute Praxis angesehen . Eine kleine Regel, die wir in unserem Team entwickelt haben - alle Vorabversionen (mit Ausnahme von 3 Nummern, die am Ende noch etwas anderes enthalten, z. B. alpha1) veröffentlichen wir mit Assemblys, die in der Debug-Konfiguration zusammengestellt sind, bzw. mit Releases. in Release.
  • Versionshinweise (releaseNotes) - eine sehr nützliche Sache, schreiben Sie dort, die sich gegenüber der vorherigen Version geändert hat. Benutzer sollten verstehen, was sie mit jedem Update erhalten.
  • Abhängigkeiten Wenn Sie Abhängigkeiten beschreiben, müssen Sie überlegen, wie Ihr Paket installiert wird: Wenn der Benutzer nur Ihr Paket und nichts anderes benötigt, gibt es keine Abhängigkeiten. Wenn Ihre Assemblys nur funktionieren, wenn ein anderes Paket vorhanden ist, z. B. SharpZipLib, müssen Sie diese Abhängigkeit auf jeden Fall registrieren. Es ist wichtig zu verstehen, dass SharpZipLib wiederum seine eigenen Abhängigkeiten hat und während der Installation auch zum Benutzer „fliegt“, selbst wenn Sie diese nicht zu Hause angeben.
    Die Installation erfolgt rekursiv, sodass ein Benutzer in einer der hypothetischen Situationen mit der Installation eines Pakets beginnen kann und mehr als hundert Pakete für ihn installiert werden - nur durch Abhängigkeiten. Während der Paketinstallation ist die Auswahl der Version des abhängigen Pakets sehr schwierig.. Wenn Sie die Versionsnummer nicht angeben, wird die neueste Release-Version installiert, andernfalls die, die in der Abhängigkeit explizit angegeben ist. Übrigens, wenn Sie von Zeit zu Zeit mehrere unabhängige Pakete verwenden, können Sie ein leeres Paket mit Abhängigkeiten von den benötigten Paketen erstellen und dieses eigene Paket installieren - der Rest wird danach selbst installiert.
  • Dateibeschreibungen können bestimmte Namen oder Masken enthalten. Wir empfehlen dringend, dass Sie die richtige Paketstruktur beachten, wenn der Inhaltstyp, die Version des .net-Frameworks und andere Dinge in Übereinstimmung mit der Vereinbarung in target geschrieben sind . Es ist wichtig zu verstehen, dass Sie beim Angeben des Dateipfads im Attribut src vom aktuellen Verzeichnis ausgehen müssen, in dessen Kontext der Befehl package packaging ausgeführt wird.

Nachdem die NuSpec-Datei fertig ist, können Sie mit der Testerstellung des Pakets beginnen. Dazu wird ein einfaches NuGet.exe-Dienstprogramm ausgeführt: nuget pack MyAssembly.nuspec.

Daher sollten wir das begehrte "erste Paket" oder "Prototyppaket" erhalten, dh eine nupkg-Datei, die für die Installation in Projekten über den NuGet Package Manager oder über NuGet.exe verwendet werden kann .

Ausstellung fertiger Verpackungen


Wir haben also ein Paket, das den Benutzern irgendwie über einen „Paketverteilungskanal“ zugestellt werden muss. Wir glauben, dass die meisten Benutzer Pakete über Visual Studio installieren werden. Der darin integrierte NuGet-Paket-Manager kennt zwei Optionen zum Platzieren von Paketen:

  • Paketgalerie, verfügbar über das Netzwerk;
  • Windows-Ordner (lokal oder Netzwerk).

Sie können Ihre eigenen Paketquellen in den Einstellungen hinzufügen. Diese werden nacheinander beim Installieren oder Wiederherstellen von Paketen gescannt, bis die gewünschte ID gefunden wird. Die Option, wenn dasselbe (!) Paket in mehreren Quellen liegt, ist durchaus akzeptabel.

Die einfachste Möglichkeit zum Verteilen von Paketen besteht darin, einen Netzwerkordner zu erstellen und die Pakete dort abzulegen.

Es ist erwähnenswert, dass Sie mit NuGet nicht nur mit der allgemeinen Paketgalerie https://nuget.org arbeiten , sondern auch Ihre eigenen Galerien erstellen können. Dazu können Sie dieselbe Engine an einem Ort bereitstellen, der unter https://nuget.org verwendet wird. Unser Team bevorzugt diese Option, da es in diesem Fall möglich ist, Download-Statistiken zu verfolgen und Berechtigungen über die Website zu verwalten. Letztendlich ist es einfach nur schön.



Das Installieren einer Galerie erfordert möglicherweise kleine Tänze mit einem Tamburin, zumindest in Bezug auf die Autorisierung, aber es ist nichts kompliziertes daran. Pakete werden auf die gleiche Weise wie auf NuGet.org veröffentlicht. Beim Aktualisieren der Galerie-Site ist es wichtig, dass das Archiv mit bereits heruntergeladenen Paketen nicht verloren geht. Sie werden im Site-Verzeichnis gespeichert. In diesem Fall sieht das Einrichten von NuGet Package Manager für Benutzer ungefähr so ​​aus:



Befindet sich die lokale Paketquelle in der Nähe von Benutzern, z. B. im selben lokalen Netzwerk, wird empfohlen, alle Pakete mit Abhängigkeiten herunterzuladen. Dadurch wird die Zeit für das Herunterladen von Paketen für neue Benutzer verkürzt. Nupkg-Dateien aus abhängigen Paketen zu finden ist sehr einfach - sie befinden sich immer in dem Paketordner, in dem diese Pakete installiert sind (normalerweise im Verzeichnis mit der sln-Datei). Die Reihenfolge ist auch im Einstellungsfenster für Paketquellen wichtig. Das Studio durchläuft die Quellen, wenn die Pakete in der in den Einstellungen angegebenen Reihenfolge wiederhergestellt werden. Wenn Ihr Paket daher nur lokal verfügbar ist, platzieren Sie zuerst Ihre Quelle, damit keine unnötigen Anforderungen an nuget.org gestellt werden.

NuGet Packet Factory


Nachdem das „Prototyp-Paket“ erstellt und der „Vertriebskanal für Pakete“ eingerichtet wurde, können Sie die Zusammenstellung von Paketen automatisieren, sodass wir mit dem ersten Mausklick das heißeste und frischeste NuGet-Paket erhalten.

Sehen wir uns an, wie dies im Fall von Team Foundation Server 2013/2015 durchgeführt wird. Bei anderen ähnlichen CI-Systemen ist der Prozess ähnlich.
In den XAML-Eigenschaften (Build Definition) können Sie das PowerShell-Skript angeben, das bei erfolgreicher Erstellung ausgeführt wird. In diesem Skript werden wir unseren "Packer" aufrufen und als Parameter den Pfad zur NuSpec-Datei übergeben.

Es gibt einige Punkte, die Sie selbst klären sollten: Wo wird NuGet.exe selbst und alle benötigten Dateien (zumindest die Konfigurationsdatei) liegen, wo wird sich die NuSpec-Datei befinden? Einerseits können Sie sich darauf verlassen, dass sich NuGet.exe an einem bestimmten Ort auf dem Build-Server befindet. Wenn jedoch mehrere Build-Server vorhanden sind und Sie diese nicht verwalten möchten, ist es am einfachsten, NuGet.exe in der Quellcodeverwaltung abzulegen und ein Verzeichnis mit seinem Speicherort hinzuzufügen im Arbeitsbereich, mit dem der Build ausgeführt wird. NuSpec kann bequem neben der SLN-Datei aufbewahrt und sogar in Solution Items aufgenommen werden, um über den Solution Explorer schnell darauf zugreifen zu können.

Wenn es mehrere Lösungen gibt und Sie mehrere Pakete erstellen möchten, wird empfohlen, ein gemeinsames PowerShell-Skript zu implementieren, das den Pfad zur NuSpec-Datei als Parameter erhält.

Unten finden Sie Auszüge aus einem solchen Skript:

Auszüge aus dem PowerShell-Skript
# Create NuGet Package after successfully server build.
# Enable -Verbose option for this script call.
[CmdletBinding()]
Param(
    # Disable parameter.
    # Convenience option so you can debug this script or disable it in 
    # your build definition without having to remove it from
    # the 'Post-build script path' build process parameter.
    [switch] $Disable,
    # This script used NuGet.exe from current directory by default.
    # You can change this path to meet your needs.
    [String] $NuGetExecutablePath = (Get-Item -Path ".\" -Verbose).FullName + "\NuGet.exe",
    $BinariesDirectoryPostfixes = @("\Debug", "\Release"),
    # Path to the nuspec file. Path relative TFS project root directory.
    [Parameter(Mandatory=$True)]
    [String] $NuspecFilePath,
    # Disable Doxygen.
    [switch] $NoDoxygen
    # ...
    # Go, go, go!
    $nugetOutputLines = & $NuGetExecutablePath pack $realNuspecFilePath -BasePath $basePath
        -OutputDirectory $outputDirectory -NonInteractive;
    ForEach ($outputLine in $nugetOutputLines) {
        Write-Verbose $outputLine;
    }
    # ...


Im Skript werden Operationen ausgeführt, um relative Pfade in absolute Pfade umzuwandeln (Sie können leicht eine Beschreibung der verfügbaren Variablen finden, die vom CI-System beim Ausführen des Skripts angegeben werden). In einigen Fällen ist eine Änderung der NuSpec-Datei in diesem Skript erforderlich. Auf diese Weise können Sie beispielsweise Pakete für verschiedene Konfigurationen erstellen (Beliebige CPU, x86).

Dadurch wird der automatische Mechanismus zum Erstellen von NuGet-Paketen eingerichtet. Wir starten die Assembly auf dem Build-Server und überprüfen, ob alles funktioniert hat. Vergessen Sie nicht, –Verbose in die Skriptparameter in den Builddefinitionseinstellungen zu schreiben, um Informationen zum Debuggen zu erhalten, falls ein Fehler aufgetreten ist. Gießen Sie die fertigen Pakete in eine freigegebene Ressource oder Galerie und laden Sie die ersten Benutzer ein.

Die Feinheiten des Prozesses


Wie das Sprichwort sagt: "Die Hauptaufgabe des Programmierers ist es, den Perfektionisten in sich selbst zu töten." Wenn der interne Perfektionist noch nicht aufgegeben hat, sollten die folgenden Punkte nützlich sein.

Zusätzlich zur Möglichkeit, NuGet-Pakete zu erstellen, kann ein Skript für den Build-Server für jedes Paket ein Hilfsprogramm zum Generieren einer automatischen Dokumentation basierend auf XML-Kommentaren im Code ausführen. Diese Funktion ist praktisch, da wir für jede Version des Pakets eine eigene Version der automatischen Dokumentation haben. Sie ist praktisch, wenn Benutzer verschiedene Versionen von NuGet-Paketen verwenden. Wir verwenden Doxygen , um eine Autodokumentation zu erstellen . Hier ist der Skriptabschnitt zur automatischen Dokumentation:

Auszüge aus einem PowerShell-Skript zum Generieren einer automatischen Dokumentation
if($NoDoxygen)
{
    Write-Verbose "Doxygen option is disabled. Skip generation of the project documentation.";
}
else
{
    Write-Verbose "Doxygen option is enabled. Start documentation generation.";
    # Copy doxygen config file.
    $doxyConfigSourcePath = Join-Path -Path $toolsFolderPath -ChildPath "DoxyConfig" -Resolve;
    $doxyConfigDestinationPath = Join-Path -Path $Env:TF_BUILD_BINARIESDIRECTORY -ChildPath "DoxyConfig";
    # Modify doxigen config file according with given nuspec.
    $nuspecXml = [xml](Get-Content $NuspecFilePath);
    $doxyConfig = Get-Content -Path $doxyConfigSourcePath;
    $projectName = $nuspecXml.GetElementsByTagName("title").Item(0).InnerText + " " +
    $nuspecXml.GetElementsByTagName("version").Item(0).InnerText;
    $doxyConfig = $doxyConfig -replace "FlexberryProjectName", $projectName;
    $projectLogoPath = Join-Path -Path $toolsFolderPath -ChildPath "logo.png" -Resolve;
    $doxyConfig = $doxyConfig -replace "FlexberryProjectLogo", $projectLogoPath -replace "\\", "/";
    $doxyConfig = $doxyConfig -replace "FlexberryOutputDirectory", $Env:TF_BUILD_BINARIESDIRECTORY -replace "\\", "/";
    $doxyConfig = $doxyConfig -replace "FlexberryInputDirectory", $Env:TF_BUILD_SOURCESDIRECTORY -replace "\\", "/";
    $doxyWarnLogFilePath = Join-Path -Path $Env:TF_BUILD_BINARIESDIRECTORY -ChildPath "doxygen_log.txt";
    $doxyConfig = $doxyConfig -replace "FlexberryWarnLogFile", $doxyWarnLogFilePath -replace "\\", "/";
    $doxyConfig | Out-File $doxyConfigDestinationPath default;
    # Run doxygen.
    $doxygenExecutablePath = Join-Path -Path $toolsFolderPath -ChildPath "doxygen.exe" -Resolve;
    $doxygenOutputLines = & $doxygenExecutablePath $doxyConfigDestinationPath
    ForEach ($outputLine in $doxygenOutputLines) {
        Write-Verbose $outputLine;
    }
    Write-Verbose "Documentation generation done. Packing to the archive.";
    # Do archive.
    $archiveSourceFolder = Join-Path -Path $Env:TF_BUILD_BINARIESDIRECTORY -ChildPath "html" -Resolve;
    $archiveFileName = $nuspecXml.GetElementsByTagName("id").Item(0).InnerText + "." +
    $nuspecXml.GetElementsByTagName("version").Item(0).InnerText;
    $archiveDestinationFolder = Join-Path -Path $Env:TF_BUILD_BINARIESDIRECTORY -ChildPath ($archiveFileName + ".zip");
    Add-Type -assembly "system.io.compression.filesystem";
    [io.compression.zipfile]::CreateFromDirectory($archiveSourceFolder, $archiveDestinationFolder);
    # Remove html documentation files.
    Remove-Item $archiveSourceFolder -recurse;
    Write-Verbose "Done.";
}


Der zweite Punkt betrifft die Assembly des Projekts, wenn verschiedene Versionen von Assemblys für verschiedene Versionen des .net-Frameworks in einem Paket gepackt sind.

Die Tricks beginnen damit, den Buildserver zu zwingen, Assemblys für verschiedene Versionen des .net-Frameworks zu erstellen. Betrachten Sie die Projekte, die im csproj-Format und nicht im neuen json-Projektdateiformat (ASP.NET5) zusammengestellt werden sollen. Visual Studio unterstützt den Assemblykonfigurationsmechanismus. In der Regel werden zwei Konfigurationen verwendet - Debug und Release. Mit demselben Mechanismus können Sie jedoch die .net-Versionsumschaltung konfigurieren.

Sie können Ihre eigenen Konfigurationen erstellen, was wir tun. Leider müssen Sie zum Optimieren aller erforderlichen Parameter die csproj-Datei öffnen und dort in jedem Konfigurationsabschnitt mindestens TargetFrameworkVersion einstellen.
Auszüge aus der .csproj-Datei
truefullfalsebin\Debug-Net35\DEBUG;TRACEv3.5AnyCPUprompt4AllRules.rulesetbin\Debug-Net35\LogService.XMLpdbonlytruebin\Release-Net35\TRACEv3.5AnyCPUprompt4bin\Release-Net35\LogService.XMLAllRules.rulesettruebin\Debug-Net40\DEBUG;TRACEbin\Debug-Net40\LogService.XMLfullv4.0AnyCPUpromptAllRules.rulesettruebin\Debug-Net45\DEBUG;TRACEbin\Debug-Net45\LogService.XMLfullv4.5AnyCPUpromptAllRules.rulesetbin\Release-Net40\TRACEbin\Release-Net40\LogService.XMLtruepdbonlyv4.0AnyCPUpromptAllRules.rulesetbin\Release-Net45\TRACEbin\Release-Net45\LogService.XMLtruepdbonlyv4.5AnyCPUpromptAllRules.ruleset


Konfigurationen in Visual Studio werden hauptsächlich in der Symbolleiste umgeschaltet, in der Assemblydefinition auf dem Server können Sie mehrere Konfigurationen gleichzeitig auswählen, die nacheinander kompiliert werden.

Es ist erwähnenswert, dass, wenn sich Ihr Code für verschiedene Versionen des .net-Frameworks zu unterscheiden beginnt, dies mithilfe der folgenden Anweisungen verarbeitet werden kann:

        #if NETFX_35
            for (int i = 0; i < resValueLength; i++) 
        #else
            System.Threading.Tasks.Parallel.For(0, resValueLength, i =>
        #endif

In diesem Fall müssen die Konstanten im entsprechenden Abschnitt der csproj-Datei definiert werden:

DEBUG;TRACE;NETFX_35

Wenn wir fertig kompilierte Assemblies haben, wollen wir herausfinden, wie man nuspec richtig konfiguriert. Nuspec definiert spezielle Verzeichnisse für bestimmte Versionen des .net-Frameworks.

Ein Beispiel für den Dateibereich in einer NuSpec-Datei:


Ещё одна проблема, с которой можно часто столкнуться при использовании (даже не при создании) NuGet-пакетов — проблема подключения одного проекта в несколько солюшенов. Дело в том, что в csproj-файле ссылки на сборки проставляются вплоть до конкретных dll, которые по умолчанию восстанавливаются Visual Studio в папку packages рядом с sln-файлом. Отсюда возникает проблема, когда один и тот же проект включён в несколько солюшенов, располагающихся в разных папках. Для решения этой проблемы можно воспользоваться NuGet-пакетом, который включает в себя специальный Target, который переписывает ссылки перед билдом: https://www.nuget.org/packages/NuGetReferenceHintPathRewrite.

Ein weiteres Merkmal der Verwendung von NuGet-Paketen ist das Thema der Paketwiederherstellung während der Montage. Tatsache ist, dass Visual Studio für einige Zeit keine integrierten Tools zur Paketwiederherstellung hatte. Daher wurde csproj ein spezielles Target hinzugefügt, das für die Wiederherstellung verantwortlich war. In modernem Visual Studio (2013+) ist dies nicht mehr relevant. Halten Sie Ihre csproj-Dateien sauber. Sie benötigen kein Target mehr, um NuGet-Pakete wiederherzustellen.

Nun, zum Schluss können wir über die Tatsache sprechen, dass bei Verwendung von TFS der Paketordner standardmäßig in die Quellcodeverwaltung kriecht und jemand in regelmäßigen Abständen alle Assemblys in TFS überprüfen kann. Um dies zu verhindern (wir sind sicher, dass es für diejenigen, die Baugruppen in TFS betrügen, einen separaten Boiler in der Hölle geben muss), können Sie die .tfignore- Datei verwendenWer muss vor dieser Geißel retten.

Ergebnis


Wenn Sie also alle in der von uns vorgeschlagenen Anleitung beschriebenen Schritte ausgeführt haben, erhalten Sie einen vorgefertigten Mechanismus zum Verpacken von Paketen, der ohne menschliches Eingreifen funktioniert. Unsere Pakete sind so aufgebaut. Es sei denn, die Veröffentlichung selbst erfordert etwas Aufmerksamkeit.

Nützliche Links:




Jetzt auch beliebt: