Pelican mit Gitolite aufsetzen


Da mir ja bereits ein Artikel für dieses Jahr fehlt, dachte ich mir − auch zur Eigendokumentation − gleich einen nachzusetzen nach der Einführung. In diesem Artikel dokumentiere ich, wie ich diesen Blog aufgesetzt habe.

Vorüberlegungen

Blogging-Software

Für die Generierung des Blogs entschied ich mich Pelican zu verwenden. Im Gegensatz zu Lösungen wie Wordpress oder MediaWiki, die Ihre Webseiten dynamisch generieren − also beim Aufruf der Seite, basierend auf Design-Vorlagen und in Datenbanken hinterlegten Inhalten − generiert Pelican statische HTML-Dateien, die von mir beim Webserver hinterlegt und dann vom Leser abgerufen werden können. Dies nennt man einen statischen Webseiten-Generator. Der Nachteil ist, dass dadurch Inhalte nicht direkt auf der Webseite bearbeitet werden können, sondern nur durch eine erneute Generierung der Webseiten. Daraus ergibt sich allerdings der Vorteil, dass die Webseite keine Benutzerverwaltung − also Logins − benötigt, um Unbefugte am Bearbeiten der Webseite zu hindern. Somit ist die Webseite unbedenklich unter der Datenschutzgrundverordnung.

Die andere Alternative zu einem statischen Webseiten-Generator wäre natürlich die HTML-Dateien direkt zu schreiben. Dies ist aber mit Mehraufwand verbunden, da jegliche Art von Meta-Informationen und Layoutentscheidungen für die Darstellung des Textes im Browser für jede HTML-Datei einzeln bearbeitet werden müssen. Dies wird dann besonders nervig, wenn man etwas am Design später ändern möchte, da jede HTML-Datei zu einem Artikel nochmal angefasst werden muss. Ich bevorzuge es jedoch Artikel lokal auf meinem Rechner in mir bekannten Formaten wie Markdown oder reStructuredText zu schreiben, die sich rein auf die Textstruktur und nicht das Layout des Texts konzentrieren. Pelican erlaubt mir mit Hilfe von Kommandozeeilenbefehlen diese Dateiformate in HTML überzuführen.

Zu guter Letzt ermöglicht mir Pelican auch Artikel offline zu verfassen (es sind ja nur Textdateien auf meinem Rechner), was ja auch in unserer modernen Welt mal der Fall sein kann (hallo, Deutsche Bahn ;-)). Die Ausrede „hab kein Netz, kann also nicht schreiben“ zieht also nicht.

Hosting

Die HTML-Dateien müssen natürlich bei einem Webserver liegen, damit ihr sie lesen könnt. Als Hostinganbieter dafür verwende ich seit Jahren bereits Uberspace.de. Somit war klar, dass ich auch dort meinen Blog aufsetzen werde. Da Uberspace von Haus aus Unterstützung für die Programmiersprache Python mitbringt, sollte es ein Leichtes sein das Python-basierte Pelican dort aufzusetzen.

Datentransfer

Da ich ja wie gesagt die Artikel nicht auf der Webseite selbst schreibe, brauche ich eine Möglichkeit die geschriebenen Artikel von meinem lokalen Rechner auf den Uberspace-Host zu kopieren. Dazu ist Git für mich persönlich die beste Lösung. Git ist ein unter Softwareentwicklern sehr beliebtes dezentrales Tool zur Versions- und Inhaltsverwaltung, mit dem ich langjährige Erfahrung habe. GitHub ist ein bekannter Hoster für Git-Repositories. Für mich persönlich bietet Git den maximalen Komfort in Sachen Workflow und eine niedrige Einstiegshürde um „mal eben“ einen Artikel zu schreiben.

Schritt 1: Ein Git-Repository anlegen

Glücklicherweise, hatte ich bereits vor Jahren auf meinem Uberspace-Host eine Gitolite-Instanz aufgesetzt. Gitolite ist ein Tool um Git-Repositories − also das Verzeichnis in dem Git Inhalte verwaltet − mit Git selbst zu verwalten. Uberspace selbst bietet eine Anleitung an wie man Gitolite dort einrichten kann. Somit musste ich mich nur noch um die Erstellung eines Repositories kümmern, indem ich Gitolite entsprechend konfigurierte. Dazu erweiterte ich conf/gitolite.conf im gitolite-admin-Repository mit den folgenden Zeilen erweitern:

repo    blog.martine-lenders.eu
        RW+     =   myuser

Diese Konfiguration erstellt ein Repository namens blog.martine-lenders.eu und der Benutzer myuser bekommt Lese- und Schreibrechte (oder genauer, wem auch immer Zugriff auf den privaten Schlüssel zum passenden öffentlichen Schlüssel keys/myuser.pub hat bekommt die Rechte; mehr Details findet Ihr bei Wikipedia).

Das Ganze habe ich dann zur Versionskontrolle des gitolite-admin-Repositorys hinzugefügt:

git add conf/gitolite.conf
git commit -m "Add blog repository"

und zum Uberspace hochgeladen

git push

Dies führte dann dazu, dass das Repository blog.martine-lenders.eu auf dem Uberspace host erstellt wurde.

Schritt 2: Pelican installieren

Um Pelican auf dem Uberspace-Host zu installieren, musste ich mich erstmal in eine Kommandozeileninstanz auf dem Host einloggen:

ssh myuser@example.uberspace.de

Auf meinem Uberspace läuft CentOS 6, wo Python 3.4 die Standardversion für Python 3 ist. Ich wollte für Pelican jedoch auch Markdown support betreiben. Die aktuellste Version von Python's Markdown-Bibliothek unterstützt jedoch Python 3.4 nicht mehr. Ich befürchtete zunächst das dies Probleme bereiten könnte. Das tat es aber nicht, da auf der Maschine auch Python 3.7 installiert ist. Alles was zum Benutzen dieser Pythonversion getan werden muss, ist python3.7 statt python3 aufzurufen.

Wenn es um Python-Software in Produktion geht, bevorzuge ich es eine dedizierte Virtualenv laufen zu lassen. Somit komme ich mit anderen Python-Programmen die auf der Maschine laufen nicht in die Quere. Virtualenv ist auf Uberspace vorinstalliert, somit musste ich nur eine Instanz erzeugen und aktivieren:

virtualenv -p python3.7 blog-env
source blog-env/bin/activate

Jetzt konnte ich endlich Pelican mit Markdown-Support installieren mithilfe von PIP installieren:

pip install pelican[Markdown]

Nun musste ich nur noch Gitolite beibringen, dass wenn ich Inhalte zum blog.martine-lenders.eu-Repository kopiere, diese auch veröffentlicht werden. Ich richtete mich hierbei nach einer Anleitung meines früheren Kommilitonen Alex. Sein Ansatz basiert darauf, dass Pelican nach einem git push automatisch den Blog mit einem post-receive Hook neugeneriert. Hooks sind Scripte (also kleine Programme), die bei bestimmten Git-Ereignissen ausgeführt werden. Der post-receive Hook wird also nachdem (post) das Git-Repository etwas empfangen hat (receive) ausgeführt. In diesem Fall ist der Hook in der UNIX-Shell-Scriptsprache geschrieben. Durch die neuere Pelican-Version, der leicht anderen Umgebung auf Uberspace und dem Fakt, dass ich Themes optional als sogenannte Git-Submodules einbinden wollte, einen hatte ich einen etwas anderen post-receive Hook als Alex in seiner Anleitung:

#!/bin/sh
# Set created files to be readable only for user and group
umask 0022

# Get pelican configuration of repository
TARGET_DIR="$(git config --get pelican.targetdir)"
SETTINGS="$(git config --get pelican.settingsfile)"
# check for required settings, if not finish silently
[ -n "${TARGET_DIR}" -o -n "${SETTINGS}" ] || exit 0

# create temporary directory
TMP_DIR="$(mktemp -d)"

# Copy repository including submodules into temporary directory
git clone --recursive --depth=1 file://${PWD} "${TMP_DIR}"

# If copying repository was successful
if [ $? -eq 0 ]; then
    # Activate Virtualenv
    source ~/blog-env/bin/activate
    # Generate output files to target directory
    echo "Generating blog: pelican -d -o \"${TARGET_DIR}\" -s \"${SETTINGS}\""
    pelican -d -o "${TARGET_DIR}" -s "${TMP_DIR}/${SETTINGS}" && \
    # and if an `.htaccess` file exists
    if [ -f ${TMP_DIR}/.htaccess ]; then
        # copy it to the target directory as well
        echo "Copying .htaccess: cp .htaccess ${TARGET_DIR}"
        cp ${TMP_DIR}/.htaccess ${TARGET_DIR}
    fi
fi

# remove temporary directory
rm -rf "${TMP_DIR}"

Die .htaccess-Datei dient dem Erzwingen von HTTPS.

Die Gitolite-Konfiguration von oben musste ich entsprechend auch um die pelican-Konfigurationsparameter erweitern, die der Hook erwartet:

repo    blog.martine-lenders.eu
        RW+     =   myuser
        config pelican.targetdir = /var/www/virtual/myuser/blog.martine-lenders.eu/
        config pelican.settingsfile = pelicanconf.py

Schritt 3: Den Blog erstellen

Jetzt musste ich noch auf meinem lokalen Rechner alles zum Schreiben einrichten. Dazu habe ich mir zunächst das − noch leere − Git-Repository geklont; also auf meinen Rechner heruntergeladen:

git clone myuser@example.uberspace.de:blog.martine-lenders.eu.git
cd blog.martine-lenders.eu

Dem Repository habe ich dann erstmal eine .gitignore hinzugefügt, die so aussah:

*~
.*.swp
*env/
__pycache__/
output/

Alle Dateien, die in dieser Datei gelistet werden, werden von der Versionskontrolle ignoriert. * steht in dem Fall für eine beliebige Folge von Zeichen. Sollte ich sie dennoch versehentlich hinzufügen gibt es eine entsprechende Fehlermeldung. Die .gitignore muss natürlich zur Versionskontrolle hinzugefügt werden

git add .gitignore
git commit -m "Add .gitignore"

Dann habe ich mir auch lokal eine Virtualenv aufgesetzt und aktiviert:

virtualenv -p python3 env
source env/bin/activate
pip install pelican[Markdown]

Die lokalen Dateien die ich zum Betreiben des Blogs benötige, hatte ich dann einfach mit dem Befehl pelican-quickstart, gemäß der Pelican-Dokumentation erstellt. Auch diese wurden zur Versionskontrolle hinzugefügt:

pelican-quickstart
git add *
git commit -m "Initial import of pelican files"

Nach längerem Suchen fand ich ein Gestaltungsthema für Pelican, dass mir gefiel: Pelican Alchemy. Dieses fügte ich meinem Blog-Repository dann als Submodule hinzu:

mkdir themes
git submodule add https://github.com/nairobilug/pelican-alchemy.git themes/pelican-alchemy
git commit -m "Import pelican-alchemy theme"

und konfigurierte Pelican der Anleitung entsprechend.

Danach konfigurierte ich Pelican noch weiter nach meinem Geschmack. Hier ist die fertige pelicanconf.py:

#!/usr/bin/env python
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals

# Meta-information about the site
AUTHOR = 'Martine S. Lenders'
SITENAME = "Kaffee mit Miri"
SITEURL = 'https://blog.martine-lenders.eu'
SITEIMAGE = '/images/profile.jpg width=160 height=160 title="Bild-Copyright: Tina Proske"'

TIMEZONE = 'Europe/Berlin'

DEFAULT_LANG = 'de'

# Build parameters
PATH = 'content'
THEME = 'themes/pelican-alchemy/alchemy'

# Feed configuration
FEED_ALL_ATOM = 'feeds/all.atom.xml'
FEED_ALL_RSS = 'feeds/all.xml'
# no other feeds
CATEGORY_FEED_ATOM = None
CATEGORY_FEED_RSS = None
AUTHOR_FEED_RSS = None
AUTHOR_FEED_ATOM = None
RSS_FEED_SUMMARY_ONLY = True

# Blogroll
LINKS = []

# Social widget (see https://fontawesome.com/icons?from=io v4.7.0)
ICONS= (('github', 'https://github.com/miri64'),
        ('twitter', 'https://twitter.com/miri_64'),
        ('instagram', 'https://www.instagram.com/miri64__/'),
        ('cloud', 'https://box.martine-lenders.eu/apps/social/@miri64/'),)

DEFAULT_PAGINATION = 20

DEFAULT_METADATA = {
    # https://docs.getpelican.com/en/stable/content.html#publishing-drafts
    'status': 'draft'
}

# Code blocks configuration
PYGMENTS_STYLE = "friendly"
PYGMENTS_RST_OPTIONS = {'linenos': 'table'}

Diese Änderungen mussten natürlich auch der Versionskontrolle hinzugefügt werden

git add pelicanconf.py
git commit -m "Configure pelican"

Schritt 4: Anfangen zu Schreiben

Nun muss ich bloß noch Anfangen einen Artikel zu Schreiben. Dies mache ich, indem ich eine Markdown-, reStructuredText-, oder HTML-Datei im Verzeichnis content/ erstelle und mit einem Texteditor (in meinem Fall vim aber es geht natürlich auch mit z.B. dem Texteditor in Windows) mit Text befülle:

vim content/pelican.md

Damit der Artikel auch auf der Hauptseite erscheint und nicht als Entwurf veröffentlicht ist, muss der Status in den Metadaten der Textdatei auf „published“ gesetzt ist. Jetzt kann ich das Ganze mit Git veröffentlichen:

git add content/pelican.md
git commit -m "Publish article about setting up pelican"
git push

Der Quelltext für diese Seite sieht übrigens so aus.

Zusammenfassung

Insgesamt habe ich für das Aufsetzen etwa eine Stunde gebraucht. Es hat in der Tat länger gebraucht, diesen Artikel zu schreiben ^^". Doch nun ist alles, was ich zum Veröffentlichen tun muss ein simples git push.