[{"data":1,"prerenderedAt":2589},["ShallowReactive",2],{"/de-de/blog/basics-of-gitlab-ci-updated":3,"navigation-de-de":1805,"banner-de-de":2236,"footer-de-de":2244,"blog-post-authors-de-de-Itzik Gan Baruch":2485,"blog-related-posts-de-de-basics-of-gitlab-ci-updated":2499,"blog-promotions-de-de":2525,"next-steps-de-de":2579},{"id":4,"title":5,"authors":6,"body":8,"category":1784,"date":1785,"description":1786,"extension":1787,"externalUrl":1788,"faq":1788,"featured":1789,"heroImage":1790,"meta":1791,"navigation":359,"path":1792,"seo":1793,"slug":1797,"stem":1798,"tags":1799,"template":1802,"updatedDate":1803,"__hash__":1804},"blogPosts/de-de/blog/basics-of-gitlab-ci-updated.md","Grundlagen der GitLab-CI-Pipeline: Aufgaben sequenziell parallel oder ohne Reihenfolge ausführen",[7],"Itzik Gan Baruch",{"type":9,"value":10,"toc":1771},"minimark",[11,26,31,164,204,212,229,232,235,264,282,327,330,333,336,377,385,448,456,459,462,566,569,726,734,789,792,803,806,850,881,884,893,1043,1056,1059,1068,1114,1117,1158,1161,1164,1167,1309,1316,1609,1622,1625,1628,1632,1647,1709,1724,1748,1755,1762,1767],[12,13,14,15,20,21,25],"p",{},"Nehmen wir an, dass du nichts über ",[16,17,19],"a",{"href":18},"/topics/ci-cd/","kontinuierliche Integration (CI)"," and ",[16,22,24],{"href":23},"/blog/how-to-keep-up-with-ci-cd-best-practices/","why it's needed"," weißt und darüber, warum sie im Lebenszyklus der Softwareentwicklung benötigt wird.",[27,28,30],"h2",{"id":29},"inhaltsverzeichnis","Inhaltsverzeichnis",[32,33,34,41,47,53,59,65,71,77,97,125,131,137,151,157],"ul",{},[35,36,37],"li",{},[16,38,40],{"href":39},"#der-erste-test-in-ci","Der erste Test in CI",[35,42,43],{},[16,44,46],{"href":45},"#ergebnisse-von-builds-zum-herunterladen-bereitstellen","Ergebnisse von Builds zum Herunterladen bereitstellen",[35,48,49],{},[16,50,52],{"href":51},"#auftr%C3%A4ge-der-reihe-nach-ausf%C3%BChren","Aufträge der Reihe nach ausführen",[35,54,55],{},[16,56,58],{"href":57},"#welches-docker-image-muss-verwendet-werden%3F","Welches Docker Image muss verwendet werden?",[35,60,61],{},[16,62,64],{"href":63},"#umgang-mit-komplexen-szenarien","Umgang mit komplexen Szenarien",[35,66,67],{},[16,68,70],{"href":69},"#umgang-mit-fehlender-softwarepaketen","Umgang mit fehlender Software/Paketen",[35,72,73],{},[16,74,76],{"href":75},"#directed-acyclic-graphs-schnellere-und-flexiblere-pipelines","Directed Acyclic Graphs: Schnellere und flexiblere Pipelines",[35,78,79,83],{},[16,80,82],{"href":81},"#wie-wertest-du-deine-pipeline-auf%3F","Wie wertest du deine Pipeline auf?",[32,84,85,91],{},[35,86,87],{},[16,88,90],{"href":89},"#automatisierte-tests-in-ci-pipelines-einbinden","Automatisierte Tests in CI-Pipelines einbinden",[35,92,93],{},[16,94,96],{"href":95},"#matrix-builds","Matrix-Builds",[35,98,99,103],{},[16,100,102],{"href":101},"#unit-tests","Unit-Tests",[32,104,105,111],{},[35,106,107],{},[16,108,110],{"href":109},"#was-sind-unit-tests%3F","Was sind Unit-Tests?",[35,112,113,117],{},[16,114,116],{"href":115},"#best-practices-f%C3%BCr-unit-tests","Best Practices für Unit-Tests",[32,118,119],{},[35,120,121],{},[16,122,124],{"href":123},"#junit-test-report","JUnit Test-Report",[35,126,127],{},[16,128,130],{"href":129},"#strategien-f%C3%BCr-integrations--und-end-to-end-tests","Strategien für Integrations- und End-to-End-Tests",[35,132,133],{},[16,134,136],{"href":135},"#testumgebung","Testumgebung",[35,138,139,143],{},[16,140,142],{"href":141},"#implementierung-von-sicherheitsscans-in-ci-pipelines","Implementierung von Sicherheitsscans in CI-Pipelines",[32,144,145],{},[35,146,147],{},[16,148,150],{"href":149},"#sast-und-dast-integration","SAST und DAST-Integration",[35,152,153],{},[16,154,156],{"href":155},"#zusammenfassung","Zusammenfassung",[35,158,159,163],{},[16,160,162],{"href":161},"#beschreibungen-der-keywords","Beschreibungen der Keywords","\nStell dir vor, du arbeitest an einem Projekt, bei dem der gesamte Code aus zwei Textdateien besteht. Dabei ist es sehr wichtig, dass die Verkettung dieser beiden Dateien die Phrase „Hello world\" enthält.\nWenn das nicht der Fall ist, wird das gesamte Development-Team in diesem Monat nicht bezahlt. Ja, so ernst ist es!\nDer oder die verantwortliche Softwareentwickler(in) hat ein kleines Skript geschrieben, das jedes Mal ausgeführt wird, wenn wir unseren Code an die Kunden senden wollen.\nDer Code ist ziemlich komplex:",[165,166,171],"pre",{"className":167,"code":168,"language":169,"meta":170,"style":170},"language-bash shiki shiki-themes github-light","cat file1.txt file2.txt | grep -q \"Hello world\"\n","bash","",[172,173,174],"code",{"__ignoreMap":170},[175,176,179,183,187,190,194,197,201],"span",{"class":177,"line":178},"line",1,[175,180,182],{"class":181},"s7eDp","cat",[175,184,186],{"class":185},"sYBdl"," file1.txt",[175,188,189],{"class":185}," file2.txt",[175,191,193],{"class":192},"sD7c4"," |",[175,195,196],{"class":181}," grep",[175,198,200],{"class":199},"sYu0t"," -q",[175,202,203],{"class":185}," \"Hello world\"\n",[12,205,206,207,211],{},"Das Problem ist, dass das Team aus 10 Entwickler(inne)n besteht. Da bleiben menschliche Fehler nicht aus.\nVor einer Woche vergaß einer der Mitarbeiter(innen), das Skript auszuführen, und drei Kund(inn)en erhielten fehlerhafte Builds. Also hast du beschlossen, dieses Problem endgültig zu lösen. Glücklicherweise befindet sich der Code bereits auf GitLab, und du erinnerst dich, dass es eine ",[16,208,210],{"href":209},"/de-de/solutions/continuous-integration/","integrierte CI"," gibt. Zudem hast du auf einer Konferenz gehört, dass viele Entwickler(innen) eine CI verwenden, um Tests durchzuführen...",[213,214,215],"blockquote",{},[12,216,217,221,222],{},[218,219,220],"strong",{},"12x kürzere Bereitstellungszeit: Dank GitLabs vollständiger Integration lebt Hilti Effizienz."," GitLab bringt vollständige Transparenz, eine umfassende Codeverwaltung und umfangreiche Sicherheitsscans mit, um Hilti neue Softwarefähigkeiten zu ermöglichen. Erfahre, wie Hilti seine Softwareentwicklung revolutioniert hat. ",[218,223,224],{},[16,225,228],{"href":226,"rel":227},"https://about.gitlab.com/de-de/customers/hilti/",[],"Erfolgsstory lesen",[27,230,40],{"id":231},"der-erste-test-in-ci",[12,233,234],{},"Nach ein paar Minuten Suche und Lesen der Dokumentation scheint es, dass wir nur diese zwei Codezeilen benötigen, die wir in einer Datei namens .gitlab-ci.yml finden:",[165,236,240],{"className":237,"code":238,"language":239,"meta":170,"style":170},"language-yaml shiki shiki-themes github-light","test:\n  script: cat file1.txt file2.txt | grep -q 'Hello world'\n","yaml",[172,241,242,252],{"__ignoreMap":170},[175,243,244,248],{"class":177,"line":178},[175,245,247],{"class":246},"shJU0","test",[175,249,251],{"class":250},"sgsFI",":\n",[175,253,255,258,261],{"class":177,"line":254},2,[175,256,257],{"class":246},"  script",[175,259,260],{"class":250},": ",[175,262,263],{"class":185},"cat file1.txt file2.txt | grep -q 'Hello world'\n",[12,265,266,267,272,273,277,278,281],{},"Wir übertragen die Zeilen, und siehe da– unser Build ist erfolgreich:\n",[268,269],"img",{"alt":270,"src":271},"build succeeded","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674096/Blog/Content%20Images/build_succeeded.png","\nNun ändern wir in der zweiten Datei \"World\" zu \"Africa\" und prüfen, was passiert:\n",[268,274],{"alt":275,"src":276},"build failed","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674096/Blog/Content%20Images/build_failed.png","\nDer Build schlägt wie erwartet fehl!\nNun haben wir hier automatisierte Tests! GitLab CI führt unser Testskript jedes Mal aus, wenn wir neuen Code in das Quellcode-Repository in der DevOps-Umgebung übertragen.\n",[218,279,280],{},"Hinweis:"," Im obigen Beispiel gehen wir davon aus, dass file1.txt und file2.txt auf dem Runner-Host vorhanden sind.\nUm dieses Beispiel in GitLab auszuführen, verwende den folgenden Code, der zunächst die Dateien erstellt und dann das Skript ausführt.",[165,283,285],{"className":237,"code":284,"language":239,"meta":170,"style":170},"test:\nbefore_script:\n      - echo \"Hello \" > | tr -d \"\\n\" | > file1.txt\n      - echo \"world\" > file2.txt\nscript: cat file1.txt file2.txt | grep -q 'Hello world'\n",[172,286,287,293,300,309,317],{"__ignoreMap":170},[175,288,289,291],{"class":177,"line":178},[175,290,247],{"class":246},[175,292,251],{"class":250},[175,294,295,298],{"class":177,"line":254},[175,296,297],{"class":246},"before_script",[175,299,251],{"class":250},[175,301,303,306],{"class":177,"line":302},3,[175,304,305],{"class":250},"      - ",[175,307,308],{"class":185},"echo \"Hello \" > | tr -d \"\\n\" | > file1.txt\n",[175,310,312,314],{"class":177,"line":311},4,[175,313,305],{"class":250},[175,315,316],{"class":185},"echo \"world\" > file2.txt\n",[175,318,320,323,325],{"class":177,"line":319},5,[175,321,322],{"class":246},"script",[175,324,260],{"class":250},[175,326,263],{"class":185},[12,328,329],{},"Aus Gründen der Übersichtlichkeit gehen wir davon aus, dass diese Dateien auf dem Host vorhanden sind und werden sie in den folgenden Beispielen nicht erstellen.",[27,331,46],{"id":332},"ergebnisse-von-builds-zum-herunterladen-bereitstellen",[12,334,335],{},"Die nächste Anforderung besteht darin, den Code zu paketieren, bevor wir ihn an unsere Kunden senden. Lass uns auch diesen Teil des Softwareentwicklungsprozesses automatisieren!\nAlles, was wir machen müssen, ist, einen weiteren Job für CI zu definieren. Nennen wir den Auftrag mal „Package\":",[165,337,339],{"className":237,"code":338,"language":239,"meta":170,"style":170},"test:\n  script: cat file1.txt file2.txt | grep -q 'Hello world'\n\npackage:\n  script: cat file1.txt file2.txt | gzip > package.gz\n",[172,340,341,347,355,361,368],{"__ignoreMap":170},[175,342,343,345],{"class":177,"line":178},[175,344,247],{"class":246},[175,346,251],{"class":250},[175,348,349,351,353],{"class":177,"line":254},[175,350,257],{"class":246},[175,352,260],{"class":250},[175,354,263],{"class":185},[175,356,357],{"class":177,"line":302},[175,358,360],{"emptyLinePlaceholder":359},true,"\n",[175,362,363,366],{"class":177,"line":311},[175,364,365],{"class":246},"package",[175,367,251],{"class":250},[175,369,370,372,374],{"class":177,"line":319},[175,371,257],{"class":246},[175,373,260],{"class":250},[175,375,376],{"class":185},"cat file1.txt file2.txt | gzip > package.gz\n",[12,378,379,380,384],{},"Nun haben wir zwei Tabs:\n",[268,381],{"alt":382,"src":383},"Two tabs - generated from two jobs","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674096/Blog/Content%20Images/two_tabs.png","\nWir haben jedoch vergessen anzugeben, dass die neue Datei ein Build-Artefakt ist, damit sie heruntergeladen werden kann. Wir können dies beheben, indem wir einen Abschnitt für Artifacts hinzufügen:",[165,386,388],{"className":237,"code":387,"language":239,"meta":170,"style":170},"test:\n  script: cat file1.txt file2.txt | grep -q 'Hello world'\n\npackage:\n  script: cat file1.txt file2.txt | gzip > packaged.gz\n  artifacts:\n    paths:\n    - packaged.gz\n",[172,389,390,396,404,408,414,423,431,439],{"__ignoreMap":170},[175,391,392,394],{"class":177,"line":178},[175,393,247],{"class":246},[175,395,251],{"class":250},[175,397,398,400,402],{"class":177,"line":254},[175,399,257],{"class":246},[175,401,260],{"class":250},[175,403,263],{"class":185},[175,405,406],{"class":177,"line":302},[175,407,360],{"emptyLinePlaceholder":359},[175,409,410,412],{"class":177,"line":311},[175,411,365],{"class":246},[175,413,251],{"class":250},[175,415,416,418,420],{"class":177,"line":319},[175,417,257],{"class":246},[175,419,260],{"class":250},[175,421,422],{"class":185},"cat file1.txt file2.txt | gzip > packaged.gz\n",[175,424,426,429],{"class":177,"line":425},6,[175,427,428],{"class":246},"  artifacts",[175,430,251],{"class":250},[175,432,434,437],{"class":177,"line":433},7,[175,435,436],{"class":246},"    paths",[175,438,251],{"class":250},[175,440,442,445],{"class":177,"line":441},8,[175,443,444],{"class":250},"    - ",[175,446,447],{"class":185},"packaged.gz\n",[12,449,450,451,455],{},"Checking... it is there:\n",[268,452],{"alt":453,"src":454},"Checking the download button","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674096/Blog/Content%20Images/artifacts.png","\nSo klappt's. Wir haben jedoch noch ein Problem zu lösen: Die Aufträge laufen parallel, aber wir wollen unsere Anwendung nicht paketieren, wenn unsere Tests fehlschlagen.",[27,457,52],{"id":458},"aufträge-der-reihe-nach-ausführen",[12,460,461],{},"Der Auftrag „Paket\" soll nur ausgeführt werden, wenn die Tests erfolgreich sind. Definieren wir die Reihenfolge, indem wir stages angeben:",[165,463,465],{"className":237,"code":464,"language":239,"meta":170,"style":170},"stages:\n  - test\n  - package\n\ntest:\n  stage: test\n  script: cat file1.txt file2.txt | grep -q 'Hello world'\n\npackage:\n  stage: package\n  script: cat file1.txt file2.txt | gzip > packaged.gz\n  artifacts:\n    paths:\n    - packaged.gz\n",[172,466,467,474,482,489,493,499,508,516,520,527,536,545,552,559],{"__ignoreMap":170},[175,468,469,472],{"class":177,"line":178},[175,470,471],{"class":246},"stages",[175,473,251],{"class":250},[175,475,476,479],{"class":177,"line":254},[175,477,478],{"class":250},"  - ",[175,480,481],{"class":185},"test\n",[175,483,484,486],{"class":177,"line":302},[175,485,478],{"class":250},[175,487,488],{"class":185},"package\n",[175,490,491],{"class":177,"line":311},[175,492,360],{"emptyLinePlaceholder":359},[175,494,495,497],{"class":177,"line":319},[175,496,247],{"class":246},[175,498,251],{"class":250},[175,500,501,504,506],{"class":177,"line":425},[175,502,503],{"class":246},"  stage",[175,505,260],{"class":250},[175,507,481],{"class":185},[175,509,510,512,514],{"class":177,"line":433},[175,511,257],{"class":246},[175,513,260],{"class":250},[175,515,263],{"class":185},[175,517,518],{"class":177,"line":441},[175,519,360],{"emptyLinePlaceholder":359},[175,521,523,525],{"class":177,"line":522},9,[175,524,365],{"class":246},[175,526,251],{"class":250},[175,528,530,532,534],{"class":177,"line":529},10,[175,531,503],{"class":246},[175,533,260],{"class":250},[175,535,488],{"class":185},[175,537,539,541,543],{"class":177,"line":538},11,[175,540,257],{"class":246},[175,542,260],{"class":250},[175,544,422],{"class":185},[175,546,548,550],{"class":177,"line":547},12,[175,549,428],{"class":246},[175,551,251],{"class":250},[175,553,555,557],{"class":177,"line":554},13,[175,556,436],{"class":246},[175,558,251],{"class":250},[175,560,562,564],{"class":177,"line":561},14,[175,563,444],{"class":250},[175,565,447],{"class":185},[12,567,568],{},"Das sollte funktionieren!\nAußerdem haben wir vergessen zu erwähnen, dass die Zusammenstellung (die in unserem Fall durch Verkettung dargestellt wird) eine Weile dauert, sodass wir sie nicht zweimal ausführen wollen. Definieren wir also einen separaten Schritt dafür:",[165,570,572],{"className":237,"code":571,"language":239,"meta":170,"style":170},"stages:\n  - compile\n  - test\n  - package\n\ncompile:\n  stage: compile\n  script: cat file1.txt file2.txt > compiled.txt\n  artifacts:\n    paths:\n    - compiled.txt\n\ntest:\n  stage: test\n  script: cat compiled.txt | grep -q 'Hello world'\n\npackage:\n  stage: package\n  script: cat compiled.txt | gzip > packaged.gz\n  artifacts:\n    paths:\n    - packaged.gz\n",[172,573,574,580,587,593,599,603,610,618,627,633,639,646,650,656,664,674,679,686,695,705,712,719],{"__ignoreMap":170},[175,575,576,578],{"class":177,"line":178},[175,577,471],{"class":246},[175,579,251],{"class":250},[175,581,582,584],{"class":177,"line":254},[175,583,478],{"class":250},[175,585,586],{"class":185},"compile\n",[175,588,589,591],{"class":177,"line":302},[175,590,478],{"class":250},[175,592,481],{"class":185},[175,594,595,597],{"class":177,"line":311},[175,596,478],{"class":250},[175,598,488],{"class":185},[175,600,601],{"class":177,"line":319},[175,602,360],{"emptyLinePlaceholder":359},[175,604,605,608],{"class":177,"line":425},[175,606,607],{"class":246},"compile",[175,609,251],{"class":250},[175,611,612,614,616],{"class":177,"line":433},[175,613,503],{"class":246},[175,615,260],{"class":250},[175,617,586],{"class":185},[175,619,620,622,624],{"class":177,"line":441},[175,621,257],{"class":246},[175,623,260],{"class":250},[175,625,626],{"class":185},"cat file1.txt file2.txt > compiled.txt\n",[175,628,629,631],{"class":177,"line":522},[175,630,428],{"class":246},[175,632,251],{"class":250},[175,634,635,637],{"class":177,"line":529},[175,636,436],{"class":246},[175,638,251],{"class":250},[175,640,641,643],{"class":177,"line":538},[175,642,444],{"class":250},[175,644,645],{"class":185},"compiled.txt\n",[175,647,648],{"class":177,"line":547},[175,649,360],{"emptyLinePlaceholder":359},[175,651,652,654],{"class":177,"line":554},[175,653,247],{"class":246},[175,655,251],{"class":250},[175,657,658,660,662],{"class":177,"line":561},[175,659,503],{"class":246},[175,661,260],{"class":250},[175,663,481],{"class":185},[175,665,667,669,671],{"class":177,"line":666},15,[175,668,257],{"class":246},[175,670,260],{"class":250},[175,672,673],{"class":185},"cat compiled.txt | grep -q 'Hello world'\n",[175,675,677],{"class":177,"line":676},16,[175,678,360],{"emptyLinePlaceholder":359},[175,680,682,684],{"class":177,"line":681},17,[175,683,365],{"class":246},[175,685,251],{"class":250},[175,687,689,691,693],{"class":177,"line":688},18,[175,690,503],{"class":246},[175,692,260],{"class":250},[175,694,488],{"class":185},[175,696,698,700,702],{"class":177,"line":697},19,[175,699,257],{"class":246},[175,701,260],{"class":250},[175,703,704],{"class":185},"cat compiled.txt | gzip > packaged.gz\n",[175,706,708,710],{"class":177,"line":707},20,[175,709,428],{"class":246},[175,711,251],{"class":250},[175,713,715,717],{"class":177,"line":714},21,[175,716,436],{"class":246},[175,718,251],{"class":250},[175,720,722,724],{"class":177,"line":721},22,[175,723,444],{"class":250},[175,725,447],{"class":185},[12,727,728,729,733],{},"Lass uns jetzt auf unsere Artifacts an:\n",[268,730],{"alt":731,"src":732},"Unnecessary artifact","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782399213/blog/Content%20Images/the-basics-of-gitlab-ci/clean-artifacts.png","\nWir brauchen diese „Kompilierungsdatei\" nicht zum Herunterladen. Deshalb lassen wir unsere temporären Artefakte ablaufen, indem wir expire_in auf „20 Minuten\" setzen:",[165,735,737],{"className":237,"code":736,"language":239,"meta":170,"style":170},"compile:\n  stage: compile\n  script: cat file1.txt file2.txt > compiled.txt\n  artifacts:\n    paths:\n    - compiled.txt\n    expire_in: 20 minutes\n",[172,738,739,745,753,761,767,773,779],{"__ignoreMap":170},[175,740,741,743],{"class":177,"line":178},[175,742,607],{"class":246},[175,744,251],{"class":250},[175,746,747,749,751],{"class":177,"line":254},[175,748,503],{"class":246},[175,750,260],{"class":250},[175,752,586],{"class":185},[175,754,755,757,759],{"class":177,"line":302},[175,756,257],{"class":246},[175,758,260],{"class":250},[175,760,626],{"class":185},[175,762,763,765],{"class":177,"line":311},[175,764,428],{"class":246},[175,766,251],{"class":250},[175,768,769,771],{"class":177,"line":319},[175,770,436],{"class":246},[175,772,251],{"class":250},[175,774,775,777],{"class":177,"line":425},[175,776,444],{"class":250},[175,778,645],{"class":185},[175,780,781,784,786],{"class":177,"line":433},[175,782,783],{"class":246},"    expire_in",[175,785,260],{"class":250},[175,787,788],{"class":185},"20 minutes\n",[12,790,791],{},"Jetzt sieht unsere Konfiguration ziemlich beeindruckend aus:",[32,793,794,797,800],{},[35,795,796],{},"Wir haben drei aufeinanderfolgende Phasen zum Kompilieren, Testen und Paketieren unserer Anwendung.",[35,798,799],{},"Wir übergeben die kompilierte Anwendung an die nächsten Stufen, damit die Kompilierung nicht zweimal ausgeführt werden muss (und somit schneller läuft).",[35,801,802],{},"Wir speichern eine paketierte Version unserer Anwendung in Build-Artefakten für die weitere Verwendung.",[27,804,58],{"id":805},"welches-docker-image-muss-verwendet-werden",[12,807,808,809,813,814,818,819,824,825,832,833,840,841,844,845,849],{},"Es scheint, dass unsere Builds immer noch langsam sind. Werfen wir einen Blick auf die Protokolle.\n",[268,810],{"alt":811,"src":812},"ruby3.1","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674096/Blog/Content%20Images/ruby-31.png","\nWas ist Ruby 3.1?\nGitLab.com verwendet Docker-Images, um unsere ",[16,815,817],{"href":816},"/blog/shared-runners/","Builds"," auszuführen, und ",[16,820,823],{"href":821,"rel":822},"https://docs.gitlab.com/user/gitlab_com/#shared-runners",[],"standardmäßig"," wird das ",[16,826,829],{"href":827,"rel":828},"https://hub.docker.com/_/ruby/",[],[172,830,831],{},"ruby:3.1","-Image verwendet. Dieses Image enthält natürlich viele Pakete, die wir nicht brauchen. Nach einer Minute des Googlens finden wir heraus, dass es ein Image namens ",[16,834,837],{"href":835,"rel":836},"https://hub.docker.com/_/alpine/",[],[172,838,839],{},"alpine"," gibt, das ein fast leeres Linux-Image ist.\nWir geben also explizit an, dass wir dieses Image verwenden wollen, indem wir image: alpine",[172,842,843],{},"to",".gitlab-ci.yml`.\nWir haben so drei Minuten gespart:\n",[268,846],{"alt":847,"src":848},"Build speed improved","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782399214/blog/Content%20Images/the-basics-of-gitlab-ci/speed.png","\nEs sieht so aus, als gäbe es viele öffentliche Images:",[32,851,852,859,866,873],{},[35,853,854],{},[16,855,858],{"href":856,"rel":857},"https://hub.docker.com/_/mysql/",[],"mysql",[35,860,861],{},[16,862,865],{"href":863,"rel":864},"https://hub.docker.com/_/python/",[],"Python",[35,867,868],{},[16,869,872],{"href":870,"rel":871},"https://hub.docker.com/_/java/",[],"Java",[35,874,875,880],{},[16,876,879],{"href":877,"rel":878},"https://hub.docker.com/_/php/",[],"php","\nWir können also einfach eines für unseren Technologie-Stack nehmen. Es ist sinnvoll, ein Image anzugeben, das keine zusätzliche Software enthält, da dies die Downloadzeit verringert.",[27,882,64],{"id":883},"umgang-mit-komplexen-szenarien",[12,885,886,887,892],{},"Nehmen wir nun aber an, wir haben neue Kund(inn)en, die möchten, dass wir unsere Anwendung in ein .iso-Image statt in ein .gz-Image packen. ISO-Images können mit dem Befehl ",[16,888,891],{"href":889,"rel":890},"http://www.w3big.com/linux/linux-comm-mkisofs.html",[],"mkisofs"," erstellt werden. Da CI die ganze Arbeit erledigt, können wir einfach einen weiteren Job hinzufügen. Darauf basierend sollte unsere Konfiguration so aussehen:",[165,894,896],{"className":237,"code":895,"language":239,"meta":170,"style":170},"image: alpine\n\nstages:\n  - compile\n  - test\n  - package\n\n# ... \"compile\" and \"test\" jobs are skipped here for the sake of compactness\n\npack-gz:\n  stage: package\n  script: cat compiled.txt | gzip > packaged.gz\n  artifacts:\n    paths:\n    - packaged.gz\n\npack-iso:\n  stage: package\n  script:\n  - mkisofs -o ./packaged.iso ./compiled.txt\n  artifacts:\n    paths:\n    - packaged.iso\n",[172,897,898,908,912,918,924,930,936,940,946,950,957,965,973,979,985,991,995,1002,1010,1016,1023,1029,1035],{"__ignoreMap":170},[175,899,900,903,905],{"class":177,"line":178},[175,901,902],{"class":246},"image",[175,904,260],{"class":250},[175,906,907],{"class":185},"alpine\n",[175,909,910],{"class":177,"line":254},[175,911,360],{"emptyLinePlaceholder":359},[175,913,914,916],{"class":177,"line":302},[175,915,471],{"class":246},[175,917,251],{"class":250},[175,919,920,922],{"class":177,"line":311},[175,921,478],{"class":250},[175,923,586],{"class":185},[175,925,926,928],{"class":177,"line":319},[175,927,478],{"class":250},[175,929,481],{"class":185},[175,931,932,934],{"class":177,"line":425},[175,933,478],{"class":250},[175,935,488],{"class":185},[175,937,938],{"class":177,"line":433},[175,939,360],{"emptyLinePlaceholder":359},[175,941,942],{"class":177,"line":441},[175,943,945],{"class":944},"sAwPA","# ... \"compile\" and \"test\" jobs are skipped here for the sake of compactness\n",[175,947,948],{"class":177,"line":522},[175,949,360],{"emptyLinePlaceholder":359},[175,951,952,955],{"class":177,"line":529},[175,953,954],{"class":246},"pack-gz",[175,956,251],{"class":250},[175,958,959,961,963],{"class":177,"line":538},[175,960,503],{"class":246},[175,962,260],{"class":250},[175,964,488],{"class":185},[175,966,967,969,971],{"class":177,"line":547},[175,968,257],{"class":246},[175,970,260],{"class":250},[175,972,704],{"class":185},[175,974,975,977],{"class":177,"line":554},[175,976,428],{"class":246},[175,978,251],{"class":250},[175,980,981,983],{"class":177,"line":561},[175,982,436],{"class":246},[175,984,251],{"class":250},[175,986,987,989],{"class":177,"line":666},[175,988,444],{"class":250},[175,990,447],{"class":185},[175,992,993],{"class":177,"line":676},[175,994,360],{"emptyLinePlaceholder":359},[175,996,997,1000],{"class":177,"line":681},[175,998,999],{"class":246},"pack-iso",[175,1001,251],{"class":250},[175,1003,1004,1006,1008],{"class":177,"line":688},[175,1005,503],{"class":246},[175,1007,260],{"class":250},[175,1009,488],{"class":185},[175,1011,1012,1014],{"class":177,"line":697},[175,1013,257],{"class":246},[175,1015,251],{"class":250},[175,1017,1018,1020],{"class":177,"line":707},[175,1019,478],{"class":250},[175,1021,1022],{"class":185},"mkisofs -o ./packaged.iso ./compiled.txt\n",[175,1024,1025,1027],{"class":177,"line":714},[175,1026,428],{"class":246},[175,1028,251],{"class":250},[175,1030,1031,1033],{"class":177,"line":721},[175,1032,436],{"class":246},[175,1034,251],{"class":250},[175,1036,1038,1040],{"class":177,"line":1037},23,[175,1039,444],{"class":250},[175,1041,1042],{"class":185},"packaged.iso\n",[12,1044,1045,1046,1050,1052,1053,1055],{},"Beachte, dass die Auftragsnamen nicht unbedingt gleich sein sollten. Wären sie identisch, wäre es nicht möglich, die Aufträge innerhalb derselben Phase des Softwareentwicklungsprozesses parallel laufen zu lassen. Sollte es daher doch mal vorkommen, kannst du das getrost als Zufall betrachten.\nWie dem auch sei, zurück zu unserem Job, da läuft es nicht recht rund – der Build schlägt fehl:\n",[268,1047],{"alt":1048,"src":1049},"Failed build because of missing mkisofs","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782399214/blog/Content%20Images/the-basics-of-gitlab-ci/mkisofs.png",[172,1051,891],{}," ist nicht im ",[172,1054,839],{}," Image mit dabei, also müssenw ir es erstmal installieren.",[27,1057,70],{"id":1058},"umgang-mit-fehlender-softwarepaketen",[12,1060,1061,1062,1067],{},"Laut der ",[16,1063,1066],{"href":1064,"rel":1065},"https://pkgs.alpinelinux.org/contents?file=mkisofs&path=&name=&branch=edge&repo=&arch=",[],"Alpine Linux website"," ist mkisofs Teil der Pakete xorriso und cdrkit. Dies sind die Befehle, die wir ausführen müssen, um ein Paket zu installieren:",[165,1069,1071],{"className":167,"code":1070,"language":169,"meta":170,"style":170},"echo \"ipv6\" >> /etc/modules  # enable networking\napk update                   # update packages list\napk add xorriso              # install package\n",[172,1072,1073,1090,1101],{"__ignoreMap":170},[175,1074,1075,1078,1081,1084,1087],{"class":177,"line":178},[175,1076,1077],{"class":199},"echo",[175,1079,1080],{"class":185}," \"ipv6\"",[175,1082,1083],{"class":192}," >>",[175,1085,1086],{"class":185}," /etc/modules",[175,1088,1089],{"class":944},"  # enable networking\n",[175,1091,1092,1095,1098],{"class":177,"line":254},[175,1093,1094],{"class":181},"apk",[175,1096,1097],{"class":185}," update",[175,1099,1100],{"class":944},"                   # update packages list\n",[175,1102,1103,1105,1108,1111],{"class":177,"line":302},[175,1104,1094],{"class":181},[175,1106,1107],{"class":185}," add",[175,1109,1110],{"class":185}," xorriso",[175,1112,1113],{"class":944},"              # install package\n",[12,1115,1116],{},"Für CI sind dies die gleichen Befehle wie für alle anderen. Die vollständige Liste der Befehle, die wir dem Skriptabschnitt übergeben müssen, sollte wie folgt aussehen:",[165,1118,1122],{"className":1119,"code":1120,"language":1121,"meta":170,"style":170},"language-yml shiki shiki-themes github-light","script:\n- echo \"ipv6\" >> /etc/modules\n- apk update\n- apk add xorriso\n- mkisofs -o ./packaged.iso ./compiled.txt\n","yml",[172,1123,1124,1130,1138,1145,1152],{"__ignoreMap":170},[175,1125,1126,1128],{"class":177,"line":178},[175,1127,322],{"class":246},[175,1129,251],{"class":250},[175,1131,1132,1135],{"class":177,"line":254},[175,1133,1134],{"class":250},"- ",[175,1136,1137],{"class":185},"echo \"ipv6\" >> /etc/modules\n",[175,1139,1140,1142],{"class":177,"line":302},[175,1141,1134],{"class":250},[175,1143,1144],{"class":185},"apk update\n",[175,1146,1147,1149],{"class":177,"line":311},[175,1148,1134],{"class":250},[175,1150,1151],{"class":185},"apk add xorriso\n",[175,1153,1154,1156],{"class":177,"line":319},[175,1155,1134],{"class":250},[175,1157,1022],{"class":185},[12,1159,1160],{},"Um es jedoch semantisch korrekt zu machen, sollten wir die Befehle, die sich auf die Paketinstallation beziehen, in before_script unterbringen. Beachte, dass, wenn du before_script auf der obersten Ebene einer Konfiguration verwendest, die Befehle vor allen Aufträgen ausgeführt werden. In unserem Fall wollen wir nur, dass sie vor einem bestimmten Auftrag ausgeführt werden.",[27,1162,76],{"id":1163},"directed-acyclic-graphs-schnellere-und-flexiblere-pipelines",[12,1165,1166],{},"Wir haben die Stufen so definiert, dass die Paketaufgaben nur ausgeführt werden, wenn die Tests bestanden wurden. Was aber, wenn wir die Phasenabfolge ein wenig aufbrechen und einige Aufträge früher ausführen wollen, auch wenn sie in einer späteren Phase definiert sind? In einigen Fällen kann die herkömmliche Phasenabfolge die Gesamtausführungszeit der Pipeline verlangsamen.\nStell dir vor, dass unsere Testphase einige umfangreichere Tests enthält, deren Ausführung viel Zeit in Anspruch nimmt und diese Tests nicht unbedingt mit den Paketaufgaben zusammenhängen. In diesem Fall wäre es effizienter, wenn die Paketaufgaben nicht auf den Abschluss dieser Tests warten müssten, bevor sie beginnen können. An dieser Stelle kommen Directed Acyclic Graphs (DAG) ins Spiel: Um die Phasenreihenfolge für bestimmte Aufträge zu unterbrechen, kannst du Abhängigkeiten von Aufgaben definieren, die die reguläre Phasenreihenfolge übergehen.\nGitLab verfügt über ein spezielles Keyword „needs\", das Abhängigkeiten zwischen Aufträgen schafft und es ermöglicht, Aufträge früher auszuführen, sobald ihre abhängigen Aufträge abgeschlossen sind.\nIm folgenden Beispiel werden die Paketaufgaben ausgeführt, sobald der Testjob abgeschlossen ist. Wenn also in Zukunft jemand weitere Tests in der Testphase hinzufügt, beginnen die Paketjobs zu laufen, bevor die neuen Testjobs abgeschlossen sind",[165,1168,1170],{"className":237,"code":1169,"language":239,"meta":170,"style":170},"pack-gz:\n  stage: package\n  script: cat compiled.txt | gzip > packaged.gz\n  needs: [\"test\"]\n  artifacts:\n    paths:\n    - packaged.gz\n\npack-iso:\n  stage: package\n  before_script:\n  - echo \"ipv6\" >> /etc/modules\n  - apk update\n  - apk add xorriso\n  script:\n  - mkisofs -o ./packaged.iso ./compiled.txt\n  needs: [\"test\"]\n  artifacts:\n    paths:\n    - packaged.iso\n",[172,1171,1172,1178,1186,1194,1208,1214,1220,1226,1230,1236,1244,1251,1257,1263,1269,1275,1281,1291,1297,1303],{"__ignoreMap":170},[175,1173,1174,1176],{"class":177,"line":178},[175,1175,954],{"class":246},[175,1177,251],{"class":250},[175,1179,1180,1182,1184],{"class":177,"line":254},[175,1181,503],{"class":246},[175,1183,260],{"class":250},[175,1185,488],{"class":185},[175,1187,1188,1190,1192],{"class":177,"line":302},[175,1189,257],{"class":246},[175,1191,260],{"class":250},[175,1193,704],{"class":185},[175,1195,1196,1199,1202,1205],{"class":177,"line":311},[175,1197,1198],{"class":246},"  needs",[175,1200,1201],{"class":250},": [",[175,1203,1204],{"class":185},"\"test\"",[175,1206,1207],{"class":250},"]\n",[175,1209,1210,1212],{"class":177,"line":319},[175,1211,428],{"class":246},[175,1213,251],{"class":250},[175,1215,1216,1218],{"class":177,"line":425},[175,1217,436],{"class":246},[175,1219,251],{"class":250},[175,1221,1222,1224],{"class":177,"line":433},[175,1223,444],{"class":250},[175,1225,447],{"class":185},[175,1227,1228],{"class":177,"line":441},[175,1229,360],{"emptyLinePlaceholder":359},[175,1231,1232,1234],{"class":177,"line":522},[175,1233,999],{"class":246},[175,1235,251],{"class":250},[175,1237,1238,1240,1242],{"class":177,"line":529},[175,1239,503],{"class":246},[175,1241,260],{"class":250},[175,1243,488],{"class":185},[175,1245,1246,1249],{"class":177,"line":538},[175,1247,1248],{"class":246},"  before_script",[175,1250,251],{"class":250},[175,1252,1253,1255],{"class":177,"line":547},[175,1254,478],{"class":250},[175,1256,1137],{"class":185},[175,1258,1259,1261],{"class":177,"line":554},[175,1260,478],{"class":250},[175,1262,1144],{"class":185},[175,1264,1265,1267],{"class":177,"line":561},[175,1266,478],{"class":250},[175,1268,1151],{"class":185},[175,1270,1271,1273],{"class":177,"line":666},[175,1272,257],{"class":246},[175,1274,251],{"class":250},[175,1276,1277,1279],{"class":177,"line":676},[175,1278,478],{"class":250},[175,1280,1022],{"class":185},[175,1282,1283,1285,1287,1289],{"class":177,"line":681},[175,1284,1198],{"class":246},[175,1286,1201],{"class":250},[175,1288,1204],{"class":185},[175,1290,1207],{"class":250},[175,1292,1293,1295],{"class":177,"line":688},[175,1294,428],{"class":246},[175,1296,251],{"class":250},[175,1298,1299,1301],{"class":177,"line":697},[175,1300,436],{"class":246},[175,1302,251],{"class":250},[175,1304,1305,1307],{"class":177,"line":707},[175,1306,444],{"class":250},[175,1308,1042],{"class":185},[12,1310,1311,1312,1315],{},"Unsere finale Version von: ",[172,1313,1314],{},".gitlab-ci.yml",":",[165,1317,1319],{"className":237,"code":1318,"language":239,"meta":170,"style":170},"image: alpine\n\nstages:\n  - compile\n  - test\n  - package\n\ncompile:\n  stage: compile\n  before_script:\n      - echo \"Hello  \" | tr -d \"\\n\" > file1.txt\n      - echo \"world\" > file2.txt\n  script: cat file1.txt file2.txt > compiled.txt\n  artifacts:\n    paths:\n    - compiled.txt\n    expire_in: 20 minutes\n\ntest:\n  stage: test\n  script: cat compiled.txt | grep -q 'Hello world'\n\npack-gz:\n  stage: package\n  script: cat compiled.txt | gzip > packaged.gz\n  needs: [\"test\"]\n  artifacts:\n    paths:\n    - packaged.gz\n\npack-iso:\n  stage: package\n  before_script:\n  - echo \"ipv6\" >> /etc/modules\n  - apk update\n  - apk add xorriso\n  script:\n  - mkisofs -o ./packaged.iso ./compiled.txt\n  needs: [\"test\"]\n  artifacts:\n    paths:\n    - packaged.iso\n",[172,1320,1321,1329,1333,1339,1345,1351,1357,1361,1367,1375,1381,1388,1394,1402,1408,1414,1420,1428,1432,1438,1446,1454,1458,1464,1473,1482,1493,1500,1507,1514,1519,1526,1535,1542,1549,1556,1563,1570,1577,1588,1595,1602],{"__ignoreMap":170},[175,1322,1323,1325,1327],{"class":177,"line":178},[175,1324,902],{"class":246},[175,1326,260],{"class":250},[175,1328,907],{"class":185},[175,1330,1331],{"class":177,"line":254},[175,1332,360],{"emptyLinePlaceholder":359},[175,1334,1335,1337],{"class":177,"line":302},[175,1336,471],{"class":246},[175,1338,251],{"class":250},[175,1340,1341,1343],{"class":177,"line":311},[175,1342,478],{"class":250},[175,1344,586],{"class":185},[175,1346,1347,1349],{"class":177,"line":319},[175,1348,478],{"class":250},[175,1350,481],{"class":185},[175,1352,1353,1355],{"class":177,"line":425},[175,1354,478],{"class":250},[175,1356,488],{"class":185},[175,1358,1359],{"class":177,"line":433},[175,1360,360],{"emptyLinePlaceholder":359},[175,1362,1363,1365],{"class":177,"line":441},[175,1364,607],{"class":246},[175,1366,251],{"class":250},[175,1368,1369,1371,1373],{"class":177,"line":522},[175,1370,503],{"class":246},[175,1372,260],{"class":250},[175,1374,586],{"class":185},[175,1376,1377,1379],{"class":177,"line":529},[175,1378,1248],{"class":246},[175,1380,251],{"class":250},[175,1382,1383,1385],{"class":177,"line":538},[175,1384,305],{"class":250},[175,1386,1387],{"class":185},"echo \"Hello  \" | tr -d \"\\n\" > file1.txt\n",[175,1389,1390,1392],{"class":177,"line":547},[175,1391,305],{"class":250},[175,1393,316],{"class":185},[175,1395,1396,1398,1400],{"class":177,"line":554},[175,1397,257],{"class":246},[175,1399,260],{"class":250},[175,1401,626],{"class":185},[175,1403,1404,1406],{"class":177,"line":561},[175,1405,428],{"class":246},[175,1407,251],{"class":250},[175,1409,1410,1412],{"class":177,"line":666},[175,1411,436],{"class":246},[175,1413,251],{"class":250},[175,1415,1416,1418],{"class":177,"line":676},[175,1417,444],{"class":250},[175,1419,645],{"class":185},[175,1421,1422,1424,1426],{"class":177,"line":681},[175,1423,783],{"class":246},[175,1425,260],{"class":250},[175,1427,788],{"class":185},[175,1429,1430],{"class":177,"line":688},[175,1431,360],{"emptyLinePlaceholder":359},[175,1433,1434,1436],{"class":177,"line":697},[175,1435,247],{"class":246},[175,1437,251],{"class":250},[175,1439,1440,1442,1444],{"class":177,"line":707},[175,1441,503],{"class":246},[175,1443,260],{"class":250},[175,1445,481],{"class":185},[175,1447,1448,1450,1452],{"class":177,"line":714},[175,1449,257],{"class":246},[175,1451,260],{"class":250},[175,1453,673],{"class":185},[175,1455,1456],{"class":177,"line":721},[175,1457,360],{"emptyLinePlaceholder":359},[175,1459,1460,1462],{"class":177,"line":1037},[175,1461,954],{"class":246},[175,1463,251],{"class":250},[175,1465,1467,1469,1471],{"class":177,"line":1466},24,[175,1468,503],{"class":246},[175,1470,260],{"class":250},[175,1472,488],{"class":185},[175,1474,1476,1478,1480],{"class":177,"line":1475},25,[175,1477,257],{"class":246},[175,1479,260],{"class":250},[175,1481,704],{"class":185},[175,1483,1485,1487,1489,1491],{"class":177,"line":1484},26,[175,1486,1198],{"class":246},[175,1488,1201],{"class":250},[175,1490,1204],{"class":185},[175,1492,1207],{"class":250},[175,1494,1496,1498],{"class":177,"line":1495},27,[175,1497,428],{"class":246},[175,1499,251],{"class":250},[175,1501,1503,1505],{"class":177,"line":1502},28,[175,1504,436],{"class":246},[175,1506,251],{"class":250},[175,1508,1510,1512],{"class":177,"line":1509},29,[175,1511,444],{"class":250},[175,1513,447],{"class":185},[175,1515,1517],{"class":177,"line":1516},30,[175,1518,360],{"emptyLinePlaceholder":359},[175,1520,1522,1524],{"class":177,"line":1521},31,[175,1523,999],{"class":246},[175,1525,251],{"class":250},[175,1527,1529,1531,1533],{"class":177,"line":1528},32,[175,1530,503],{"class":246},[175,1532,260],{"class":250},[175,1534,488],{"class":185},[175,1536,1538,1540],{"class":177,"line":1537},33,[175,1539,1248],{"class":246},[175,1541,251],{"class":250},[175,1543,1545,1547],{"class":177,"line":1544},34,[175,1546,478],{"class":250},[175,1548,1137],{"class":185},[175,1550,1552,1554],{"class":177,"line":1551},35,[175,1553,478],{"class":250},[175,1555,1144],{"class":185},[175,1557,1559,1561],{"class":177,"line":1558},36,[175,1560,478],{"class":250},[175,1562,1151],{"class":185},[175,1564,1566,1568],{"class":177,"line":1565},37,[175,1567,257],{"class":246},[175,1569,251],{"class":250},[175,1571,1573,1575],{"class":177,"line":1572},38,[175,1574,478],{"class":250},[175,1576,1022],{"class":185},[175,1578,1580,1582,1584,1586],{"class":177,"line":1579},39,[175,1581,1198],{"class":246},[175,1583,1201],{"class":250},[175,1585,1204],{"class":185},[175,1587,1207],{"class":250},[175,1589,1591,1593],{"class":177,"line":1590},40,[175,1592,428],{"class":246},[175,1594,251],{"class":250},[175,1596,1598,1600],{"class":177,"line":1597},41,[175,1599,436],{"class":246},[175,1601,251],{"class":250},[175,1603,1605,1607],{"class":177,"line":1604},42,[175,1606,444],{"class":250},[175,1608,1042],{"class":185},[12,1610,1611,1612,1614,1615,1617,1618],{},"Wir haben gerade eine Pipeline erstellt! Wir haben drei sequenzielle Stufen, die Aufträge ",[172,1613,954],{}," und ",[172,1616,999],{}," innerhalb der package-Stufe laufen parallel:\n",[268,1619],{"alt":1620,"src":1621},"Pipelines illustration","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782399214/blog/Content%20Images/the-basics-of-gitlab-ci/pipeline.png",[27,1623,82],{"id":1624},"wie-wertest-du-deine-pipeline-auf",[12,1626,1627],{},"So kannst du deine Pipeline aufwerten.",[1629,1630,90],"h3",{"id":1631},"automatisierte-tests-in-ci-pipelines-einbinden",[12,1633,1634,1635,1640,1641,1646],{},"Eine wichtige Regel der DevOps-Strategie für die Softwareentwicklung besteht darin, wirklich großartige Anwendungen mit erstaunlicher Benutzererfahrung zu entwickeln. Fügen wir also einige Tests in unsere CI-Pipeline ein, um Fehler frühzeitig im gesamten Prozess zu erkennen. Auf diese Weise können wir Probleme beheben, bevor sie zu groß werden und bevor wir an einem neuen Projekt weiterarbeiten.\nGitLab macht uns das Leben leichter, indem es fertige Vorlagen für verschiedene ",[16,1636,1639],{"href":1637,"rel":1638},"https://docs.gitlab.com/ci/testing/",[],"Tests"," anbietet. Alles, was wir tun müssen, ist, diese Vorlagen in unsere CI-Konfiguration aufzunehmen.\nIn diesem Beispiel schließen wir auch ",[16,1642,1645],{"href":1643,"rel":1644},"https://docs.gitlab.com/ci/testing/accessibility_testing/",[],"Accessibility-Tests"," ein:",[165,1648,1650],{"className":237,"code":1649,"language":239,"meta":170,"style":170},"stages:\n  - accessibility\n\nvariables:\n  a11y_urls: \"https://about.gitlab.com https://www.example.com\"\n\ninclude:\n  - template: \"Verify/Accessibility.gitlab-ci.yml\"\n",[172,1651,1652,1658,1665,1669,1676,1686,1690,1697],{"__ignoreMap":170},[175,1653,1654,1656],{"class":177,"line":178},[175,1655,471],{"class":246},[175,1657,251],{"class":250},[175,1659,1660,1662],{"class":177,"line":254},[175,1661,478],{"class":250},[175,1663,1664],{"class":185},"accessibility\n",[175,1666,1667],{"class":177,"line":302},[175,1668,360],{"emptyLinePlaceholder":359},[175,1670,1671,1674],{"class":177,"line":311},[175,1672,1673],{"class":246},"variables",[175,1675,251],{"class":250},[175,1677,1678,1681,1683],{"class":177,"line":319},[175,1679,1680],{"class":246},"  a11y_urls",[175,1682,260],{"class":250},[175,1684,1685],{"class":185},"\"https://about.gitlab.com https://www.example.com\"\n",[175,1687,1688],{"class":177,"line":425},[175,1689,360],{"emptyLinePlaceholder":359},[175,1691,1692,1695],{"class":177,"line":433},[175,1693,1694],{"class":246},"include",[175,1696,251],{"class":250},[175,1698,1699,1701,1704,1706],{"class":177,"line":441},[175,1700,478],{"class":250},[175,1702,1703],{"class":246},"template",[175,1705,260],{"class":250},[175,1707,1708],{"class":185},"\"Verify/Accessibility.gitlab-ci.yml\"\n",[12,1710,1711,1712,1717,1718,1723],{},"Passe die Variable a11y_urls an, um die URLs der Webseiten aufzulisten, die mit ",[16,1713,1716],{"href":1714,"rel":1715},"https://pa11y.org/",[],"Pa11y"," und der ",[16,1719,1722],{"href":1720,"rel":1721},"https://docs.gitlab.com/ci/testing/code_quality/",[],"Codequalität"," getestet werden sollen.",[165,1725,1727],{"className":237,"code":1726,"language":239,"meta":170,"style":170},"   include:\n   - template: Jobs/Code-Quality.gitlab-ci.yml\n",[172,1728,1729,1736],{"__ignoreMap":170},[175,1730,1731,1734],{"class":177,"line":178},[175,1732,1733],{"class":246},"   include",[175,1735,251],{"class":250},[175,1737,1738,1741,1743,1745],{"class":177,"line":254},[175,1739,1740],{"class":250},"   - ",[175,1742,1703],{"class":246},[175,1744,260],{"class":250},[175,1746,1747],{"class":185},"Jobs/Code-Quality.gitlab-ci.yml\n",[12,1749,1750,1751],{},"Mit GitLab kannst du den Testbericht direkt im Widget-Bereich der Zusammenführungsanforderung sehen. Wenn du die Codeüberprüfung, den Pipelinestatus und die Testergebnisse an einem Ort hast, wird alles reibungsloser und effizienter.\n",[268,1752],{"alt":1753,"src":1754},"Accessibility report","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674096/Blog/Content%20Images/Screenshot_2024-04-02_at_10.56.41.png",[1756,1757,1758],"center",{},[1759,1760,1761],"i",{},"Widget für die Zusammenführung von Accessibility-Anfragen",[1756,1763,1764],{},[1759,1765,1766],{},"Widget für Zusammenführungsanfragen in Codequalität",[1768,1769,1770],"style",{},"html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}",{"title":170,"searchDepth":254,"depth":254,"links":1772},[1773,1774,1775,1776,1777,1778,1779,1780,1781],{"id":29,"depth":254,"text":30},{"id":231,"depth":254,"text":40},{"id":332,"depth":254,"text":46},{"id":458,"depth":254,"text":52},{"id":805,"depth":254,"text":58},{"id":883,"depth":254,"text":64},{"id":1058,"depth":254,"text":70},{"id":1163,"depth":254,"text":76},{"id":1624,"depth":254,"text":82,"children":1782},[1783],{"id":1631,"depth":302,"text":90},"engineering","2020-12-10","Neu in der Continuous Integration? Erfahre, wie du deine erste CI-Pipeline mit GitLab erstellst.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749662061/Blog/Hero%20Images/cicdcover.png",{},"/de-de/blog/basics-of-gitlab-ci-updated",{"title":5,"description":1786,"ogTitle":5,"ogDescription":1786,"noIndex":1789,"ogImage":1790,"ogUrl":1794,"ogSiteName":1795,"ogType":1796,"canonicalUrls":1794},"https://about.gitlab.com/blog/basics-of-gitlab-ci-updated","https://about.gitlab.com","article","basics-of-gitlab-ci-updated","de-de/blog/basics-of-gitlab-ci-updated",[1800,1801],"CI","tutorial","BlogPost","2025-05-16","y6tXpuBsRNFOM4NJmdrr5_azTORMfNGvYGJvftiIMYc",{"logo":1806,"freeTrial":1811,"sales":1816,"login":1821,"items":1826,"search":2154,"minimal":2188,"duo":2206,"switchNav":2215,"pricingDeployment":2226},{"config":1807},{"href":1808,"dataGaName":1809,"dataGaLocation":1810},"/de-de/","gitlab logo","header",{"text":1812,"config":1813},"Kostenlose Testversion anfordern",{"href":1814,"dataGaName":1815,"dataGaLocation":1810},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/de-de&glm_content=default-saas-trial/","free trial",{"text":1817,"config":1818},"Demo anfordern",{"href":1819,"dataGaName":1820,"dataGaLocation":1810},"/de-de/sales/?contact-topic=request-demo","sales",{"text":1822,"config":1823},"Anmelden",{"href":1824,"dataGaName":1825,"dataGaLocation":1810},"https://gitlab.com/users/sign_in/","sign in",[1827,1856,1957,1962,2076,2132],{"text":1828,"config":1829,"menu":1831},"Plattform",{"dataNavLevelOne":1830},"platform",{"type":1832,"columns":1833},"cards",[1834,1840,1848],{"title":1828,"description":1835,"link":1836},"Die intelligente Orchestrierungsplattform für DevSecOps",{"text":1837,"config":1838},"Die Plattform erkunden",{"href":1839,"dataGaName":1830,"dataGaLocation":1810},"/de-de/platform/",{"title":1841,"description":1842,"link":1843},"GitLab Duo Agent Platform","Agentische KI für den gesamten Software-Lebenszyklus",{"text":1844,"config":1845},"Lerne GitLab Duo kennen",{"href":1846,"dataGaName":1847,"dataGaLocation":1810},"/de-de/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":1849,"description":1850,"link":1851},"Warum GitLab?","Erfahre, warum sich Unternehmen für GitLab entscheiden",{"text":1852,"config":1853},"Mehr erfahren",{"href":1854,"dataGaName":1855,"dataGaLocation":1810},"/de-de/why-gitlab/","why gitlab",{"text":1857,"left":359,"config":1858,"menu":1860},"Produkt",{"dataNavLevelOne":1859},"solutions",{"type":1861,"link":1862,"columns":1866,"feature":1936},"lists",{"text":1863,"config":1864},"Alle Lösungen anzeigen",{"href":1865,"dataGaName":1859,"dataGaLocation":1810},"/de-de/solutions/",[1867,1891,1914],{"title":1868,"description":1869,"link":1870,"items":1875},"Automatisierung","CI/CD und Automatisierung zur Beschleunigung der Bereitstellung",{"config":1871},{"icon":1872,"href":1873,"dataGaName":1874,"dataGaLocation":1810},"AutomatedCodeAlt","/de-de/solutions/delivery-automation/","automated software delivery",[1876,1879,1882,1887],{"text":1877,"config":1878},"CI/CD",{"href":209,"dataGaLocation":1810,"dataGaName":1877},{"text":1841,"config":1880},{"href":1846,"dataGaLocation":1810,"dataGaName":1881},"gitlab duo agent platform - product menu",{"text":1883,"config":1884},"Quellcodeverwaltung",{"href":1885,"dataGaLocation":1810,"dataGaName":1886},"/de-de/solutions/source-code-management/","Source Code Management",{"text":1888,"config":1889},"Automatische Softwarebereitstellung",{"href":1873,"dataGaLocation":1810,"dataGaName":1890},"Automated software delivery",{"title":1892,"description":1893,"link":1894,"items":1899},"Sicherheit","Entwickle Code schneller ohne Abstriche bei der Sicherheit",{"config":1895},{"href":1896,"dataGaName":1897,"dataGaLocation":1810,"icon":1898},"/de-de/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[1900,1904,1909],{"text":1901,"config":1902},"Anwendungssicherheitstests",{"href":1896,"dataGaName":1903,"dataGaLocation":1810},"Application security testing",{"text":1905,"config":1906},"Schutz der Software-Lieferkette",{"href":1907,"dataGaLocation":1810,"dataGaName":1908},"/de-de/solutions/supply-chain/","Software supply chain security",{"text":1910,"config":1911},"Software-Compliance",{"href":1912,"dataGaName":1913,"dataGaLocation":1810},"/de-de/solutions/software-compliance/","software compliance",{"title":1915,"link":1916,"items":1921},"Messung",{"config":1917},{"icon":1918,"href":1919,"dataGaName":1920,"dataGaLocation":1810},"DigitalTransformation","/de-de/solutions/visibility-measurement/","visibility and measurement",[1922,1926,1931],{"text":1923,"config":1924},"Sichtbarkeit und Messung",{"href":1919,"dataGaLocation":1810,"dataGaName":1925},"Visibility and Measurement",{"text":1927,"config":1928},"Wertstrommanagement",{"href":1929,"dataGaLocation":1810,"dataGaName":1930},"/de-de/solutions/value-stream-management/","Value Stream Management",{"text":1932,"config":1933},"Analysen und Einblicke",{"href":1934,"dataGaLocation":1810,"dataGaName":1935},"/de-de/solutions/analytics-and-insights/","Analytics and insights",{"title":1937,"type":1861,"items":1938},"GitLab für",[1939,1945,1951],{"text":1940,"config":1941},"Enterprise",{"icon":1942,"href":1943,"dataGaLocation":1810,"dataGaName":1944},"Building","/de-de/enterprise/","enterprise",{"text":1946,"config":1947},"Kleinunternehmen",{"icon":1948,"href":1949,"dataGaLocation":1810,"dataGaName":1950},"Work","/de-de/small-business/","small business",{"text":1952,"config":1953},"Öffentlicher Sektor",{"icon":1954,"href":1955,"dataGaLocation":1810,"dataGaName":1956},"Organization","/de-de/solutions/public-sector/","public sector",{"text":1958,"config":1959},"Preise",{"href":1960,"dataGaName":1961,"dataGaLocation":1810,"dataNavLevelOne":1961},"/de-de/pricing/","pricing",{"text":1963,"config":1964,"menu":1966},"Ressourcen",{"dataNavLevelOne":1965},"resources",{"type":1861,"link":1967,"columns":1971,"feature":2065},{"text":1968,"config":1969},"Alle Ressourcen anzeigen",{"href":1970,"dataGaName":1965,"dataGaLocation":1810},"/de-de/resources/",[1972,2005,2032],{"title":1973,"items":1974},"Erste Schritte",[1975,1980,1985,1990,1995,2000],{"text":1976,"config":1977},"Installieren",{"href":1978,"dataGaName":1979,"dataGaLocation":1810},"/de-de/install/","install",{"text":1981,"config":1982},"Kurzanleitungen",{"href":1983,"dataGaName":1984,"dataGaLocation":1810},"/de-de/get-started/","quick setup checklists",{"text":1986,"config":1987},"Lernen",{"href":1988,"dataGaLocation":1810,"dataGaName":1989},"https://university.gitlab.com/","learn",{"text":1991,"config":1992},"Produktdokumentation",{"href":1993,"dataGaName":1994,"dataGaLocation":1810},"https://docs.gitlab.com/","product documentation",{"text":1996,"config":1997},"Best-Practice-Videos",{"href":1998,"dataGaName":1999,"dataGaLocation":1810},"/de-de/getting-started-videos/","best practice videos",{"text":2001,"config":2002},"Integrationen",{"href":2003,"dataGaName":2004,"dataGaLocation":1810},"/de-de/integrations/","integrations",{"title":2006,"items":2007},"Entdecken",[2008,2013,2018,2023,2027],{"text":2009,"config":2010},"Kundenerfolge",{"href":2011,"dataGaName":2012,"dataGaLocation":1810},"/de-de/customers/","customer success stories",{"text":2014,"config":2015},"Blog",{"href":2016,"dataGaName":2017,"dataGaLocation":1810},"/de-de/blog/","blog",{"text":2019,"config":2020},"Demo-Hub",{"href":2021,"dataGaName":2022,"dataGaLocation":1810},"/de-de/demo-hub/","demo hub",{"text":2024,"config":2025},"The Source",{"href":2026,"dataGaName":2017,"dataGaLocation":1810},"/de-de/the-source/",{"text":2028,"config":2029},"Remote",{"href":2030,"dataGaName":2031,"dataGaLocation":1810},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":2033,"items":2034},"Vernetzen",[2035,2040,2045,2050,2055,2060],{"text":2036,"config":2037},"GitLab-Services",{"href":2038,"dataGaName":2039,"dataGaLocation":1810},"/de-de/services/","services",{"text":2041,"config":2042},"Beitragen",{"href":2043,"dataGaName":2044,"dataGaLocation":1810},"https://contributors.gitlab.com","contribute",{"text":2046,"config":2047},"Community",{"href":2048,"dataGaName":2049,"dataGaLocation":1810},"/community/","community",{"text":2051,"config":2052},"Forum",{"href":2053,"dataGaName":2054,"dataGaLocation":1810},"https://forum.gitlab.com/","forum",{"text":2056,"config":2057},"Veranstaltungen",{"href":2058,"dataGaName":2059,"dataGaLocation":1810},"/events/","events",{"text":2061,"config":2062},"Partner",{"href":2063,"dataGaName":2064,"dataGaLocation":1810},"/de-de/partners/","partners",{"config":2066,"title":2069,"text":2070,"link":2071},{"background":2067,"textColor":2068},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1777322348/qpq8yrgn8knii57omj0c.png')","#000","Neues bei GitLab","Über die neuesten Funktionen und Verbesserungen auf dem Laufenden bleiben.",{"text":2072,"config":2073},"Aktuelle Nachrichten",{"href":2074,"dataGaName":2075,"dataGaLocation":1810},"/de-de/whats-new/","whats new",{"text":2077,"config":2078,"menu":2080},"Unternehmen",{"dataNavLevelOne":2079},"company",{"type":1861,"columns":2081},[2082],{"items":2083},[2084,2089,2095,2097,2102,2107,2112,2117,2122,2127],{"text":2085,"config":2086},"Über",{"href":2087,"dataGaName":2088,"dataGaLocation":1810},"/de-de/company/","about",{"text":2090,"config":2091,"footerGa":2094},"Karriere",{"href":2092,"dataGaName":2093,"dataGaLocation":1810},"/jobs/","jobs",{"dataGaName":2093},{"text":2056,"config":2096},{"href":2058,"dataGaName":2059,"dataGaLocation":1810},{"text":2098,"config":2099},"Geschäftsführung",{"href":2100,"dataGaName":2101,"dataGaLocation":1810},"/company/team/e-group/","leadership",{"text":2103,"config":2104},"Handbuch",{"href":2105,"dataGaName":2106,"dataGaLocation":1810},"https://handbook.gitlab.com/","handbook",{"text":2108,"config":2109},"Investor Relations",{"href":2110,"dataGaName":2111,"dataGaLocation":1810},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":2113,"config":2114},"Trust Center",{"href":2115,"dataGaName":2116,"dataGaLocation":1810},"/de-de/security/","trust center",{"text":2118,"config":2119},"AI Transparency Center",{"href":2120,"dataGaName":2121,"dataGaLocation":1810},"/de-de/ai-transparency-center/","ai transparency center",{"text":2123,"config":2124},"Newsletter",{"href":2125,"dataGaName":2126,"dataGaLocation":1810},"/company/contact/#contact-forms","newsletter",{"text":2128,"config":2129},"Presse",{"href":2130,"dataGaName":2131,"dataGaLocation":1810},"/press/","press",{"text":2133,"config":2134,"menu":2135},"Kontakt",{"dataNavLevelOne":2079},{"type":1861,"columns":2136},[2137],{"items":2138},[2139,2144,2149],{"text":2140,"config":2141},"Vertrieb kontaktieren",{"href":2142,"dataGaName":2143,"dataGaLocation":1810},"/de-de/sales/","talk to sales",{"text":2145,"config":2146},"Support-Portal",{"href":2147,"dataGaName":2148,"dataGaLocation":1810},"https://support.gitlab.com/hc/en-us","support portal",{"text":2150,"config":2151},"Kundenportal",{"href":2152,"dataGaName":2153,"dataGaLocation":1810},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":2155,"login":2156,"suggestions":2163},"Schließen",{"text":2157,"link":2158},"Um Repositorys und Projekte zu durchsuchen, melde dich an bei",{"text":2159,"config":2160},"gitlab.com",{"href":1824,"dataGaName":2161,"dataGaLocation":2162},"search login","search",{"text":2164,"default":2165},"Vorschläge",[2166,2168,2173,2175,2180,2185],{"text":1841,"config":2167},{"href":1846,"dataGaName":1841,"dataGaLocation":2162},{"text":2169,"config":2170},"Codevorschläge (KI)",{"href":2171,"dataGaName":2172,"dataGaLocation":2162},"/de-de/solutions/code-suggestions/","Code Suggestions (AI)",{"text":1877,"config":2174},{"href":209,"dataGaName":1877,"dataGaLocation":2162},{"text":2176,"config":2177},"GitLab auf AWS",{"href":2178,"dataGaName":2179,"dataGaLocation":2162},"/de-de/partners/technology-partners/aws/","GitLab on AWS",{"text":2181,"config":2182},"GitLab auf Google Cloud",{"href":2183,"dataGaName":2184,"dataGaLocation":2162},"/de-de/partners/technology-partners/google-cloud-platform/","GitLab on Google Cloud",{"text":1849,"config":2186},{"href":1854,"dataGaName":2187,"dataGaLocation":2162},"Why GitLab?",{"freeTrial":2189,"mobileIcon":2194,"desktopIcon":2199,"secondaryButton":2202},{"text":2190,"config":2191},"Kostenlos testen",{"href":2192,"dataGaName":1815,"dataGaLocation":2193},"https://gitlab.com/-/trials/new/","nav",{"altText":2195,"config":2196},"GitLab-Symbol",{"src":2197,"dataGaName":2198,"dataGaLocation":2193},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":2195,"config":2200},{"src":2201,"dataGaName":2198,"dataGaLocation":2193},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":1973,"config":2203},{"href":2204,"dataGaName":2205,"dataGaLocation":2193},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/de-de/get-started/","get started",{"freeTrial":2207,"mobileIcon":2211,"desktopIcon":2213},{"text":2208,"config":2209},"Mehr über GitLab Duo erfahren",{"href":1846,"dataGaName":2210,"dataGaLocation":2193},"gitlab duo",{"altText":2195,"config":2212},{"src":2197,"dataGaName":2198,"dataGaLocation":2193},{"altText":2195,"config":2214},{"src":2201,"dataGaName":2198,"dataGaLocation":2193},{"button":2216,"mobileIcon":2221,"desktopIcon":2223},{"text":2217,"config":2218},"/Option",{"href":2219,"dataGaName":2220,"dataGaLocation":2193},"#contact","switch",{"altText":2195,"config":2222},{"src":2197,"dataGaName":2198,"dataGaLocation":2193},{"altText":2195,"config":2224},{"src":2225,"dataGaName":2198,"dataGaLocation":2193},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":2227,"mobileIcon":2232,"desktopIcon":2234},{"text":2228,"config":2229},"Zurück zur Preisübersicht",{"href":1960,"dataGaName":2230,"dataGaLocation":2193,"icon":2231},"back to pricing","GoBack",{"altText":2195,"config":2233},{"src":2197,"dataGaName":2198,"dataGaLocation":2193},{"altText":2195,"config":2235},{"src":2201,"dataGaName":2198,"dataGaLocation":2193},{"title":2237,"button":2238,"config":2242},"GitLab Orbit ist da: die Kontextebene für KI-Agenten",{"text":1852,"config":2239},{"href":2240,"dataGaName":2241,"dataGaLocation":1810},"/de-de/gitlab-orbit/","orbit",{"layout":2243,"disabled":1789},"release",{"data":2245},{"text":2246,"source":2247,"edit":2253,"contribute":2258,"config":2263,"items":2268,"minimal":2474},"Git ist eine Marke von Software Freedom Conservancy und unsere Verwendung von „GitLab“ erfolgt unter Lizenz.",{"text":2248,"config":2249},"Quelltext der Seite anzeigen",{"href":2250,"dataGaName":2251,"dataGaLocation":2252},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":2254,"config":2255},"Diese Seite bearbeiten",{"href":2256,"dataGaName":2257,"dataGaLocation":2252},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":2259,"config":2260},"Beteilige dich",{"href":2261,"dataGaName":2262,"dataGaLocation":2252},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":2264,"facebook":2265,"youtube":2266,"linkedin":2267},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[2269,2314,2367,2409,2441],{"title":1958,"links":2270,"subMenu":2285},[2271,2275,2280],{"text":2272,"config":2273},"Tarife anzeigen",{"href":1960,"dataGaName":2274,"dataGaLocation":2252},"view plans",{"text":2276,"config":2277},"Vorteile von Premium",{"href":2278,"dataGaName":2279,"dataGaLocation":2252},"/de-de/pricing/premium/","why premium",{"text":2281,"config":2282},"Vorteile von Ultimate",{"href":2283,"dataGaName":2284,"dataGaLocation":2252},"/de-de/pricing/ultimate/","why ultimate",[2286],{"title":2133,"links":2287},[2288,2290,2292,2294,2299,2304,2309],{"text":2140,"config":2289},{"href":2142,"dataGaName":1820,"dataGaLocation":2252},{"text":2145,"config":2291},{"href":2147,"dataGaName":2148,"dataGaLocation":2252},{"text":2150,"config":2293},{"href":2152,"dataGaName":2153,"dataGaLocation":2252},{"text":2295,"config":2296},"Status",{"href":2297,"dataGaName":2298,"dataGaLocation":2252},"https://status.gitlab.com/","status",{"text":2300,"config":2301},"Nutzungsbedingungen",{"href":2302,"dataGaName":2303,"dataGaLocation":2252},"/terms/","terms of use",{"text":2305,"config":2306},"Datenschutzerklärung",{"href":2307,"dataGaName":2308,"dataGaLocation":2252},"/de-de/privacy/","privacy statement",{"text":2310,"config":2311},"Cookie-Einstellungen",{"dataGaName":2312,"dataGaLocation":2252,"id":2313,"isOneTrustButton":359},"cookie preferences","ot-sdk-btn",{"title":1857,"links":2315,"subMenu":2324},[2316,2320],{"text":2317,"config":2318},"DevSecOps-Plattform",{"href":1839,"dataGaName":2319,"dataGaLocation":2252},"devsecops platform",{"text":2321,"config":2322},"KI-unterstützte Entwicklung",{"href":1846,"dataGaName":2323,"dataGaLocation":2252},"ai-assisted development",[2325],{"title":2326,"links":2327},"Themen",[2328,2332,2337,2342,2347,2352,2357,2362],{"text":1877,"config":2329},{"href":2330,"dataGaName":2331,"dataGaLocation":2252},"/de-de/topics/ci-cd/","cicd",{"text":2333,"config":2334},"GitOps",{"href":2335,"dataGaName":2336,"dataGaLocation":2252},"/de-de/topics/gitops/","gitops",{"text":2338,"config":2339},"DevOps",{"href":2340,"dataGaName":2341,"dataGaLocation":2252},"/de-de/topics/devops/","devops",{"text":2343,"config":2344},"Versionskontrolle",{"href":2345,"dataGaName":2346,"dataGaLocation":2252},"/de-de/topics/version-control/","version control",{"text":2348,"config":2349},"DevSecOps",{"href":2350,"dataGaName":2351,"dataGaLocation":2252},"/de-de/topics/devsecops/","devsecops",{"text":2353,"config":2354},"Cloud-nativ",{"href":2355,"dataGaName":2356,"dataGaLocation":2252},"/de-de/topics/cloud-native/","cloud native",{"text":2358,"config":2359},"KI für das Programmieren",{"href":2360,"dataGaName":2361,"dataGaLocation":2252},"/de-de/topics/devops/ai-for-coding/","ai for coding",{"text":2363,"config":2364},"Agentische KI",{"href":2365,"dataGaName":2366,"dataGaLocation":2252},"/de-de/topics/agentic-ai/","agentic ai",{"title":2368,"links":2369},"Lösungen",[2370,2373,2375,2380,2384,2387,2390,2393,2395,2397,2399,2404],{"text":1901,"config":2371},{"href":1896,"dataGaName":2372,"dataGaLocation":2252},"Application Security Testing",{"text":1888,"config":2374},{"href":1873,"dataGaName":1874,"dataGaLocation":2252},{"text":2376,"config":2377},"Agile Entwicklung",{"href":2378,"dataGaName":2379,"dataGaLocation":2252},"/de-de/solutions/agile-delivery/","agile delivery",{"text":2381,"config":2382},"SCM",{"href":1885,"dataGaName":2383,"dataGaLocation":2252},"source code management",{"text":1877,"config":2385},{"href":209,"dataGaName":2386,"dataGaLocation":2252},"continuous integration & delivery",{"text":1927,"config":2388},{"href":1929,"dataGaName":2389,"dataGaLocation":2252},"value stream management",{"text":2333,"config":2391},{"href":2392,"dataGaName":2336,"dataGaLocation":2252},"/de-de/solutions/gitops/",{"text":1940,"config":2394},{"href":1943,"dataGaName":1944,"dataGaLocation":2252},{"text":1946,"config":2396},{"href":1949,"dataGaName":1950,"dataGaLocation":2252},{"text":1952,"config":2398},{"href":1955,"dataGaName":1956,"dataGaLocation":2252},{"text":2400,"config":2401},"Bildungswesen",{"href":2402,"dataGaName":2403,"dataGaLocation":2252},"/de-de/solutions/education/","education",{"text":2405,"config":2406},"Finanzdienstleistungen",{"href":2407,"dataGaName":2408,"dataGaLocation":2252},"/de-de/solutions/finance/","financial services",{"title":1963,"links":2410},[2411,2413,2415,2417,2420,2422,2425,2427,2429,2431,2433,2435,2437,2439],{"text":1976,"config":2412},{"href":1978,"dataGaName":1979,"dataGaLocation":2252},{"text":1981,"config":2414},{"href":1983,"dataGaName":1984,"dataGaLocation":2252},{"text":1986,"config":2416},{"href":1988,"dataGaName":1989,"dataGaLocation":2252},{"text":1991,"config":2418},{"href":1993,"dataGaName":2419,"dataGaLocation":2252},"docs",{"text":2014,"config":2421},{"href":2016,"dataGaName":2017,"dataGaLocation":2252},{"text":2423,"config":2424},"Neuerungen",{"href":2074,"dataGaName":2075,"dataGaLocation":2252},{"text":2009,"config":2426},{"href":2011,"dataGaName":2012,"dataGaLocation":2252},{"text":2028,"config":2428},{"href":2030,"dataGaName":2031,"dataGaLocation":2252},{"text":2036,"config":2430},{"href":2038,"dataGaName":2039,"dataGaLocation":2252},{"text":2041,"config":2432},{"href":2043,"dataGaName":2044,"dataGaLocation":2252},{"text":2046,"config":2434},{"href":2048,"dataGaName":2049,"dataGaLocation":2252},{"text":2051,"config":2436},{"href":2053,"dataGaName":2054,"dataGaLocation":2252},{"text":2056,"config":2438},{"href":2058,"dataGaName":2059,"dataGaLocation":2252},{"text":2061,"config":2440},{"href":2063,"dataGaName":2064,"dataGaLocation":2252},{"title":2077,"links":2442},[2443,2445,2447,2449,2451,2453,2458,2463,2465,2467,2469],{"text":2085,"config":2444},{"href":2087,"dataGaName":2079,"dataGaLocation":2252},{"text":2090,"config":2446},{"href":2092,"dataGaName":2093,"dataGaLocation":2252},{"text":2098,"config":2448},{"href":2100,"dataGaName":2101,"dataGaLocation":2252},{"text":2103,"config":2450},{"href":2105,"dataGaName":2106,"dataGaLocation":2252},{"text":2108,"config":2452},{"href":2110,"dataGaName":2111,"dataGaLocation":2252},{"text":2454,"config":2455},"Nachhaltigkeit",{"href":2456,"dataGaName":2457,"dataGaLocation":2252},"/sustainability/","Sustainability",{"text":2459,"config":2460},"Vielfalt, Inklusion und Zugehörigkeit",{"href":2461,"dataGaName":2462,"dataGaLocation":2252},"/de-de/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":2113,"config":2464},{"href":2115,"dataGaName":2116,"dataGaLocation":2252},{"text":2123,"config":2466},{"href":2125,"dataGaName":2126,"dataGaLocation":2252},{"text":2128,"config":2468},{"href":2130,"dataGaName":2131,"dataGaLocation":2252},{"text":2470,"config":2471},"Transparenzerklärung zu moderner Sklaverei",{"href":2472,"dataGaName":2473,"dataGaLocation":2252},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":2475},[2476,2479,2482],{"text":2477,"config":2478},"Terms",{"href":2302,"dataGaName":2303,"dataGaLocation":2252},{"text":2480,"config":2481},"Cookies",{"dataGaName":2312,"dataGaLocation":2252,"id":2313,"isOneTrustButton":359},{"text":2483,"config":2484},"Datenschutz",{"href":2307,"dataGaName":2308,"dataGaLocation":2252},[2486],{"id":2487,"title":7,"body":1788,"config":2488,"content":2490,"description":1788,"extension":1121,"meta":2494,"navigation":359,"path":2495,"seo":2496,"stem":2497,"__hash__":2498},"blogAuthors/en-us/blog/authors/itzik-gan-baruch.yml",{"template":2489},"BlogAuthor",{"name":7,"config":2491},{"headshot":2492,"ctfId":2493},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749658921/Blog/Author%20Headshots/iganbaruch-headshot.jpg","iganbaruch",{},"/en-us/blog/authors/itzik-gan-baruch",{},"en-us/blog/authors/itzik-gan-baruch","bz9VMiTQ1ixvnoxUFk0jiUcnLG3oQsymgXNCqyRqfsk",[2500,2509,2517],{"title":2501,"description":2502,"heroImage":2503,"category":1784,"date":2504,"authors":2505,"slug":2508,"externalUrl":1788},"Confidential AI für GitLab Self-Hosted","KI-Coding-Agenten in GitLab Duo nutzen, ohne dass Quellcode eine hardwareverschlüsselte Grenze verlässt, und ohne eigene GPUs zu betreiben.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1773866173/vte9qh8rriznvyclhkes.png","2026-08-06",[2506,2507],"Mathias Ewald","Martin Paloncy","confidential-ai-for-gitlab-self-hosted",{"title":2510,"description":2511,"heroImage":2512,"category":1784,"date":2513,"authors":2514,"slug":2516,"externalUrl":1788},"Green DevOps: Warum CO2-Messung in die CI/CD-Pipeline gehört","Jeder Pipeline-Lauf verbraucht Energie, die nirgends sichtbar wird. Eco CI und Carmen machen diesen CO2-Ausstoß sichtbar, direkt in den Pipelines.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1765809212/noh0mdfn9o94ry9ykura.png","2026-07-09",[2515],"Lysanne Pinto","green-devops-carbon-measurement-cicd-pipeline",{"title":2518,"description":2519,"heroImage":2520,"category":1784,"date":2521,"authors":2522,"slug":2524,"externalUrl":1788},"Was ist CI/CD? Bedeutung, Vorteile und Tool-Auswahl","Was CI/CD bedeutet, welche Vorteile die Einführung bringt und worauf bei der Tool-Auswahl zu achten ist.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749663328/Blog/Hero%20Images/cicd3.jpg","2026-06-22",[2523],"GitLab","what-is-ci-cd",{"promotions":2526},[2527,2541,2553,2565],{"id":2528,"categories":2529,"header":2531,"text":2532,"button":2533,"image":2538},"ai-modernization",[2530],"ai","Hält KI, was uns versprochen wurde?","Das Quiz dauert maximal 5 Minuten.",{"text":2534,"config":2535},"Ermittle deinen KI-Reifegrad",{"href":2536,"dataGaName":2537,"dataGaLocation":2017},"/de-de/assessments/ai-modernization-assessment/","modernization assessment",{"config":2539},{"src":2540},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":2542,"categories":2543,"header":2545,"text":2532,"button":2546,"image":2550},"devops-modernization",[2544,2351],"product","Verwaltest du Tool-Chaos oder stellst du Innovationen bereit?",{"text":2547,"config":2548},"Ermittle deinen DevOps-Reifegrad",{"href":2549,"dataGaName":2537,"dataGaLocation":2017},"/de-de/assessments/devops-modernization-assessment/",{"config":2551},{"src":2552},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":2554,"categories":2555,"header":2557,"text":2532,"button":2558,"image":2562},"security-modernization",[2556],"security","Tauschst du Schnelligkeit gegen Sicherheit ein?",{"text":2559,"config":2560},"Ermittle deinen Sicherheitsreifegrad",{"href":2561,"dataGaName":2537,"dataGaLocation":2017},"/de-de/assessments/security-modernization-assessment/",{"config":2563},{"src":2564},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":2566,"paths":2567,"header":2570,"text":2571,"button":2572,"image":2577},"github-azure-migration",[2568,2569],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Ist dein Team bereit für den Umzug von GitHub nach Azure?","GitHub stellt bereits auf Azure um. Finde heraus, was das für dich bedeutet.",{"text":2573,"config":2574},"Erfahre, wie GitLab im Vergleich zu GitHub abschneidet",{"href":2575,"dataGaName":2576,"dataGaLocation":2017},"/de-de/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":2578},{"src":2552},{"header":2580,"blurb":2581,"button":2582,"secondaryButton":2587},"Beginne noch heute, schneller zu entwickeln","Entdecke, was dein Team mit der intelligenten Orchestrierungsplattform für DevSecOps erreichen kann.\n",{"text":2583,"config":2584},"Kostenlosen Test starten",{"href":2585,"dataGaName":1815,"dataGaLocation":2586},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/de-de/","feature",{"text":2140,"config":2588},{"href":2142,"dataGaName":1820,"dataGaLocation":2586},1786803739236]