Migration und Praxis
only und except waren jahrelang der Standardweg, um Jobs in GitLab CI bedingt auszufuehren, doch das Keyword rules loest beide seit Langem ab und erlaubt Bedingungslogik, die mit only/except schlicht nicht abbildbar ist. Dieser Artikel zeigt, warum sich die Migration lohnt und wie sie in bestehenden Pipelines Schritt fuer Schritt gelingt.
Inhaltsverzeichnis
- 1. Warum only/except an seine Grenzen stoesst
- 2. Wie rules Bedingungen kombinierbar macht
- 3. when, manual und die neue Standardlogik
- 4. Migrationsstrategie fuer bestehende Pipelines
- 5. Doppelte Pipelines vermeiden
- 6. Haeufige Fehler bei der Umstellung
- 7. Was Teams nach der Migration typischerweise gewinnen
- 8. Tooling und Validierung waehrend der Umstellung
- 9. Fazit: rules als Standard, only/except als Auslaufmodell
- 10. Zusammenfassung
- 11. FAQ
1. Warum only/except an seine Grenzen stoesst
only und except entstanden in einer fruehen Phase von GitLab CI, als Pipelines meist simple Fragen beantworten mussten: Laeuft der Job nur auf dem main-Branch, nur bei Tags, oder soll er bei Merge Requests ausgesetzt werden. Fuer solche einfachen Faelle reichten die beiden Keywords lange aus, weil sie eine kurze Liste von Refs, Branches oder Variablen-Bedingungen akzeptierten und GitLab intern daraus eine Ja/Nein-Entscheidung ableitete. Das Problem zeigt sich erst, wenn mehrere Bedingungen gleichzeitig gelten sollen, etwa nur bei Aenderungen an einem bestimmten Verzeichnis und nur auf geschuetzten Branches und nicht bei geplanten Pipelines. only/except kennt dafuer keine saubere UND/ODER-Verknuepfung, sodass Teams frueher mit doppelten Jobs, kryptischen Variablen-Namen oder verschachtelten Bash-Bedingungen im script-Block arbeiten mussten, nur um eine Bedingung abzubilden, die eigentlich zur Pipeline-Konfiguration gehoert.
Ein weiteres strukturelles Problem ist, dass only/except zwischen zwei impliziten Standardverhalten unterscheidet, je nachdem ob das Keyword ueberhaupt gesetzt ist, was in gewachsenen Pipelines regelmaessig zu Ueberraschungen fuehrt: Ein Job ohne only/except laeuft standardmaessig auf Branches und Tags, nicht aber automatisch bei Merge-Request-Events, waehrend ein Job mit only: [merge_requests] ploetzlich ein komplett anderes Default-Verhalten fuer refs annimmt. Wer eine Pipeline uebernimmt, die ueber Jahre gewachsen ist, verbringt oft mehr Zeit damit herauszufinden, warum ein Job unerwartet laeuft oder eben nicht laeuft, als mit der eigentlichen Pipeline-Logik. GitLab selbst empfiehlt seit Version 12 offiziell rules als Nachfolger und only/except gilt inzwischen als Legacy-Feature, das zwar weiterhin funktioniert, aber keine neuen Faehigkeiten mehr bekommt.
2. Wie rules Bedingungen kombinierbar macht
rules definiert eine geordnete Liste von Regeln, die GitLab von oben nach unten auswertet, wobei jede Regel aus einer optionalen if-Bedingung, optionalen changes- oder exists-Klauseln und einem Ergebnis besteht, das den Job ausfuehrt, ueberspringt oder auf manuellen Trigger stellt. Trifft die erste Regel in der Liste zu, wird ihr Ergebnis angewendet und die restlichen Regeln werden nicht mehr geprueft, was rules zu einer Art switch-Statement fuer Pipeline-Bedingungen macht statt zu einer einfachen Filterliste. Das if-Feld nutzt dabei dieselbe CI/CD-Variablenausdruckssprache wie workflow:rules und erlaubt Vergleiche, Regex-Matches und logische Verknuepfungen mit && und ||, sodass etwa CI_COMMIT_BRANCH == "main" && CI_PIPELINE_SOURCE == "push" in einer einzigen Zeile genau die Bedingung ausdrueckt, fuer die man frueher zwei separate only-Bloecke gebraucht haette.
Die eigentliche Staerke zeigt sich, wenn if mit changes oder exists kombiniert wird: changes prueft, ob sich Dateien in bestimmten Pfaden seit dem letzten erfolgreichen Pipeline-Lauf geaendert haben, und exists prueft, ob bestimmte Dateien im Repository ueberhaupt vorhanden sind, etwa um einen Docker-Build-Job nur auszufuehren, wenn tatsaechlich ein Dockerfile existiert. Beide Klauseln lassen sich pro Regel mit if kombinieren, sodass ein Job zum Beispiel nur dann laeuft, wenn der Branch main ist UND sich Dateien unter src/ geaendert haben. Diese Kombinierbarkeit ist der zentrale Vorteil gegenueber only/except, weil sich damit selektive, ressourcenschonende Pipelines bauen lassen, die nur die Jobs ausfuehren, die durch eine konkrete Aenderung tatsaechlich betroffen sind, statt bei jedem Commit die komplette Pipeline durchzujagen.
deploy_job:
stage: deploy
script:
- ./deploy.sh production
rules:
- if: '$CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"'
changes:
- src/**/*
- deploy/**/*
- if: '$CI_COMMIT_TAG'
when: on_success
- when: never
3. when, manual und die neue Standardlogik
Ein oft uebersehener Unterschied zwischen only/except und rules betrifft das when-Verhalten: Bei only/except ist when normalerweise on_success, unabhaengig davon, welche only/except-Bedingung greift, waehrend rules jeder einzelnen Regel ein eigenes when zuweisen kann, etwa on_success, on_failure, always, manual oder never. Das erlaubt fein granulare Steuerung innerhalb eines einzigen Jobs, ohne dass man den Job dafuer duplizieren muss. Ein klassisches Beispiel ist ein Deployment-Job, der auf main automatisch laeuft, auf anderen protected Branches nur manuell ausgeloest werden kann und auf allen uebrigen Branches komplett uebersprungen wird, alles in einer einzigen rules-Liste mit drei Eintraegen statt drei separaten Jobs mit unterschiedlichen only/except-Bloecken.
Wichtig ist die implizite Fallback-Regel: Trifft keine der definierten Regeln zu, wird der Job standardmaessig uebersprungen, sofern nicht explizit eine catch-all-Regel mit when: never oder einer leeren Bedingung ergaenzt wird. Das unterscheidet sich fundamental von only/except, wo ein Job ohne explizite Einschraenkung grundsaetzlich laeuft. Wer eine bestehende Pipeline migriert, muss deshalb am Ende jeder rules-Liste bewusst entscheiden, was mit allen nicht abgedeckten Faellen passieren soll, statt sich wie bisher auf ein implizites Verhalten zu verlassen. In der Praxis empfiehlt es sich, jede rules-Liste explizit mit einer letzten Regel abzuschliessen, damit spaeter niemand raten muss, warum ein Job in einem bestimmten Kontext nicht auftaucht.
release:
stage: deploy
script:
- ./release.sh
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: on_success
- if: '$CI_COMMIT_BRANCH =~ /^release\//'
when: manual
- when: never
4. Migrationsstrategie fuer bestehende Pipelines
Eine Migration in einem einzigen grossen Commit ist riskant, weil sich das Verhalten pro Job schnell unbeabsichtigt aendert und Fehler oft erst auffallen, wenn ein Deployment nicht laeuft oder unerwartet auf main triggert. Der sichere Weg ist eine job-fuer-job-Migration: Zunaechst wird dokumentiert, welches only/except-Verhalten der jeweilige Job tatsaechlich hat, inklusive impliziter Defaults, danach wird die aequivalente rules-Logik daneben geschrieben und in einem Merge-Request Pipeline getestet, bevor der alte only/except-Block entfernt wird. Besonders hilfreich ist es, testweise einen Debug-Job einzufuegen, der ausschliesslich echo $CI_PIPELINE_SOURCE, echo $CI_COMMIT_BRANCH und aehnliche Variablen ausgibt, um zu verifizieren, dass die neue rules-Bedingung in genau den erwarteten Faellen greift.
Ein haeufiger Migrationsfehler ist, only: [merge_requests] blind durch if: '$CI_PIPELINE_SOURCE == "merge_request_event"' zu ersetzen, ohne zu beachten, dass only: [merge_requests] gleichzeitig implizit alle anderen refs deaktiviert hat, was bei rules explizit nachgebildet werden muss. Sinnvoll ist es ausserdem, die Migration mit einer Vereinfachung zu verbinden statt nur eine 1:1-Uebersetzung vorzunehmen: Viele Pipelines enthalten historisch gewachsene Job-Duplikate, die urspruenglich nur wegen der beschraenkten only/except-Logik entstanden sind und sich mit kombinierbaren rules-Bedingungen in einen einzigen Job mit mehreren Regeln zusammenfassen lassen, was die Wartbarkeit spuerbar verbessert und die Pipeline-Datei kuerzer macht.
# Vorher: getrennte Jobs wegen only/except-Limitierung
# test_mr:
# only: [merge_requests]
# test_branch:
# only: [branches]
# except: [main]
# Nachher: ein Job, zwei kombinierte Regeln
# test:
# rules:
# - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
# - if: '$CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != "main"'
5. Doppelte Pipelines vermeiden
Ein bekanntes Nebenproblem bei der Umstellung auf Merge-Request-Workflows ist, dass sowohl eine Branch-Pipeline als auch eine Merge-Request-Pipeline fuer denselben Commit laufen koennen, wenn rules nicht sauber konfiguriert ist. GitLab fuehrt standardmaessig fuer jeden Push eine Pipeline aus und zusaetzlich eine weitere, sobald ein Merge Request fuer diesen Branch existiert, was CI-Minuten unnoetig verdoppelt und die Merge-Request-Ansicht mit zwei parallelen Pipeline-Status ueberfrachtet. Die uebliche Loesung ist eine workflow:rules-Konfiguration auf oberster Ebene, die Branch-Pipelines unterdrueckt, sobald fuer denselben Branch bereits eine offene Merge-Request-Pipeline existiert, kombiniert mit denselben if-Bedingungen wie bei den einzelnen Jobs.
In der Praxis bewaehrt sich ein einheitliches Muster, das an mehreren Stellen der .gitlab-ci.yml wiederverwendet wird: Zuerst eine Regel fuer merge_request_event, danach eine Regel fuer Pushes auf den Default-Branch, danach eine Regel fuer Tags, und am Ende eine explizite Ausschlussregel fuer alle uebrigen Faelle. Dieses Muster laesst sich als YAML-Anker definieren und in mehreren Jobs referenzieren, sodass Aenderungen an der Grundlogik nur an einer Stelle gepflegt werden muessen, statt sie in jedem einzelnen Job separat zu synchronisieren, was gerade in groesseren Pipelines mit Dutzenden Jobs den Unterschied zwischen wartbarer und chaotischer Konfiguration ausmacht.
6. Haeufige Fehler bei der Umstellung
Der haeufigste Fehler ist, changes-Bedingungen ohne den passenden Pipeline-Typ zu verwenden: changes vergleicht standardmaessig gegen den letzten erfolgreichen Pipeline-Lauf auf demselben Ref, was bei Merge-Request-Pipelines oft nicht das gewuenschte Ergebnis liefert, weil dort ein Vergleich gegen den Ziel-Branch sinnvoller ist. Fuer diesen Fall bietet GitLab eine erweiterte changes-Syntax mit compare_to, mit der explizit ein Referenz-Branch angegeben werden kann, statt sich auf die automatische Erkennung zu verlassen. Wer diese Feinheit uebersieht, erlebt entweder Jobs, die trotz relevanter Aenderungen nicht laufen, oder Jobs, die bei jedem Commit unnoetig ausgefuehrt werden, weil der Vergleichspunkt falsch gewaehlt ist.
Ein zweiter typischer Fehler ist die Annahme, dass rules-Regeln wie unabhaengige Filter funktionieren, die alle gleichzeitig geprueft werden. Tatsaechlich stoppt GitLab bei der ersten zutreffenden Regel und ignoriert alle folgenden, weshalb die Reihenfolge der Regeln entscheidend ist. Wird zum Beispiel eine allgemeine if-Bedingung versehentlich vor einer spezifischeren platziert, greift immer die allgemeine Regel und die speziellere wird nie erreicht. Die Faustregel lautet deshalb, Regeln von der spezifischsten zur allgemeinsten zu sortieren und am Ende immer eine explizite Fallback-Regel zu setzen, damit das Verhalten fuer alle nicht ausdruecklich behandelten Faelle klar dokumentiert ist statt implizit dem Standardverhalten von GitLab zu ueberlassen.
7. Was Teams nach der Migration typischerweise gewinnen
Teams, die von only/except auf rules migrieren, berichten meist von zwei messbaren Effekten: kuerzere Pipeline-Dateien, weil Job-Duplikate wegfallen, und kuerzere durchschnittliche Pipeline-Laufzeiten, weil sich Jobs mit changes-Bedingungen gezielt auf tatsaechlich betroffene Codebereiche beschraenken lassen. Gerade in Monorepos oder in Projekten mit klar getrennten Frontend- und Backend-Verzeichnissen spart eine gut konfigurierte changes-Bedingung erhebliche CI-Zeit, weil nicht mehr bei jeder Aenderung an der Dokumentation die komplette Test-Suite fuer den Anwendungscode durchlaeuft. Dieser Effekt ist bei only/except praktisch nicht erreichbar, weil dort keine Datei-basierten Bedingungen existierten, bevor rules eingefuehrt wurde.
Der zweite, oft unterschaetzte Effekt betrifft die Nachvollziehbarkeit: Eine rules-Liste mit klaren if/changes/exists-Bedingungen und einer expliziten Fallback-Regel ist fuer neue Teammitglieder deutlich leichter zu lesen als verstreute only/except-Bloecke mit stillschweigenden Annahmen ueber Default-Verhalten. In Code-Reviews faellt zudem sofort auf, wenn eine Bedingung fehlt oder eine Regel zu breit gefasst ist, weil die gesamte Logik an einer Stelle im Job steht statt ueber mehrere Attribute verteilt zu sein. Fuer Teams, die regelmaessig neue Mitglieder einarbeiten oder Pipelines an externe Dienstleister uebergeben, ist dieser Lesbarkeitsgewinn oft der eigentliche Hauptgrund fuer die Migration, mehr noch als die reine Funktionserweiterung.
8. Tooling und Validierung waehrend der Umstellung
GitLab bietet mit der CI/CD-Pipeline-Editor-Ansicht im Projekt eine Live-Validierung der .gitlab-ci.yml, die Syntaxfehler in rules-Bloecken sofort markiert, bevor ein Commit ueberhaupt gepusht wird. Zusaetzlich zeigt die Visualisierung im Editor an, welche Jobs in welcher Stage landen, was bei einer Migration hilft, versehentlich verschobene Jobs schnell zu erkennen. Fuer komplexere Migrationen lohnt es sich, die CI Lint API zu nutzen, die serverseitig genau simuliert, wie GitLab eine gegebene .gitlab-ci.yml fuer einen bestimmten Ref und ein bestimmtes Pipeline-Source interpretieren wuerde, ohne dass dafuer tatsaechlich eine Pipeline gestartet werden muss.
Ergaenzend empfiehlt sich ein temporaerer Merge Request, der ausschliesslich dazu dient, alle relevanten Trigger-Szenarien durchzuspielen: ein Push auf einen Feature-Branch, ein Push auf main, das Setzen eines Tags und das Oeffnen eines Merge Requests. Fuer jedes Szenario wird dokumentiert, welche Jobs laufen sollten und welche tatsaechlich laufen, bevor der alte only/except-Code entfernt wird. Dieser Testdurchlauf mag zusaetzlichen Aufwand bedeuten, verhindert aber zuverlaessig, dass eine Migration in Produktion zu einem stillschweigend uebersprungenen Deployment-Job fuehrt, was in der Praxis der teuerste denkbare Fehler bei dieser Art von Refactoring ist.
9. Fazit: rules als Standard, only/except als Auslaufmodell
only/except bleibt aus Kompatibilitaetsgruenden funktionsfaehig, ist aber fuer neue Pipelines keine sinnvolle Wahl mehr, weil rules jede Faehigkeit von only/except abdeckt und zusaetzlich kombinierbare Bedingungen, granulares when-Verhalten und dateibasierte changes-Pruefungen bietet. Fuer bestehende Pipelines lohnt sich eine schrittweise, gut getestete Migration, weil sie nicht nur neue Moeglichkeiten eroeffnet, sondern bestehende Pipeline-Dateien meist deutlich verkuerzt und verstaendlicher macht. Wer diese Migration sorgfaeltig plant, gewinnt am Ende eine Pipeline, die sich leichter erweitern, debuggen und an neue Teammitglieder uebergeben laesst.
In der folgenden Tabelle sind die wichtigsten Unterschiede zwischen only/except und rules noch einmal gegenuebergestellt, als praktische Referenz fuer die eigene Migrationsplanung.
| Aspekt | only/except | rules | Praxis-Empfehlung |
|---|---|---|---|
| Bedingungen kombinieren | nicht moeglich | if/changes/exists frei kombinierbar | rules fuer alle Mehrfachbedingungen nutzen |
| Default bei fehlender Regel | Job laeuft | Job wird uebersprungen | immer explizite Fallback-Regel setzen |
| Dateibasierte Bedingungen | nicht unterstuetzt | changes mit Pfaden und compare_to | fuer Monorepos und selektive Pipelines nutzen |
| when pro Bedingung | global fuer den Job | pro Regel individuell | manuelle Deployments gezielt mit rules steuern |
| Wartungsstatus bei GitLab | Legacy, keine neuen Features | aktiv weiterentwickelt | fuer neue Pipelines ausschliesslich rules verwenden |
Mironsoft
CI/CD-Pipelines, Zero-Downtime-Deployments und Release-Automatisierung
Deployments, die ohne Ausfallzeit und ohne Nervenkitzel laufen?
Wir prüfen bestehende GitLab-Pipelines auf fragile Deployment-Schritte und fehlende Absicherung und bauen daraus einen Release-Prozess mit Zero-Downtime-Deployments, automatisierten Checks und einem Rollback, dem ihr im Ernstfall vertrauen könnt.
Pipeline-Review
Bestehende .gitlab-ci.yml auf Fragilität, fehlende Stages und Sicherheitslücken prüfen.
Zero-Downtime-Deployment
Symlink-Releases, Health-Checks und Rollback-Strategien für Magento-Shops aufbauen.
CI/CD-Automatisierung
Tests, Security-Scans und Deployments zu einer zuverlässigen Pipeline verbinden.
10. Zusammenfassung
rules vs. only/except: Das Wichtigste auf einen Blick
Kernproblem von only/except
Keine Kombination mehrerer Bedingungen, kein Datei-basiertes Filtern, uneinheitliche implizite Defaults je nach Konfiguration.
Kernvorteil von rules
if, changes und exists frei kombinierbar, individuelles when pro Regel, aktive Weiterentwicklung durch GitLab.
Migrationsansatz
Job fuer Job migrieren, Verhalten vorher dokumentieren, mit Debug-Jobs verifizieren, danach erst den alten Block entfernen.
Haeufigste Stolperfalle
Fehlende explizite Fallback-Regel am Ende der Liste sowie falsch gewaehlter Vergleichspunkt bei changes.