Mona, die freundliche Octocat.
(Maskottchen von Github)
Die Vorgeschichte
Die levigo solutions nutzt für ihre Produkte schon seitdem ich Anfang 2013 dem Unternehmen beigetreten bin die Versionsverwaltungssoftware (oder VCS für Version Control System) namens git für die kollaborative Entwicklung in Teams, sodass unterschiedliche Personen gleichzeitig Code zum gemeinsamen Projekt entwickeln können. Hierfür haben wir bis 2020 eine On-Prem Lösung im Haus gehabt. Den Sourcecode in der Datenbank zu halten bringt allein aber wenig, denn Sie als Person, die unsere Software in Ihrem Projekt integrieren wollen, benötigen ja die Artefakte wie .jar Dateien für ein Java Projekt, .tgz Dateien für npm-Projekte oder gleich Docker Images.
Daher hatten wir damals schon, ebenfalls On-Prem, vor allem Software wie Jenkins im Hause levigo, um die Releases zu bauen, die Sie dann wiederum in Ihrem Projekt einbauen.
Diese Lösung hat aber irgendwann nicht mehr skaliert, da immer mehr Produkte und Projekte dazu kamen. Zudem hatte die On-Prem Lösung einen erheblichen Wartungsbedarf. Dies führte u.a. oft dazu, dass wir Build-Abbrüche hatten aufgrund eines Ressourcenmangels. Zudem kam dann noch eine Änderung im Vertriebsmodell der VCS dazu, sodass wir gezwungen waren eine andere Lösung zu finden.
GitHub als Organisations-VCS und Build Platform
Da zu besagtem Zeitpunkt auch klar war, dass die GitHub Actions, die sozusagen unser Jenkins Ersatz wurden, weiter an Fahrt aufnehmen würden, entschlossen wir uns zu GitHub zu wechseln, um dies nicht nur als VCS, sondern auch als Build Platform zu nutzen. Hierfür nutzen wir das Bezahl-Modell namens „Team“. Dies beinhaltet eine gewisse Rechenzeit über die gesamte Organisation im Monat, die durch die Anzahl an registrierten Teammitgliedern definiert wird. Da diese Rechenzeit üblicherweise nicht ausreicht, haben wir darüber hinaus ein relativ kleines Limit gesetzt, bis zu dem wir automatisch Rechenzeit hinzubuchen können.
Dies hat bis dato für all unsere Build-Prozesse ausgereicht. Falls wir aber doch einmal nachbessern müssten, der erste Kandidat für Optimierungen wurde bereits identifiziert.
Funktionsweise der Github Actions
Durch die GitHub Actions haben wir quasi in all unseren Projekten mindestens einen Continuous Delivery (CD) Workflow für Releases sowie einen Continuous Integration (CI) Workflow für die Arbeit im Pull Request (PR).
D.h. sobald ein Teammitglied neuen Code in Form eines Commits dem git Repository übermittelt, werden die GitHub Actions getriggert und der Build beginnt. Auf einem Runner wird der Codestand des Commits in das Filesystem gepackt und der Workflow bestimmt dann, welche Aktionen ausgeführt werden, um einen „Probebuild“ zu vollenden, der noch nicht zu einem Release führt. Sehr wohl aber wird dadurch bestimmt, ob der Code gültig ist und je nach Status wird das Ergebnis nicht nur in der GitHub UI abgebildet, sondern auch in unser Notification-System Mattermost gemeldet.
In unserem Standard-Workflow, hier auf das Wesentliche zusammen gekürzt, verwenden wir also unsere eigenen simplen Befehle, aber auch eine auf dem öffentlichen GitHub Marketplace veröffentlichte Action. Dies können wir so weit erweitern, dass wir ganze Shell-Skripte einhängen können wenn wir wollen.
Besonders im zweiten Workflow, dem CD, wird dies in unseren Hauptprodukten angewandt. Dort werden bspw. für unser jadice web toolkit in der 5er Versionslinie nicht nur die Maven-Artefakte in unser Nexus deployt, damit Sie diese dort abholen können, sondern es werden auch die Docker Images für z.B. unsere Demos auf GitHub Infrastruktur gebaut und in unseren Nexus gepusht. Zusätzlich werden nun auch in der neuen Versionslinie jadice web viewer die npm-Artefakte nach demselben Prinzip gebaut und gepusht, wenn auch mit anderen Befehlen.
Weitere Automatisierung
Nach einiger Zeit hatten wir einen sehr stabilen Build-Prozess. Die Stabilität und Availability von GitHub ist auch heute noch sehr zufrieden stellend. Allerdings gab es mit der Zeit einen ziemlichen Wildwuchs, da damals noch keine Code-Snippets zwischen den Actions ausgetauscht werden konnten, sodass viel copy & paste war. Mittlerweile gibt es eine Lösung in Form von reusable Workflows, sodass wir u.a. für folgende Tasks unternehmensweite Templates haben:
- CD für simple Projekte
- CI für simple Projekte
- Helm-Chart Releases
- Benachrichtigungen in unser JIRA-System
- Benachrichtigungen in unser Mattermost-System
- SBOM-Scans
So können wir mittlerweile sehr schnell auf Änderungen agieren, bspw. wenn wie jetzt im Juni passiert, GitHub seine Node-Version erhöht. Nachdem wir unser Test-Projekt auf die neueste Version der reusable Actions losgelassen haben und für gut befunden haben, wurde die neueste Version als produktionsreif getaggt und danach arbeiteten die Projekte, welche die reusable Workflows nutzen, mit dem neuesten Stand.
Sicherheit
Der sogenannte Dependabot scannt nightly unsere Projekte und legt Pull Request (PRs) für neue Versionen der third-party-libraries an, die wir in unseren Projekten nutzen. Sobald diese PRs in der CI als grün markiert wurden, können wir sie mergen um unsere Produkte aktuell zu halten. Wir sehen aber genauso bei API-Breaks sofort, dass wir noch etwas tun müssen und können direkt darauf reagieren oder Dinge langfristig einplanen, wie wir bspw. den javax. auf jakarta Umstieg eingeplant haben, der von Anfang an erkennbar ein Projekt für mehrere Monate war.
Ebenso können wir nun SBOMs der Projekte erzeugen lassen und in unseren self-hosted dependency-Tracker hochladen, um nachzuvollziehen ob bestimmte Versionen der Vergangenheit von CVEs (Common Vulnerabilities and Exposures) betroffen sind/waren.
Zusätzlich besteht die Möglichkeit, bei bekannten CVEs in aktuellen Versionen ein JIRA-Ticket zu erzeugen, um bestmöglich auf aktuelle Sicherheitslücken zu reagieren.
Backup
Durch ein insgesamt 31 Zeilen langes Skript, haben wir uns auch für den äußerst unwahrscheinlichen Fall vorbereitet, dass der Zugriff auf die GitHub-Repositories warum auch immer verloren geht und können damit einen regelmäßigen Backup via Befehlen gegen die GitHub CLI fahren, um all unsere Repositories zu sichern, hier der wichtigste Teil:
Auch hier profitieren wir von der Weiterentwicklung der Actions, in diesem Fall fine-grained Personal Access Tokens
Ausblick
Weitere Utility-Projekte wie das oben genannte zur Sicherung aller Repositories könnten vonseiten levigo noch öffentlich gemacht werden. Ebenso könnten Personen außerhalb der levigo über das bestehende Mittel der „outside collaborators“ gezielt auf einzelne Projekte berechtigt werden für Pull Requests etc.
Darüber hinaus sind weitere Entwicklungen in der GitHub Welt zu erwarten, auf die wir uns freuen, wie z.B. eine kürzlich eingeführte Funktion in der GitHub Integration von IntelliJ, sodass wir bereits in der IDE sehen, ob ein Build erfolgreich war: