Was passiert zuerst in einer CI/CD-Pipeline: Checkout und Umgebungseinrichtung

Sie pushen einen Commit. Ihr CI/CD-Tool erkennt die Änderung und startet einen neuen Pipeline-Lauf. Doch bevor Build, Test oder Deployment beginnen, muss die Pipeline drei grundlegende Fragen beantworten: Mit welchem Code arbeite ich? Welche Werkzeuge stehen mir zur Verfügung? Und ist mein Arbeitsbereich sauber?

Diese erste Phase wird leicht übersehen, aber hier beginnen viele Pipeline-Fehler tatsächlich. Ein verschmutzter Arbeitsbereich, eine nicht passende Tool-Version oder eine fehlende Abhängigkeit können eine gesamte Pipeline zum Scheitern bringen, bevor sie überhaupt mit der eigentlichen Arbeit beginnt. Lassen Sie uns durchgehen, was während des Checkouts und der anfänglichen Vorbereitung passiert und warum es so wichtig ist, dies richtig zu machen – für jede nachfolgende Pipeline.

Der Checkout-Schritt: Den richtigen Code holen

Wenn eine Pipeline ausgelöst wird, trägt sie Informationen vom auslösenden Ereignis mit sich: einen Commit-Hash, einen Branch-Namen oder einen Tag. Der Checkout-Schritt verwendet diese Informationen, um die exakte Version des Codes aus dem Repository in den Arbeitsbereich der Pipeline zu ziehen.

Der Arbeitsbereich ist ein temporärer Ordner auf der Maschine, die die Pipeline ausführt. Diese Maschine wird normalerweise als Runner oder Agent bezeichnet, je nach verwendetem CI/CD-Tool. Die Pipeline lädt den Code in diesen Ordner herunter, und alles Weitere – Builds, Tests und Deployments – findet innerhalb dieses Bereichs statt.

Stellen Sie es sich vor wie das Ankommen an einem neuen Schreibtisch bei der Arbeit. Sie müssen wissen, an welchem Projekt Sie arbeiten, welche Version des Projekts Sie verwenden sollen und welche Werkzeuge an Ihrem Schreibtisch verfügbar sind. Ohne das können Sie nicht beginnen.

Das folgende Flussdiagramm zeigt die Abfolge der Aktionen, die in der ersten Phase einer CI/CD-Pipeline stattfinden:

Hier ist ein praktisches Beispiel mit GitHub Actions:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js 18
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'
flowchart TD A[Trigger Event: Commit, Branch oder Tag] --> B[Checkout Code: exakten Commit-Hash holen] B --> C[Arbeitsbereich bereinigen: übrig gebliebene Dateien entfernen] C --> D[Build identifizieren: Artefakt nach Branch/Tag benennen] D --> E[Umgebung einrichten: Tools und Abhängigkeiten installieren] E --> F{Cache verfügbar?} F -- Ja --> G[Zwischengespeicherte Abhängigkeiten wiederherstellen] F -- Nein --> H[Neue Abhängigkeiten herunterladen] G --> I[Bereit für Build-Phase] H --> I

Warum ein sauberer Arbeitsbereich wichtig ist

Jeder Pipeline-Lauf sollte mit einem sauberen Arbeitsbereich beginnen. Wenn Dateien von einem vorherigen Lauf übrig bleiben, können sie den aktuellen Build verunreinigen. Eine übrig gebliebene Konfigurationsdatei, ein veraltetes kompiliertes Binary oder eine alte Abhängigkeit können dazu führen, dass die Pipeline ein Artefakt produziert, das nicht zum aktuellen Code passt. Das Debuggen solcher Probleme ist mühsam, weil das Problem nicht in Ihrem Code liegt, sondern im übrig gebliebenen Müll eines vorherigen Laufs.

Die meisten CI/CD-Tools bieten eine Option für einen sauberen Arbeitsbereich. Manche aktivieren sie standardmäßig, andere erfordern eine manuelle Konfiguration. Wenn Sie eine Pipeline einrichten, stellen Sie sicher, dass diese Option aktiviert ist. Es ist eine kleine Einstellung, die eine große Klasse schwer zu findender Fehler verhindert.

Identifizieren, was Sie bauen

Nach dem Checkout muss die Pipeline wissen, welchen Branch oder Tag sie verarbeitet. Diese Information bestimmt, wie das resultierende Artefakt benannt wird und wohin es als Nächstes geht.

Zum Beispiel könnte ein Commit auf den main-Branch ein Artefakt mit der Bezeichnung latest oder stable erzeugen. Ein Commit auf einen Feature-Branch könnte ein Artefakt mit dem Branch-Namen und einer Build-Nummer erzeugen. Ein Tag wie v1.2.3 sollte ein Artefakt erzeugen, das mit dieser exakten Version gekennzeichnet ist.

Diese Kennzeichnung ist wichtig, weil sie Teams hilft, Artefakte zu ihrer Quelle zurückzuverfolgen. Wenn jemand fragt: „Welche Version des Codes hat dieses Artefakt erzeugt?“, sollte die Kennzeichnung eine klare Antwort geben. Ohne konsistente Kennzeichnung wird das Artefaktmanagement zum Ratespiel.

Einrichten der Umgebung

Sobald der Code bereit ist, muss die Pipeline ihre Umgebung vorbereiten. Umgebung bedeutet hier mehr als nur den Ordner, in dem der Code lebt. Sie umfasst alle Tools und Abhängigkeiten, die zum Bauen, Testen und Bereitstellen der Anwendung erforderlich sind.

Eine Java-Anwendung benötigt eine bestimmte JDK-Version. Eine Node.js-Anwendung benötigt die richtige Node.js-Laufzeitumgebung und npm. Eine Datenbankmigration benötigt ein Migrationstool wie Flyway oder Liquibase. Jedes dieser Tools muss in der Pipeline-Umgebung in der richtigen Version verfügbar sein.

Das „Auf meinem Rechner funktioniert es“-Problem

Eine der häufigsten Frustrationen bei CI/CD ist die Diskrepanz zwischen der lokalen Umgebung eines Entwicklers und der Pipeline-Umgebung. Ein Entwickler führt den Build auf seinem Laptop aus, alles besteht, und er pusht den Code. Die Pipeline nimmt ihn auf, führt denselben Build aus, und er schlägt fehl.

Die Ursache ist fast immer ein Umgebungsunterschied. Der Entwickler hat JDK 17 lokal installiert, aber die Pipeline verwendet JDK 11. Der Entwickler hat ein globales npm-Paket, das die Pipeline nicht hat. Der Laptop des Entwicklers hat ein anderes Betriebssystem oder eine andere Architektur.

Dies ist das klassische „Auf meinem Rechner funktioniert es“-Problem, und es ist ein Zeichen dafür, dass die Pipeline-Umgebung nicht explizit genug definiert ist.

Umgebungen reproduzierbar machen

Die Lösung besteht darin, die Pipeline-Umgebung explizit zu definieren, sodass sie überall reproduziert werden kann. Es gibt mehrere gängige Ansätze:

  • Docker-Images: Packen Sie alle erforderlichen Tools in ein Docker-Image. Die Pipeline läuft in einem Container, der auf diesem Image basiert, und garantiert so jedes Mal dieselbe Umgebung.
  • Tool-Versionsdateien: Verwenden Sie Dateien wie .tool-versions (für asdf), .nvmrc (für Node.js) oder .ruby-version, um exakte Tool-Versionen zu deklarieren. Die Pipeline liest diese Dateien und installiert die angegebenen Versionen.
  • Umgebungsmanager: Tools wie Conda für Python oder SDKMAN für Java können Tool-Versionen deklarativ verwalten.

Das Ziel ist dasselbe: Die Pipeline-Umgebung sollte überall dort reproduzierbar sein, wo die Pipeline läuft. Wenn Sie die Pipeline auf Ihrem Laptop, auf einem CI-Server und auf dem Rechner eines Kollegen ausführen können und dasselbe Ergebnis erzielen, haben Sie das Problem der Umgebungskonsistenz gelöst.

Caching: Geschwindigkeit vs. Aktualität

Einige Pipelines fügen in dieser Phase Caching hinzu, um nachfolgende Läufe zu beschleunigen. Abhängigkeiten, die in einem vorherigen Lauf heruntergeladen wurden, können gespeichert und wiederverwendet werden. Dies ist sinnvoll für große Abhängigkeitssets wie Node.js node_modules oder Pythons venv.

Aber Caching bringt einen Zielkonflikt mit sich. Ein veralteter Cache kann dazu führen, dass die Pipeline alte Abhängigkeiten verwendet, die hätten ersetzt werden sollen. Wenn eine Abhängigkeit im Repository aktualisiert wurde, der Cache aber noch die alte Version enthält, könnte die Pipeline gegen veralteten Code bauen.

Wenn Sie Caching verwenden, stellen Sie sicher, dass der Cache-Schlüssel genügend Informationen enthält, um ihn ungültig zu machen, wenn sich Abhängigkeiten ändern. Ein gängiger Ansatz ist, die Abhängigkeitsdatei (wie package-lock.json oder requirements.txt) zu hashen und diesen Hash als Teil des Cache-Schlüssels zu verwenden. Wenn sich die Abhängigkeitsdatei ändert, ändert sich der Cache-Schlüssel, und es erfolgt ein neuer Download.

Was die Pipeline nach dieser Phase hat

Bis Checkout und Umgebungseinrichtung abgeschlossen sind, hat die Pipeline:

  • Den exakten Code vom auslösenden Commit, Branch oder Tag
  • Einen sauberen Arbeitsbereich ohne übrig gebliebene Dateien
  • Eine bekannte Kennzeichnung für das Artefakt, das sie produzieren wird
  • Alle Tools und Abhängigkeiten, die für die nächsten Phasen benötigt werden

Dies ist die Grundlage. Ohne sie basiert jede nachfolgende Phase – Build, Test, Deployment – auf Unsicherheit. Mit ihr kann die Pipeline zuversichtlich voranschreiten.

Eine kurze Checkliste für die erste Phase Ihrer Pipeline

  • Sauberer Arbeitsbereich ist aktiviert oder konfiguriert
  • Checkout verwendet den exakten Commit-Hash vom Auslöser
  • Artefaktkennzeichnung entspricht den Branch- oder Tag-Konventionen
  • Tool-Versionen sind explizit deklariert (Docker, Tool-Versionsdateien oder Umgebungsmanager)
  • Cache-Schlüssel enthalten Hashes der Abhängigkeitsdateien, wenn Caching verwendet wird

Das Fazit

Die ersten Sekunden eines Pipeline-Laufs bestimmen, ob der Rest der Pipeline auf solidem Grund läuft. Ein sauberer Arbeitsbereich, der richtige Code und eine reproduzierbare Umgebung sind keine optionalen Details. Sie sind die Grundlage, die jede nachfolgende Phase vorhersagbar und debugbar macht. Investieren Sie Zeit, um diese Phase richtig zu machen, und Sie werden weit mehr Zeit sparen, die Sie sonst mit der Fehlersuche bei Problemen verbringen würden, die sich als Umgebungs- und nicht als Code-Probleme herausstellen.