Hugo mit Git: Vom ersten Theme-Import bis zur automatisierten Veröffentlichung
Update (28.08.2018): Git-Theme-Management und Codeship-Deployment aktualisiert
Wie bereits erwähnt, basiert meine Website seit neuestem auf dem Website-Generator Hugo. Hugo und die meisten seiner Themes verwenden zur Versionsverwaltung die Software git. Meine Meinung zu Git ist einfach: Git lohnt sich für jedes Projekt, das auf Textdateien (“plain-text”) basiert. Somit ist Git auch Teil meines Workflows mit Hugo, den ich im Folgenden beschreiben will. Kenntnisse in Git und Hugo werden vorausgesetzt (ggf. hilft Google!).
Kurzfassung
- Ich empfehle A Successful Git branching model als allgemeine Struktur für Git-basierte Projekte.
- Zum Importieren eines Hugo Themes bietet sich Subtree Merging an.
- Die Veröffentlichung der Website lässt sich mit Hilfe von “Continuous Deployment” weitgehend automatisieren. Dabei werden die statischen Website-Dateien automatisch generiert und auf dem Webserver veröffentlicht, sobald der “master”-Branch auf dem Remote Repository (GitHub oder Bitbucket) geändert wird.
Werkzeugkoffer
Zwingend erforderlich sind natürlich Hugo sowie git. Folgende Tools kann ich darüber hinaus empfehlen:
- Notepad++ zur Bearbeitung der Quelldateien
- Markdown Syntax Highlighting als “user-defined language” für Notepad++
- SourceTree als grafische Benutzeroberfläche (GUI) für Git. Git lässt sich zwar theoretisch nur über die Kommandozeile bedienen, mit einer GUI behält man meiner Meinung nach jedoch besser den Überblick.
Der allgemeine Workflow mit Git
Im Artikel A Successful Git branching model wird eine allgemeine Struktur für Git-basierte Projekte beschrieben. Ich kann dieses Konzept aus eigener Erfahrung absolut empfehlen und werde auch im Folgenden darauf aufbauen.
Kurz gefasst besteht ein Projekt immer mindestens aus einem “develop”-Branch, der den aktuellen Arbeitsstand reflektiert, sowie einem “master”-Branch, der dem aktuell veröffentlichten Status entspricht. Auf Hugo bezogen sollte also jeder Relase im “master”-Branch auch eine Veröffentlichung auf dem Webserver nach sich ziehen – dazu später mehr.
Einrichten der Hugo-Umgebung
Zunächst erstellen wir gemäß den Schritten 1–2 des Hugo Quickstart-Guides eine leere Hugo-Website mit den Namen “sample”:
$ hugo new site sample
Einrichten des Git-Repositories
Als nächstes erstellen wir in dem neuen Verzeichnis ein Git-Repository, entweder über den Assistenten von SourceTree oder über die Kommandozeile:
$ cd sample
$ git init
Initialized empty Git repository in d:/temp/sample/.git/
An dieser Stelle sollte man nun eine .gitignore-Datei erstellen und mit Hilfe dieser Git anweisen, das public/-Verzeichnis zu ignorieren.
$ echo "public/*" > .gitignore
Dann fügen wir die von Hugo erstellen Dateien zum Git-Index hinzu und bestätigen die Änderungen:
$ git add .
$ git commit -m "Add initial Hugo config file"
[master (root-commit) 0153a9e] Add initial Hugo config file
2 files changed, 4 insertions(+)
create mode 100644 .gitignore
create mode 100644 config.toml
Damit ist der “master”-Branch initialisiert und wir können zur weiteren Entwicklung in den “develop”-Branch wechseln:
$ git checkout -b develop master
Switched to a new branch 'develop'
Importieren eines Hugo-Themes mittels Git Subtree Merging
Zur Verwendung eines Themes greifen wir auf das Subtree Merging-Konzept von Git zurück. Damit lässt sich das Theme in einem separaten Branch verwalten und aktualisieren, und dann in ein Unterverzeichnis des eigentlichen Projekts einfügen.
Zunächst fügen wir (exemplarisch) das Hyde-Y-Theme als Remote Repository hinzu und erstellen daraus einen neuen lokalen Branch “theme-hyde-y”:
$ git remote add -f hyde-y_remote https://github.com/enten/hyde-y
Updating hyde-y_remote
warning: no common commits
remote: Counting objects: 865, done.
remote: Total 865 (delta 0), reused 0 (delta 0), pack-reused 865 eceiving object
s: 93% (805/865), 988.01 KiB | 467.00 KiB/s
Receiving objects: 100% (865/865), 1.08 MiB | 467.00 KiB/s, done.
Resolving deltas: 100% (335/335), done.
From https://github.com/enten/hyde-y
* [new branch] master -> hyde-y_remote/master
* [new branch] revert-9-master -> hyde-y_remote/revert-9-master
$ git checkout -b theme-hyde-y hyde-y_remote/master
Branch theme-hyde-y set up to track remote branch master from hyde-y_remote.
Switched to a new branch 'theme-hyde-y'
Anschließend verwenden wir diesen Branch als Unterverzeichnis in “develop” und machen einen neuen Commit:
$ git checkout develop
Switched to branch 'develop'
$ git read-tree --prefix=themes/hyde-y/ -u theme-hyde-y
$ git commit -m "Import theme hyde-y"
(Summary of changes)
Aktualisieren des Themes
Wenn es Änderungen am Theme gibt, lassen sich diese mit den folgenden Befehlen einbinden:
$ git checkout theme-hyde-y
$ git pull
$ git checkout develop
$ git merge --squash -s subtree --no-commit --allow-unrelated-histories theme-hyde-y
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested
Details dazu in der Git Dokumentation.
Anmerkung: Seit git 2.9 führt das Zusammenführen von Branches mit unterschiedlichem Ursprung standardmäßig zu einem Fehler,
deshalb ist seitdem im obigen Code die Option --allow-unrelated-histories
erforderlich (Quelle: Stackoverflow).
Aktivieren des Themes und Erstellen einer Beispielseite
Das Theme können wir nun in der config.toml aktivieren:
baseurl = "http://replace-this-with-your-hugo-site.com/"
languageCode = "de-de"
title = "Meine erste Seite mit Hugo und Git"
theme = "hyde-y"
… und mit Hilfe einer ersten Seite testen (siehe auch die Schritte 3–5 des Hugo Quickstart-Guides):
$ hugo new post/hello_world.md
D:\temp\sample\content\post\hello_world.md created
+++
date = "2016-03-20T18:20:42+01:00"
draft = true
title = "Hallo, Welt!"
+++
## Hallo, Welt!
Hier könnte ein interessanter Text stehen.
Git kann leere Verzeichnisse nicht verfolgen, so dass durch den Wechsel zwischen den einzelnen Branches die Verzeichnisse layouts und static gelöscht wurden. Bevor wir Hugo starten, müssen diese deshalb neu erstellen:
$ mkdir layouts
$ mkdir static
$ hugo server -wD
1 of 1 draft rendered
0 future content
1 pages created
2 paginator pages created
0 tags created
0 categories created
in 144 ms
Watching for changes in D:\temp\sample/{content,layouts,static,themes\hyde-y}
Serving pages from D:\temp\sample\public
Web Server is available at http://127.0.0.1:1313/
Press Ctrl+C to stop
Wenn alles geklappt hat, erstellen wir einen Commit mit den Änderungen:
$ git add config.toml
$ git add content/post/hello_world.md
$ git commit -m "Add sample post"
[develop 99ac435] Add sample post
2 files changed, 12 insertions(+), 2 deletions(-)
create mode 100644 content/post/hello_world.md
Vorbereitung der Veröffentlichung
Nun ist es Zeit für die Veröffentlichung! Wie im bereits erwähnten Git branching model beschrieben, geschieht das am Besten über einen “release”-Branch, in dem letzte Kleinigkeiten (z.B. eine Versionsnummer) angepasst werden können. Wir kürzen das Ganze jedoch an dieser Stelle ab und führen den “develop”-Branch direkt mit dem “master”-Branch zusammen:
$ git checkout master
Switched to branch 'master'
$ git merge --no-ff develop
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.0 -m "First public release"
Continuous Deployment: Automatisierte Veröffentlichung mit Codeship
Ein Nachteil von Hugo und Co.: Bereits aus diesem einfachen Beispiel entstehen 68 statische Dateien – bei “echten” Projekten werden es dementsprechend mehr sein. Der Zeitauwand kann also schnell steigen, wenn man bei der Aktualisierung der Website alle geänderten statischen Dateien auf den Webserver kopieren müsste. Muss man aber nicht – denn der Prozess lässt sich mit Hilfe des “Continuous Deployment”-Konzepts weitgehend automatisieren. Dieser Artikel beschreibt sehr ausführlich, was “Continuous Integration, Delivery and Deployment” bedeutet und wie das Ganze mit Hilfe der Plattform Codeship abläuft. Ich fasse das ganze im Folgenden kurz zusammen.
1 – Remote Repository erstellen
Zunächst erstellen wir ein neues (leeres) Git Remote Repository auf einer Plattform, die mit Codeship kompatibel ist – momentan sind das GitHub und Bitbucket.
2 – Projekt bei Codeship erstellen
Wir erstellen nun ein neues Projekt bei Codeship und verknüpfen es mit dem Repository auf GitHub bzw. Bitbucket. Im Schritt “configure project” können zunächst die Standard-Einstellungen beibehalten werden.
3 – Verknüpfung zwischen Codeship und Git Remote testen
Im nächsten Schritt werden wir aufgefordert, Dateien auf unser Remote Repository zu laden, um die Verknüpfung zu testen:
$ git remote add origin git@bitbucket.org:your_user_name_here/sample.git
$ git push origin master
(Summary of changes)
Branch master set up to track remote branch master from origin.
Die Remote-Adresse (git@bitbucket.org:your_user_name_here/sample.git
) muss mit der eigenen Adresse ersetzt werden.
Codeship sollte die Veröffentlichung automatisch erfassen und bestätigen:
Congratulations! You got your first green build. Keep pushing to your repository. The Codeship will take care of the rest!
4 – Verbindung zwischen Codeship und dem eigenen Webserver einrichten
Damit das Codeship-Projekt auf unseren Webserver zugreifen kann, muss jetzt dessen öffentlicher SSH-Schlüssel auf den Webserver kopiert werden.
5 – Deployment-Script erstellen
Beim Build in der der Codeship-Umgebung muss sichergestellt werden, dass die aktuellste Version von Go verwendet wird. (Die standardmäßig unter Codeship verfügbare Go-Installation ist häufig veraltet und führt zu Build-Fehlern.) Dazu öffnen wir auf der Codeship-Website unter “Project Settings” den Punkt “Tests” und geben unter “Setup Commands” folgenden Code ein:
#!/bin/bash
# Install a custom Go version, https://golang.org/
GO_VERSION=${GO_VERSION:="1.9.2"}
CLEANED_PATH=$(echo "${PATH}" | sed -r 's|/(usr/local\|tmp)/go(/([0-9]\.)+[0-9])?/bin:||g')
CACHED_DOWNLOAD="${HOME}/cache/go${GO_VERSION}.linux-amd64.tar.gz"
# configure the new GOROOT and PATH
export GOROOT="/tmp/go/${GO_VERSION}"
export PATH="${GOROOT}/bin:${CLEANED_PATH}"
# no set -e because this file is sourced and with the option set a failing command
# would cause an infrastructure error message on Codeship.
mkdir -p "${GOROOT}"
wget --continue --output-document "${CACHED_DOWNLOAD}" "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz"
tar -xaf "${CACHED_DOWNLOAD}" --strip-components=1 --directory "${GOROOT}"
# check the correct version is used
go version | grep "${GO_VERSION}"
Quelle: Deploy Hugo with Codeship
Anschließend öffnen wir auf der Codeship-Website den Punkt “Deployment”. Wir wählen dort unter “Unnamed Branch” “Branch is exactly” aus und geben als Branch-Name “master” an. Nach einem Klick auf “Save” wählen wir “Custom script” und geben Folgendes ein:
# Import Hugo and compile
go get github.com/magefile/mage
go get -d github.com/gohugoio/hugo
cd ${GOPATH:-$HOME/go}/src/github.com/gohugoio/hugo
mage vendor
mage install
# Generate Site with Hugo
cd ~/clone/
hugo
# Send public files to Server
rsync -av -e 'ssh -p 22' ~/clone/public/ user@server:/absolute/path/to/site
Dabei müssen die Benutzer- und Pfadangaben (user@server:/absolute/path/to/site
) für den rsync-Befehl in der letzten Zeile angepasst werden.
Das war’s! Sobald sich nun der “master”-branch auf dem Remote Repository (Bitbucket) ändert, wird Codeship automatisch folgende Aktionen ausführen:
- Die fünf Zeilen ab
# Import Hugo and compile
: die aktuellste Version von Hugo von Github herunterladen und kompilieren (siehe Hugo-Dokumentation) cd ~/clone/
: auf dem Codeship-Server in das Verzeichnis wechseln, in den der “master”-Branch geladen wurdenhugo
: die statische Website erstellenrsync -av -e 'ssh -p 22' ~/clone/public/ user@server:/absolute/path/to/site
: die statischen Dateien mittels rsync mit dem eigenen Webserver synchronisieren.