Mit Monorepos kannst du den Code von mehreren Anwendungen in einem einzigen Repository hosten. In GitLab bedeutet das, dass du den Quellcode verschiedener Anwendungen in getrennten Verzeichnissen in einem Projekt ablegen musst. So kannst du zwar deinen Code versionskontrolliert speichern, aber die CI/CD-Pipeline von GitLab nicht voll ausschöpfen … bis jetzt!
Der Idealfall: CI/CD in einem Monorepo
Da du mehr als den Code einer Anwendung in deinem Repository hast, wirst du mehr als eine Pipeline-Konfiguration benötigen. Wenn du z. B. eine .NET-Anwendung und eine Spring-Anwendung in einem Projekt hast, kann es sein, dass für jede Anwendung unterschiedliche Build- und Testjobs ausgeführt werden müssen. Im Idealfall kannst du die Pipelines vollständig entkoppeln und jede Pipeline nur auf der Grundlage von Änderungen am Quellcode der jeweiligen Anwendung ausführen.
Der technische Ansatz hierfür wäre eine .gitlab-ci.yml
-Pipeline-Konfigurationsdatei auf Projektebene, die eine bestimmte YAML-Datei enthält, die auf Änderungen in einem bestimmten Verzeichnis basiert. Die .gitlab-ci.yml
-Pipeline dient als Steuerungsebene, die auf der Grundlage der Änderungen am Code die entsprechende Pipeline anstößt.
Der Legacy-Ansatz
Vor GitLab 16.4 war es nicht möglich, eine YAML-Datei bei Änderungen an einem Verzeichnis oder einer Datei in einem Projekt einzubinden. Wir konnten diese Funktionalität jedoch mit einem Workaround realisieren.
In unserem Monorepo-Projekt haben wir zwei Verzeichnisse für verschiedene Anwendungen. In diesem Beispiel gibt es die Verzeichnisse java
und python
, die jeweils eine Java- und eine Python-Anwendung repräsentieren. Jedes Verzeichnis hat eine anwendungsspezifische YAML-Datei, um jede App zu bauen. In der Pipeline-Datei des Projekts binden wir einfach beide Anwendungspipeline-Dateien ein und führen die logische Verarbeitung direkt in diesen Dateien durch.
.gitlab-ci.yml
:
stages:
- build
- test
- deploy
top-level-job:
stage: build
script:
- echo "Hello world..."
include:
- local: '/java/j.gitlab-ci.yml'
- local: '/python/py.gitlab-ci.yml'
In jeder anwendungsspezifischen Pipeline-Datei erstellen wir einen versteckten Auftrag mit dem Namen .java-common oder .python-common, der nur ausgeführt wird, wenn es Änderungen im Verzeichnis der jeweiligen Anwendung gibt. Versteckte Jobs (nur in englischer Sprache verfügbar werden standardmäßig nicht ausgeführt und werden oft verwendet, um bestimmte Jobkonfigurationen wiederzuverwenden. Jede Pipeline erweitert diesen versteckten Job, um die Regeln zu übernehmen, die festlegen, welche Dateien auf Änderungen überwacht werden sollen, die dann den Pipeline-Job auslösen.
j.gitlab-ci.yml
:
stages:
- build
- test
- deploy
.java-common:
rules:
- changes:
- '../java/*'
java-build-job:
extends: .java-common
stage: build
script:
- echo "Building Java"
java-test-job:
extends: .java-common
stage: test
script:
- echo "Testing Java"
py.gitlab-ci.yml
:
stages:
- build
- test
- deploy
.python-common:
rules:
- changes:
- '../python/*'
python-build-job:
extends: .python-common
stage: build
script:
- echo "Building Python"
python-test-job:
extends: .python-common
stage: test
script:
- echo "Testing Python"
Das hat einige Nachteile, z. B. muss der Job für jeden anderen Job in der YAML-Datei erweitert werden, um sicherzustellen, dass er mit den Regeln übereinstimmt, was eine Menge redundanten Code und Raum für menschliche Fehler schafft. Außerdem können erweiterte Jobs keine doppelten Schlüssel haben, so dass du nicht in jedem Job deine eigene rules
-Logik definieren kannst, da es zu einer Kollision der Schlüssel kommen würde und ihre Werte nicht zusammengeführt werden (nur in englischer Sprache verfügbar).
Das führt dazu, dass eine Pipeline ausgeführt wird, die die j.gitlab-ci.yml
-Jobs enthält, wenn java/
aktualisiert wird, und py.gitlab-ci.yml
-Jobs, wenn python/
aktualisiert wird.
Der neue Ansatz: Bedingtes Einbeziehen von Pipeline-Dateien
In GitLab 16.4 haben wir include
mit rules:changes
für Pipelines (nur in englischer Sprache verfügbar) eingeführt. Bisher konntest du include
mit rules:if
verwenden, aber nicht mit rules:changes
. Das macht diese Aktualisierung extrem effizient. Jetzt kannst du einfach das Schlüsselwort include
verwenden und die Monorepo-Regeln in deiner Projekt-Pipeline-Konfiguration definieren.
Neue .gitlab-ci.yml
:
stages:
- build
- test
top-level-job:
stage: build
script:
- echo "Hello world..."
include:
- local: '/java/j.gitlab-ci.yml'
rules:
- changes:
- 'java/*'
- local: '/python/py.gitlab-ci.yml'
rules:
- changes:
- 'python/*'
Jetzt kann sich die YAML jeder Anwendung nur noch auf das Erstellen und Testen des Codes dieser Anwendung konzentrieren, ohne dass ein versteckter Job wiederholt erweitert werden muss. Das ermöglicht mehr Flexibilität bei der Definition von Jobs und reduziert das Neuschreiben von Code für Entwickler(innen).
Neue j.gitlab-ci.yml
:
stages:
- build
- test
- deploy
java-build-job:
stage: build
script:
- echo "Building Java"
java-test-job:
stage: test
script:
- echo "Testing Java"
Neue py.gitlab-ci.yml
:
stages:
- build
- test
- deploy
python-build-job:
stage: build
script:
- echo "Building Python"
python-test-job:
stage: test
script:
- echo "Testing Python"
Damit werden die Java- und Python-Jobs nur dann einbezogen, wenn ihre Verzeichnisse geändert werden. Bei deiner Implementierung solltest du bedenken, dass Jobs unerwartet ausgeführt werden können, wenn du changes
verwendest (nur in englischer Sprache verfügbar). Die Regel „changes“ wird immer als wahr interpretiert, wenn ein neuer Branch oder ein neues Tag in GitLab gepusht wird, so dass alle Jobs unabhängig von der Definition von rules:changes
beim ersten Push in einen Branch ausgeführt werden. Du kannst dieses Problem umgehen, indem du zuerst deinen Feature-Branch erstellst und dann eine Merge Request öffnest, um mit der Entwicklung zu beginnen, da der erste Push auf den Branch, wenn er erstellt wird, alle Jobs zur Ausführung zwingt.
Letztlich sind Monorepos eine Strategie, die mit GitLab und CI/CD genutzt werden kann. Mit unserer neuen Funktion include
mit rules:changes
haben wir eine bessere bewährte Methode für die Nutzung von GitLab CI mit Monorepos. Um Monorepos zu verwenden, hol dir noch heute eine kostenlose Testversion von Gitlab Ultimate.