In diesem Artikel wird ein leicht nachvollziebares Grundgerüst für die Automatisierung eines einfachen Workflows für das Deployment eines Web-Projekts mit Hilfe von Grunt.js unter Windows vorgestellt. Dabei wird nur die Grundstruktur implementiert, so dass spezielle Anforderungen, wie z.B. Minification, SASS-Compilierung, die Unterstützung von Test-Driven-Design oder Mobile-Cross-Platform-Development vom Entwickler je nach seinen speziellen Bedürfnissen hinzugefügt werden können.
Die Aufgabe
Die Aufgabe besteht darin, mit Hilfe von Grunt.js ein typisches Projektverzeichnis mit Sourcen gefiltert in ein lokales Build-Verzeichnis zu kopieren, wo sie komprimiert werden. Die komprimierte Datei wird dann über Secured Shell-Kommandos (ssh, scp) auf einen Remote-Server kopiert und dort im gewünschten Zielverzeichnis entpackt.
Unser Grunt.js-Skript soll zwei Befehle beherrschen:
- grunt build kopiert das Projekt gefiltert in einen lokalen build-Ordner und komprimiert es dort,
- grunt deploy führt zunächst grunt build aus, überträgt das damit erzeugte Archiv auf den Remote-Server, entpackt es dort und löscht außerdem während dieses Prozesses alle nicht mehr benötigten lokal und remote erzeugten Dateien.
Die Voraussetzungen
Um dieses Ziel zu erreichen, sind eine ganze Reihe von Tools auf Windows zu installieren.
Bitte beziehen Sie die folgenden Tools nur aus den angegebenen Quellen. Wer andere Quellen verwendet, stößt bei den meist für Linux-Umgebungen entwickelten Tools leicht auf ungenügend compilierte Windows-Versionen.
Die Zeit, die man deshalb mit Fehlersuche vertrödelt, machte den größten Teil der Arbeit für diesen Artikel aus. Diese Zeit erspare ich Ihnen gerne.
Wir installieren im folgenden:
- Node.js inklusive des wichtigen Node Package Managers npm,
- Grunt.js mit den Modulen grunt, grunt-contrib-jshint und grunt-shell,
- Git von git-scm.com, davon benötigen wir nur Git Bash, ssh.exe und scp.exe aus dem bin-Verzeichnis,
- tar und gzip für Windows (GnuWin32).
Bei der Installation ist einiges zu beachten. Sollten also schon einige der aufgezählten Tools installiert sein, so sollte man trotzdem noch einmal prüfen, ob sie auch in der Art eingerichtet wurden, wie es im Folgenden empfohlen wird.
Falls andere Tools als die vorgeschlagenen verwendet werden, die auch Kommandos für die Windows-Console zur Verfügung stellen - z.B. OpenSHH für ssh -, wird dringend empfohlen, diese zu deinstallieren und stattdessen die vorgeschlagenen zu verwenden. Es kommt nämlich sonst garantiert zu sehr zeitraubenden Installationsproblemen.
Installation Node.js inklusive npm
Grunt.js setzt auf Node.js auf. Node.js ist aber auch ein Framework "mit Zukunft", das von vielen anderen sehr interessanten Tools benötigt wird, weshalb es grundsätzlich keine schlechte Idee ist, es zu installieren.
Wir installieren Node.js über den Windows-Installer, den man auf der Homepage des Projekts downloaden kann.
Nach der Installation findet man im Startmenu unter Alle Programme / Node.js die Möglichkeit, Node.js entweder direkt als Console oder über eine Windows-Console zu starten (Kommando node in die Console eingeben).
Man sollte unter Start / Systemsteuerung / System / Erweiterte Systemeinstellungen / Umgebungsvariablen / PATH / Bearbeiten prüfen, ob der Pfad:
- C:\Program Files\nodejs;
existiert. Wenn nicht, dann sollte man ihn eintragen, damit Node.js von jedem Ordner im Dateisystem per Console zugänglich ist.
Nach der Installation von Node.js kann man den wichtigen Node Package Manger npm nutzen. Auch ihn sollte man aus jedem Verzeichnis heraus aufrufen können. Deshalb auch hier überprüfen, ob folgender Dateipfad in die Umgebungsvariable PATH von Windows eingetragen ist:
- C:\Users\<User>\AppData\Roaming\npm;
<User> ist hier der Name des eigenen Windows-Account, bei mir z.B. Admin.
Man kann dann bequem alle möglichen Node.js-Pakete installieren aus jedem Verzeichnis heraus, indem man z.B. einfach eingibt (bitte hier nicht ausführen):
- npm install -g <module>
<module> steht dann für ein npm bekanntes Modul - z.B. grunt -, das man für seine Entwicklung benötigt. npm holt sich damit dieses Paket über das Internet - man braucht gar nicht zu wissen, woher - und installiert es im C:\Users\<user>\AppData\Roaming\npm\node_modules - Verzeichnis. Ich werde später npm verwenden, um globale und nur Projekt-gültige Grunt-Module zu installieren (s. weiter unten).
Der Parameter -g steht übrigens für "global verfügbar", d.h. ein so installiertes Node.js-Paket (also ein Javascript-Programm basierend auf Node.js) ist aus jedem Verzeichnis von Windows heraus aufrufbar.
Bis hierher reicht es, dass wir Node.js installiert haben mit dem geeigneten Windows-Installer und überprüft haben, ob die beiden Pfade in der Umgebungsvariable PATH existieren.
Installation Grunt.js
Als nächstes installieren wir Grunt.js. Dafür sind allerdings mehrere Schritte notwendig.
Wir installieren hier nur den Grunt-Client, indem wir ein Windows-Consolen-Fenster als Administrator (!) starten und dann das entsprechende Node.js-Package mit Hilfe des Node Package Mangagers npm mit folgendem Befehl installieren:
- npm install -g grunt-cli
Man startet eine Console als Administrator am einfachsten über
- Start / Alle Programme / Zubehör / Eingabeaufforderung (mit rechter Maus-Taste als Administrator starten).
Wichtig: Dies ist übrigens das einzige Mal im Verlauf unserer Installationen, dass wir eine Console als Administrator öffnen müssen, weshalb wir nach erfolgreicher Ausführung des Befehls oben die Console sofort wieder schließen, so dass wir die folgenden Installationen nicht versehentlich als Administrator ausführen.
Wir können jetzt einzelne Grunt.js-Projekte in unseren jeweiligen Projektordnern anlegen. Dies wird später im Zusammenhang mit unserem Beispiel beschrieben. Hier geht es jetzt erstmal nur um die Installation aller benötigten Tools.
Installation ssh, scp (Git)
Wir wollen unser Automatisierungsprojekt ja dafür einsetzen, Sourcen aus dem lokalen Entwicklerprojekt in ein Remote-Produktionssystem zu übertragen. Dafür benötigen wir Funktionen, mit denen wir
- Dateien vom lokalen Rechner auf den Remote-Server übertragen können und
- Kommandos z.B. für das Entpacken von übertragenen Archiven auf dem Remote-Server über die Unix- bzw. Linux-Shell ausführen können.
Für diese Aufgaben eignen sich am besten Secured Shell (ssh) und Secured Copy (scp).
Es gibt dafür das weit verbreitete OpenSSH, das wir aber hier im Windows-Kontext nicht (!) verwenden. Bei diesem Paket funktioniert die Authentifizierung für ssh und scp leider nicht richtig auf Windows, was dann zu unglaublich aufwändigen aber fruchtlosen Versuchen führt, Fehler zu finden.
Stattdessen installieren wir einfach Git für Windows, falls es nicht schon installiert ist. Ist dieses Tool installiert, finden wir dort im bin-Verzeichnis die ausführbaren Programme shh.exe und scp.exe.
Damit funktioniert auch die Authentifizierung vor allem über RSA-Key ausgezeichnet, so dass wir nicht bei jedem Kommando, das wir mit ssh auf dem Remote-Rechner ausführen wollen, das Passwort in einer zweiten Zeile hinzufügen müssen.
Dies ist Voraussetzung dafür, dass wir diese Kommandos innerhalb von Grunt.js für die Automatisierung nützen können.
Nach der Installation müssen wir allerdings noch die Umgebungsvariablen aktualisieren, damit ssh und scp auch aus jedem Verzeichnis heraus gefunden werden. Wir fügen in der PATH-Variablen folgenden Pfad hinzu:
- C:\Program Files (x86)\Git\bin;
Git selber benötigen wir für unser rudimentäres Deployment-Projekt nicht. Es ist aber durchaus denkbar, dass es in einer Ausbaustufe desselben irgendwann zum Einsatz kommen kann.
Einrichten der SSH-Authentifizierung mit Schlüsselpaaren
Nach der Einrichtung von ssh sollten wir testen, ob wir Zugang zum Server bekommen.
Wir rufen normalerweise ssh mit unserem User- und SSH-Hostnamen in einer Console auf:
ssh username@ssh-host.de Password:
Allerdings ist für den ersten Aufruf die Installation eines SSH/SFTP-Key (Fingerprints) erforderlich. Die Zugangsdaten und den Fingerprint erhält man von seinem Domain-Hoster.
Wer sich die Sache einfach machen möchte - und das machen wir hier -, der installiert das Windows-Programm Putty. Während der Installation von Putty wird der SSH/SFTP-Key gesetzt, so dass man dies für ssh (GnuWin32) nicht mehr konfigurieren muss. Den SSH/SFTP-Key besorgt man sich vorher von seinem Hoster. Man kann es auch so zusammenfassen: Wer Putty erfolgreich installiert hat, kann auf den Schritt der Installation des Fingerprints per Konfiguration für Secured Shell (ssh, scp) verzichten.
Im Kommando oben stört aber für unser Automatisierungsprojekt noch die Abfrage des Passworts. Sonst müssten wir nämlich während der Ausführung einer Automatisierungs-Task ständig mehrmals das Passwort neu eingeben und zwar jedesmal, wenn ein ssh-Kommando ausgeführt wird.
Deshalb ändern wir für ssh die Authentifizierung von Passwort zu SSH-Authentifizierung mit Schlüsselpaaren.
Dieses Verfahren funktioniert so, dass auf dem Remote-Server ein öffentlicher Schlüssel abgelegt wird und auf dem lokalen Server ein privater Schlüssel. Beim Verbindungsaufbau wird anhand dieses Schlüsselpaares die Legitimation geprüft.
Zunächst richten wir den öffentlichen Schlüssel auf dem Remote-Server ein.
Dies funktioniert überall etwas anders. Deshalb sollte man sich diesbezüglich bei seinem Hoster erkundigen. Ich bin bei Domain Factory und dort funktioniert es so:
Man verbindet sich mit Putty oder unserer ssh-Konsole, die ja im Moment mit Passwort-Zugang funktioniert, mit dem Server und geht in das Root-Verzeichnis seines Accounts.
Dort führt man dann folgenden Befehl aus
ssh-keygen
Dieser Befehl erzeugt nun das Schlüsselpaar in einem Ordner .ssh. Wir finden dann in diesem Ordner zwei Datein:
- id_rsa, das ist der private Schlüssel und
- id_rsa.pub, dies ist der öffentliche Schlüssel.
Beim Ausführen des Kommandos wird man gefragt, ob man eine Paraphrase anlegen möchte. Dies verneinen wir, da man ansonsten wie beim Passwort bei jeder Ausführung danach gefragt wird.
Alternativ kann man auch in einem beliebigen lokalen Ordner z.B. folgenden Befehl ausführen:
ssh-keygen -t rsa -b 2048
Die Ausgabe des Schlüsselpaares ist identisch wie oben. Auch hier geben wir keine Paraphrase ein.
Wir benennen den öffentlichen Schlüssel auf dem Remote-Server um in authorized_keys und kopieren den privaten Schlüssel auf den lokalen Rechner und zwar in einen Ordner, der nur dem lokalen User zugänglich ist, z.B. in den Ordner:
- C:\Users\<user>\SSH_private_key
In diesem Ordner finden wird also jetzt den privaten Schlüssel id_rsa.
Wir müssen aber noch die Berechtigungen für diesen Ordner genau einstellen, denn sonst bekommen wir Fehlermeldungen. Dazu verwenden wir jetzt die Shell von Git (Start / Alle Programme / Git / Git Bash). Danach befinden wir uns direkt in unserem User-Verzeichnis, also im Verzeichnis C:\Users\<user>, wobei <user> für unseren Windows-Username steht. Dort führen wir dann folgenden Befehlt aus in der Git Bash-Console (mit der Windows-Konsole geht das nicht):
$ chmod -Rf 400 ~/SSH_private_key/
Wenn wir alles richtig gemacht haben, können wir jetzt ssh auf Basis des Schlüsselpaares testen z.B. mit folgendem Befehl, der jetzt aber in in einer Windows-Konsole ausgeführt wird (!):
ssh -i C:/Users/<user>/SSH_private_key/id_rsa <ssh-username>@<ssh-host.de> ls
Für <user> müssen wir natürlich unseren Windows-Username setzen und für <ssh-username> und <ssh-host.de> unseren SSH-Usernamen und SSH-Hostnamen, die wir von unserem Hoster bekommen haben.
Als Ergebnis würden jetzt alle Verzeichnisse und Dateien auf der Root-Ebene unseres Remote-Servers angezeigt und zwar ohne dass wir nach einem Passwort gefragt werden.
Tipp: Man kann sich jetzt übrigens eine bequeme Passwort-freie Console für den SSH-Zugang einrichten auf seinem Desktop, der bessser ist als Putty. Dazu erzeugt man eine Verknüpfung auf dem Desktop und gibt dann unter Verknüpfung / Ziel (bzw. in das Rechte Maustaste / Verknüpfung erstellen / aufpoppende Fenster) Folgendes ein:
C:\Windows\System32\cmd.exe /k ssh -i C:/Users/<user>/SSH_private_key/id_rsa <ssh-user>@<ssh-host>
Bei Klick auf diese Verknüpfung haben wir jetzt immer direkten Zugang zu unserem Remote-Server über dessen Unix-Shell und zwar ohne Passworteingabe.
Installation tar, gzip (GnuWin32)
Als letztes benötigen wir noch Tools, mit denen wir auf Console-Ebene Projekt-Ressourcen packen können. Ich verwende hier das sogenannte tar.gz-Format.
Allerdings gibt es für Windows auch hier eine Besonderheit zu beachten. Beim tar.gz-Format werden in einem ersten Schritt alle Dateien bzw. Verzeichnisse, die zu packen sind, gesammelt und in einer .tar-Datei zusammengefasst. Diese Datei wird normalerweise auf Linux-Systemen mittels GZip komprimiert, wenn man die -z Option von tar verwendet.
Genau dies -z Option funktioniert nicht unter Windows. Stattdessen muss man tatsächlich zwei Kommandos hintereinander ausführen, einmal tar (ohne -z) und danach gzip und erhält somit das gleiche Ergebnis.
Wir brauchen also folgende auf Window-Consolen ausführbare Programme:
- Tar for Windows (GnuWin32) und
- Gzip for Windows (GnuWin32).
Wir fügen nach der Installation dann Folgendes in die Umgebungsvariable PATH ein:
- C:\Program Files (x86)\GnuWin32\bin;
Nun haben wir alle erforderlichen Tools und Programme für unsere Deployment-Automatisierung eingerichtet.
Zwischenstand
Es empfiehlt sich, wirklich nur die vorgeschlagenen Quellen für die Installationen zu verwenden. Es kostet unglaublich viel Zeit, z.B. ssh und scp über OpenSSH zu implementieren, um dann frustiert abzubrechen und irgendwann zu entdecken, dass die beiden Programme sauber für Git compiliert wurden, aber das Build für Windows von OpenSSH schwer nachvollziehbare Fehlerquellen enthält.
Dies Gleiche gilt auch für tar bzw. gzip. Es benötigt einige Zeit für's Ausprobieren, bis man feststellt, dass der -z Parameter (gzip-Komprimierung) unter Windows nicht funktioniert.
Es kommen noch weitere Installationsaufgaben auf uns zu, diese beziehen sich allerdings nicht auf Programme, sondern auf Module, die wir je nach Bedarf für einzelne Projekte einrichten müssen. Deshalb werden diese Installationen im Zusammenhang mit dem folgenden Beispiel vorgenommen.
Beispiel für die Automatisierung des Deployments eines Test-Projekts
Wir können jetzt unser Grunt.js-Automatisierungsprojekt in jedem beliebigen Ordner von Windows einrichten. Vorzugsweise kommt dafür der Root-Ordner eines konkreten Entwickler-Projekts in Frage.
Entwickeln wir z.B. ein Modul oder ein Theme für ein Content-Management-System wie Drupal, richten wir unser Grunt-Projekt im Modul- oder Theme-Ordner ein. Wollen wir die Durchführung eines Release-Updates automatisieren (was hier nicht der Fall ist), richten wird unser Projekt in einem übergeordneten Ordner oder sonstigen Ort im Dateisystem ein (außer natürlich in den Verzeichnissen unseres CMS).
In einem Ordner ist immer nur ein Grunt-Projekt möglich. Es können aber innerhalb eines Grunt-Projekts beliebig viele Automatisierungsaufgaben definiert werden. In unserem Projekt z.B. erscheinen zwei Aufgaben sinnvoll:
- grunt build kopiert alle für die Auslieferung relevanten Ressourcen in einen Ordner außerhalb des Projekts,
- grunt deploy führt zunächst build aus und packt dann die im build-Ordner vorhandenen Ressourcen, überträgt sie auf den Remote-Server und entpackt sie dort im Zielverzeichnis. Alle nicht benötigten Dateien - z.B. archive.tar.gz - werden wieder gelöscht und zwar sowohl lokal als auch remote.
Unser Projektordner besteht aus einigen Files und Dateien.
Abb. 1: Projekt-Ordner, der auf einen Remote-Rechner kopiert werden soll
Vorbereitung der Automatisierungsaufgabe
Im Root des Projekt-Ordner rufe ich die Console auf (Alt-Taste drücken, mit der Maus in einen leeren Bereich klicken, rechte Maustaste und Eingabeaufforderung hier öffnen aus dem Popup-Menu auswählen). Die Console bitte nicht im Administrator-Modus starten.
In die Console gebe ich jetzt hintereinander Folgendes ein:
npm init npm install grunt --save-dev npm install grunt-contrib-jshint --save-dev npm install grunt-shell --save-dev
Mit npm init wird package.json angelegt, das einige Projekt-Parameter speichert, die während der Installation abgefragt werden. Falls Sie sich bei einigen Fragen unsicher sind, was Sie eingeben sollen, drücken Sie einfach Enter für einen Default-Wert.
Als Nächstes wird das eigentliche Grunt-Modul installiert. Mit dem dritten Befehl installieren Sie ein Debug-Tool, das konkrete Hinweise mit Zeilennummern bei fehlerhaftem Javascript liefert. Dieses Modul sollten sie immer installieren.
Der letzte Befehl enthält das für unsere Automatisierungsaufgabe wichtigste Modul grunt-shell, mit dem synchronisiert Console-Kommandos ausgeführt werden können.
Mehr brauchen wir nicht. Es gibt z.B. spezielle Module für ssh (grunt-ssh) oder alternative Module für bash-Kommandos wie (grunt-exec). Für die meisten Aufgaben benötigt man diese Module nicht, auch wenn viele Beispiele im Internet sie verwenden (insbesondere das Module grunt-ssh, das mir aber zu kompliziert für Windows zu konfigurieren war). grunt-shell reicht vollkommen aus, um
- Client-Programme auf Window auszuführen. Das gilt auch für Erweiterungen unseres Workflows z.B. in Richtung Compass oder Git,
- Dateien auf einen Remote-Server zu kopieren, da unser oben sauber installiertes scp.exe ebenfalls mit grunt-shell synchron ausgeführt wird und
- Kommandos auf dem Remote-Server auszuführen, da auch ssh.exe wunderbar synchron funktioniert.
Abb. 2: Projekt-Ordner nach der Installation von Grunt mit Modulen
In dem von Grunt bei der Installation angelegten Ordner node_modules in Abb. 2 befinden sich jetzt die drei Module grunt, grunt-contrib-jshint und grunt-shell. In Abb. 2 erkennen wir außerdem die Datei package.js mit den grundlegenden Projektdaten.
Wir legen jetzt noch zusätzlich im Root eine Datei Gruntfile.js an, die dann unser Automatisierungsprogramm enthalten soll.
Außerdem erzeugen wir auf der obersten Ebene unseres Datei-Systems einen Ordner für unser Build:
- C.\BuildTestProject.
Damit haben wir alle vorbereitenden Aktivitäten für unser Automatisierungsprojekt abgeschlossen und können uns nun der Programmierung der Automatisierungsaufgaben, dem Build und Deploy unseres Projekt-Ordner widmen.
Programmierung der Automatisierungsaufgabe
Für unser Automatisierungsproblem müssen wir jetzt drei Dateien manipulieren bzw. erzeugen:
- package.json: Hier können zusätzlich zu den generierten Projektinformationen globale Parameter abgelegt werden, z.B. Zugangsdaten zum Remote-Server, zentrale Pfade, etc.
- exclude.txt: Hier werden die Ordner und Dateien gelistet, die vom Build-Prozess ausgenommen werden sollen.
- Gruntfile.js: Enthält unser Programm.
Fangen wir mit der einfachsten Datei an. exclude.txt könnte z.B. so aussehen:
docs node_modules Gruntfile.js package.json exclude.txt npm-debug.log stuff-not-used.js
Wie diese Datei aufgebaut sein muss, hängt vom verwendeten copy-Befehl ab. Wir verwenden hier xcopy mit dem Paramter /EXCLUDE:exclude.txt. Was die Möglichkeiten dieses Parameters betrifft, sei auf die entsprechende Dokumentation zu xcopy verwiesen.
Nun die Datei package.json:
{ "name": "GruntTestProject", "version": "1.0.0", "devDependencies": { "grunt": "^0.4.5", "grunt-contrib-jshint": "^0.10.0", "grunt-shell": "^1.1.1" }, "archive": "archive", "exclude": "exclude", "buildPath": "../../../Work/BuildTestProject", "remote": { "login": "ssh-user@ssh-host.com", "keyPath": "C:\\Users\\admin\\SSH_private_key\\id_rsa", "targetPath": "webseiten/deployment.test" } }
Wenn man die Datei mit der generierten vergleicht, sieht man, dass hier einige überflüssige generierte Parameter weggelassen wurden und einige andere hinzugekommen sind und zwar folgende:
- archive ist der Name der Datei, die das gepackte Projekt enhalten soll, das auf den Server kopiert wird und dort dann im Zielverzeichnis entpackt wird.
- exclude ist der Name unserer Exclude-Datei für den xcopy-Befehl.
- buildPath ist der Pfad zum Ordner, in den das Build des Projekts bei einem grunt build - Kommando kopiert wird.
- remote : login ist der Ausdruck aus SSH-Username und SSH-Hostname für unser ssh- und scp-Login.
- remote : keyPath ist der lokale Pfad zum Autorisierungsschlüssel für die SSH-Verbindung
- remote: targetPath ist das Verzeichnis auf dem Remote-Server, in welches das Build hochgeladen werden soll.
Tipp: Verwenden Sie für den buildPath unbedingt relative Pfade, denn scp und ssh verstehen offensichtlich keine Pfadnamen mit Laufwerksbuchstaben, wie sie zur Angabe von absoluten Pfaden in Windows üblich sind. Diese Programme kommen ja aus der Linux-Welt. Jedenfalls habe ich keine Möglichkeit zur Verwendung von absoluten Pfaden gefunden. Bei Angabe des Schlüssels dagegen ist der absolute Pfad kein Problem. Dann muss man aber zwei Backslashs verwenden, die dann in der Javascript-String-Verarbeitung in einen Backslash umgewandelt werden.
Durch die Auslagerung dieser Parameter in package.json machen wir unser Automatisierungs-Programm Gruntfile.js wiederverwendbar, d.h. wenn wir ein neues Projekt anlegen (z.B. mit Yeoman - keine Sorge: benötigen wir hier nicht), brauchen wir nur diese Parameter zu ändern und schon müsste es funktionieren.
Kommen wir nun zum eigentlichen Programm, das in Gruntfile.js gespeichert wird und schauen wir uns das fertige Ergebnis an, um es anschließend zu diskutieren.
'use strict'; module.exports = function(grunt) { grunt.initConfig({ pkg : grunt.file.readJSON('package.json'), ssh : 'ssh.exe -i <%= pkg.remote.keyPath %> <%= pkg.remote.login %> -v', scp : 'scp -i <%= pkg.remote.keyPath %> -vr', shell : { "delete" : { command : ['echo "del /Q \'<%= pkg.buildPath %>/<%= pkg.archive %>.tar.gz\'"', 'del /Q "<%= pkg.buildPath %>/<%= pkg.archive %>.tar.gz"', 'del /Q "<%= pkg.buildPath %>/<%= pkg.archive %>.tar"' ].join('&&') }, "copy" : { command : 'xcopy *.* "<%= pkg.buildPath %>" /S /Y /EXCLUDE:<%= pkg.exclude %>.txt ' }, "package" : { command : ['tar -cvf <%= pkg.archive %>.tar *', 'gzip --force --best <%= pkg.archive %>.tar' ].join('&&'), options: { stderr: false, execOptions: { cwd: '<%= pkg.buildPath %>' } } }, "deploy" : { command : ['<%= scp %> "<%= pkg.buildPath %>/<%= pkg.archive %>.tar.gz" <%= pkg.remote.login %>:<%= pkg.remote.targetPath %>', '<%= ssh %> tar -xzvf <%= pkg.remote.targetPath %>/<%= pkg.archive %>.tar.gz -C <%= pkg.remote.targetPath %>/', '<%= ssh %> rm <%= pkg.remote.targetPath %>/<%= pkg.archive %>.tar.gz', '<%= ssh %> chmod 755 <%= pkg.remote.targetPath %>/*', ].join('&&') } } }); grunt.loadNpmTasks('grunt-shell'); grunt.registerTask('build', ['shell:copy', 'shell:package']); grunt.registerTask('deploy', ['shell:delete', 'build', 'shell:deploy']); };
Das Programm besteht aus drei Teilen.
- Den größten Teil nimmt die initale Konfigurierung ein.
- Es folgt das Laden der benötigten Module und als letztes die
- Definition der Aufgaben, wobei diese hier aus einer synchronen linearen Ausführung der in der initialen Konfiguration definierten Einzelaufgaben bestehen.
Wir benötigen z.B. zweimal einen Befehl zum Löschen der Datei archive.tar.gz und zwar einmal lokal und einmal remote.
Für die lokale Löschung verwenden wir den Windows-Terminal-Befehl del mit der Option /Q für Quiet, d.h. es soll kein Bestätigungs-Prompt erfolgen, weil dieser bei Automatisierung stören würde.
Für die Löschung auf dem Remote-Server müssen wir das analoge Linux-Kommando rm über ssh ausführen. Wir schauen uns den Befehl unter "deploy":command einmal ausführlich ohne die Ersetzungsvariable <%= ssh %> an
ssh.exe -i <%= pkg.remote.keyPath %> <%= pkg.remote.login %> -v rm webseiten/staging.test/archive.tar.gz
Das Windows-Shell-Kommando ssh.exe ermöglicht uns den Secured Shell-Zugriff auf unsere Remote-Linux-Shell. Der Parameter -v steht übrigens für verbose (geschwätzig). Man bekommt durch ihn sehr viele Konsolausgaben für das Debugging. Später kann man ihn weglassen.
Damit dieser Zugriff ohne Passwort-Abfrage funktioniert, verweisen wir über den -i Parameter auf den Ort auf unserem lokalen Rechner, auf dem ssh den privaten Schlüssel für die Verbindung findet.
Desweiteren geben wir über eine Ersetzungsvariable unseren SSH-Usernamen und den SSH-Hostnamen an.
Dann folgt der Linux-Befehl zum Löschen der archive.tar.gz-Datei, die hoffentlich sauber entpackt wurde. Dabei wird der vollständige Pfad angegeben ausgehend vom obersten Verzeichnis des User-Accounts, da die Ausführung des Linux-Kommandos per ssh aus dem Home-Verzeichnis erfolgt.
Soviel zum Prinzip, das hier zugrunde gelegt wird. Es sei noch einmal festgestellt, dass es für ssh und scp ein spezielles Grunt-Modul gibt. Dies erwies sich allerdings als sehr schwierig zu parametrisieren unter Windows und ist mit dem Grunt-Module grunt-shell eigentlich auch überflüssig. Ich habe die Möglichkeiten dieser Pakete allerdings nicht genauer untersucht. Mag sein, dass es auch Vorteile gibt, die den Einsatz lohnenswert machen.
Schauen wir uns als Letztes die beiden registerTask-Zeilen an.
Die Build-Aufgabe besteht hier nur im Kopieren der nötigsten Projektdateien in das Build-Verzeichnis. In einem realen Projekt würde man diesen Schritt z.B. erweitern um weitere Aufgaben, wie z.B. Minifying von Javascripten und CSS-Dateien. Auch hierfür gibt es spezielle Module, die man sich in dem Zusammenhang ansehen sollte.
Ein anderes Modul, mit dem man sich auf jeden Fall vertraut machen sollte, das in unserem Fall aber nicht zum Einsatz kommt, ist das Modul grunt-watch. Mit ihm lassen sich die Inhalte von Verzeichnissen synchronisieren. Dies ist manchmal sinnvoll vor allem im Zusammenhang mit dem Einsatz von Emulatoren für die Mobile-Entwicklung. Ein entsprechendes Grunt-Skript wird in diesem Zusammenhang zukünftig in eine anderen Artikel noch vorgestellt.
Die grunt-deploy Aufgabe besteht dann zuletzt z.B. aus folgender Sequenz:
- shell:delete: Löschen einer eventuell veralteten archive.tar.gz-Datei,
- build: also die komplette Ausführung der vorher definierten Task,
- shell:deploy: Kopieren der gepackten Dateien auf den Server und entpackt sie dort im Zielverzeichnis.
Zusammenfassung
Wir haben jetzt ein grundlegendes Gerüst, um unter Windows Standard-Aufgaben im Umfeld von Webentwicklungsprojekten zu automatisieren.
Hier wurde ein Ansatz gezeigt, der als Basis hauptsächlich auf das Grunt-Modul grunt-shell setzt, weil es unter Windows sehr stabil läuft und einfach zu handhaben ist.
Wie im Artikel deutlich wurde, ist die Voraussetzung für eine komfortable Automatisierung die saubere Installation verschiedener Tools. Die Installation ist unter Windows problematisch, da diese Tools im Linux-Umfeld entwickelt wurden und nicht aus allen Bezugsquellen immer einwandfrei funktionieren.
Die hier vorgestellte Kombination stellt aus Sicht des Autors eine Best Practice dar, bei der durch Einhaltung der Bezugsquellen und Ergänzungen der Installation der frustrationsfreie Einsatz von Grunt.js möglicht wird.
Das hier vorgestellte Demoprojekt kann als Ausgangspunkt für eigene Automatisierungsaufgaben verwendet werden.
Kommentare
scp.exe statt sco.exe!
Hallo Wolfgang,
Danke für die ausführlichen Erläuterungen!
Ich denke, Du meintest "scp" statt "sco"; jedenfalls gibt es in meinem bin-Verzeichnis der Version 1.9.4.msysgit.0 kein "sco.exe".
Außerdem ist mehrfach bei "id_rsa" das i verloren gegangen.
Zugegeben, Kleinkram, aber vielleicht magst Du es korrigieren. ;-)
Vielen Dank für die Korrektur
@fd
Hinweise dieser Art kann ich immer gut gebrauchen, zumal ich selbst weiß, wie lange man über so etwas brütet, bis man endlich darauf kommt, dass z.B. sco eigentlich scp sein soll.
Ich hab's deshalb gerne korrigiert!