Project Scaffold

Auf dem Developer Open Space 2014 in Leipzig stellten A. Groß und S. Forkmann im Rahmen einer Vorführung des Paketmanagers Paket das Projekt ProjectScaffold vor.

Die Idee ist, ähnlich wie bei Rails mit einem kleinen kommandozeilenbasierten Tool ein paar benötigte Daten abzufragen, um daraus eine neue Visual Studio Umgebung zu erzeugen. Diese Umgebung basiert auf dem Buildmanager FAKE und dem Paketmanager Paket. Man kann sie direkt mit einem Github-Projekt verknüpfen, muss aber nicht. Heraus kommt ein Skelett, bestehend aus einem kleinen Projekt inklusive Dokumentation, dass man per build.cmd direkt bauen, testen und deployen kann.

Da man eigenes Projekt sowieso zur Zeit noch ältere Funktionen von FAKE benutzt, nehme ich die Gelegenheit zum Anlass um umzusteigen.

Installation

Die Installationsidee ist erst einmal gewöhnungsbedürftig: man klont das Github-Projekt:

git clone https://github.com/fsprojects/ProjectScaffold.git

Danach ruft man erstmalig die build.cmd auf, die auch später per Doppelklick das Projekt bauen wird. Beim ersten Aufruf werden bereits alle benötigten Softwarepakete mittels Paket heruntergeladen. Der initiale Build dauert dementsprechend etwas länger. Es werden einige Information zu dem Projekt abgefragt, wie etwa Name, eine kurze Beschreibung, eine längere Beschreibung, Autor und einige Dinge mehr, die im Build-Skript hinterlegt werden. Danach stürzt das Skript beim Ausführen der mitgelieferten Tests ab – das liegt allerdings daran, dass ich den Batch unter der Cygwin-Bash gestartet hab. Startet man ihn in einem Konsolenfenster, läuft er sauber durch.

 

Bestandsaufnahme

Uns wurde also ein Projektrahmen erzeugt – aber was fangen wir nun damit an?

Beginnen wir mit den Git-Dateien. Wer sich von Github bereits eine .gitignore hat zusammenstellen lassen, kann diese natürlich weiterverwenden. Die hier mitgelieferte ist jedenfalls um einiges umfangreicher als meine bisherige. Die .gitattributes war bei mir bisher leer, so dass ich auch diese Daten gerne übernehme.

Die README.md hatte ich bereits mit einer kleinen Beschreibung gefüllt, der neue Inhalt wird also verworfen. Die RELEASE_NOTES.md dagegen nutze ich als Template und fülle die erste Versionszeile. Eine LICENSE.txt hat mir bislang auch gefehlt.

Die für die Mono-Version meines Projekts zuständigen Dateien build.sh sowie .travis.yml lasse ich weg. Durch die WPF-lastigkeit der Programme ist eine Kompilierung unter dieser Umgebung zumindest fragwürdig.

Die appveyor.yml für den gleichnamigen Online-Buildserver wird übernommen, die build.cmd ebenso. Letztere wird allerdings so geändert, dass sie wie bisher auf der Kommandozeile nachfragt, ob ich einen weiteren Build starten möchte:

@echo off
cls

.paket\paket.bootstrapper.exe
if errorlevel 1 (
  exit /b %errorlevel%
)

.paket\paket.exe restore
if errorlevel 1 (
  exit /b %errorlevel%
)

IF NOT EXIST build.fsx (
  .paket\paket.exe update
  packages\FAKE\tools\FAKE.exe init.fsx
)

:Build
packages\FAKE\tools\FAKE.exe build.fsx %*

rem Bail if we're running a TeamCity build.
if defined TEAMCITY_PROJECT_NAME goto Quit
if defined APPVEYOR goto Quit

rem Loop the build script.
set CHOICE=nothing
echo (Q)uit, (Enter) runs the build again
set /P CHOICE= 
if /i "%CHOICE%"=="Q" goto :Quit

GOTO Build

:Quit
exit /b %errorlevel%

Die Dateien paket.dependencies und paket.lock werden ebenso übernommen. In ihnen sind die bisherigen Abhängigkeiten des Projekts vermerkt.

Als Schaltstelle der zu erzeugenden Solutions, der auszuführenden Test, der zu erstellenden Dokumente sowie die zusammenzustellenden Deployments wird später die build.fsx  die größten Änderungen erfahren.

Ich füge noch das Verzeichnis .paket zur Quellcodeverwaltung hinzu und kann bereits den ersten Versuch starten – dem Skript fehlen NUnit-Tests, und die AssemblyInfo.cs werden in den falschen Unterverzeichnissen erzeugt. Da ich noch nichts an meine Solutions angepasst habe, ist das ein zufriedenstellendes Ergebnis.

 

Anpassungen

Die build.fsx ist in dokumentierte Abschnitte unterteilt. So kann ich die zusammengehörenden Elemente bestens lokalisieren. Mein Projekt benötigt beispielsweise keine NuGet-Unterstützung, also kann ich im selben Step auch die Tags entfernen, die das Init-Skript angelegt hat. Letztendlich werde ich aber fast alle Targets entfernen, da ich auch (noch?) keine Dokumentation habe, die zu erstellen wäre. Auch das automatische Committen auf GitHub entfällt. Dementsprechend kann ich natürlich auch die Abhängigkeiten aus der paket.dependencies löschen, wie z.B. NUnit oder Octokit.

ProjectScaffold schlägt vor, die Solutions direkt im Root-Pfad des Projekts abzulegen. Der Vorteil, der sich bietet: die Paket-Abhängigkeiten von allen enthaltenen Solutions und die des Build-Prozesses selbst werden in einem einzigen packages-Unterordner vereint. Ich werde den Vorschlag insofern aufgreifen, dass ich die Solution aus dem Ordner src/BackOffice nach src verschiebe. So kann ich die Pakete der verschiedenen Solutions vereinen, aber dennoch getrennt halten von denen des Build-Systems.
Wahrscheinlich wäre es besser, dennoch beide packages-Ordner zusammenzufassen. Dadurch könnten alle benötigten Abhängigkeiten zu Beginn des Buildskripts nachgeladen werden, so muss ich sie zusätzlich zur Quellcodeverwaltung hinzufügen. Ich beruhige mein Gewissen damit, dass ich das bisher auch so handhabe und merke mir vor, die andere Variante in Zukunft wenigstens einmal auszuprobieren.

 

Versionsinfo

Der von ProjectScaffold angedachte Weg, die aktuelle Versionsnummer in die Assemblys einzupflegen, sieht vor, alle .csproj-Dateien zu finden und ihre AssemblyInfo.cs neu zu generieren. Hier bin ich mir noch nicht sicher, ob eventuell vorhandene Dateien überschrieben werden. Das hatte in früheren Versionen von FAKE zur Folge, dass man pro Projekt die GUID in der build.fsx angeben musste. Da es nun eine eigene Funktion ist, die die AssemblyInfo.cs erzeugt, sollte dem eigentlich nicht mehr so sein.
Ein neues Problem ergibt sich dadurch, dass genCSAssemblyInfo davon ausgeht, dass das Projekt direkt unter src/ProjektName zu finden ist. Das ist bei mir jedoch nicht der Fall, die Projekte liegen unter src/BackOffice/Projektname. Eventuell passe ich das Skript hier noch an, so dass der Pfad zur AssemblyInfo.cs korrekt zusammengesetzt wird.

Solange behelfe ich mir mit meiner bisherigen Methode, auf die auch meine Studio-Projekte ausgelegt sind: die AssemblyInfo.cs wurde zweigeteilt. In der Datei mit diesem Namen befinden sich die projektspezifischen Einstellungen wie GUID, Description und Title. Die Versionsnummer befindet sich in der Datei src/VersionInfo.cs. Diese wird in jedes Projekt verlinkt und muss dadurch nur einmal erzeugt werden.

Target "SetAssemblyInfo" (fun _ ->

    [Attribute.Product project
     Attribute.Version currentVersion
     Attribute.Company "Slesa Solutions"
     Attribute.Copyright "Copyright ©  2012"
     Attribute.Trademark "GPL V2"]
     |> CreateCSharpAssemblyInfo "./src/VersionInfo.cs"
)

Der Nachteil liegt auf der Hand: bei jedem neuen Projekt müssen einmal die doppelten Einträge aus der lokalen AssemblyInfo.cs entfernt werden, zusätzlich muss der Link zur VersionInfo.cs hinzugefügt werden. Das automatische Generieren der Dateien pro Projekt ist eindeutig vorzuziehen.

 

Buildserver

Warum ich den ganzen Spaß überhaupt auf mich genommen habe – AppVeyor hat stets die Buildnummer als Versionsnummer interpretiert. Dadurch kam es zur Fehlermeldung beim Erzeugen des Setup-Skripts – eine Version 17 ist bei WiX verboten.
Mir ist nicht ganz klar, was genau sich durch das neue Skript alles geändert hat, aber offensichtlich werden die Werte nun richtig interpretiert, das Skript läuft durch. Hurra!

 

Fazit

Ich denke, ProjectScaffold kommt seinem Ruby-Vorbild überaus nahe. Ohne Installation einer Komponente erzeugt sich durch das Abrufen eines GitHub-Projektes ein neues, eigenes Projekt wie von selbst. Das sowieso benötigte Buildskript fragt beim ersten Aufruf die benötigten Daten ab. Es werden jede Menge Build-Targets erzeugt, die entweder direkt so benutzt werden können, wie zB das erzeugte NuGet-Paket, oder man entfernt die nicht benötigten Targets. Das Ergebnis lässt sich ohne weiteres auf einem Buildserver wie TeamCity oder AppVeyor starten und liefert ein erstes Ergebnis.

Was man hierbei auch nicht unterschätzen darf: die ganze Welt von FAKE steckt hier unter der Haube. Ein zusätzliches ClickOnce-Setup, andere Prozesse aufrufen, … das Buildsystem hat so mannigfaltige Möglichkeiten, die es zu erkunden gilt.

Im Zusammenspiel mit dem neuen Paketmanager Paket entsteht so ein Ökosystem, auf dem man mit Sicherheit aufbauen kann.

Dieser Beitrag wurde unter .NET, Build-Tools, C#, F#, FAKE abgelegt und mit , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Hinterlasse einen Kommentar