[{"data":1,"prerenderedAt":766},["ShallowReactive",2],{"/de-de/blog/efficient-devsecops-workflows-hands-on-python-gitlab-api-automation":3,"navigation-de-de":41,"banner-de-de":444,"footer-de-de":454,"blog-post-authors-de-de-Michael Friedrich":659,"blog-related-posts-de-de-efficient-devsecops-workflows-hands-on-python-gitlab-api-automation":673,"assessment-promotions-de-de":717,"next-steps-de-de":756},{"id":4,"title":5,"authorSlugs":6,"body":8,"categorySlug":9,"config":10,"content":14,"description":8,"extension":28,"isFeatured":12,"meta":29,"navigation":30,"path":31,"publishedDate":20,"seo":32,"stem":36,"tagSlugs":37,"__hash__":40},"blogPosts/de-de/blog/efficient-devsecops-workflows-hands-on-python-gitlab-api-automation.yml","Efficient Devsecops Workflows Hands On Python Gitlab Api Automation",[7],"michael-friedrich",null,"engineering",{"slug":11,"featured":12,"template":13},"efficient-devsecops-workflows-hands-on-python-gitlab-api-automation",false,"BlogPost",{"title":15,"description":16,"authors":17,"heroImage":19,"date":20,"body":21,"category":9,"tags":22,"updatedDate":27},"Effiziente DevSecOps-Workflows: Praktische python-gitlab-API-Automatisierung","Die Python-GitLab-Bibliothek ist eine nützliche Basis für die GitLab-API. In diesem Tutorial erfährst du mehr über praktische Beispiele und bewährte Verfahren.",[18],"Michael Friedrich","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659883/Blog/Hero%20Images/post-cover-image.jpg","2023-02-01","Ein oft zitiertes Sprichwort aus einer Konferenzpräsentation lautet: „Manuelle Arbeit ist ein Fehler“. Bei sich wiederholenden Aufgaben in Arbeitsabläufen ist es ratsam, so viel wie möglich zu automatisieren. Ein Beispiel dafür ist die Nutzung einer REST-API zur Bestandsaufnahme von Einstellungen oder das Erstellen neuer Kommentare in GitLab-Issues und Merge-Anfragen mittels API-Aktionen.\n\n## GitLab-API-Interaktion: Möglichkeiten und Unterstützung durch API-Abstraktionsbibliotheken\n\nDie Interaktion mit der REST-API von GitLab kann auf verschiedene Weise erfolgen: entweder durch HTTP-Anfragen mit curl (oder hurl) in der Befehlszeile oder durch das Schreiben von Skripten in einer Programmiersprache. Letzteres kann dazu führen, dass der Code für HTTP-Anfragen und das Parsen der JSON-Antworten neu entwickelt werden muss. Glücklicherweise unterstützt die umfangreiche GitLab-Community viele Programmiersprachen mit API-Abstraktionsbibliotheken.\n\nDiese Bibliotheken bieten Unterstützung für alle API-Attribute, fügen Hilfsfunktionen zum Abrufen, Erstellen und Löschen von Objekten hinzu und helfen Entwicklern dabei, sich auf ihre Kernaufgaben zu konzentrieren. Die [python-gitlab Bibliothek](https://python-gitlab.readthedocs.io/en/stable/) ist ein besonders funktionsreiches und einfach zu verwendendes Beispiel für eine solche Bibliothek in Python.\n\nIn diesem Blogbeitrag wird die grundlegende Nutzung der python-gitlab-Bibliothek erläutert, einschließlich der Arbeit mit API-Objekten, Attributen, Paginierung und Resultsets. Zudem werden spezifischere Anwendungsfälle vorgestellt, in denen Daten gesammelt, Zusammenfassungen gedruckt und Daten in die API geschrieben werden, um Kommentare und Commits zu erstellen. Viele dieser Anwendungsfälle basieren auf Fragen aus der Community in Foren, auf Hacker News oder in Issues.\n\n## Inhaltsverzeichnis\n- [GitLab-API-Interaktion: Möglichkeiten und Unterstützung durch API-Abstraktionsbibliotheken](#gitlab-api-interaktion-möglichkeiten-und-unterstützung-durch-api-abstraktionsbibliotheken)\n- [Los geht's: python-gitlab Install](#los-geht's-python-gitlab-install)\n- [GitLab API Python: Konfiguration](#gitlab-api-python-konfiguration)\n- [Objekte verwalten: das GitLab-Objekt](#objekte-verwalten-das-gitlab-objekt)\n  - [Objektmanager und Laden von Objekten](#objektmanager-und-laden-von-objekten)\n  - [Paginierung der Ergebnisse](#paginierung-der-ergebnisse)\n  - [Arbeiten mit Objektbeziehungen](#arbeiten-mit-objektbeziehungen)\n  - [Arbeiten mit verschiedenen Objektsammlungsbereichen](#arbeiten-mit-verschiedenen-objektsammlungsbereichen)\n- [DevSecOps-Anwendungsfälle für API-Leseaktionen](#devsecops-anwendungsfälle-für-api-leseaktionen)\n  - [Zweige nach Zusammenführungsstatus auflisten](#zweige-nach-zusammenführungsstatus-auflisten)\n  - [Drucken von Projekteinstellungen zur Überprüfung: MR-Genehmigungsregeln](#drucken-von-projekteinstellungen-zur-überprüfung-mr-genehmigungsregeln)\n  - [Inventarisierung: Abrufen aller CI/CD-Variablen, die geschützt oder maskiert sind](#inventarisierung-abrufen-aller-cicd-variablen-die-geschützt-oder-maskiert-sind)\n  - [Herunterladen einer Datei aus dem Repository](#herunterladen-einer-datei-aus-dem-repository)\n  - [Hilfe zur Migration: Auflistung aller zertifikatsbasierten Kubernetes-Cluster](#hilfe-zur-migration-auflistung-aller-zertifikatsbasierten-kubernetes-cluster)\n  - [Team-Effizienz: Prüfe, ob bestehende Merge-Requests nach dem Mergen einer großen Refactoring-MR neu gebasht werden müssen](#team-effizienz-prüfe-ob-bestehende-merge-requests-nach-dem-mergen-einer-großen-refactoring-mr-neu-gebasht-werden-müssen)\n- [DevSecOps-Anwendungsfälle für API-Schreibaktionen](#devsecops-anwendungsfälle-für-api-schreibaktionen)\n  - [Verschieben von Epics zwischen Gruppen](#verschieben-von-epics-zwischen-gruppen)\n  - [Automatisierung des Verschiebens von Epics](#automatisierung-des-verschiebens-von-epics)\n  - [Compliance: Sicherstellen, dass Projekteinstellungen nicht überschrieben werden](#compliance-sicherstellen-dass-projekteinstellungen-nicht-überschrieben-werden)\n  - [Notizen machen, Fälligkeitsübersicht erstellen](#notizen-machen-fälligkeitsübersicht-erstellen)\n\n## Los geht's: python-gitlab Install\n\nDie Dokumentation von python-gitlab bietet eine umfassende Einführung in die Nutzung, einschließlich Anleitungen für den Einstieg, Informationen zu Objekttypen und deren Methoden sowie kombinierte Workflow-Beispiele. Ergänzend dazu ist die Dokumentation der GitLab-API-Ressourcen hilfreich, da sie die verfügbaren Objektattribute detailliert beschreibt. Zusammen sind diese beiden Dokumentationen die besten Ressourcen für den Einstieg.\n\nDie Code-Beispiele in diesem Blogbeitrag setzen Python 3.8+ und die python-gitlab-Bibliothek voraus. Weitere notwendige Abhängigkeiten sind in der Datei requirements.txt aufgeführt. Ein Beispiel erfordert die Bibliothek pyyaml zum Parsen von YAML-Konfigurationen. Um den Code der Anwendungsfälle nachzuvollziehen und zu üben, empfiehlt es sich, das Projekt zu klonen, die Anforderungen zu installieren und die Skripte auszuführen.\n\n```shell\ngit clone https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python.git\n\ncd gitlab-api-python\n\nbrew install python\n\npip3 install -r requirements.txt\n\npython3 \u003Cscriptname>.py\n```\nDie Skripte verwenden absichtlich keine gemeinsam genutzte Bibliothek, die z. B. generische Funktionen für das Lesen von Parametern oder zusätzliche Hilfsfunktionen bereitstellt. Die Idee ist, einfach zu verstehende Beispiele zu zeigen, die eigenständig zum Testen verwendet werden können und lediglich die Installation der python-gitlab-Bibliothek erfordern.\n\nEs wird empfohlen, den Code für den Produktionseinsatz zu verbessern. Dies kann auch beim Aufbau eines gewarteten API-Tooling-Projekts helfen, das z. B. Container-Images und CI/CD-Vorlagen für Entwickler enthält, die auf einer DevSecOps-Plattform genutzt werden können.\n\n## GitLab API Python: Konfiguration\n\nOhne Konfiguration führt python-gitlab unauthentifizierte Anfragen an den Standardserver https://gitlab.com aus. Die häufigsten Konfigurationseinstellungen beziehen sich auf die GitLab-Instanz, mit der eine Verbindung hergestellt werden soll, und die Authentifizierungsmethode durch Angabe von Zugriffstokens. python-gitlab unterstützt verschiedene Arten der Konfiguration: Eine Konfigurationsdatei oder Umgebungsvariablen.\n\nDie Konfigurationsdatei ist für die API-Bibliotheksbindungen und die CLI verfügbar (die CLI wird in diesem Blogpost nicht erläutert). Die Konfigurationsdatei unterstützt Credential Helpers für den direkten Zugriff auf Token.\n\nUmgebungsvariablen als alternative Konfigurationsmethode bieten eine einfache Möglichkeit, das Skript auf dem Terminal auszuführen, in Container-Images zu integrieren und sie für die Ausführung in CI/CD-Pipelines vorzubereiten.\n\nDie Konfiguration muss in den Kontext des Python-Skripts geladen werden. Beginne mit dem Import der os-Bibliothek, um die Umgebungsvariablen mit der Methode os.environ.get() abzurufen. Der erste Parameter gibt den Schlüssel an, der zweite Parameter legt den Standardwert fest, wenn die Variable in der Umgebung nicht verfügbar ist.\n\n```python\nimport os\n\ngl_server = os.environ.get('GL_SERVER', 'https://gitlab.com')\n\nprint(gl_server)\n```\n\nDie Parametrisierung auf dem Terminal kann direkt nur für den Befehl erfolgen oder in die Shell-Umgebung exportiert werden.\n\n```shell\n$ GL_SERVER=’https://gitlab.company.com’ python3 script.py\n\n$ export GL_SERVER=’https://gitlab.company.com’\n$ python3 script.py\n```\n\nEs wird empfohlen, Sicherheitsprüfungen hinzuzufügen, um sicherzustellen, dass alle Variablen gesetzt sind, bevor das Programm weiter ausgeführt wird. Der folgende Ausschnitt importiert die erforderlichen Bibliotheken, liest die Umgebungsvariable GL_SERVER und erwartet, dass der Benutzer die Variable GL_TOKEN setzt. Wenn dies nicht der Fall ist, gibt das Skript Fehler aus und ruft sys.exit(1) auf, um einen Fehlerstatus anzuzeigen.\n\n```python\nimport gitlab\nimport os\nimport sys\n\nGITLAB_SERVER = os.environ.get('GL_SERVER', 'https://gitlab.com')\nGITLAB_TOKEN = os.environ.get('GL_TOKEN')\n\nif not GITLAB_TOKEN:\n    print(\"Please set the GL_TOKEN env variable.\")\n    sys.exit(1)\n\n```\n\nFolgend siehst du ein ausführlicheres Beispiel, das eine Verbindung zur API herstellt und eine tatsächliche Datenabfrage durchführt.\n\n## Objekte verwalten: das GitLab-Objekt\n\nFür jede Interaktion mit der API muss das GitLab-Objekt instanziiert werden. Dies ist der Einstiegspunkt für die Konfiguration des GitLab-Servers für die Verbindung, die Authentifizierung mithilfe von Zugriffstokens und weitere globale Einstellungen für die Paginierung, das Laden von Objekten und mehr.\n\nIm folgenden Beispiel wird eine nicht authentifizierte Anfrage an gitlab.com ausgeführt. Es ist möglich, auf öffentliche API-Endpunkte zuzugreifen und zum Beispiel eine bestimmte [.gitignore Vorlage für Python](https://python-gitlab.readthedocs.io/en/stable/gl_objects/templates.html#gitignore-templates) abzurufen.\n\n[python_gitlab_object_unauthenticated.py](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/python_gitlab_object_unauthenticated.py)\n\n```python\nimport gitlab\n\ngl = gitlab.Gitlab()\n\n# Get .gitignore templates without authentication\ngitignore_templates = gl.gitignores.get('Python')\n\nprint(gitignore_templates.content)\n```\n\nDie nächsten Abschnitte geben weitere Einblicke in:\n\n- Objektmanager und Laden von Objekten\n- Paginierung von Ergebnissen\n- Arbeiten mit Objektbeziehungen\n- Arbeiten mit verschiedenen Objektsammlungsbereichen\n\n### Objektmanager und Laden von Objekten\n\nDie python-gitlab-Bibliothek ermöglicht den Zugriff auf GitLab-Ressourcen über sogenannte „[Manager](https://python-gitlab.readthedocs.io/en/stable/api-usage.html#managers)\". Jeder Managertyp implementiert Methoden zur Arbeit mit den Datensätzen (list, get, etc.).\n\nDas Skript zeigt, wie man auf Untergruppen, direkte Projekte, alle Projekte einschließlich Untergruppen, Issues, Epics und To-dos zugreifen kann. Diese Methoden und der API-Endpunkt erfordern eine Authentifizierung für den Zugriff auf alle Attribute. Der Codeschnipsel verwendet daher Variablen, um das Authentifizierungs-Token abzurufen sowie die GROUP_ID-Variable, um eine Hauptgruppe anzugeben, bei der die Suche beginnen soll.\n\n```python\n#!/usr/bin/env python\n\nimport gitlab\nimport os\nimport sys\n\nGITLAB_SERVER = os.environ.get('GL_SERVER', 'https://gitlab.com')\n# https://gitlab.com/gitlab-da/use-cases/\nGROUP_ID = os.environ.get('GL_GROUP_ID', 16058698)\nGITLAB_TOKEN = os.environ.get('GL_TOKEN')\n\nif not GITLAB_TOKEN:\n    print(\"Please set the GL_TOKEN env variable.\")\n    sys.exit(1)\n\ngl = gitlab.Gitlab(GITLAB_SERVER, private_token=GITLAB_TOKEN)\n\n# Main\nmain_group = gl.groups.get(GROUP_ID)\n\nprint(\"Sub groups\")\nfor sg in main_group.subgroups.list():\n    print(\"Subgroup name: {sg}\".format(sg=sg.name))\n\nprint(\"Projects (direct)\")\nfor p in main_group.projects.list():\n    print(\"Project name: {p}\".format(p=p.name))\n\nprint(\"Projects (including subgroups)\")\nfor p in main_group.projects.list(include_subgroups=True, all=True):\n     print(\"Project name: {p}\".format(p=p.name))\n\nprint(\"Issues\")\nfor i in main_group.issues.list(state='opened'):\n    print(\"Issue title: {t}\".format(t=i.title))\n\nprint(\"Epics\")\nfor e in main_group.issues.list():\n    print(\"Epic title: {t}\".format(t=e.title))\n\nprint(\"Todos\")\nfor t in gl.todos.list(state='pending'):\n    print(\"Todo: {t} url: {u}\".format(t=t.body, u=t.target_url\n\n```\n\nDu kannst das Skript [`python_gitlab_object_manager_methods.py`](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/python_gitlab_object_manager_methods.py) ausführen, indem du die GROUP_ID-Variable auf GitLab.com SaaS für deine eigene zu analysierende Gruppe überschreibst. Die Variable GL_SERVER muss für selbstverwaltete Instanzziele angegeben werden. GL_TOKEN muss das persönliche Zugriffstoken enthalten.\n\n```shell\nexport GL_TOKEN=xxx\n\nexport GL_SERVER=”https://gitlab.company.com”\n\nexport GL_SERVER=”https://gitlab.com”\n\nexport GL_GROUP_ID=1234\n\npython3 python_gitlab_object_manager_methods.py\n```\nIn Zukunft werden die Beispiel-Snippets die Python-Header und das Parsen von Umgebungsvariablen nicht mehr zeigen, um sich auf den Algorithmus und die Funktionalität zu konzentrieren. Alle Skripte sind Open Source unter der MIT-Lizenz und in [diesem Projekt](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python) verfügbar.\n\n### Paginierung der Ergebnisse\n\nStandardmäßig gibt die GitLab-API nicht alle Ergebnissätze zurück und erfordert, dass die Clients die Paginierung verwenden, um durch alle Ergebnisseiten zu iterieren. Mit der python-gitlab-Bibliothek können Benutzer die Einstellungen global im GitLab-Objekt oder bei jedem list()-Aufruf [festlegen](https://python-gitlab.readthedocs.io/en/stable/api-usage.html#pagination). Standardmäßig würden alle Ergebnissätze API-Anfragen auslösen, was die Skriptausführung verlangsamen kann. Die empfohlene Methode ist die Verwendung von iterator=True, die ein Generatorobjekt zurückgibt, und API-Aufrufe werden beim Zugriff auf das Objekt bei Bedarf ausgelöst.\n\nDas folgende Beispiel sucht nach dem Gruppennamen everyonecancontribute und verwendet eine [Paginierung](https://docs.gitlab.com/ee/api/rest/index.html#pagination) der Schlüsselsätze mit 100 Ergebnissen auf jeder Seite. Der Iterator wird bei gl.groups.list(iterator=True) auf true gesetzt, um bei Bedarf neue Ergebnissätze abzurufen. Wird der gesuchte Gruppenname gefunden, bricht die Schleife ab und gibt eine Zusammenfassung aus, einschließlich der Messung der Dauer der gesamten Suchanfrage.\n\n```python\nSEARCH_GROUP_NAME=\"everyonecancontribute\"\n\n# Use keyset pagination\n# https://python-gitlab.readthedocs.io/en/stable/api-usage.html#pagination\ngl = gitlab.Gitlab(GITLAB_SERVER, private_token=GITLAB_TOKEN,\n    pagination=\"keyset\", order_by=\"id\", per_page=100)\n\n# Iterate over the list, and fire new API calls in case the result set does not match yet\ngroups = gl.groups.list(iterator=True)\n\nfound_page = 0\nstart = timer()\n\nfor group in groups:\n    if SEARCH_GROUP_NAME == group.name:\n        # print(group) # debug\n        found_page = groups.current_page\n        break\n\nend = timer()\n\nduration = f'{end-start:.2f}'\n\nif found_page > 0:\n    print(\"Pagination API example for Python with GitLab{desc} - found group {g} on page {p}, duration {d}s\".format(\n        desc=\", the DevSecOps platform\", g=SEARCH_GROUP_NAME, p=found_page, d=duration))\nelse:\n    print(\"Could not find group name '{g}', duration {d}\".format(g=SEARCH_GROUP_NAME, d=duration))\n\n```\n\nBeim Ausführen von python_gitlab_pagination.py wurde die Gruppe [everyonecancontribute](https://gitlab.com/everyonecancontribute) auf Seite 5 gefunden.\n\n```shell\n$ python3 python_gitlab_pagination.py\nPagination API example for Python with GitLab, the DevSecOps platform - found group everyonecancontribute on page 5, duration 8.51s\n```\n\n### Arbeiten mit Objektbeziehungen\n\nBei der Arbeit mit Objektbeziehungen – z. B. beim Sammeln aller Projekte in einer bestimmten Gruppe – müssen zusätzliche Schritte unternommen werden. Die zurückgegebenen Projektobjekte enthalten standardmäßig nur begrenzte Attribute. Für verwaltbare Objekte ist ein zusätzlicher get()-Aufruf erforderlich, der das vollständige Projektobjekt von der API im Hintergrund anfordert. Dieser On-Demand-Workflow hilft, Wartezeiten und Datenverkehr zu vermeiden, indem er die sofort zurückgegebenen Attribute reduziert.\n\nDas folgende Beispiel veranschaulicht das Problem, indem es eine Schleife durch alle Projekte in einer Gruppe durchläuft und versucht, die Funktion project.branches.list() aufzurufen, was eine Ausnahme im try/except-Flow auslöst. Im zweiten Beispiel wird ein verwaltbares Projektobjekt ermittelt und der Funktionsaufruf erneut versucht.\n\n```python\n# Main\ngroup = gl.groups.get(GROUP_ID)\n\n# Collect all projects in group and subgroups\nprojects = group.projects.list(include_subgroups=True, all=True)\n\nfor project in projects:\n    # Try running a method on a weak object\n    try:\n       print(\"🤔 Project: {pn} 💡 Branches: {b}\\n\".format(\n        pn=project.name,\n        b=\", \".join([x.name for x in project.branches.list()])))\n    except Exception as e:\n        print(\"Got exception: {e} \\n ===================================== \\n\".format(e=e))\n\n    # Retrieve a full manageable project object\n    # https://python-gitlab.readthedocs.io/en/stable/gl_objects/groups.html#examples\n    manageable_project = gl.projects.get(project.id)\n\n    # Print a method available on a manageable object\n    print(\"🤔 Project: {pn} 💡 Branches: {b}\\n\".format(\n        pn=manageable_project.name,\n        b=\", \".join([x.name for x in manageable_project.branches.list()])))\n\n```\n\nDer Exception-Handler in der python-gitlab-Bibliothek gibt die Fehlermeldung aus und verlinkt auch auf die Dokumentation. Es ist hilfreich, bei der Fehlersuche zu beachten, dass Objekte möglicherweise nicht verwaltet werden können, wenn du nicht auf Objektattribute oder Funktionsaufrufe zugreifen kannst.\n\n```shell\n$ python3 python_gitlab_manageable_objects.py\n\n🤔 Project: GitLab API Playground 💡 Branches: cicd-demo-automated-comments, docs-mr-approval-settings, main\n\nGot exception: 'GroupProject' object has no attribute 'branches'\n\n\u003Cclass 'gitlab.v4.objects.projects.GroupProject'> was created via a\nlist() call and only a subset of the data may be present. To ensure\nall data is present get the object using a get(object.id) call. For\nmore details, see:\n\nhttps://python-gitlab.readthedocs.io/en/v3.8.1/faq.html#attribute-error-list\n =====================================\n\n```\n\nHier findest du das vollständige [Skript](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/python_gitlab_manageable_objects.py).\n\n### Arbeiten mit verschiedenen Objektsammlungsbereichen\n\nManchmal muss das Skript alle Projekte aus einer selbstverwalteten Instanz, aus einer Gruppe mit Untergruppen oder aus einem einzelnen Projekt sammeln. Letzteres ist hilfreich, um die erforderlichen Attribute schneller testen zu können, und der Gruppenabruf hilft später beim Testen im großen Maßstab. Das folgende Snippet sammelt alle Projektobjekte in der projects-Liste und fügt Objekte aus verschiedenen eingehenden Konfigurationen hinzu. Du wirst auch wieder das verwaltbare Objektmuster für Projekte in Gruppen sehen.\n\n```python\n\n    # Collect all projects, or prefer projects from a group id, or a project id\n    projects = []\n\n    # Direct project ID\n    if PROJECT_ID:\n        projects.append(gl.projects.get(PROJECT_ID))\n\n    # Groups and projects inside\n    elif GROUP_ID:\n        group = gl.groups.get(GROUP_ID)\n\n        for project in group.projects.list(include_subgroups=True, all=True):\n            # https://python-gitlab.readthedocs.io/en/stable/gl_objects/groups.html#examples\n            manageable_project = gl.projects.get(project.id)\n            projects.append(manageable_project)\n\n    # All projects on the instance (may take a while to process)\n    else:\n        projects = gl.projects.list(get_all=True)\n\n```\n\nDas vollständige Beispiel befindet sich in [diesem Skript](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/get_mr_approval_rules.py) für die Auflistung der Einstellungen der MR-Genehmigungsregeln für bestimmte Projektziele.\n\n## DevSecOps-Anwendungsfälle für API-Leseaktionen\n\nDas authentifizierte Zugriffstoken benötigt den Bereich [`read_api` scope](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#personal-access-token-scopes).\n\nDie folgenden Anwendungsfälle werden diskutiert:\n\n- Zweige nach Zusammenführungsstatus auflisten\n- Drucken von Projekteinstellungen zur Überprüfung: MR-Genehmigungsregeln\n- Inventarisierung: Abrufen aller CI/CD-Variablen, die geschützt oder maskiert sind\n- Herunterladen einer Datei aus dem Repository\n- Hilfe zur Migration: Auflistung aller zertifikatsbasierten Kubernetes-Cluster\n- Team-Effizienz: Prüfen Sie, ob bestehende Merge-Requests nach dem Mergen einer großen Refactoring-MR neu gebasht werden müssen\n\n### Zweige nach Zusammenführungsstatus auflisten\n\nEin häufiges Anliegen ist es, in einem Projekt ein wenig Git-Housekeeping zu betreiben und zu sehen, wie viele zusammengeführte und nicht zusammengeführte Zweige im Umlauf sind. Eine [Frage](https://forum.gitlab.com/t/python-gitlab-project-branch-list-filter/80257) im GitLab-Community-Forum zum Filtern von Zweiglisten hat mich dazu inspiriert, ein [Skript](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/get_branches_by_state.py) zu schreiben, mit dem sich dieses Ziel erreichen lässt. Die Methode branches.list() gibt alle Zweigobjekte zurück, die in einer temporären Liste für die spätere Verarbeitung in zwei Schleifen gespeichert werden: Sammeln der Namen der zusammengeführten Zweige und der Namen der nicht zusammengeführten Zweige. Das Attribut merged des branch-Objekts ist ein boolescher Wert, der angibt, ob der Zweig zusammengeführt wurde.\n\n```python\nproject = gl.projects.get(PROJECT_ID, lazy=False, pagination=\"keyset\", order_by=\"updated_at\", per_page=100)\n\n# Get all branches\nreal_branches = []\nfor branch in project.branches.list():\n    real_branches.append(branch)\n\nprint(\"All branches\")\nfor rb in real_branches:\n    print(\"Branch: {b}\".format(b=rb.name))\n\n# Get all merged branches\nmerged_branches_names = []\nfor branch in real_branches:\n    if branch.default:\n        continue # ignore the default branch for merge status\n\n    if branch.merged:\n        merged_branches_names.append(branch.name)\n\nprint(\"Branches merged: {b}\".format(b=\", \".join(merged_branches_names)))\n\n# Get un-merged branches\nnot_merged_branches_names = []\nfor branch in real_branches:\n    if branch.default:\n        continue # ignore the default branch for merge status\n\n    if not branch.merged:\n        not_merged_branches_names.append(branch.name)\n\nprint(\"Branches not merged: {b}\".format(b=\", \".join(not_merged_branches_names)))\n```\n\nDer Arbeitsablauf ist absichtlich schrittweise zu lesen. Du kannst die Optimierung des Python-Codes für die bedingte Zweignamensammlung üben.\n\n### Drucken von Projekteinstellungen zur Überprüfung: MR-Genehmigungsregeln\n\nDas folgende [Skript](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/get_mr_approval_rules.py) geht durch alle gesammelten Projektobjekte und prüft, ob Genehmigungsregeln angegeben sind. Wenn die Länge der Liste größer als Null ist, durchläuft es die Liste in einem Loop und druckt die Einstellungen mit einer JSON-Ph pretty-print-Methode aus.\n\n```python\n\n    # Loop over projects and print the settings\n    # https://python-gitlab.readthedocs.io/en/stable/gl_objects/merge_request_approvals.html\n    for project in projects:\n        if len(project.approvalrules.list()) > 0:\n            #print(project) #debug\n            print(\"# Project: {name}, ID: {id}\\n\\n\".format(name=project.name_with_namespace, id=project.id))\n            print(\"[MR Approval settings]({url}/-/settings/merge_requests)\\n\\n\".format(url=project.web_url))\n\n            for ar in project.approvalrules.list():\n                print(\"## Approval rule: {name}, ID: {id}\".format(name=ar.name, id=ar.id))\n                print(\"\\n```json\\n\")\n                print(json.dumps(ar.attributes, indent=2)) # TODO: can be more beautiful, but serves its purpose with pretty print JSON\n                print(\"\\n```\\n\")\n\n```\n\n### Inventarisierung: Abrufen aller CI/CD-Variablen, die geschützt oder maskiert sind\n\n[CI/CD Variablen](https://docs.gitlab.com/ee/ci/variables/) sind hilfreich für die Pipeline-Parametrisierung und können global auf der Instanz, in Gruppen und in Projekten konfiguriert werden. Auch Daten, Passwörter und andere sensible Informationen können dort gespeichert werden. Manchmal kann es notwendig sein, sich einen Überblick über alle CI/CD-Variablen zu verschaffen, die entweder geschützt oder maskiert sind, um ein Gefühl dafür zu bekommen, wie viele Variablen aktualisiert werden müssen, wenn Token zum Beispiel rotieren.\n\nDas folgende [Skript](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/get_all_cicd_variables_masked_or_protected.py) ruft alle Gruppen und Projekte ab und versucht, die CI/CD-Variablen der globalen Instanz (erfordert Admin-Rechte), der Gruppen und Projekte (erfordert Maintainer-/Eigentümer-Rechte) zu sammeln. Es gibt alle CI/CD-Variablen aus, die entweder geschützt oder maskiert sind, und fügt hinzu, dass ein möglicher geheimer Wert gespeichert ist.\n\n```python\n#!/usr/bin/env python\n\nimport gitlab\nimport os\nimport sys\n\n# Helper function to evaluate secrets and print the variables\ndef eval_print_var(var):\n    if var.protected or var.masked:\n        print(\"🛡️🛡️🛡️ Potential secret: Variable '{name}', protected {p}, masked: {m}\".format(name=var.key,p=var.protected,m=var.masked))\n\nGITLAB_SERVER = os.environ.get('GL_SERVER', 'https://gitlab.com')\nGITLAB_TOKEN = os.environ.get('GL_TOKEN') # token requires maintainer+ permissions. Instance variables require admin access.\nPROJECT_ID = os.environ.get('GL_PROJECT_ID') #optional\nGROUP_ID = os.environ.get('GL_GROUP_ID', 8034603) # https://gitlab.com/everyonecancontribute\n\nif not GITLAB_TOKEN:\n    print(\"🤔 Please set the GL_TOKEN env variable.\")\n    sys.exit(1)\n\ngl = gitlab.Gitlab(GITLAB_SERVER, private_token=GITLAB_TOKEN)\n\n# Collect all projects, or prefer projects from a group id, or a project id\nprojects = []\n# Collect all groups, or prefer group from a group id\ngroups = []\n\n# Direct project ID\nif PROJECT_ID:\n    projects.append(gl.projects.get(PROJECT_ID))\n\n# Groups and projects inside\nelif GROUP_ID:\n    group = gl.groups.get(GROUP_ID)\n\n    for project in group.projects.list(include_subgroups=True, all=True):\n        # https://python-gitlab.readthedocs.io/en/stable/gl_objects/groups.html#examples\n        manageable_project = gl.projects.get(project.id)\n        projects.append(manageable_project)\n\n    groups.append(group)\n\n# All projects/groups on the instance (may take a while to process, use iterators to fetch on-demand).\nelse:\n    projects = gl.projects.list(iterator=True)\n    groups = gl.groups.list(iterator=True)\n\nprint(\"# List of all CI/CD variables marked as secret (instance, groups, projects)\")\n\n# https://python-gitlab.readthedocs.io/en/stable/gl_objects/variables.html\n\n# Instance variables (if the token has permissions)\nprint(\"Instance variables, if accessible\")\ntry:\n    for i_var in gl.variables.list(iterator=True):\n        eval_print_var(i_var)\nexcept:\n    print(\"No permission to fetch global instance variables, continueing without.\")\n    print(\"\\n\")\n\n# group variables (maintainer permissions for groups required)\nfor group in groups:\n    print(\"Group {n}, URL: {u}\".format(n=group.full_path, u=group.web_url))\n    for g_var in group.variables.list(iterator=True):\n        eval_print_var(g_var)\n\n    print(\"\\n\")\n\n# Loop over projects and print the settings\nfor project in projects:\n    # skip archived projects, they throw 403 errors\n    if project.archived:\n        continue\n\n    print(\"Project {n}, URL: {u}\".format(n=project.path_with_namespace, u=project.web_url))\n    for p_var in project.variables.list(iterator=True):\n        eval_print_var(p_var)\n\n    print(\"\\n\")\n\n```\nDas Skript druckt die Variablenwerte absichtlich nicht aus; dies soll als Übung für sichere Umgebungen dienen. Für die Speicherung von Daten empfiehlt sich die Verwendung [externer Anbieter](https://docs.gitlab.com/ee/ci/secrets/).\n\n### Herunterladen einer Datei aus dem Repository\n\nZiel des [Skripts](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/get_raw_file_content.py) ist es, einen Dateipfad von einem angegebenen Verzweigungsnamen herunterzuladen und dessen Inhalt in einer neuen Datei zu speichern.\n\n```python\n# Goal: Try to download README.md from https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/README.md\nFILE_NAME = 'README.md'\nBRANCH_NAME = 'main'\n\n# Search the file in the repository tree and get the raw blob\nfor f in project.repository_tree():\n    print(\"File path '{name}' with id '{id}'\".format(name=f['name'], id=f['id']))\n\n    if f['name'] == FILE_NAME:\n        f_content = project.repository_raw_blob(f['id'])\n        print(f_content)\n\n# Alternative approach: Get the raw file from the main branch\nraw_content = project.files.raw(file_path=FILE_NAME, ref=BRANCH_NAME)\nprint(raw_content)\n\n# Store the file on disk\nwith open('raw_README.md', 'wb') as f:\n    project.files.raw(file_path=FILE_NAME, ref=BRANCH_NAME, streamed=True, action=f.write)\n\n```\n\n### Hilfe zur Migration: Auflistung aller zertifikatsbasierten Kubernetes-Cluster\n\nDie zertifikatsbasierte Integration von Kubernetes-Clustern in GitLab wurde [abgeschafft](https://docs.gitlab.com/ee/update/deprecations.html#self-managed-certificate-based-integration-with-kubernetes). Um Migrationspläne zu unterstützen, kann die Erfassung bestehender Gruppen und Projekte mithilfe der GitLab-API automatisiert werden.\n\n```python\ngroups = [ ]\n\n# get GROUP_ID group\ngroups.append(gl.groups.get(GROUP_ID))\n\nfor group in groups:\n    for sg in group.subgroups.list(include_subgroups=True, all=True):\n        real_group = gl.groups.get(sg.id)\n        groups.append(real_group)\n\ngroup_clusters = {}\nproject_clusters = {}\n\nfor group in groups:\n    #Collect group clusters\n    g_clusters = group.clusters.list()\n\n    if len(g_clusters) > 0:\n        group_clusters[group.id] = g_clusters\n\n    # Collect all projects in group and subgroups and their clusters\n    projects = group.projects.list(include_subgroups=True, all=True)\n\n    for project in projects:\n        # https://python-gitlab.readthedocs.io/en/stable/gl_objects/groups.html#examples\n        manageable_project = gl.projects.get(project.id)\n\n        # skip archived projects\n        if project.archived:\n            continue\n\n        p_clusters = manageable_project.clusters.list()\n\n        if len(p_clusters) > 0:\n            project_clusters[project.id] = p_clusters\n\n# Print summary\nprint(\"## Group clusters\\n\\n\")\nfor g_id, g_clusters in group_clusters.items():\n    url = gl.groups.get(g_id).web_url\n    print(\"Group ID {g_id}: {u}\\n\\n\".format(g_id=g_id, u=url))\n    print_clusters(g_clusters)\n\nprint(\"## Project clusters\\n\\n\")\nfor p_id, p_clusters in project_clusters.items():\n    url = gl.projects.get(p_id).web_url\n    print(\"Project ID {p_id}: {u}\\n\\n\".format(p_id=p_id, u=url))\n    print_clusters(p_clusters)\n\n```\n\nHier findest du das vollständige [Skript](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/list_cert_based_kubernetes_clusters.py).\n\n### Team-Effizienz: Prüfe, ob bestehende Merge-Requests nach dem Mergen einer großen Refactoring-MR neu gebasht werden müssen\n\nDas [GitLab-Handbuch-Repository](https://handbook.gitlab.com/handbook/) ist ein großes Monorepo mit zahlreichen Merge-Requests, die erstellt, geprüft, genehmigt und zusammengeführt werden müssen. Einige Prüfungen dauern länger als andere, insbesondere wenn Zusammenführungsanfragen mehrere Seiten betreffen, beispielsweise wenn ein String umbenannt wird oder Änderungen auf [alle](https://handbook.gitlab.com/handbook/about/#count-handbook-pages) Handbuchseiten ausgedehnt werden. Das Marketing-Handbuch wurde umstrukturiert, was zu vielen Verschiebungen oder Umbenennungen von Verzeichnissen und Pfaden führte.\n\nMit der Zeit nahmen die Issues zu, und es bestand die Sorge, dass andere Merge-Anfragen nach dem Zusammenführen der großen Änderungen auf Konflikte stoßen könnten. Es wurde festgestellt, dass Python-Gitlab in der Lage ist, alle Merge-Requests in einem bestimmten Projekt abzurufen, einschließlich Details über den Git-Zweig, geänderte Quellpfade und vieles mehr.\n\nDas daraus resultierende Skript konfiguriert eine Liste von Quellpfaden, die von allen pythongitlab-Merge-Requests berührt werden, und vergleicht die Diffs der Merge-Requests mit mr.diffs.list(), um festzustellen, ob ein Muster mit dem Wert in old_path. übereinstimmt. Bei einer Übereinstimmung protokolliert das Skript diese und speichert die Zusammenführungsanforderung im seen_mr-Wörterbuch für die spätere Zusammenfassung. Zusätzlich werden Attribute gesammelt, um eine Markdown-Aufgabenliste mit URLs zum leichteren Einfügen in Issue-Beschreibungen zu erstellen.\n\n```python\nPATH_PATTERNS = [\n    'path/to/handbook/source/page.md',\n]\n\n# Only list opened MRs\n# https://python-gitlab.readthedocs.io/en/stable/gl_objects/merge_requests.html#project-merge-requests\nmrs = project.mergerequests.list(state='opened', iterator=True)\n\nseen_mr = {}\n\nfor mr in mrs:\n    # https://docs.gitlab.com/ee/api/merge_requests.html#list-merge-request-diffs\n    real_mr = project.mergerequests.get(mr.get_id())\n    real_mr_id = real_mr.attributes['iid']\n    real_mr_url = real_mr.attributes['web_url']\n\n    for diff in real_mr.diffs.list(iterator=True):\n        real_diff = real_mr.diffs.get(diff.id)\n\n        for d in real_diff.attributes['diffs']:\n            for p in PATH_PATTERNS:\n                if p in d['old_path']:\n                    print(\"MATCH: {p} in MR {mr_id}, status '{s}', title '{t}' - URL: {mr_url}\".format(\n                        p=p,\n                        mr_id=real_mr_id,\n                        s=mr_status,\n                        t=real_mr.attributes['title'],\n                        mr_url=real_mr_url))\n\n                    if not real_mr_id in seen_mr:\n                        seen_mr[real_mr_id] = real_mr\n\nprint(\"\\n# MRs to update\\n\")\n\nfor id, real_mr in seen_mr.items():\n    print(\"- [ ] !{mr_id} - {mr_url}+ Status: {s}, Title: {t}\".format(\n        mr_id=id,\n        mr_url=real_mr.attributes['web_url'],\n        s=real_mr.attributes['detailed_merge_status'],\n        t=real_mr.attributes['title']))\n\n```\n\n## DevSecOps-Anwendungsfälle für API-Schreibaktionen\n\nDas authentifizierte Zugriffstoken benötigt den vollen Anwendungsbereich der [`api`](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#personal-access-token-scopes).\n\nDie folgenden Anwendungsfälle werden diskutiert:\n\n- Verschieben von Epics zwischen Gruppen\n- Compliance: Sicherstellen, dass Projekteinstellungen nicht überschrieben werden\n- Notizen machen, Fälligkeitsübersicht erstellen\n\n### Verschieben von Epics zwischen Gruppen\n\nManchmal ist es erforderlich, Epics, ähnlich wie Issues, in eine andere Gruppe zu verschieben. Eine Frage im GitLab-Marketing-Slack-Kanal hat dazu geführt, einen [Funktionsvorschlag](https://gitlab.com/gitlab-org/gitlab/-/issues/12689) für die Benutzeroberfläche und die [Schnellaktionen](/blog/improve-your-gitlab-productivity-with-these-10-tips/) zu prüfen und später über das Schreiben eines API-Skripts nachzudenken, um die Schritte zu automatisieren.\n\n### Automatisierung des Verschiebens von Epics\n\nDie Idee ist einfach: Ein Epic wird von einer Quellgruppe in eine Zielgruppe verschoben, wobei Titel, Beschreibung und Labels kopiert werden. Da Epics es erlauben, Themen zu gruppieren, müssen sie auch dem Ziel-Epic neu zugewiesen werden. Parent-Child-Epic-Relationships müssen dabei berücksichtigt werden: Alle Child-Epics der Quell-Epics müssen dem Ziel-Epic neu zugewiesen werden.\n\nDas folgende Skript sucht zunächst alle [Attribute](https://python-gitlab.readthedocs.io/en/stable/gl_objects/epics.html) des Quellepos und erstellt dann ein neues Zielepos mit den minimalen Attributen: Titel und Beschreibung. Die Liste der Bezeichnungen wird kopiert und die Änderungen werden mit dem save()-Aufruf beibehalten. Die Ausgaben, die dem Epos zugeordnet sind, müssen im Zielepos neu erstellt werden.\n\nDer create()-Aufruf erzeugt das Beziehungselement und nicht ein neues Issue-Objekt selbst. Das Verschieben von Child-Epics erfordert einen anderen Ansatz, da die Beziehung umgekehrt ist: Die parent_id des Child-Epics muss mit der ID des Quell-Epics verglichen und bei Übereinstimmung auf die ID des Ziel-Epics aktualisiert werden. Nachdem alles erfolgreich kopiert wurde, muss das Quell-Epos in den closed-Zustand versetzt werden.\n\n```python\n#!/usr/bin/env python\n\n# Description: Show how epics can be moved between groups, including title, description, labels, child epics and issues.\n# Requirements: python-gitlab Python libraries. GitLab API write access, and maintainer access to all configured groups/projects.\n# Author: Michael Friedrich \u003Cmfriedrich@gitlab.com>\n# License: MIT, (c) 2023-present GitLab B.V.\n\nimport gitlab\nimport os\nimport sys\n\nGITLAB_SERVER = os.environ.get('GL_SERVER', 'https://gitlab.com')\n# https://gitlab.com/gitlab-da/use-cases/gitlab-api\nSOURCE_GROUP_ID = os.environ.get('GL_SOURCE_GROUP_ID', 62378643)\n# https://gitlab.com/gitlab-da/use-cases/gitlab-api/epic-move-target\nTARGET_GROUP_ID = os.environ.get('GL_TARGET_GROUP_ID', 62742177)\n# https://gitlab.com/groups/gitlab-da/use-cases/gitlab-api/-/epics/1\nEPIC_ID = os.environ.get('GL_EPIC_ID', 1)\nGITLAB_TOKEN = os.environ.get('GL_TOKEN')\n\nif not GITLAB_TOKEN:\n    print(\"Please set the GL_TOKEN env variable.\")\n    sys.exit(1)\n\ngl = gitlab.Gitlab(GITLAB_SERVER, private_token=GITLAB_TOKEN)\n\n# Main\n# Goal: Move epic to target group, including title, body, labels, and child epics and issues.\nsource_group = gl.groups.get(SOURCE_GROUP_ID)\ntarget_group = gl.groups.get(TARGET_GROUP_ID)\n\n# Create a new target epic and copy all its items, then close the source epic.\nsource_epic = source_group.epics.get(EPIC_ID)\n# print(source_epic) #debug\n\nepic_title = source_epic.title\nepic_description = source_epic.description\nepic_labels = source_epic.labels\nepic_issues = source_epic.issues.list()\n\n# Create the epic with minimal attributes\ntarget_epic = target_group.epics.create({\n    'title': epic_title,\n    'description': epic_description,\n})\n\n# Assign the list\ntarget_epic.labels = epic_labels\n\n# Persist the changes in the new epic\ntarget_epic.save()\n\n# Epic issues need to be re-assigned in a loop\nfor epic_issue in epic_issues:\n    ei = target_epic.issues.create({'issue_id': epic_issue.id})\n\n# Child epics need to update their parent_id to the new epic\n# Need to search in all epics, use lazy object loading\nfor sge in source_group.epics.list(lazy=True):\n    # this epic has the source epic as parent epic?\n    if sge.parent_id == source_epic.id:\n        # Update the parent id\n        sge.parent_id = target_epic.id\n        sge.save()\n\nprint(\"Copied source epic {source_id} ({source_url}) to target epic {target_id} ({target_url})\".format(\n    source_id=source_epic.id, source_url=source_epic.web_url,\n    target_id=target_epic.id, target_url=target_epic.web_url))\n\n# Close the old epic\nsource_epic.state_event = 'close'\nsource_epic.save()\nprint(\"Closed source epic {source_id} ({source_url})\".format(\n    source_id=source_epic.id, source_url=source_epic.web_url))\n\n```\n\n```shell\n$  python3 move_epic_between_groups.py\nCopied source epic 725341 (https://gitlab.com/groups/gitlab-da/use-cases/gitlab-api/-/epics/1) to target epic 725358 (https://gitlab.com/groups/gitlab-da/use-cases/gitlab-api/epic-move-target/-/epics/6)\nClosed source epic 725341 (https://gitlab.com/groups/gitlab-da/use-cases/gitlab-api/-/epics/1)\n```\n\nDas [Ziel-Epic](https://gitlab.com/groups/gitlab-da/use-cases/gitlab-api/epic-move-target/-/epics/5) wurde erstellt und zeigt das erwartete Ergebnis: Derselbe Titel, dieselbe Beschreibung, dieselben Bezeichnungen, dasselbe untergeordnete Epic und dieselben Issues.\n\n![Target epic which has all attributes copied from the source epic: title, description, labels, child epics, issues](https://about.gitlab.com/images/blogimages/efficient-devsecops-workflows-python-gitlab-handson/python_gitlab_moved_epic_with_all_attributes.png){: .shadow}\n\n**Übung:** Das Skript kopiert noch keine [Kommentare](https://python-gitlab.readthedocs.io/en/stable/gl_objects/notes.html) und [Diskussionsstränge](https://python-gitlab.readthedocs.io/en/stable/gl_objects/discussions.html). Recherchiere und hilf mit, das Skript zu aktualisieren – Merge-Requests willkommen!\n\n### Compliance: Sicherstellen, dass Projekteinstellungen nicht überschrieben werden\n\nProjekt- und Gruppeneinstellungen können versehentlich von Teammitgliedern mit Administratorrechten geändert werden. Die Compliance-Anforderungen müssen erfüllt werden. Ein weiterer Anwendungsfall ist die Verwaltung der Konfiguration mit Infrastructure-as-Code-Tools, um sicherzustellen, dass die Konfiguration von GitLab-Instanzen, -Gruppen, -Projekten usw. erhalten bleibt und immer dieselbe ist. Tools wie Ansible oder Terraform können ein API-Skript aufrufen oder die Python-GitLab-Bibliothek verwenden, um Aufgaben zur Verwaltung von Einstellungen auszuführen.\n\nIm folgenden Beispiel ist nur der main-Zweig geschützt.\n\n![GitLab project settings for repositories and protected branches, main branch](https://about.gitlab.com/images/blogimages/efficient-devsecops-workflows-python-gitlab-handson/python_gitlab_protected_branches_settings_main.png){: .shadow}\n\nNehmen wir an, dass ein neuer production-Zweig hinzugefügt wurde und ebenfalls geschützt werden soll. Das folgende [Skript](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/enforce_protected_branches.py) definiert das Wörterbuch der geschützten Zweige und ihre Zugriffsebenen für Push-/Merge-Berechtigungen auf Maintainer-Ebene und baut die Vergleichslogik auf der Grundlage der [python-gitlab-Dokumentation](https://python-gitlab.readthedocs.io/en/stable/gl_objects/protected_branches.html) zu geschützten Zweigen auf.\n\n```python\n#!/usr/bin/env python\n\nimport gitlab\nimport os\nimport sys\n\nGITLAB_SERVER = os.environ.get('GL_SERVER', 'https://gitlab.com')\n# https://gitlab.com/gitlab-da/use-cases/\nGROUP_ID = os.environ.get('GL_GROUP_ID', 16058698)\nGITLAB_TOKEN = os.environ.get('GL_TOKEN')\n\nPROTECTED_BRANCHES = {\n    'main': {\n        'merge_access_level': gitlab.const.AccessLevel.MAINTAINER,\n        'push_access_level': gitlab.const.AccessLevel.MAINTAINER\n    },\n    'production': {\n        'merge_access_level': gitlab.const.AccessLevel.MAINTAINER,\n        'push_access_level': gitlab.const.AccessLevel.MAINTAINER\n    },\n}\n\nif not GITLAB_TOKEN:\n    print(\"Please set the GL_TOKEN env variable.\")\n    sys.exit(1)\n\ngl = gitlab.Gitlab(GITLAB_SERVER, private_token=GITLAB_TOKEN)\n\n# Main\ngroup = gl.groups.get(GROUP_ID)\n\n# Collect all projects in group and subgroups\nprojects = group.projects.list(include_subgroups=True, all=True)\n\nfor project in projects:\n    # Retrieve a full manageable project object\n    # https://python-gitlab.readthedocs.io/en/stable/gl_objects/groups.html#examples\n    manageable_project = gl.projects.get(project.id)\n\n    # https://python-gitlab.readthedocs.io/en/stable/gl_objects/protected_branches.html\n    protected_branch_names = []\n\n    for pb in manageable_project.protectedbranches.list():\n        manageable_protected_branch = manageable_project.protectedbranches.get(pb.name)\n        print(\"Protected branch name: {n}, merge_access_level: {mal}, push_access_level: {pal}\".format(\n            n=manageable_protected_branch.name,\n            mal=manageable_protected_branch.merge_access_levels,\n            pal=manageable_protected_branch.push_access_levels\n        ))\n\n        protected_branch_names.append(manageable_protected_branch.name)\n\n    for branch_to_protect, levels in PROTECTED_BRANCHES.items():\n        # Fix missing protected branches\n        if branch_to_protect not in protected_branch_names:\n            print(\"Adding branch {n} to protected branches settings\".format(n=branch_to_protect))\n            p_branch = manageable_project.protectedbranches.create({\n                'name': branch_to_protect,\n                'merge_access_level': gitlab.const.AccessLevel.MAINTAINER,\n                'push_access_level': gitlab.const.AccessLevel.MAINTAINER\n            })\n\n```\n\nWenn das Skript ausgeführt wird, werden der bestehende main-Zweig und ein Hinweis, dass die production aktualisiert wird, ausgegeben. Der Screenshot aus den Repository-Einstellungen verdeutlicht diese Aktion.\n\n```shell\n$ python3 enforce_protected_branches.py ─╯\nProtected branch name: main, merge_access_level: [{'id': 67294702, 'access_level': 40, 'access_level_description': 'Maintainers', 'user_id': None, 'group_id': None}], push_access_level: [{'id': 68546039, 'access_level': 40, 'access_level_description': 'Maintainers', 'user_id': None, 'group_id': None}]\nAdding branch production to protected branches settings\n```\n\n![GitLab project settings for repositories and protected branches, main and production branch](https://about.gitlab.com/images/blogimages/efficient-devsecops-workflows-python-gitlab-handson/python_gitlab_protected_branches_settings_main_production.png){: .shadow}\n\n### Notizen machen, Fälligkeitsübersicht erstellen\n\nEine Diskussion auf [Hacker News](https://news.ycombinator.com/item?id=32155848) über Tools zum Erstellen von Notizen hat mich dazu inspiriert, eine Übersicht in Form einer Markdown-Tabelle zu erstellen, die aus Dateien, die Notizen aufnehmen, geholt und nach dem geparsten Fälligkeitsdatum sortiert wird. Das [Skript](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/generate_snippets_index_by_due_date.py) ist hier zu finden und etwas komplexer zu verstehen.\n\n\u003C!--\n# 2022-07-19 Notes\n\nHN topic about taking notes: https://news.ycombinator.com/item?id=32152935\n\n-->\n\n***Die englischsprachige [Originalversion](https://about.gitlab.com/blog/efficient-devsecops-workflows-hands-on-python-gitlab-api-automation/) dieses Artikels wurde bereits aktualisiert und erhält einige weitere Tipps, welche wir der deutschen Version beizeiten hinzufügen werden.***\n",[23,24,25,26],"integrations","tutorial","DevSecOps","DevSecOps platform","2025-05-16","yml",{},true,"/de-de/blog/efficient-devsecops-workflows-hands-on-python-gitlab-api-automation",{"title":15,"description":16,"ogTitle":15,"ogDescription":16,"noIndex":12,"ogImage":19,"ogUrl":33,"ogSiteName":34,"ogType":35,"canonicalUrls":33},"https://about.gitlab.com/blog/efficient-devsecops-workflows-hands-on-python-gitlab-api-automation","https://about.gitlab.com","article","de-de/blog/efficient-devsecops-workflows-hands-on-python-gitlab-api-automation",[23,24,38,39],"devsecops","devsecops-platform","dI6wpygXcbqKYuGFzrGajUildwjBwWyB4uKtDHp0mzE",{"data":42},{"logo":43,"freeTrial":48,"sales":53,"login":58,"items":63,"search":371,"minimal":406,"duo":424,"pricingDeployment":434},{"config":44},{"href":45,"dataGaName":46,"dataGaLocation":47},"/de-de/","gitlab logo","header",{"text":49,"config":50},"Kostenlose Testversion anfordern",{"href":51,"dataGaName":52,"dataGaLocation":47},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":54,"config":55},"Vertrieb kontaktieren",{"href":56,"dataGaName":57,"dataGaLocation":47},"/de-de/sales/","sales",{"text":59,"config":60},"Anmelden",{"href":61,"dataGaName":62,"dataGaLocation":47},"https://gitlab.com/users/sign_in/","sign in",[64,91,187,192,292,352],{"text":65,"config":66,"cards":68},"Plattform",{"dataNavLevelOne":67},"platform",[69,75,83],{"title":65,"description":70,"link":71},"Die intelligente Orchestrierungsplattform für DevSecOps",{"text":72,"config":73},"Erkunde unsere Plattform",{"href":74,"dataGaName":67,"dataGaLocation":47},"/de-de/platform/",{"title":76,"description":77,"link":78},"GitLab Duo Agent Platform","Agentische KI für den gesamten Softwareentwicklungszyklus",{"text":79,"config":80},"Lerne GitLab Duo kennen",{"href":81,"dataGaName":82,"dataGaLocation":47},"/de-de/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":84,"description":85,"link":86},"Gründe, die für GitLab sprechen","Erfahre, warum Unternehmen auf GitLab setzen",{"text":87,"config":88},"Mehr erfahren",{"href":89,"dataGaName":90,"dataGaLocation":47},"/de-de/why-gitlab/","why gitlab",{"text":92,"left":30,"config":93,"link":95,"lists":99,"footer":169},"Produkt",{"dataNavLevelOne":94},"solutions",{"text":96,"config":97},"Alle Lösungen anzeigen",{"href":98,"dataGaName":94,"dataGaLocation":47},"/de-de/solutions/",[100,125,147],{"title":101,"description":102,"link":103,"items":108},"Automatisierung","CI/CD und Automatisierung zur Beschleunigung der Bereitstellung",{"config":104},{"icon":105,"href":106,"dataGaName":107,"dataGaLocation":47},"AutomatedCodeAlt","/de-de/solutions/delivery-automation/","automated software delivery",[109,113,116,121],{"text":110,"config":111},"CI/CD",{"href":112,"dataGaLocation":47,"dataGaName":110},"/de-de/solutions/continuous-integration/",{"text":76,"config":114},{"href":81,"dataGaLocation":47,"dataGaName":115},"gitlab duo agent platform - product menu",{"text":117,"config":118},"Quellcodeverwaltung",{"href":119,"dataGaLocation":47,"dataGaName":120},"/de-de/solutions/source-code-management/","Source Code Management",{"text":122,"config":123},"Automatisierte Softwarebereitstellung",{"href":106,"dataGaLocation":47,"dataGaName":124},"Automated software delivery",{"title":126,"description":127,"link":128,"items":133},"Sicherheit","Entwickle schneller, ohne die Sicherheit zu gefährden",{"config":129},{"href":130,"dataGaName":131,"dataGaLocation":47,"icon":132},"/de-de/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[134,138,143],{"text":135,"config":136},"Application Security Testing",{"href":130,"dataGaName":137,"dataGaLocation":47},"Application security testing",{"text":139,"config":140},"Schutz der Software-Lieferkette",{"href":141,"dataGaLocation":47,"dataGaName":142},"/de-de/solutions/supply-chain/","Software supply chain security",{"text":144,"config":145},"Software Compliance",{"href":146,"dataGaName":144,"dataGaLocation":47},"/de-de/solutions/software-compliance/",{"title":148,"link":149,"items":154},"Bewertung",{"config":150},{"icon":151,"href":152,"dataGaName":153,"dataGaLocation":47},"DigitalTransformation","/de-de/solutions/visibility-measurement/","visibility and measurement",[155,159,164],{"text":156,"config":157},"Sichtbarkeit und Bewertung",{"href":152,"dataGaLocation":47,"dataGaName":158},"Visibility and Measurement",{"text":160,"config":161},"Wertstrommanagement",{"href":162,"dataGaLocation":47,"dataGaName":163},"/de-de/solutions/value-stream-management/","Value Stream Management",{"text":165,"config":166},"Analysen und Einblicke",{"href":167,"dataGaLocation":47,"dataGaName":168},"/de-de/solutions/analytics-and-insights/","Analytics and insights",{"title":170,"items":171},"GitLab für",[172,177,182],{"text":173,"config":174},"Enterprise",{"href":175,"dataGaLocation":47,"dataGaName":176},"/de-de/enterprise/","enterprise",{"text":178,"config":179},"Kleinunternehmen",{"href":180,"dataGaLocation":47,"dataGaName":181},"/de-de/small-business/","small business",{"text":183,"config":184},"den öffentlichen Sektor",{"href":185,"dataGaLocation":47,"dataGaName":186},"/de-de/solutions/public-sector/","public sector",{"text":188,"config":189},"Preise",{"href":190,"dataGaName":191,"dataGaLocation":47,"dataNavLevelOne":191},"/de-de/pricing/","pricing",{"text":193,"config":194,"link":196,"lists":200,"feature":279},"Ressourcen",{"dataNavLevelOne":195},"resources",{"text":197,"config":198},"Alle Ressourcen anzeigen",{"href":199,"dataGaName":195,"dataGaLocation":47},"/de-de/resources/",[201,233,251],{"title":202,"items":203},"Erste Schritte",[204,209,214,219,224,229],{"text":205,"config":206},"Installieren",{"href":207,"dataGaName":208,"dataGaLocation":47},"/de-de/install/","install",{"text":210,"config":211},"Kurzanleitungen",{"href":212,"dataGaName":213,"dataGaLocation":47},"/de-de/get-started/","quick setup checklists",{"text":215,"config":216},"Lernen",{"href":217,"dataGaLocation":47,"dataGaName":218},"https://university.gitlab.com/","learn",{"text":220,"config":221},"Produktdokumentation",{"href":222,"dataGaName":223,"dataGaLocation":47},"https://docs.gitlab.com/","product documentation",{"text":225,"config":226},"Best-Practice-Videos",{"href":227,"dataGaName":228,"dataGaLocation":47},"/de-de/getting-started-videos/","best practice videos",{"text":230,"config":231},"Integrationen",{"href":232,"dataGaName":23,"dataGaLocation":47},"/de-de/integrations/",{"title":234,"items":235},"Entdecken",[236,241,246],{"text":237,"config":238},"Kundenerfolge",{"href":239,"dataGaName":240,"dataGaLocation":47},"/de-de/customers/","customer success stories",{"text":242,"config":243},"Blog",{"href":244,"dataGaName":245,"dataGaLocation":47},"/de-de/blog/","blog",{"text":247,"config":248},"Remote",{"href":249,"dataGaName":250,"dataGaLocation":47},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":252,"items":253},"Vernetzen",[254,259,264,269,274],{"text":255,"config":256},"GitLab-Services",{"href":257,"dataGaName":258,"dataGaLocation":47},"/de-de/services/","services",{"text":260,"config":261},"Community",{"href":262,"dataGaName":263,"dataGaLocation":47},"/community/","community",{"text":265,"config":266},"Forum",{"href":267,"dataGaName":268,"dataGaLocation":47},"https://forum.gitlab.com/","forum",{"text":270,"config":271},"Veranstaltungen",{"href":272,"dataGaName":273,"dataGaLocation":47},"/events/","events",{"text":275,"config":276},"Partner",{"href":277,"dataGaName":278,"dataGaLocation":47},"/de-de/partners/","partners",{"backgroundColor":280,"textColor":281,"text":282,"image":283,"link":287},"#2f2a6b","#fff","Perspektiven für die Softwareentwicklung der Zukunft",{"altText":284,"config":285},"the source promo card",{"src":286},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":288,"config":289},"Lies die News",{"href":290,"dataGaName":291,"dataGaLocation":47},"/de-de/the-source/","the source",{"text":293,"config":294,"lists":296},"Unternehmen",{"dataNavLevelOne":295},"company",[297],{"items":298},[299,304,310,312,317,322,327,332,337,342,347],{"text":300,"config":301},"Über",{"href":302,"dataGaName":303,"dataGaLocation":47},"/de-de/company/","about",{"text":305,"config":306,"footerGa":309},"Karriere",{"href":307,"dataGaName":308,"dataGaLocation":47},"/jobs/","jobs",{"dataGaName":308},{"text":270,"config":311},{"href":272,"dataGaName":273,"dataGaLocation":47},{"text":313,"config":314},"Geschäftsführung",{"href":315,"dataGaName":316,"dataGaLocation":47},"/company/team/e-group/","leadership",{"text":318,"config":319},"Team",{"href":320,"dataGaName":321,"dataGaLocation":47},"/company/team/","team",{"text":323,"config":324},"Handbuch",{"href":325,"dataGaName":326,"dataGaLocation":47},"https://handbook.gitlab.com/","handbook",{"text":328,"config":329},"Investor Relations",{"href":330,"dataGaName":331,"dataGaLocation":47},"https://ir.gitlab.com/","investor relations",{"text":333,"config":334},"Trust Center",{"href":335,"dataGaName":336,"dataGaLocation":47},"/de-de/security/","trust center",{"text":338,"config":339},"AI Transparency Center",{"href":340,"dataGaName":341,"dataGaLocation":47},"/de-de/ai-transparency-center/","ai transparency center",{"text":343,"config":344},"Newsletter",{"href":345,"dataGaName":346,"dataGaLocation":47},"/company/contact/#contact-forms","newsletter",{"text":348,"config":349},"Presse",{"href":350,"dataGaName":351,"dataGaLocation":47},"/press/","press",{"text":353,"config":354,"lists":355},"Kontakt",{"dataNavLevelOne":295},[356],{"items":357},[358,361,366],{"text":54,"config":359},{"href":56,"dataGaName":360,"dataGaLocation":47},"talk to sales",{"text":362,"config":363},"Support-Portal",{"href":364,"dataGaName":365,"dataGaLocation":47},"https://support.gitlab.com","support portal",{"text":367,"config":368},"Kundenportal",{"href":369,"dataGaName":370,"dataGaLocation":47},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":372,"login":373,"suggestions":380},"Schließen",{"text":374,"link":375},"Um Repositories und Projekte zu durchsuchen, melde dich an bei",{"text":376,"config":377},"gitlab.com",{"href":61,"dataGaName":378,"dataGaLocation":379},"search login","search",{"text":381,"default":382},"Vorschläge",[383,385,390,392,397,402],{"text":76,"config":384},{"href":81,"dataGaName":76,"dataGaLocation":379},{"text":386,"config":387},"Code Suggestions (KI)",{"href":388,"dataGaName":389,"dataGaLocation":379},"/de-de/solutions/code-suggestions/","Code Suggestions (AI)",{"text":110,"config":391},{"href":112,"dataGaName":110,"dataGaLocation":379},{"text":393,"config":394},"GitLab auf AWS",{"href":395,"dataGaName":396,"dataGaLocation":379},"/de-de/partners/technology-partners/aws/","GitLab on AWS",{"text":398,"config":399},"GitLab auf Google Cloud",{"href":400,"dataGaName":401,"dataGaLocation":379},"/de-de/partners/technology-partners/google-cloud-platform/","GitLab on Google Cloud",{"text":403,"config":404},"Warum GitLab?",{"href":89,"dataGaName":405,"dataGaLocation":379},"Why GitLab?",{"freeTrial":407,"mobileIcon":412,"desktopIcon":417,"secondaryButton":420},{"text":408,"config":409},"Kostenlos testen",{"href":410,"dataGaName":52,"dataGaLocation":411},"https://gitlab.com/-/trials/new/","nav",{"altText":413,"config":414},"GitLab-Symbol",{"src":415,"dataGaName":416,"dataGaLocation":411},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":413,"config":418},{"src":419,"dataGaName":416,"dataGaLocation":411},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":202,"config":421},{"href":422,"dataGaName":423,"dataGaLocation":411},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/de-de/compare/gitlab-vs-github/","get started",{"freeTrial":425,"mobileIcon":430,"desktopIcon":432},{"text":426,"config":427},"Erfahre mehr über GitLab Duo",{"href":428,"dataGaName":429,"dataGaLocation":411},"/de-de/gitlab-duo/","gitlab duo",{"altText":413,"config":431},{"src":415,"dataGaName":416,"dataGaLocation":411},{"altText":413,"config":433},{"src":419,"dataGaName":416,"dataGaLocation":411},{"freeTrial":435,"mobileIcon":440,"desktopIcon":442},{"text":436,"config":437},"Zurück zur Preisübersicht",{"href":190,"dataGaName":438,"dataGaLocation":411,"icon":439},"back to pricing","GoBack",{"altText":413,"config":441},{"src":415,"dataGaName":416,"dataGaLocation":411},{"altText":413,"config":443},{"src":419,"dataGaName":416,"dataGaLocation":411},{"title":445,"button":446,"config":451},"Sieh dir an, wie agentische KI die Softwarebereitstellung transformiert",{"text":447,"config":448},"GitLab Transcend jetzt ansehen",{"href":449,"dataGaName":450,"dataGaLocation":47},"/de-de/events/transcend/virtual/","transcend event",{"layout":452,"icon":453},"release","AiStar",{"data":455},{"text":456,"source":457,"edit":463,"contribute":468,"config":473,"items":478,"minimal":651},"Git ist eine Marke von Software Freedom Conservancy und unsere Verwendung von „GitLab“ erfolgt unter Lizenz.",{"text":458,"config":459},"Quelltext der Seite anzeigen",{"href":460,"dataGaName":461,"dataGaLocation":462},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":464,"config":465},"Diese Seite bearbeiten",{"href":466,"dataGaName":467,"dataGaLocation":462},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":469,"config":470},"Beteilige dich",{"href":471,"dataGaName":472,"dataGaLocation":462},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":474,"facebook":475,"youtube":476,"linkedin":477},"https://x.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[479,502,557,584,618],{"title":65,"links":480,"subMenu":485},[481],{"text":482,"config":483},"DevSecOps-Plattform",{"href":74,"dataGaName":484,"dataGaLocation":462},"devsecops platform",[486],{"title":188,"links":487},[488,492,497],{"text":489,"config":490},"Tarife anzeigen",{"href":190,"dataGaName":491,"dataGaLocation":462},"view plans",{"text":493,"config":494},"Vorteile von Premium",{"href":495,"dataGaName":496,"dataGaLocation":462},"/de-de/pricing/premium/","why premium",{"text":498,"config":499},"Vorteile von Ultimate",{"href":500,"dataGaName":501,"dataGaLocation":462},"/de-de/pricing/ultimate/","why ultimate",{"title":503,"links":504},"Lösungen",[505,510,513,515,520,525,529,532,535,540,542,544,547,552],{"text":506,"config":507},"Digitale Transformation",{"href":508,"dataGaName":509,"dataGaLocation":462},"/de-de/topics/digital-transformation/","digital transformation",{"text":511,"config":512},"Sicherheit und Compliance",{"href":130,"dataGaName":137,"dataGaLocation":462},{"text":122,"config":514},{"href":106,"dataGaName":107,"dataGaLocation":462},{"text":516,"config":517},"Agile Entwicklung",{"href":518,"dataGaName":519,"dataGaLocation":462},"/de-de/solutions/agile-delivery/","agile delivery",{"text":521,"config":522},"Cloud-Transformation",{"href":523,"dataGaName":524,"dataGaLocation":462},"/de-de/topics/cloud-native/","cloud transformation",{"text":526,"config":527},"SCM",{"href":119,"dataGaName":528,"dataGaLocation":462},"source code management",{"text":110,"config":530},{"href":112,"dataGaName":531,"dataGaLocation":462},"continuous integration & delivery",{"text":160,"config":533},{"href":162,"dataGaName":534,"dataGaLocation":462},"value stream management",{"text":536,"config":537},"GitOps",{"href":538,"dataGaName":539,"dataGaLocation":462},"/de-de/solutions/gitops/","gitops",{"text":173,"config":541},{"href":175,"dataGaName":176,"dataGaLocation":462},{"text":178,"config":543},{"href":180,"dataGaName":181,"dataGaLocation":462},{"text":545,"config":546},"Öffentlicher Sektor",{"href":185,"dataGaName":186,"dataGaLocation":462},{"text":548,"config":549},"Bildungswesen",{"href":550,"dataGaName":551,"dataGaLocation":462},"/de-de/solutions/education/","education",{"text":553,"config":554},"Finanzdienstleistungen",{"href":555,"dataGaName":556,"dataGaLocation":462},"/de-de/solutions/finance/","financial services",{"title":193,"links":558},[559,561,563,565,568,570,572,574,576,578,580,582],{"text":205,"config":560},{"href":207,"dataGaName":208,"dataGaLocation":462},{"text":210,"config":562},{"href":212,"dataGaName":213,"dataGaLocation":462},{"text":215,"config":564},{"href":217,"dataGaName":218,"dataGaLocation":462},{"text":220,"config":566},{"href":222,"dataGaName":567,"dataGaLocation":462},"docs",{"text":242,"config":569},{"href":244,"dataGaName":245,"dataGaLocation":462},{"text":237,"config":571},{"href":239,"dataGaName":240,"dataGaLocation":462},{"text":247,"config":573},{"href":249,"dataGaName":250,"dataGaLocation":462},{"text":255,"config":575},{"href":257,"dataGaName":258,"dataGaLocation":462},{"text":260,"config":577},{"href":262,"dataGaName":263,"dataGaLocation":462},{"text":265,"config":579},{"href":267,"dataGaName":268,"dataGaLocation":462},{"text":270,"config":581},{"href":272,"dataGaName":273,"dataGaLocation":462},{"text":275,"config":583},{"href":277,"dataGaName":278,"dataGaLocation":462},{"title":293,"links":585},[586,588,590,592,594,596,598,602,607,609,611,613],{"text":300,"config":587},{"href":302,"dataGaName":295,"dataGaLocation":462},{"text":305,"config":589},{"href":307,"dataGaName":308,"dataGaLocation":462},{"text":313,"config":591},{"href":315,"dataGaName":316,"dataGaLocation":462},{"text":318,"config":593},{"href":320,"dataGaName":321,"dataGaLocation":462},{"text":323,"config":595},{"href":325,"dataGaName":326,"dataGaLocation":462},{"text":328,"config":597},{"href":330,"dataGaName":331,"dataGaLocation":462},{"text":599,"config":600},"Sustainability",{"href":601,"dataGaName":599,"dataGaLocation":462},"/sustainability/",{"text":603,"config":604},"Vielfalt, Inklusion und Zugehörigkeit",{"href":605,"dataGaName":606,"dataGaLocation":462},"/de-de/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":333,"config":608},{"href":335,"dataGaName":336,"dataGaLocation":462},{"text":343,"config":610},{"href":345,"dataGaName":346,"dataGaLocation":462},{"text":348,"config":612},{"href":350,"dataGaName":351,"dataGaLocation":462},{"text":614,"config":615},"Transparenzerklärung zu moderner Sklaverei",{"href":616,"dataGaName":617,"dataGaLocation":462},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":619,"links":620},"Nimm Kontakt auf",[621,624,629,631,636,641,646],{"text":622,"config":623},"Sprich mit einem Experten/einer Expertin",{"href":56,"dataGaName":57,"dataGaLocation":462},{"text":625,"config":626},"Support",{"href":627,"dataGaName":628,"dataGaLocation":462},"/support/","get help",{"text":367,"config":630},{"href":369,"dataGaName":370,"dataGaLocation":462},{"text":632,"config":633},"Status",{"href":634,"dataGaName":635,"dataGaLocation":462},"https://status.gitlab.com/","status",{"text":637,"config":638},"Nutzungsbedingungen",{"href":639,"dataGaName":640,"dataGaLocation":462},"/terms/","terms of use",{"text":642,"config":643},"Datenschutzerklärung",{"href":644,"dataGaName":645,"dataGaLocation":462},"/de-de/privacy/","privacy statement",{"text":647,"config":648},"Cookie-Einstellungen",{"dataGaName":649,"dataGaLocation":462,"id":650,"isOneTrustButton":30},"cookie preferences","ot-sdk-btn",{"items":652},[653,655,657],{"text":637,"config":654},{"href":639,"dataGaName":640,"dataGaLocation":462},{"text":642,"config":656},{"href":644,"dataGaName":645,"dataGaLocation":462},{"text":647,"config":658},{"dataGaName":649,"dataGaLocation":462,"id":650,"isOneTrustButton":30},[660],{"id":661,"title":18,"body":8,"config":662,"content":664,"description":8,"extension":28,"meta":668,"navigation":30,"path":669,"seo":670,"stem":671,"__hash__":672},"blogAuthors/en-us/blog/authors/michael-friedrich.yml",{"template":663},"BlogAuthor",{"name":18,"config":665},{"headshot":666,"ctfId":667},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659879/Blog/Author%20Headshots/dnsmichi-headshot.jpg","dnsmichi",{},"/en-us/blog/authors/michael-friedrich",{},"en-us/blog/authors/michael-friedrich","lJ-nfRIhdG49Arfrxdn1Vv4UppwD51BB13S3HwIswt4",[674,688,702],{"content":675,"config":686},{"category":9,"tags":676,"body":678,"date":679,"heroImage":680,"authors":681,"title":684,"description":685},[24,677,110],"git","Enterprise-Migrationen in regulierten Branchen wie Finanzwesen und Automobilindustrie erfordern systematisches Risikomanagement gemäß NIS2-Richtlinie Artikel 21. Die mehrstufige Migrationsstruktur mit Testläufen vor Produktions-Wellen und kontrollierten Change-Freezes demonstriert Business-Continuity-Management in der Praxis. GitLab Professional Services organisiert Migrationen in Wellen von 200-300 Projekten, um Komplexität zu managen und API-Rate-Limits zu respektieren.\n\n## Überblick\n\nGitLab bietet [Congregate](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/) (maintained by GitLab Professional Services) und [eingebauten Git-Repository-Import](https://docs.gitlab.com/user/project/import/repo_by_url/) für Migrationen von Azure DevOps (ADO). Beide Optionen unterstützen Repository-basierte oder Bulk-Migration und erhalten Git-Commit-History, Branches und Tags. Mit Congregate werden zusätzliche Assets wie Wikis, Work Items, CI/CD-Variablen, Container-Images, Packages und Pipelines migriert (siehe [Feature-Matrix](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/blob/master/customer/ado-migration-features-matrix.md)).\n\nEnterprises folgen typischerweise einem mehrstufigen Ansatz:\n\n- Repositories von ADO zu GitLab migrieren (Congregate oder eingebauter Import)\n- Pipelines von Azure Pipelines zu GitLab CI/CD migrieren\n- Verbleibende Assets wie Boards, Work Items und Artifacts zu GitLab Issues, Epics und Package/Container Registries migrieren\n\nMehrstufige Migrationsphasen (siehe [Diagramm](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/#overview)):\n\n- Prerequisites (IdP, Runners, Change Management)\n- Migration Phase (Source Code, History, Work Items)\n- Post-Migration (Pipelines, Assets, Security)\n\n## Migration planen\n\n**Zentrale Planungsfragen:**\n\n- Wie schnell muss die Migration abgeschlossen werden?\n- Was genau wird migriert?\n- Wer führt die Migration durch?\n- Welche Organisationsstruktur wird in GitLab benötigt?\n- Welche Einschränkungen, Limitierungen oder Fallstricke müssen berücksichtigt werden?\n\nDie Timeline bestimmt weitgehend den Migrationsansatz. Identifizierung von Champions oder Teams mit ADO- und GitLab-Erfahrung unterstützt Adoption und Guidance.\n\n**Inventar erstellen:**\n\nDas GitLab Professional Services [Evaluate](https://gitlab.com/gitlab-org/professional-services-automation/tools/utilities/evaluate#beta-azure-devops)-Tool produziert ein vollständiges Inventar der Azure DevOps Organisation: Repositories, PR-Counts, Contributors, Pipelines, Work Items, CI/CD-Variablen. Bei Professional Services Engagements wird dieser Report mit Engagement Manager oder Technical Architect geteilt für Migrationsplanung.\n\nMigrations-Timing wird primär bestimmt durch Pull-Request-Count, Repository-Größe und Contribution-Menge. Beispiel: 1.000 kleine Repositories mit wenigen PRs migrieren schneller als wenige Repositories mit Zehntausenden PRs. Inventar-Daten ermöglichen Aufwands-Schätzung und Test-Run-Planung.\n\nIn Professional Services Engagements werden Migrationen in Wellen von 200-300 Projekten organisiert, um Komplexität zu managen und API-Rate-Limits zu respektieren (sowohl [GitLab](https://docs.gitlab.com/security/rate_limits/) als auch [ADO](https://learn.microsoft.com/en-us/azure/devops/integrate/concepts/rate-limits?view=azure-devops)).\n\n**Tool-Auswahl:**\n\nGitLabs [eingebauter Repository-Importer](https://docs.gitlab.com/user/project/import/repo_by_url/) migriert Git-Repositories (Commits, Branches, Tags) einzeln. Congregate erhält Pull Requests (in GitLab: Merge Requests), Kommentare und Metadata; der eingebaute Import fokussiert auf Git-Daten (History, Branches, Tags).\n\nAssets die typischerweise separate Migration oder manuelle Neuerstellung erfordern:\n\n- Azure Pipelines → GitLab CI/CD Pipelines (siehe [CI/CD YAML](https://docs.gitlab.com/ci/yaml/) oder [CI/CD Components](https://docs.gitlab.com/ci/components/)). Alternativ: AI-basierte Pipeline-Konvertierung in Congregate.\n- Work Items und Boards → GitLab Issues, Epics, Issue Boards\n- Artifacts, Container-Images (ACR) → GitLab Package/Container Registry\n- Service Hooks, externe Integrationen → in GitLab neu erstellen\n\n[Permissions-Modelle](https://docs.gitlab.com/user/permissions/) unterscheiden sich zwischen ADO und GitLab. Permissions-Mapping planen statt exakter Preservation zu erwarten.\n\n**Organisationsstruktur in GitLab:**\n\nEmpfohlener Ansatz: ADO-Organisationen auf GitLab-Groups mappen (nicht viele kleine Groups). Migration als Gelegenheit nutzen, GitLab-Struktur zu rationalisieren (siehe [Struktur-Diagramm](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/#planning-your-migration)):\n\n- **ADO Organization** → GitLab Top-level Group\n- **ADO Project** → GitLab Subgroup (optional)\n- **ADO Repository** → GitLab Project\n\nWeitere Empfehlungen:\n\n- Subgroups und Project-Level-Permissions für verwandte Repositories nutzen\n- Zugriff über GitLab Groups und Group-Membership managen\n- GitLab [Permissions](https://docs.gitlab.com/ee/user/permissions.html) und [SAML Group Links](https://docs.gitlab.com/user/group/saml_sso/group_sync/) für Enterprise-RBAC-Modell evaluieren\n\n**ADO Work Items Migration:**\n\nADO Boards und Work Items mappen zu GitLab Issues, Epics und Issue Boards. ADO Epics und Features werden GitLab Epics. Andere Work-Item-Typen (User Stories, Tasks, Bugs) werden project-scoped Issues. Standard-Felder bleiben erhalten; ausgewählte Custom Fields können migriert werden. Parent-Child-Relationships bleiben erhalten. Links zu Pull Requests werden zu Merge-Request-Links konvertiert.\n\n![Migration eines Work Items zu GitLab Issue](https://res.cloudinary.com/about-gitlab-com/image/upload/v1764769188/ztesjnxxfbwmfmtckyga.png)\n\n**Pipelines-Migration:**\n\nCongregate bietet [AI-basierte Konvertierung](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/merge_requests/1298) für multi-stage YAML-Pipelines von Azure DevOps zu GitLab CI/CD. Die automatisierte Konvertierung funktioniert optimal für einfache Single-File-Pipelines und liefert einen funktionalen Ausgangspunkt, nicht ein produktionsreifes `.gitlab-ci.yml`-File. Das Tool generiert funktional äquivalente GitLab-Pipelines zur weiteren Optimierung.\n\n- Konvertiert Azure Pipelines YAML zu `.gitlab-ci.yml` automatisch\n- Geeignet für straightforward Single-File-Pipeline-Konfigurationen\n- Liefert Boilerplate zur Migrations-Beschleunigung, nicht finales Production-Artifact\n- Erfordert Review und Anpassung für komplexe Szenarien, Custom Tasks oder Enterprise-Requirements\n- Unterstützt keine Azure DevOps Classic Release Pipelines\n\nRepository-Owner sollten [GitLab CI/CD Dokumentation](https://docs.gitlab.com/ci/) konsultieren für weitere Pipeline-Optimierung nach initialer Konvertierung.\n\n## Migrationen durchführen\n\nNach der Planung werden Migrationen in Stages durchgeführt, beginnend mit Trial Runs. Trial Migrations identifizieren organisations-spezifische Issues frühzeitig und ermöglichen Duration-Messung, Outcome-Validierung und Approach-Finetuning vor Production.\n\n**Was Trial Migrations validieren:**\n\n- Ob Repository und Assets erfolgreich migrieren (History, Branches, Tags; plus MRs/Comments bei Congregate)\n- Ob Destination sofort nutzbar ist (Permissions, Runners, CI/CD-Variablen, Integrationen)\n- Wie lange jeder Batch benötigt für Schedule- und Stakeholder-Expectations\n\n**Downtime-Guidance:**\n\nGitLabs eingebauter Git-Import und Congregate erfordern inhärent keine Downtime. Für Production-Wellen: Changes in ADO freezen (Branch-Protections oder Read-only), um verpasste Commits, PR-Updates oder mid-migration Work Items zu vermeiden. Trial Runs erfordern keine Freezes.\n\n**Batching-Guidance:**\n\nTrial-Batches back-to-back durchführen für kürzere elapsed Time. Teams validieren Results asynchron. Geplante Group/Subgroup-Struktur für Batch-Definition nutzen und API-Rate-Limits respektieren.\n\n**Empfohlene Schritte:**\n\n1. Test-Destination in GitLab erstellen (GitLab.com: dedicated Group/Namespace; Self-managed: Top-level Group)\n2. Authentication vorbereiten (Azure DevOps PAT, GitLab Personal Access Token mit api und read_repository Scopes)\n3. Trial Migrations durchführen (Repos only: eingebauter Import; Repos + PRs/MRs: Congregate)\n4. Post-Trial Follow-up (Repo-History, Branches, Tags, Merge Requests, Issues/Epics, Labels, Relationships verifizieren)\n5. Permissions/Roles, Protected Branches, Runners/Tags, Variables/Secrets, Integrations/Webhooks prüfen\n6. Pipelines (`.gitlab-ci.yml`) oder konvertierte Pipelines validieren\n7. User-Validierung für Functionality und Data-Fidelity\n8. Production-Migrationen in Waves durchführen (Change-Freezes in ADO erzwingen, Progress und Logs monitoren)\n\n\u003Cfigure class=\"video_container\">\n  \u003Ciframe src=\"https://www.youtube.com/embed/ibIXGfrVbi4?si=ZxOVnXjCF-h4Ne0N\" frameborder=\"0\" allowfullscreen=\"true\">\u003C/iframe>\n\u003C/figure>\n\n## Zentrale ADO-zu-GitLab-Mappings\n\nWichtigste Struktur-Mappings für Migrationsplanung:\n\n- **ADO Organization** → GitLab Group (Top-level Namespace)\n- **ADO Project** → GitLab Group oder Subgroup (Permissions-Boundary)\n- **ADO Repository** → GitLab Project (Git-Repo plus Issues, CI/CD, Wiki)\n- **Pull Request** → Merge Request (Code Review, Approvals)\n- **Azure Pipelines** → GitLab CI/CD (`.gitlab-ci.yml`)\n- **Agent Pools** → GitLab Runners (Job-Execution)\n- **Work Items** → GitLab Issues/Epics (Planning-Funktionalität)\n- **Variable Groups** → CI/CD Variables (Project/Group/Instance Level)\n\nFür vollständige Terminologie-Referenztabelle mit allen Mappings siehe [englische Originalversion](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/).\n\n## Professional Services Migrationsmuster\n\nGitLab Professional Services nutzt bewährte Muster für Enterprise-Migrationen:\n\n**Systematische Planung:** Evaluate-Tool liefert vollständiges Inventar (Repositories, PRs, Contributors, Pipelines, Work Items). Klassifikation nach Komplexität (Work Items = Planning-Team-Involvement; Pipelines = Conversion-Requirements) ermöglicht Timeline-Schätzung und Batch-Definition.\n\n**Controlled Execution:** Wellen von 200-300 Projekten managen Komplexität und respektieren API-Rate-Limits. Trial-Migrationen vor Production-Waves identifizieren Issues frühzeitig. Change-Freezes in ADO während Production-Wellen vermeiden mid-migration Updates.\n\n**Risikominimierung:** Multi-Phase-Ansatz (Prerequisites → Migration → Post-migration) mit Validierungs-Checkpoints an jeder Phase. Trial-Runs ermöglichen asynchrone Team-Validierung ohne Production-Impact.\n\n## Praktische Implementierung\n\nFür vollständige Implementierungsdetails, Konfigurationsbeispiele, Pipeline-Konvertierungs-Patterns und detaillierte Post-Migration-Checklisten siehe [englische Originalversion](https://about.gitlab.com/blog/migration-from-azure-devops-to-gitlab/).\n\nWeitere Professional Services Ressourcen:\n\n- [Professional Services Full Catalog](https://about.gitlab.com/professional-services/catalog/)\n- [Congregate Migration Tool](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/)\n- [Evaluate Inventory Tool](https://gitlab.com/gitlab-org/professional-services-automation/tools/utilities/evaluate)\n","2025-12-03","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749658924/Blog/Hero%20Images/securitylifecycle-light.png",[682,683],"Evgeny Rudinsky","Michael Leopard","Migration von Azure DevOps zu GitLab systematisch planen","Professional Services Migrationsansatz mit mehrstufiger Struktur, 200-300 Projekt-Wellen und systematischem Risikomanagement für Enterprise-Migrationen.",{"featured":30,"template":13,"slug":687},"migration-from-azure-devops-to-gitlab",{"content":689,"config":700},{"heroImage":690,"title":691,"description":692,"authors":693,"date":695,"category":9,"tags":696,"body":699},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1764108112/tyntnsy3xotlmehtnfkb.png","Wie wir die größte GitLab-Instanz 12-mal täglich bereitstellen","Systematische Deployment-Pipeline mit mehrstufigen Rollouts, Canary-Strategie (5% Traffic) und Datenbank-Migrationen für Millionen Entwickler(innen) weltweit.",[694],"John Skarbek","2025-12-01",[697,698],"product","inside GitLab","GitLab führt täglich bis zu 12 Code-Bereitstellungen auf der weltweit größten GitLab-Instanz – GitLab.com – ohne Ausfallzeiten durch. Diese Bereitstellungen nutzen GitLabs eigene CI/CD-Plattform und betreffen Millionen Entwickler(innen) weltweit. Die hohe Bereitstellungsfrequenz dient als primäres Qualitätstor und Belastungstest. Organisationen, die auf GitLab für ihre DevOps-Workflows setzen, nutzen eine auf der eigenen Infrastruktur im Enterprise-Maßstab bewährte Plattform.\n\nIn regulierten Branchen wie dem Finanzwesen und der Automobilproduktion sind Zero-Downtime-Bereitstellungen keine Option, sondern Voraussetzung. Die NIS2-Richtlinie fordert in Artikel 21 systematische Risikoanalyse und Business-Continuity-Management – genau das, was GitLab.coms progressive Rollout-Strategie in der Praxis demonstriert. Durch mehrstufige Validierung mit isoliertem Canary-Traffic (5% aller Anfragen) werden potenzielle Probleme erkannt, bevor Millionen Nutzer(innen) betroffen sind.\n\n## Die Herausforderung: Frequenz trifft Skalierung\n\n**Für GitLab:** Bereitstellungsfrequenz ist geschäftskritisch. Schnelle Deployment-Zyklen ermöglichen Reaktion auf Kundenfeedback innerhalb von Stunden, sofortige Security-Patches und Validierung neuer Features in Production vor Skalierung.\n\n**Für Kund(inn)en:** Jede Bereitstellung auf GitLab.com validiert die Deployment-Praktiken, die GitLab seinen Nutzer(inne)n empfiehlt. Features werden auf der weltweit größten GitLab-Instanz getestet, bevor sie Kundenumgebungen erreichen. Dies liefert:\n\n* Neueste Features sofort verfügbar (Stunden statt Wochen)\n* Bewährte Zuverlässigkeit im Maßstab\n* Zero-Downtime-Bereitstellungen garantieren durchgängigen Zugriff\n* Dokumentation basiert auf tatsächlichen Production-Praktiken\n\n## Code-to-Production-Architektur\n\nDie Deployment-Pipeline durchläuft strukturierte Phasen, wobei jede Phase als Checkpoint auf dem Weg zur Production-Bereitstellung fungiert:\n\n```mermaid\n    graph TD\n        A[Code Proposed] --> B[Merge Request Created]\n        B --> C[Pipeline Triggered]\n        C --> D[Build & Test]\n        D --> E{Spec/Integration/QA Tests Pass?}\n        E -->|No| F[Feedback Loop]\n        F --> B\n        E -->|Yes| G[Merge to default branch]\n        G -->|Periodically| H[Auto-Deploy Branch]\n\n        subgraph \"Deployment Pipeline\"\n            H --> I[Package Creation]\n            I --> K[Canary Environment]\n            K --> L[QA Validation]\n            L --> M[Main Environment]\n\n        end\n```\n\n## Progressive Rollout-Strategie\n\n### Build und Paket-Erstellung\n\nGitLab erstellt sowohl Omnibus-Pakete als auch Cloud Native GitLab (CNG) Images. Omnibus-Pakete werden auf der Gitaly-Flotte bereitgestellt (Git-Storage-Layer), während CNG-Images alle anderen Komponenten als containerisierte Workloads ausführen. Eine geplante Pipeline sucht regelmäßig nach dem jüngsten Commit mit erfolgreicher Pipeline und erstellt daraus einen Auto-Deploy-Branch.\n\n```mermaid\n    graph LR\n        A[Create branch] --> B[Build]\n        B --> C[Choose Built package]\n        C --> D[Start Deploy Pipeline]\n```\n\n### Canary-Strategie und mehrstufige Validierung\n\nGitLabs QA-Prozess arbeitet Hand in Hand mit der Canary-Strategie durch umgebungsbasierte Validierung. [Etwa 5% des gesamten Traffics durchlaufen die Canary-Stage](https://handbook.gitlab.com/handbook/engineering/infrastructure/environments/canary-stage/#environments-canary-stage). Dieser Ansatz erhöht die Komplexität von Datenbankmigrationen, gewährleistet aber nahtlose Bereitstellung eines zuverlässigen Produkts.\n\n**Progressive Rollout-Phasen:**\n\n1. **Staging Canary:** Initiale Validierungsumgebung\n2. **Production Canary:** Limitierter Production-Traffic\n3. **Staging Main:** Vollständige Staging-Umgebung\n4. **Production Main:** Vollständiger Production-Rollout\n\n```mermaid\n    graph TD\n        C[Staging Canary Deploy]\n        C --> D[QA Smoke Main Stage Tests]\n        C --> E[QA Smoke Canary Stage Tests]\n        D --> F\n        E --> F{Tests Pass?}\n        F -->|Yes| G[Production Canary Deploy]\n        G --> S[QA Smoke Main Stage Tests]\n        G --> T[QA Smoke Canary Stage Tests]\n        F -->|No| H[Issue Creation]\n        H --> K[Fix & Backport]\n        K --> C\n\n        S --> M[Canary Traffic Monitoring]\n        T --> M[Canary Traffic Monitoring baking period]\n        M --> U[Production Safety Checks]\n        U --> N[Staging Main]\n        N --> V[Production Main]\n```\n\nQA-Validierung erfolgt an mehreren Checkpoints: nach jeder Canary-Bereitstellung und nach Post-Deploy-Migrationen. Weitere Details zur [GitLab-Teststrategie](https://handbook.gitlab.com/handbook/engineering/testing/) finden sich im Handbook.\n\n## Deployment-Pipeline-Stages\n\nGitLab.com repräsentiert reale Deployment-Komplexität im Maßstab. Als größte bekannte GitLab-Instanz nutzen Bereitstellungen denselben offiziellen GitLab Helm Chart und dasselbe Linux-Paket wie Kund(inn)en. Mehr zur [GitLab.com-Architektur](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/architecture/#gitlab-com-architecture) im Handbook.\n\n**Dogfooding im Maßstab:** GitLab nutzt dieselben Verfahren, die für [Zero-Downtime-Upgrades](https://docs.gitlab.com/update/zero_downtime/) dokumentiert sind. Was bei GitLab nicht reibungslos funktioniert, wird Kund(inn)en nicht empfohlen.\n\nFolgende Stages laufen für alle Environment- und Stage-Upgrades:\n\n```mermaid\n    graph LR\n        a[prep] --> c[Regular Migrations - Canary stage only]\n        a --> f[Assets - Canary stage only]\n        c --> d[Gitaly]\n        d --> k8s\n\n        subgraph subGraph0[\"VM workloads\"]\n          d[\"Gitaly\"]\n        end\n\n        subgraph subGraph1[\"Kubernetes workloads\"]\n          k8s[\"k8s\"]\n        end\n\n        subgraph fleet[\"fleet\"]\n          subGraph0\n          subGraph1\n        end\n```\n\n**Stage-Details:**\n\n* **Prep:** Validiert Deployment-Bereitschaft und führt Pre-Deployment-Checks durch\n* **Migrations:** Führt reguläre Datenbankmigrationen aus (nur Canary-Stage)\n* **Assets:** Lädt statische Assets in GCS-Bucket hoch (nur Canary-Stage)\n* **Gitaly:** Aktualisiert Gitaly-VM-Storage-Layer via Omnibus-Paket\n* **Kubernetes:** Deployed containerisierte GitLab-Komponenten via Helm Chart\n\n### Multi-Version-Kompatibilität: Die versteckte Herausforderung\n\nWährend der Bereitstellung existiert eine Zeitspanne, in der das Datenbankschema dem Code voraus ist, den die Main-Stage kennt. Dies geschieht, weil die Canary-Stage bereits neuen Code deployed und reguläre Datenbankmigrationen ausführt, während die Main-Stage noch die vorherige Code-Version ausführt.\n\n**Beispiel:** Bei Hinzufügen eines neuen `merge_readiness`-Felds zu Merge Requests laufen manche Server mit Code, der dieses Feld erwartet, während andere nichts davon wissen. Schlechte Handhabung würde GitLab.com für Millionen Nutzer(innen) unterbrechen. Gute Handhabung bleibt unbemerkt.\n\nMit wenigen Ausnahmen läuft die Mehrheit der Services für eine gewisse Zeit in leicht neuerer Version in Canary. Diese Szenarien sind transiente Zustände, können aber mehrere Stunden oder Tage in Live-Production-Umgebung persistieren. Daher müssen sie mit derselben Sorgfalt wie permanente Zustände behandelt werden.\n\n## Datenbank-Operationen\n\nDatenbankmigrationen stellen eine besondere Herausforderung im Canary-Deployment-Modell dar. Schemaänderungen müssen neue Features unterstützen und gleichzeitig Rollback-Fähigkeit bewahren:\n\n* **Regular Migrations:** Laufen während Canary-Stage, rückwärtskompatibel, nur reversible Änderungen\n* **Post-Deploy-Migrations:** \"Point of no Return\"-Migrationen, die erst nach mehreren erfolgreichen Bereitstellungen laufen\n\n```mermaid\n    graph LR\n        A[Regular Migrations] --> B[Canary Stage Deploy]\n        B --> C[Main Stage Deploy]\n        C --> D[Post Deploy Migrations]\n```\n\nPost-Deploy-Migrationen enthalten oft Änderungen, die nicht einfach rückgängig gemacht werden können – Datentransformationen, Column-Drops oder strukturelle Änderungen, die ältere Code-Versionen unterbrechen würden. Durch Ausführung nach Erlangung von Vertrauen durch mehrere erfolgreiche Bereitstellungen wird sichergestellt:\n\n1. Neuer Code ist stabil, Rollback unwahrscheinlich\n2. Performance-Charakteristiken in Production verstanden\n3. Edge Cases erkannt und adressiert\n4. Blast Radius minimiert, falls etwas schiefgeht\n\nDieser Ansatz bietet optimale Balance: schnelle Feature-Bereitstellung durch Canary-Releases bei gleichzeitiger Rollback-Fähigkeit bis Vertrauen in Deployment-Stabilität besteht.\n\n**Expand-Migrate-Contract-Pattern:** Datenbank-, Frontend- und Anwendungskompatibilitäts-Änderungen folgen einem sorgfältig orchestrierten dreiphasigen Ansatz:\n\n1. **Expand:** Neue Strukturen hinzufügen, alte funktional belassen\n2. **Migrate:** Neuen Application-Code deployen, der neue Strukturen nutzt\n3. **Contract:** Alte Strukturen in Post-Deploy-Migrationen entfernen\n\n**Beispiel für `merge_readiness`-Column:**\n\n1. **Expand:** Neue Column mit Default-Value hinzufügen; bestehender Code ignoriert sie\n2. **Migrate:** Code deployen, der neue Column liest und schreibt, alten Ansatz weiter unterstützt\n3. **Contract:** Nach mehreren erfolgreichen Bereitstellungen alte Column in Post-Deploy-Migration entfernen\n\nAlle Datenbank-Operationen, Application-Code und Frontend-Code unterliegen Guidelines, dokumentiert in der [Multi-Version-Compatibility-Dokumentation](https://docs.gitlab.com/development/multi_version_compatibility/).\n\n## Ergebnisse und Auswirkungen\n\n**Für GitLab:**\n\n* Bis zu 12 Bereitstellungen täglich auf GitLab.com\n* Zero-Downtime-Bereitstellungen für Millionen Entwickler(innen)\n* Security-Patches erreichen Production innerhalb von Stunden\n* Neue Features in Production im Maßstab validiert vor General Availability\n\n**Für Kunden:**\n\n* Bewährte Deployment-Patterns für eigene Anwendungen\n* Features auf weltweit größter GitLab-Instanz getestet vor Erreichen der Kundenumgebung\n* Dokumentation reflektiert tatsächliche Production-Praktiken\n* Vertrauen, dass GitLabs empfohlene Upgrade-Prozeduren in jedem Maßstab funktionieren\n\n## Erkenntnisse für Engineering-Teams\n\nDie hier beschriebenen Muster – von Expand-Migrate-Contract für Datenbankmigrationen bis zur Multi-Version-Kompatibilität – sind auf kleinere Deployments übertragbar. Nicht jede Organisation benötigt 12 Bereitstellungen täglich, aber die systematische Herangehensweise an Rollback-Fähigkeit und Validierungspunkte gilt unabhängig von der Skalierung.\n\nGitLabs Deployment-Pipeline repräsentiert ein ausgereiftes System, das Deployment-Geschwindigkeit mit operationaler Zuverlässigkeit ausbalanciert. Das progressive Deployment-Modell, umfassende Testing-Integration und robuste Rollback-Fähigkeiten bieten Grundlage für zuverlässige Software-Auslieferung im Maßstab.\n\n**Zentrale Überlegungen für Engineering-Teams:**\n\n* **Automatisiertes Testing:** Umfassende Test-Coverage durch Deployment-Pipeline\n* **Progressive Rollouts:** Gestufte Bereitstellungen zur Risikominimierung und schnellen Wiederherstellung\n* **Monitoring-Integration:** Umfassende Observability über alle Deployment-Stages\n* **Incident Response:** Schnelle Erkennungs- und Lösungsfähigkeiten für Deployment-Probleme\n\nGitLabs Architektur demonstriert, wie moderne CI/CD-Systeme Komplexität großskaliger Bereitstellungen managen und gleichzeitig Geschwindigkeit für wettbewerbsfähige Software-Entwicklung bewahren.\n\n## Vollständige technische Dokumentation\n\nDieser Artikel beschreibt die Deployment-Patterns und systematische Herangehensweise für GitLab.com. Für vollständige Implementierungsdetails, spezifische Konfigurationsbeispiele und tiefergehende technische Architekturbeschreibungen siehe die [englische Originalversion](https://about.gitlab.com/blog/continuously-deploying-the-largest-gitlab-instance/).\n\nWeitere Dokumentation zu Auto-Deploy und Verfahren:\n\n* [Engineering Deployments](https://handbook.gitlab.com/handbook/engineering/deployments-and-releases/deployments/)\n* [Release Procedural Documentation](https://gitlab-org.gitlab.io/release/docs/)\n* [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit)\n\n## Weitere Ressourcen\n\n* [How we decreased GitLab repo backup times from 48 hours to 41 minutes](https://about.gitlab.com/blog/how-we-decreased-gitlab-repo-backup-times-from-48-hours-to-41-minutes/)\n* [How we supercharged GitLab CI statuses with WebSockets](https://about.gitlab.com/blog/how-we-supercharged-gitlab-ci-statuses-with-websockets/)\n* [How we reduced MR review time with Value Stream Management](https://about.gitlab.com/blog/how-we-reduced-mr-review-time-with-value-stream-management/)",{"featured":30,"template":13,"slug":701},"continuously-deploying-the-largest-gitlab-instance",{"content":703,"config":715},{"title":704,"description":705,"authors":706,"heroImage":708,"date":709,"category":9,"tags":710,"body":714},"MR-Review-Zeit mit Value Stream Management reduzieren","GitLab Engineering nutzt VSM zur Identifikation von Engpässen im MR-Review-Prozess. Systematische Analyse mit Custom Stages.",[707],"Haim Snir","https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097876/Blog/Hero%20Images/Blog/Hero%20Images/REFERENCE%20-%20display%20preview%20for%20blog%20images%20%282%29_2pKf8RsKzAaThmQfqHIaa7_1750097875817.png","2025-10-30",[697,711,26,712,713],"features","workflow","solutions architecture","Das GitLab Engineering-Team nutzt die eigenen Produkte intern – eine Praxis, die im Englischen als \"Dogfooding\" bezeichnet wird. Diese Selbstnutzung hat zu Verbesserungen bei der Beschleunigung der Software-Delivery-Zyklen für Kunden geführt. Dieser Artikel beleuchtet einen spezifischen Anwendungsfall, bei dem [GitLab Value Stream Management (VSM)](https://about.gitlab.com/solutions/value-stream-management/) Verbesserungen für unser Engineering-Team ermöglicht hat. Es wird gezeigt, wie VSM dabei half, zwei zentrale Herausforderungen anzugehen: die Messung des Wegs von der Idee bis zur Fertigstellung eines Merge Requests und die Optimierung der Deployment-Workflows.\n\nValue Stream Management ist in der deutschen Industrie als Wertstromanalyse etabliert – insbesondere in der Fertigung und Automobilbranche wird diese Lean-Methode zur Identifikation von Verschwendung eingesetzt. GitLabs VSM-Funktionen übertragen diesen systematischen Ansatz auf Software-Entwicklungsprozesse und ermöglichen die Unterscheidung zwischen wertschöpfenden und nicht-wertschöpfenden Aktivitäten im Entwicklungsworkflow.\n\n## Die Herausforderung: Engpässe in MR-Reviews identifizieren\n\nTrotz gut definierter Workflows stellte ein Team fest, dass Merge Requests länger als erwartet brauchten, um geprüft und gemerged zu werden. Die Herausforderung bestand nicht nur in den Verzögerungen selbst, sondern darin zu verstehen, wo im Review-Prozess diese Verzögerungen auftraten und warum.\n\nDas Ziel des Teams war klar:\n\n- Identifizieren, wo Zeit vom initialen Konzept bis zum finalen Merge eines MR verbracht wurde\n- Spezifische Engpässe im Review-Prozess lokalisieren\n- Verstehen, wie MR-Größe, Komplexität oder Dokumentationsqualität die Review-Zeit beeinflussen\n\n## Der Ansatz: MR-Review-Zeit in GitLab Value Stream Analytics messen\n\nValue Stream Analytics (VSA) ermöglicht es Organisationen, ihren gesamten Workflow von der Idee bis zur Auslieferung abzubilden und dabei zwischen wertschöpfenden Aktivitäten (VA) und nicht-wertschöpfenden Aktivitäten (NVA) im Prozessfluss zu unterscheiden. Durch die Berechnung des Verhältnisses von wertschöpfender Zeit zur gesamten Lead Time kann das Team verschwenderische Aktivitäten identifizieren, die zu Verzögerungen bei MR-Reviews führen.\n\nUm die notwendigen Metriken zu erhalten, passte das Team GitLab VSA an, um bessere Sichtbarkeit in den MR-Review-Prozess zu erhalten.\n\n### 1. Custom Stage für MR-Review einrichten\n\nDas Team fügte eine [neue Custom Stage](https://docs.gitlab.com/ee/user/group/value_stream_analytics/#value-stream-stage-events) in VSA mit dem Namen **Review Time to Merge** hinzu, um spezifisch die Zeit vom ersten Zuweisen eines Reviewers bis zum Merge des MR zu tracken.\n\n- Start-Event: MR first reviewer assigned\n- End-Event: MR merged\n\nDurch die Definition dieser Stage begann VSA, die Dauer des MR-Review-Prozesses zu messen und lieferte präzise Daten darüber, wo Zeit verbracht wurde.\n\n![Defining stage of VSA](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097884/Blog/Content%20Images/Blog/Content%20Images/image2_aHR0cHM6_1750097883929.png)\n\n### 2. Total Time Chart für Klarheit nutzen\n\nMit der Custom Stage eingerichtet, nutzte das Team das [**Total Time Chart** auf der VSA Overview-Seite](https://about.gitlab.com/blog/value-stream-total-time-chart/) (**Analyze > Value Stream**), um zu visualisieren, wie viel Zeit während der neuen MR-Review-Stage verbracht wurde. Durch den Vergleich der Werte, die durch jeden Bereich im Chart dargestellt wurden, konnte das Team schnell identifizieren, wie diese Stage zur gesamten Software-Delivery-Lifecycle-Zeit (SDLC) beitrug.\n\n![total time chart for VSA](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097884/Blog/Content%20Images/Blog/Content%20Images/image1_aHR0cHM6_1750097883930.png)\n\n### 3. Für tiefere Erkenntnisse detailliert analysieren\n\nUm spezifische Verzögerungen zu untersuchen, nutzte das Team die **Stage Navigation Bar**, um tiefer in die MR-Review-Stage einzutauchen. Diese Ansicht ermöglichte:\n\n- MRs nach Review-Zeit sortieren: Die Stage-Tabelle zeigte alle zugehörigen MRs sortiert nach Review-Dauer, sodass langsame MRs leicht erkennbar waren\n- Individuelle MRs analysieren: Für jeden MR konnte das Team Faktoren wie Verzögerungen bei der Reviewer-Zuweisung, mehrere Feedback-Runden, Leerlaufzeit nach Approval und MR-Größe/Komplexität untersuchen\n\n## Das Ergebnis: Umsetzbare Erkenntnisse und Verbesserungen\n\nDurch die Anpassung von VSA zur Verfolgung der [MR-Review-Zeit](https://docs.gitlab.com/user/project/merge_requests/reviews/) deckte das Team mehrere zentrale Erkenntnisse auf:\n\n- **Verzögerungen bei Reviewer-Zuweisung:** Einige MRs erfuhren Verzögerungen, weil Reviewer spät zugewiesen wurden oder Reviewer zu viele MRs in ihrer Queue hatten\n- **Langsame Review-Start-Zeiten:** Selbst nach Zuweisung lagen bestimmte MRs untätig, bevor Reviews begannen – oft aufgrund von Kontextwechseln oder konkurrierenden Prioritäten\n- **Mehrere Feedback-Schleifen:** Größere MRs erforderten oft mehrere Feedback-Runden, was die Review-Zeit erheblich verlängerte\n- **Leerlaufzeit nach Approval:** Einige MRs wurden approved, aber nicht zeitnah gemerged – oft aufgrund von Deployment-Koordinationsproblemen\n\nFür den Engineering Manager im Team erwies sich VSA als wertvoll für die Verwaltung des Team-Workflows: \"Ich habe VSA genutzt, um zu begründen, wo wir Zeit bei der MR-Fertigstellung verbrachten. Wir haben VSA auf unsere Bedürfnisse angepasst, und es war sehr hilfreich für unsere Untersuchungen nach Verbesserungsmöglichkeiten.\"\n\nAus dieser Dogfooding-Erfahrung entwickeln wir nun eine wichtige Erweiterung zur Verbesserung der Sichtbarkeit in den Review-Prozess. Wir fügen ein neues Event zu VSA hinzu – [Merge request last approved at](https://gitlab.com/gitlab-org/gitlab/-/issues/503754) – das eine Stage erzeugt, die MR-Review-Schritte noch granularer aufschlüsselt.\n\n## Die Kraft datengestützter Entscheidungen\n\nDurch die Nutzung von GitLabs VSA haben wir nicht nur Engpässe identifiziert – wir erhielten umsetzbare Erkenntnisse, die zu Verbesserungen bei der MR-Review-Zeit und der allgemeinen Entwickler-Produktivität führten. Wir optimierten Merge-Request-Review-Zyklen und erhöhten den Entwickler-Durchsatz, was unser Commitment zu kontinuierlicher Verbesserung durch Messung bestätigt.\n\n> Möchtest du erfahren, wie VSA deinem Team helfen kann? [Starte eine kostenlose GitLab Ultimate-Testversion](https://about.gitlab.com/free-trial/), passe deine Value Streams an und sieh, wie du Verbesserungen im gesamten SDLC für deine Teams erreichen kannst. [Teile dann dein Feedback und deine Erfahrungen in diesem Issue](https://gitlab.com/gitlab-org/gitlab/-/issues/520962).\n\n## Weiterführende Informationen\n\n- [Optimize value stream efficiency to do more with less, faster](https://about.gitlab.com/the-source/platform/optimize-value-stream-efficiency-to-do-more-with-less-faster/)\n- [New Scheduled Reports Generation tool simplifies value stream management](https://about.gitlab.com/blog/new-scheduled-reports-generation-tool-simplifies-value-stream-management/)\n- [Value stream analytics documentation](https://docs.gitlab.com/user/group/value_stream_analytics/)\n- [Value stream management: Total Time Chart simplifies top-down optimization flow](https://about.gitlab.com/blog/value-stream-total-time-chart/)\n",{"slug":716,"featured":12,"template":13},"how-we-reduced-mr-review-time-with-value-stream-management",{"promotions":718},[719,733,744],{"id":720,"categories":721,"header":723,"text":724,"button":725,"image":730},"ai-modernization",[722],"ai-ml","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":726,"config":727},"Get your AI maturity score",{"href":728,"dataGaName":729,"dataGaLocation":245},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":731},{"src":732},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":734,"categories":735,"header":736,"text":724,"button":737,"image":741},"devops-modernization",[697,38],"Are you just managing tools or shipping innovation?",{"text":738,"config":739},"Get your DevOps maturity score",{"href":740,"dataGaName":729,"dataGaLocation":245},"/assessments/devops-modernization-assessment/",{"config":742},{"src":743},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":745,"categories":746,"header":748,"text":724,"button":749,"image":753},"security-modernization",[747],"security","Are you trading speed for security?",{"text":750,"config":751},"Get your security maturity score",{"href":752,"dataGaName":729,"dataGaLocation":245},"/assessments/security-modernization-assessment/",{"config":754},{"src":755},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"header":757,"blurb":758,"button":759,"secondaryButton":764},"Beginne noch heute, schneller zu entwickeln","Entdecke, was dein Team mit der intelligenten Orchestrierungsplattform für DevSecOps erreichen kann.\n",{"text":760,"config":761},"Kostenlosen Test starten",{"href":762,"dataGaName":52,"dataGaLocation":763},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":54,"config":765},{"href":56,"dataGaName":57,"dataGaLocation":763},1772652050566]