Marius Schuller

devops | dungeon master | photographer


Pandoc und LaTeX CI-Pipeline | Marius Schuller

Pandoc und LaTeX CI-Pipeline

April 02, 2023

Inhaltsverzeichnis

Das Ergebnis dieser kleinen Übung sollte sein, dass ich auf egal welchem Gerät, auf dem kein Docker, LaTeX oder Pandoc existiert, Änderungen an meinem Lebenslauf machen kann, und am Ende in meinem selbst gehosteten GitLab für mich abgreifbar ein PDF herausfällt.

Das ist auch gelungen, aber im Laufe der Umsetzung, fand ich dann das Design des Auslösers gar nicht mal mehr so schön. Es wird wohl einen weiteren Post geben, bei dem ich dieses Setup erweitere.

Was ist Pandoc? Pandoc ist ein Open-Source-Tool, mit dem man Textdokumente in verschiedenen Formaten konvertieren kann. Es unterstützt sehr viele Formate wie Markdown, HTML, LaTeX, PDF, DOCX und mehr. Pandoc ist besonders praktisch, wenn man Dokumente von einem Format in ein anderes umwandeln möchte.
Was ist LaTeX? LaTeX ist eine Markup-Sprache und ein Satzsystem. LaTeX ist besonders in der wissenschaftlichen Welt beliebt. Es wurde entwickelt, um Dokumente mit komplexen mathematischen Formeln, Tabellen und Abbildungen zu erstellen. Im Gegensatz zu herkömmlichen Textverarbeitungsprogrammen wie Microsoft Word, bei denen man das Aussehen des Dokuments direkt bearbeitet, gibt man in LaTeX den Inhalt des Dokuments mit einer Reihe von Befehlen an. LaTeX bietet enorme Flexibilität und Kontrolle über das Aussehen eines Dokuments.

Hintergrund

Für andere Projekte hatte mich in der Vergangenheit mein Freund Florian bereits mehrfach auf Pandoc hingewiesen, und wir haben auch (seit Jahren) vor ein kleines Buch von unseren vergangenen D&D Abenteuern für die Spielgruppe zu drucken.

Mir kam dann irgendwann auf HackerNews eine Website unter, bei dem jemand moderncv benutzt, um sein Anschreiben und seinen Lebenslauf mit Pandoc und LaTeX zu bauen: https://mszep.github.io/pandoc_resume/

Der gute Mark beschwert sich hier zurecht, dass in vielen anderen Vorhaben, die PDF-Generierung automatisieren, auf wkhtmltopdf zurückgegriffen wird, obwohl Pandoc und LaTeX dazu viel besser geeignet sind.

Gleichzeitig wollte ich den gesamten Prozess vom erstellen des Docker Images, über das Veröffentlichen und die CI-Config schreiben bis zum Benutzen für mich einmal komplett selbst bearbeitet haben.

Vorraussetzungen

  • Wissen über git
  • Ein GitLab (z.B. selbst gehostet) oder einen GitLab.com Account
  • Ein GitLab-Runner oder CI bei GitLab.com
    • mit Docker-Executor
  • Eine lokale Docker-Installation

Ich betreibe privat meine eigene GitLab-Instanz, aber ich nehme an, dass auch ein Repository auf GitLab.com fast identisch funktioniert.

Ausblick

Um das Ziel zu erreichen, werden im Folgenden diese Punkte abgearbeitet:

  • Ein GitLab Repository anlegen
    • GitHub funktioniert auch, aber die CI-Config sieht dann anders aus
  • Das Repository mit den Quelldaten füllen
  • Ein Docker Image erstellen
    • Mit pandoc, context und latex darin installiert
  • Das Image öffentlich erreichbar machen
    • Das ist nicht zwingend nötig, ich wollte es aber mal gemacht haben
  • Die GitLab CI-Config erstellen
    • Hier werden alle vorherigen Schritte zusammengezogen

GitLab Repository

Wir brauchen ein git Repository, damit wir unsere Daten versioniert vorhalten und die Grundlage der CI-Pipeline haben. Ich habe mein GitLab Projekt zunächst cv.md genannt, aber ich möchte irgendwann nicht nur meine CV darin generieren lassen, sondern auch Anschreiben und so weiter.

Ich habe mszep/pandoc_resume heruntergeladen und in mein eigenes Repository geschoben.

Wenn man möchte, kann man jetzt auf seinem Gerät auch pandoc, texlive und context installieren, um zu prüfen, ob das Makefile für einen funktioniert, aber ansonsten gehen wir gleich zum nächsten Schritt: Docker!

Docker

Was Docker ist, haben schon andere besser erklärt, und wer wirklich nicht weiß, was genau das ist, fängt vielleicht am Besten beim Wikipedia-Eintrag zu Docker an.

Wir benutzen Docker, weil es nicht nötig ist, die ganze Software, die bei der Erstellung der PDFs beteiligt ist, auf jedem Gerät zu installieren. Docker wird allerdings zwingend gebraucht um das Image zu bauen.

Docker Image

Das Docker Image ist das Herzstück unseres automatisierten PDF-Generierung. Es basiert auf Ubuntu 22.04 und enthält pandoc, texlive und context in den relativ aktuellen Versionen aus den Ubuntu Repositories.

Pandoc kann unglaublich viele Konvertierungen vornehmen, aber die Start- und Ziel-Software muss dazu installiert sein, deswegen brauchen wir hier LaTeX.

Durch die Verwendung eines Docker Images müssen wir uns keine Gedanken über die Installation der Software auf jedem Gerät (Windows, Linux, mac OS) machen. Das spart viel Zeit und ist außerdem eine praktische Möglichkeit, um eine Komplettinstallation von LaTeX zu zentralisieren, die schon mehrere Gigabyte in Anspruch nehmen kann.

Das Dockerfile, das wir in unserem Repository anlegen sieht so aus:

FROM ubuntu:22.04
LABEL description="A docker image with pandoc, texlive and context."

ARG DEBIAN_FRONTEND=noninteractive

RUN apt-get update &&              \
    apt-get install -y             \
      pandoc                       \
      context                      \
      make                         \
      texlive                      \
      texlive-latex-extra          \
      texlive-fonts-extra &&       \
    rm -rf /var/lib/apt/lists/* && \
    apt-get clean

WORKDIR /data

Wenn wir das Dockerfile haben, müssen wir noch das Image tatsächlich bauen. Dabei sollte man auch einen Namen für das Image haben. Ich war einfallslos und nannte es pancontex (wegen PANdoc, CONText und laTEX). Das geht mit folgendem Befehl:

docker build -t pancontex .

Nach dem Absetzen lädt Docker das ubuntu:22.04 Image herunter und führt die im Dockerfile angegebenen Befehle aus und installiert so alle benötigten Pakete. Das kann einige Minuten dauern, denn am Ende war das Image etwa 3 Gigabyte groß.

[+] Building 206.3s (8/8) FINISHED
 => [internal] load build definition from Dockerfile                                                                                 0.1s
 => => transferring dockerfile: 786B                                                                                                 0.0s
 => [internal] load .dockerignore                                                                                                    0.0s
 => => transferring context: 2B                                                                                                      0.0s
 => [internal] load metadata for docker.io/library/ubuntu:22.04                                                                      0.9s
 => [auth] library/ubuntu:pull token for registry-1.docker.io                                                                        0.0s
 => CACHED [1/3] FROM docker.io/library/ubuntu:22.04@sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21         0.0s
 => [2/3] RUN apt update &&                                  apt install -y                                 context                178.2s
 => [3/3] WORKDIR /data                                                                                                              0.1s
 => exporting to image                                                                                                              27.0s
 => => exporting layers                                                                                                             27.0s
 => => writing image sha256:46cab9516562f2807aed9f96821fbdbe97030bd362ff69f42ce8ce89aaafdbd1                                         0.0s
 => => naming to docker.io/library/pancontex                                                                                         0.0s

So weit so gut, jetzt muss es nur noch veröffentlicht werden.

Docker Hub

Am Einfachsten ist es, wenn man das Docker Image in eine öffentlich erreichbare Docker Registry pusht. So ist sichergestellt, dass wir in den nächsten Schritten einfach auf das Image zugreifen können.

Eine davon ist z.B. https://hub.docker.com/. Hier kann man sich kostenlos einen Account anlegen.

Wenn man einen Account hat, fehlt noch, dass man seinem Docker sagt, wer man ist, man loggt sich also mit seiner Docker Installation mit seinem Benutzernamen auf hub.docker.com an:

docker login -u $USERNAME

Unser Docker Image ist gebaut und wir sind gegenüber hub.docker.com authentifiziert, jetzt fehlt unserem Image nur noch ein passendes Tag, mit dem man es später gut identifizieren kann.

Docker Tagging ist fast ein eigenes Thema für sich, darum geht es in diesem Post aber nicht. Hier ist eine kurze Einführung dazu, es gibt viele mehr.

Wir brauchen nur den folgenden Befehl, der den Benutzernamen von Docker Hub enthält:

docker image tag pancontex $USERNAME/pancontex:latest

Nachdem Docker jetzt weiß, wer wir sind, und dass dieses Image uns gehört, können wir es mit dem folgenden Befehl auch endlich hochladen:

docker image push $USERNAME/pancontex:latest

Wer möchte, darf auch gerne einfach mein Docker Image benutzen. Ich garantiere allerdings nicht, dass ich noch Änderungen daran mache.

GitLab CI Config

Nun sollte alles an seinem Platz liegen, um über die folgenden CI-Konfiguration zu einem Ganzen zusammengefügt zu werden.

Wir nutzen das in mszep/pandoc_resume vorbereitete Makefile um die Generierung von allen vorkonfigurierten Dateiformaten zu ermöglichen. Wer nur das PDF haben will, kann einfach make durch make pdf ersetzen.

Wir erstellen in unserem Repository eine Datei namens .gitlab-ci.yml und dem folgenden Inhalt:

stages:
  - build

build:
  stage: build
  image:
    name: $USERNAME/pancontex:latest
  script:
    - pandoc --version
    - mtxrun --version
    - make
  artifacts:
    paths:
      - output/*

Das image ist jenes, das wir zuvor zu hub.docker.com gepusht haben. Um auf die generierten Dateien zugriff zu bekommen, müssen wir sie als artifacts kennzeichnen, andernfalls werden sie nach dem Ausführen der Befehle unter script einfach verworfen.

Beim Hinzufügen, der .gitlab-ci.yml Datei zum Repository sollte das CI von selbst anspringen und anfangen zu arbeiten.

Ergebnis

In dem GitLab Repository unter CI/CD und dort “Jobs” sollte unser CI-Job auftauchen und dessen Ausgabe im besten Fall mit den folgenden Zeilen enden:

Uploading artifacts for successful job
Uploading artifacts...
output/*: found 8 matching artifact files and directories
Uploading artifacts as "archive" to coordinator... 201 Created  id=1881 responseStatus=201 Created token=64_zhdcz
Cleaning up project directory and file based variables
Job succeeded

In der rechten Sidebar in GitLab, sollte dann diese Box erscheinen:

CI Artifact runterladen oder online
untersuchen

Hier kann ein .zip heruntergeladen, oder eine Online-Version der Artefakte angesehen werden.

Zusammenfassung

Mit diesem Setup kann nun sehr einfach der Lebenslauf auf jedem Gerät aktualisiert werden, ohne aufwändige Installation der eigentlich nötigen Pakete.

Eigentlich kann man dank Pandoc und LaTeX das Design und die Struktur des Dokuments anpassen und erweitern, aber dazu muss man wegen der Vorlage leider ConTeXt lernen. Das habe ich festgestellt, als ich die Formattierung der linken Spalte (die Jahre, wann man wo gearbeitet hat) verkleinern wollte und es mit nicht gelungen ist. Da ich bereits LaTeX kann, war es mir auch keinen weiteren Aufwand wert, und ich werde das Setup anpassen, sodass ich gleich ganz auf Pandoc verzichten kann. Dass Pandoc noch in dem Image enthalten ist, ist Absicht, weil ich das für weitere Projekte nützlich finde und auch weiter verwenden werde.

Erweitern kann man dieses Setup noch, in dem der CI-Job das PDF oder die anderen erzeugten Dateien direkt auf einen Webspace deployed, damit die aktuellste Version des Lebenslaufs immer online abrufbar ist.

Optional kann man auch das Erstellen des Docker Images noch automatisieren. Wenn die CI-Pipeline eine Änderung am Dockerfile erkennt, könnte ein anderer Job aktiv werden, der automatisiert das Image baut und veröffentlicht.