Bevor wir irgendwelche Security-Checks nach links verschieben können, brauchen wir ein Projekt, ein Repository und eine Pipeline, die tatsächlich etwas baut. In Teil 2 unserer GitLab DevSecOps Serie loggen sich Patrick Steger und ich in GitLab ein, legen ein neues .NET-Core-Projekt aus einem Template an und schauen uns die .gitlab-ci.yml an, die GitLab automatisch für uns generiert — inklusive Build- und Test-Job, die das Fundament für alles werden, was wir später ergänzen.
Anmelden und Orientierung#
Nach dem Login zeigt GitLab als Erstes deine Projektliste — alle Repositories, in denen du Owner bist oder mitarbeitest, mit Filter und Suche, damit es übersichtlich bleibt. Die linke Navigation bietet Projects, Groups, Milestones, Snippets, Activity, Environments, Operations und Security. Auf das meiste davon kommen wir in späteren Sessions zurück. Für diese Einführung gehen wir direkt auf “New project”.
Ein Projekt aus einem Template anlegen#
GitLab bietet mehrere Wege, ein Projekt zu starten: ein leeres Repository, ein Template, ein Import aus einer anderen Plattform oder ein CI/CD-Setup, das gegen ein externes Repo läuft. Wir nehmen das Template, weil wir damit Code, Struktur und eine erste Pipeline mit einem Klick bekommen.
Die Template-Liste deckt die üblichen Sprachen ab. Wir wählen .NET Core, nennen das Projekt myDotNetCore, wählen die Projekt-URL und setzen die Sichtbarkeit auf privat. Public würde auch gehen — für ein Demo ist das egal — aber privat ist der realistische Default für alles, was du beruflich bauen würdest.
Ein paar Sekunden später hat GitLab das Projekt generiert. Du landest in der Repository-Ansicht mit README, dem .NET-Hello-World-Program.cs, der .csproj — und der Datei, die für uns am meisten zählt: .gitlab-ci.yml.
Was steht in der .gitlab-ci.yml#
Die automatisch generierte .gitlab-ci.yml definiert die komplette CI-Pipeline für dieses Projekt. Zwei Stages, build und test, jede mit einem Job gleichen Namens. Beide Jobs enthalten bereits alles, was nötig ist, um ein .NET-Projekt zu kompilieren und zu testen. Das ist das Rückgrat, an das wir später jeden Security-Check hängen — SAST, Secret Detection, SCA, Container Scanning. Dass GitLab dir vom ersten Tag an eine lauffähige Pipeline gibt, ist ein echter Produktivitätsgewinn.
Die Pipeline ausführen#
Um die Datei in Aktion zu sehen, gehen wir auf CI/CD → Pipelines. Hier gibt es auch einen Inline-Editor, falls du .gitlab-ci.yml direkt aus dem UI bearbeiten möchtest. Im Moment sind keine Pipelines vorhanden, also klicken wir auf Run pipeline. Der Build-Job startet als Erstes, läuft erfolgreich durch, und der Test-Job folgt. Klickst du in einen laufenden Job rein, bekommst du ein Live-Log — nützlich zum Debuggen, nützlich, um zu verstehen, was auf dem Runner tatsächlich passiert.
Sobald beide Jobs grün sind, ist die Pipeline durch.
Was eigentlich passiert ist: Runner und Docker Images#
Zurück zur .gitlab-ci.yml: Die Online-Variante von GitLab stellt Shared Runner zur Verfügung, die deine CI/CD-Jobs ausführen. Diese Runner nutzen Docker Images, und das Image wird in der YAML deklariert. Standardmässig läuft jeder Job in einem eigenen, frischen Docker Image. Das ist eine saubere Isolation, bedeutet aber auch, dass Build- und Test-Job jeweils eine Umgebung hochfahren, den Source-Code holen und einen Teil derselben Arbeit doppelt erledigen.
Im Build-Job-Log sieht man der Reihe nach: GitLab Runner startet, Docker Executor bereitet die Maschine vor, das Image wird gepullt, die Umgebung wird vorbereitet, die Sources werden aus Git geklont, und schliesslich läuft der dotnet build. Am Ende werden die Artifacts hochgeladen. Der Test-Job wiederholt die meisten dieser Schritte und führt dann dotnet test aus. Da im Hello-World-Projekt keine Tests existieren, läuft er ohne grosses Aufheben durch.
Zwei Dinge, die du dir merken solltest#
Es gibt zwei Details aus diesem Lauf, die du verinnerlichen willst, bevor du die Pipeline ausbaust.
Erstens: Image-Versionen pinnen. Das Default-Template verwendet das Tag latest für das Docker Image. Praktisch — bis sich das Image unter dir verändert und dein Build aus einem Grund kaputtgeht, der nichts mit deinem Code zu tun hat. Pin immer eine konkrete Version, damit du die Build-Umgebung kontrollierst.
Zweitens: Artifacts zwischen Jobs wiederverwenden. Aktuell kompiliert der Build-Job, und der Test-Job kompiliert effektiv nochmal, bevor er testet. Doppelte Arbeit ohne Mehrwert. Sobald die Pipeline wächst, gib das Build-Output als Artifact an den Test-Job weiter, sodass die Test-Stage nur noch testet. Eine der einfachsten Optimierungen überhaupt.
Key Takeaways#
Aus einem Template starten, nicht aus einem leeren Repo. GitLab generiert die Quelldateien, eine funktionierende
.gitlab-ci.ymlund eine ausführbare Pipeline mit einem Klick. Du verbringst deine Zeit mit dem, was dein Projekt einzigartig macht, nicht mit Boilerplate..gitlab-ci.ymlist das Herzstück von GitLab CI. Stages, Jobs, Scripts und Images sind alle hier deklariert. Wer die Form dieser Datei verstanden hat, hat den Rest von GitLab CI im Griff.Pipelines laufen auf Runnern, Runner nutzen Docker Images. Der Runner ist der Executor; das Image ist die Umgebung. Die Shared Runner von GitLab decken die meisten Anfangsbedürfnisse ab — aber zu wissen, wie das funktioniert, zahlt sich aus, sobald du selbst hostest.
Docker-Image-Versionen pinnen.
latestmacht Builds nicht-deterministisch. Eine gepinnte Version gibt dir eine reproduzierbare Build-Umgebung, die sich nur ändert, wenn du es entscheidest.Build-Artifacts in nachgelagerten Jobs wiederverwenden. Jeder Job läuft in einem eigenen Container. Ohne Artifact-Passing kompilierst du zweimal — einmal im Build, einmal im Test. Mit Artifact wird die Pipeline spürbar schneller.
Die zweistufige Build/Test-Pipeline ist das Fundament, nicht das Ziel. Alles, was wir in den nächsten Sessions ergänzen — SAST, Secret Detection, SCA, Container Scanning, DAST — steckt in derselben Struktur. Wer sich jetzt damit wohlfühlt, erlebt den Rest der Serie als Erweiterung, nicht als Neuerfindung.
