noqqe » blog | sammelsurium | photos | projects | about

Treat Your Blog as Code

2019-01-23 @ CD, CI, hugo, markdownlint, TravisCI

Mein Blog besteht schon seit Langem einfach nur aus Text Files. Keine Datenbank, kein dynamischer Content. Eignet sich das nicht eigentlich hervorragend um CI/CD zu machen?

Wie ich was und warum gemacht habe, wird hier erklärt.

TL;DR: markdownlint, hugo Build und rsync Deploy via travis-ci.org

Markdown Style Testing

Per Zufall bin ich auf markdownlint-cli gestoßen habe es auf meine Posts losgelassen. Tatsächlich ging das ganze CI/CD für meinen Blog mit dem Fund dieses Stücks Software los.

markdownlint content/blog/*.md

Anfangs waren das ca. 3150 Errors :/ Das hat mich erstmal ziemlich schockiert, aber klar viele Markdown Lint Rules sind nicht unbedingt Errors sondern eher Style Warnings.

Mit etwas Konfiguration, welche Regeln ich nicht ausgewertet haben möchte, hab ich dann die .markdownlintrc gebaut:

{
  "default": true,
  "fenced-code-language": false,
  "first-heading-h1": false,
  "heading-increment": false,
  "no-trailing-punctuation": false,
  "first-line-h1": false,
  "line-length": false,
  "ul-style": false,
  "no-inline-html": {
     "allowed_elements": [ "code" ]
  },
  "header-style": {
    "style": "atx"
  }
}

Und war dann runter auf 1150. Diese restlichen Errors erfordeten sed und Handarbeit.

Sehr viel Handarbeit.

CI mit Travis

Nachdem das jetzt alles “schön” war hab ich mich von Poschi3 mit travis-ci.org anfixen lassen.

Ich wollte markdownlint bei jedem Commit über mein Repo laufen lassen. Dafür hab ich mich dort eingeloggt und das Repo github.com/noqqe/noqqe.de aktiviert. Danach die .travis.yml mit folgendem Inhalt gefüllt.

language: generic

install:
  - npm install -g markdownlint-cli

script:
  - markdownlint content/*.md
  - markdownlint content/blog/*.md
  - markdownlint content/sammelsurium/*.md

Und dann lief die Sache auch (nach gefühlt 130 Anläufen) auch schon.

Siehe Build

Building

Markdown Syntax Qualität ist jetzt also sichergestellt. Wenn ich meinen Content richtig befülle, muss ich allerdings auch noch darauf achten, dass mein hugo Blog auch wirklich compiled. Also aus dem ganzen Markdown + Hugo Templating auch wirklich lesbares HTML wird.

Das geht nun wieder etwas anders. Diesmal muss ich der .travis.yml noch beibringen, go und das zugehörige Paket hugo zu installieren und schliesslich auszuführen.

language: go
go:
  - '1.11'

install:
  - go get github.com/gohugoio/hugo

script:
  - hugo

Wenn der Blog also jetzt einen neuen Commit bekommt, wird sowohl Markdown überprüft als auch ob der statische Blog Compiler den Blog wirklich bauen kann.

Yay.

Deploying

Wenn man schon den CI Teil erledigt hat, kann der Continious Deployment Teil ja auch gleich mit erledigt werden.

Im Grunde brauche ich nur einen rsync des Verzeichnisses public auf einen meiner Server. Deployment wäre um ein vielfaches leichter, wären da nicht Geheimnisse notwendig. Ich will in meinem Deployjob nicht public lesbar haben auf welchen Server, mit welchem User und schon garnicht mit welchem Passwort/Key sich auf dem Server angemeldet wird.

Das haben auch die Leute bei Travis erkannt und dafür ein HowTo verfasst das ich jetzt hier nicht replizieren muss: Encrypting Files - Travis CI. Im Grunde erzeuge ich einen SSH Key den ich via Commandline zu Travis hochlade und dann noch ein paar “geheime” Variablen im Build Job setze, die ich nachher verwenden kann.

Um den Deploy zu erledigen gibt es bei Travis deploy und before_deploy. Meine aktuelle Lösung sieht so aus:

install:
  - echo $KNOWN_HOSTS_KEY >> $HOME/.ssh/known_hosts

before_deploy:
  - openssl aes-256-cbc -K $encrypted_a01276847ef6_key -iv $encrypted_a01276847ef6_iv -in deploy_rsa.enc -out /tmp/deploy_rsa -d
  - eval "$(ssh-agent -s)"
  - chmod 600 /tmp/deploy_rsa
  - ssh-add /tmp/deploy_rsa

deploy:
- provider: script
  skip_cleanup: true
  script: rsync -avi public/ $PRODUCTION_SERVER_USER@$PRODUCTION_SERVER:public/
  on:
    branch: master

Achtung: Achtet bitte darauf welchen User mit welchen Rechten ihr auf dem Zielserver einrichtet, selbst wenn das alles nicht “public” für jedermann ersichtlich ist, kann sich der Inhaber (oder ein späterer Angreifer) von TravisCI auf eurem Server mittels dieses Users+Keys einloggen.

Wenn das alles klappt sieht das dann so aus:

Oder siehe hier der Build

Anmerkung zur echo $KNOWN_HOSTS_KEY >> $HOME/.ssh/known_hosts Sache: Es gibt zwar ein Addon für Known Hosts, das wird aber immer VOR dem auswerten der Globalen Variablen ausgeführt und ist somit nutzlos weil ich dann den Hostname nicht als Travis Setting einfügen kann.

Was jetzt?

Tatsächlich ist das der erste Blogpost den ich mit der vollautomatischen Chain hier deploye. Ich denke ich werde wenn das alles sich als sinnvoll herausstellt auch noch entbehrlich.es automatisieren, da gibt es noch ein paar Steps die ich bisher immer per Hand mache.

Wen das Endresultat interessiert kann sich auf Github die .travis.yml nochmal anschauen, in der alle 3 Steps zusammengefasst sind.

Natürlich auch die Build Logs für den aktuellen Blogpost kann man hier finden.