Zum Hauptinhalt Zur Navigation Zur Suche

Verlorene Lebenszeit

Ich habe einmal einen ganzen Tag mit Token-Berechtigungen verbracht. Diese Lebenszeit ist unwiederbringlich verloren. Ich hätte malen lernen, meine Mutter anrufen, ein neues CI-System ausgiebig testen können. Irgendetwas.

Die Parallelitätssteuerung ist sehr grob. Natürlich kann man laufende Runs aus demselben Branch abbrechen, aber nur eine ganze Verzweigung. Differenzierter geht es nicht. Das System möchte sich nicht mit Feinheiten befassen, es hat andere Prioritäten.

Secrets können nicht in if-Bedingungen verwendet werden. Man kann also keinen Befehl wie if: secrets.DEPLOY_KEY != ‚' verwenden, um einen Schritt abhängig davon auszuführen, ob ein Secret konfiguriert ist. Github möchte nicht, dass Secrets durch die Auswertung von Ausdrücken in Logs kommen. Zwar sind die Sicherheitsbedenken berechtigt, andererseits kann man in der Praxis keine Workflows schreiben, die bei fehlenden optionalen Secrets nahtlos zurückfallen.

Stattdessen benötigt man umständliche Workarounds wie das Setzen einer nicht geheimen Umgebungsvariablen, die anzeigt, ob das eigentliche Secret vorhanden ist. Bei einer Sicherheitsüberprüfung erscheinen solche Entscheidungen absolut sinnvoll, sie bringen aber jeden zum Schreien, der einen Workflow schreiben will, der sowohl in Forks als auch im Haupt-Repo funktioniert.

"Schreib doch einfach Bash-Skripte"

So kommt jeder CI-Entwickler in die Versuchung einfach ein Bash-Skript zu schreiben. "Ich könnte doch aufhören, gegen das CI-System anzukämpfen, und einfach ein großes Shell-Skript ausführen, das alles erledigt. Ich könnte es lokal ausführen und testen. Ich wäre frei,"flüstert die innere Stimme.

Ich verstehe den Reiz. Ich habe ihn selbst gespürt, spät in der Nacht, nach dem vierten fehlgeschlagenen Workflow-Run in Folge: den Wunsch, den YAML-Tempel niederzubrennen und zur einfachen, ehrlichen Welt von #!/bin/bash und set -euo pipefail zurückzukehren, die Fesseln der Marketplace-Aktionen und wiederverwendbaren Workflows abzuwerfen und einfach die verdammten Befehle zu schreiben. Es fühlt sich wie Befreiung an. Ist es aber nicht.

Denn es passiert Folgendes: Das Bash-Skript funktioniert, man fühlt sich clever. Man erzählt den Kollegen davon, das Skript wächst. Es bekommt Bedingungen, Funktionen, Argument Parsing. Jemand bindet ein zweites Skript ein und fügt Fehlerbehandlung hinzu, ein anderer Logging. Und irgendjemand (man sollte diese Person stoppen, das geschieht aber nie) fügt "nur ein bisschen Parallelität" hinzu.

Drei Monate später hat man 800 Zeilen Bash-Code, der die parallele Ausführung von Jobs mithilfe von wait-Befehlen und PID-Dateien neu implementiert, über eine eigene Wiederholungslogik verfügt, die auf einer for-Schleife und sleep-Befehlen basiert, und seine eigene Ausgabe auswertet, um Erfolg oder Misserfolg festzustellen.

Und dann gibt es eine Race Condition in der Clean-up-Trap, die sich nur unter Linux Kernel 6.x bemerkbar macht. Und man selbst ist die einzige Person, die das Skript versteht. Man ist im Urlaub und das Telefon klingelt.

Man ist CI nicht entkommen, sondern hat nur ein CI-System aufgebaut, das schlechter als jedes andere ist, weil es in Bash geschrieben ist, niemand es nachvollziehen kann, es kein Test-Framework hat und Shellcheck ins Leere ruft – und das Leere ist ebenfalls in Bash geschrieben.

Bash eignet sich gut als Bindeglied oder für "Führe diese vier Befehle der Reihe nach aus". Es ist kein Build-System und kein Test-Harness. Dass man es zwingen kann, beides zu imitieren, ist kein Vorteil, sondern ein großer Nachteil.

Man vereinfacht damit nichts, sondern verlagert Komplexität von einem Ort mit Leitplanken an einen Ort ohne Leitplanken. Die Komplexität wird für ihre neue Freiheit nicht dankbar sein. Sie wird ihre Freiheit nutzen, um einem das Leben schwerzumachen.

Nach dem Abstieg folgt in Teil 2 dieses Artikels der Aufstieg: Mit Buildkite erleben wir, wie CI sich anfühlen sollte. Kein Genuss, aber erträglich.


Relevante Themen