Blog Engineering Einfaches Erstellen einer GitLab-CI/CD-Pipeline für ein Monorepo
Aktualisiert am: May 26, 2025
5 Minuten Lesezeit

Einfaches Erstellen einer GitLab-CI/CD-Pipeline für ein Monorepo

Erfahre, wie du eine GitLab-CI/CD-Pipeline für ein Monorepo erstellst, um mehrere Anwendungen in einem Repository zu hosten.

pipeline  2 - cover

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.

Weitere CI/CD-Ressourcen

Wir möchten gern von dir hören

Hat dir dieser Blogbeitrag gefallen oder hast du Fragen oder Feedback? Erstelle ein neues Diskussionsthema im GitLab Community-Forum und tausche deine Eindrücke aus. Teile dein Feedback

Bist du bereit?

Sieh dir an, was dein Team mit einer einheitlichen DevSecOps-Plattform erreichen könnte.

Kostenlose Testversion anfordern

Finde heraus, welcher Tarif für dein Team am besten geeignet ist

Erfahre mehr über die Preise

Erfahre mehr darüber, was GitLab für dein Team tun kann

Sprich mit einem Experten/einer Expertin