Skip to content

0019 — Das Deployment weist die Qualitätskette nach, statt sie zu wiederholen ​

Status: angenommen · Milestone: Betrieb

Kontext ​

ADR 0014 lässt das Deployment pnpm verify ein zweites Mal fahren, mit der Begründung, ein Tag zeige nicht zwingend auf einen Stand, der durch die CI gelaufen ist. Das Argument ist richtig. Es beschreibt aber den Ausnahmefall, und der Preis fiel im Regelfall an: Ein Merge auf main löst die CI aus, der Tag darauf löst das Deployment aus, und beide fahren dieselbe Kette auf demselben Commit.

Gemessen im ersten Kundenprojekt (Deploy-Lauf vom 25. August 2026): 5m28s von 6m42s entfielen auf die Qualitätskette, unter zehn Sekunden auf Bauen und Übertragen. Eine Änderung bis zum Release kostete drei volle Läufe — Pull Request, main, Deployment —, davon zwei auf demselben Stand.

Innerhalb der Kette kamen zwei Dopplungen dazu, die nichts mit dem Deployment zu tun hatten und im selben Zug entfallen: tests/integration/config-build.test.ts baute die gültigen Fixtures aus Vitest heraus, kalt und parallel, obwohl build:fixtures sie Sekunden später warm noch einmal baute; und test:e2e baute alle drei Ausgaben ein weiteres Mal, obwohl verify sie gerade erzeugt hatte. Zusammen war das der größere Teil der Laufzeit.

Was danach übrig blieb, war die Bildverarbeitung: Der erste Build eines Laufs erzeugt aus jedem Quellbild alle Breiten und Formate — im Kundenprojekt zehn Bilder mit 26 MB, 166 Sekunden auf dem Zwei-Kern-Runner. Jeder weitere Build desselben Laufs findet die Ergebnisse unter node_modules/.astro/assets und braucht Sekunden. Der nächste Lauf begann wieder bei null.

Entscheidung ​

Das Deployment fragt die CI, ob sie die Kette für genau diesen Commit erfolgreich gefahren hat. Der Nachweis ist ein erfolgreicher Lauf von ci.yml mit dem Ereignis push und der head_sha des zu deployenden Commits:

sh
gh run list --workflow ci.yml --commit "$GITHUB_SHA" --event push --status success

Gibt es ihn, entfallen Browser-Installation und pnpm verify. Gibt es ihn nicht, läuft die Kette wie bisher — derselbe Befehl wie lokal und in der CI.

Gezählt wird nur der Lauf auf push. Der Lauf auf dem Pull Request prüft den Kopf des Branches; getaggt wird der Squash-Commit auf main, und den hat nur der Lauf nach dem Merge gesehen.

Der Bildcache bleibt zwischen den Läufen erhalten. node_modules/.astro/assets wird über actions/cache gesichert, geschlüsselt nach dem Inhalt von src/assets/, den Bild-Presets und der Lockdatei, mit dem letzten Stand als Rückfall. Astro benennt jede verarbeitete Datei nach dem Hash von Quelle und Transformation; ein Eintrag zu einem Bild, das es nicht mehr gibt, wird deshalb nie ausgeliefert — er bleibt nur liegen, bis der Schlüssel wechselt.

Die Kette selbst wird nicht aufgeteilt. pnpm verify bleibt ein Befehl, der lokal, im Pull Request, auf main und — ohne Nachweis — im Deployment identisch läuft.

Begründung ​

Warum nachweisen und nicht streichen. Ohne Kette im Deployment wäre ein Tag auf einem beliebigen Commit ungeprüft ausgeliefert, ebenso ein Rollback auf einen Stand, dessen CI-Lauf abgebrochen wurde. Der Nachweis deckt genau diese Fälle ab, indem er dort fehlt.

Warum der Nachweis über die CI-Läufe geht und nicht über ein Artefakt. Das Artefakt eines früheren Laufs zu übernehmen wäre schneller als jeder Neubau — es hängt aber an der Aufbewahrungsfrist, und ADR 0014 hat das aus diesem Grund verworfen. Der Lauf selbst bleibt als Datensatz erhalten, solange das Repository existiert. Und er ist ohnehin die einzige Stelle, an der steht, ob eine Prüfung bestanden wurde.

Warum abgebrochene Läufe nicht zählen. ci.yml bricht einen laufenden Lauf ab, wenn auf demselben Ref ein neuer Push kommt. Für den abgebrochenen Commit gibt es dann keinen erfolgreichen Lauf, und das Deployment fährt die Kette selbst — die richtige Antwort auf „nicht zu Ende geprüft".

Warum verify nicht in parallele Jobs zerfällt. Das wäre auf einem Zwei-Kern-Runner schneller. Es hieße aber, dass die CI etwas anderes fährt als der Befehl, den Entwickelnde lokal ausführen — und die Regel „ein Befehl überall" ist die, die verhindert, dass lokale und entfernte Ergebnisse auseinanderlaufen.

Verworfene Alternativen ​

Kette im Deployment ganz streichen. Verliert den Schutz für Tags auf ungeprüften Commits.

Deployment direkt aus dem CI-Lauf auf main. Verkürzt die Kette, macht aber jeden Merge sofort öffentlich; ADR 0014 hat das bewusst abgelehnt. Ein Release bleibt eine Handlung.

Nachweis über den Commit-Status statt über die Läufe. Ein Status lässt sich von Hand setzen. Der Lauf nicht.

Konsequenzen ​

  • deploy.yml und deploy-pages.yml brauchen actions: read.
  • Der Bildcache steht in allen drei Workflows, weil der erste Build eines Laufs der teure ist — im Deployment ist das nach dem Nachweis der Build der Auslieferung selbst. Ein Lauf ohne Cache-Treffer (neues Repository, erstes Bild) dauert so lange wie vorher; erst der zweite zeigt den Unterschied. Wer den Cache unter Verdacht hat, löscht ihn unter Actions → Caches; Falsches liefern kann er nicht.
  • Der Nachweis kennt den Workflow bei seinem Dateinamen. Wer ci.yml umbenennt, zieht den Aufruf in beiden Deploy-Workflows mit.
  • Wer die Qualitätskette ändert, ändert sie in ci.yml und package.json — nicht im Deployment. Das Deployment fährt sie nur noch als Rückfall.
  • Die Zusammenfassung eines Deployment-Laufs sagt, ob die Kette nachgewiesen oder hier gefahren wurde. Steht dort „hier gefahren", obwohl der Commit auf main liegt, ist der CI-Lauf abgebrochen oder rot — und das ist der Moment, das zu klären, nicht zu wiederholen.
  • tests/integration/config-build.test.ts startet nur noch den Build, der scheitern muss. Dass die gültigen Fixtures bauen, belegt build:fixtures; dass sie richtig bauen, test:dist.