nopcommerce WebDeploy Package erstellen mit Cake (C# Make)

Software auf eine Produktionsumgebung ausliefern ist eine Aufgabenstellung, die sich einfacher anhört, als sie tatsächlich ist. 

Ein etablierter Weg um eine Microsoft .NET Webapplikation auf den Microsoft IIS Server auszuliefern ist das Publizieren via Web Deploy. Der Prozess, bei dem aus dem normalen Programmcode ein Web Deploy Paket entsteht, nennt man auch «build» (aus dem englischen Erstellen). So ein Build-Prozess besteht typischerweise aus verschiedenen Teilschritten: Kompilieren, Unit-Tests ausführen, JavaScript und CSS verkleinern usw..

Das Kernwerkzeug für die ganzen Build-Aufgaben ist unter Microsoft das MSBuild Framework. MSBuild ist sehr mächtig, aber leider auch durch eine steile Lernkurve gesegnet. Darum macht es nicht immer nur Spass mit MSBuild direkt zu arbeiten. Ein relativ junges Framework das Abhilfe schaffen soll, ist http://cakebuild.net/. CakeBuild.net ist komplett Open Source und ist Teil der Microsoft .NET Foundation https://dotnetfoundation.org/cake

Cake (C# Make) kann viel mehr als «nur» MSBuild vereinfachen. Es gibt eine Vielzahl an integrierten Funktionalitäten und AddIn’s für eine Vielzahl an Aufgabenstellungen in einem typischen Build-Prozess. Siehe http://cakebuild.net/dsl/

Doch wie einfach geht das wirklich? Wir wagten den Selbstversuch und stellen das Deployment von nopCommerce komplett auf Cake (C# Make) um.

Ziel ist es folgende Buildaufgaben mittels Cake (C# Make) abzubilden:

  1. nopCommerce Source kompilieren
  2. NUnit Tests ausführen
  3. Web Deploy Package erstellen

Grundstruktur unseres Cake (C# Make) Setups

Zu Beginn empfiehlt es sich das sogenannte Cake (C# Make) Fundamentals durchzulesen. Das ist relativ schnell gemacht. http://cakebuild.net/docs/fundamentals/

Das zentrale Element für Cake (C# Make) sind die sogenannten Cake-Scripts. In unserem Fall heisst diese Datei build.cake. Diese Datei enthält alle Anweisungen in der DSL von Cake. Als erstes geben wir nur zwei Informationen aus: Wenn die Verarbeitung des Cake-Scripts startet und wenn die Verarbeitung fertig ist.

Setup(context =>
{
    Information("Start with Build-Process");
});

Teardown(context =>
{
    Information("Build-Process finished");
});

Alle Cake (C# Make) Elemente legen wir in unserem geklonten nopCommerce SourceCode in einem neu erstellten Ordner ab:

nopCommerce Source-Code mit cake Strukturen

In dem Ordner cakebuild befindet sich unsere cake.build Datei und das Powershell Script um Cake auszuführen:

Ansicht des Cake (C# Make) Ordners

Der Tools-Ordner legt Cake (C# Make) automatisch an, wenn das Script das erste Mal ausgeführt wird.

nopCommerce Source mit Cake (C# Make) kompilieren

Für das sauber Kompilieren sind zwei Teilschritte notwendig:

  1. Allfällige Überbleibsel von einem vorgängigen Build-Prozess aufräumen
  2. SourceCode kompilieren

Diese beiden Teilschritte lassen sich mit zwei verschiedenen Tasks bewerkstelligen. Dabei hat der Task "build" eine Abhängigkeit zum "clean" Task. So ist sichergestellt, dass immer zuerst alles Weggeräumt wird, bevor ein neuer Build ausgeführt wird.

Task("clean").Does(() => {
	CleanDirectories("../src/**/bin/debug");
	CleanDirectories("../src/**/obj/debug");
	CleanDirectories("../src/**/obj/release");
	CleanDirectories("../src/**/bin/release");
	CleanDirectories("../deploy");
});

Task("build").IsDependentOn("clean").Does(() => {
	DotNetBuild("../src/NopCommerce.sln",settings=>settings.SetConfiguration(buildConfiguration));
});

UnitTests von nopCommerce ausführen

Nach dem Build wollen wir die UnitTests ausführen um sicher zu sein, dass allfällige Änderungen keine "Breaking-Changes" verursacht haben. Dazu erstellen wir einen neuen Task. Zusätzlich muss das Tool NUnit.Runner inkludiert werden. Siehe http://cakebuild.net/dsl/nunit/ 

Da wir nur UnitTests ausführen wollen, welche keine Datenbank benötigen, exkludieren wir alle Tests die das Wort "Data" enthalten.

#tool "nuget:?package=NUnit.Runners&version=2.6.4"

Task("run-unittests").IsDependentOn("build").Does(() => {
	Func<IFileSystemInfo, bool> exclude_data_tests = fileSystemInfo => !fileSystemInfo.Path.FullPath.Contains("Data");
	FilePathCollection unitTestDlls = GetFiles("../src/**/bin/**/*.Tests.dll", exclude_data_tests);
	NUnit(unitTestDlls);
});

nopCommerce als Web Deploy Package

Nun bleibt zu letzt noch das Verpacken von dem kompilierten und getesteten Source Code in ein Web Deploy Package. Auch dazu erstellen wir einen neuen Task und fügen das AddIn Cake.WebDeploy hinzu:

#addin "Cake.WebDeploy"

Task("create-webdeploy-package").IsDependentOn("run-unittests").Does(() => {
	MSBuild("../src/Presentation/Nop.Web/Nop.Web.csproj", new MSBuildSettings {
		Configuration = buildConfiguration,		
		ToolVersion = MSBuildToolVersion.VS2015,
		ArgumentCustomization = args => 
			args.Append("/t:Package")
			.Append("/p:VisualStudioVersion=14.0")
			.Append("/p:PackageLocation="deploy\\nopcommerce-3.8.zip")
	});
});

Das ganze Script auf einen Blick

Nachfolgend ist das ganze Script zu sehen, das benötigt wird

#tool "nuget:?package=NUnit.Runners&version=2.6.4"
#addin "Cake.WebDeploy"
#addin "Cake.FileHelpers"

var target = Argument("target", "create-webdeploy-package");
var buildConfiguration = "Release";

Setup(context =>
{
    Information("Start with Build-Process");
});

Teardown(context =>
{
    Information("Build-Process finished");
});

Task("clean").Does(() => {
	CleanDirectories("../src/**/bin/debug");
	CleanDirectories("../src/**/obj/debug");
	CleanDirectories("../src/**/obj/release");
	CleanDirectories("../src/**/bin/release");
	CleanDirectories("./deploy");
});

Task("build").IsDependentOn("clean").Does(() => {
	DotNetBuild("../src/NopCommerce.sln",settings=>settings.SetConfiguration(buildConfiguration));
});

Task("run-unittests").IsDependentOn("build").Does(() => {
	Func<IFileSystemInfo, bool> exclude_data_tests = fileSystemInfo => !fileSystemInfo.Path.FullPath.Contains("Data");
	FilePathCollection unitTestDlls = GetFiles("../src/**/bin/**/*.Tests.dll", exclude_data_tests);
	NUnit(unitTestDlls);
});

Task("create-webdeploy-package-replacesettings").IsDependentOn("run-unittests).IsDependentOn("create-webdeploy-package").Does(() => {});

Task("create-webdeploy-package").IsDependentOn("run-unittests").Does(() => {
	MSBuild("../src/Presentation/Nop.Web/Nop.Web.csproj", new MSBuildSettings {
		Configuration = buildConfiguration,		
		ToolVersion = MSBuildToolVersion.VS2015,
		ArgumentCustomization = args => 
			args.Append("/t:Package")
			.Append("/p:VisualStudioVersion=14.0")
			.Append("/p:PackageLocation="./deploy/nopcommerce-3.8.zip")
	});
});


RunTarget(target);

Fazit

Das Arbeiten mit Cake (C# Make) macht extrem Spass. Nach nur wenigen Minuten haben wir die Prinzipen hinter Cake (C# Make) verstanden und konnten auch komplexere Tasks (z.B. Updaten einer Datenbank mit AliaSQL) sehr schnell bewerkstelligen. Definitiv ein Werkzeug, dass man im Microsoft .NET Umfeld stark im Auge behalten sollte.

bambit-logo abgeschnitten