Der erste Schritt zum SSDLC-Pokal
Freudig ist das Entwicklerleben und vielzählig seine Geschichten. Nicht nur nennt man mich gerne Yannik Zietz, sondern ebenso gerne beschäftige ich mich in meiner Rolle als Softwareentwickler bei der CM4all zumeist mit den Themen Automatisierung und Security. In diesem Kontext gibt es nach all den Jahren einige Erlebnisse zu berichten, von welchen ich heute das folgende mit Ihnen teilen werde.
Das neue Software-Projekt: Saftige grüne Wiesen, der Klang von Freiheit und unbegrenzte Möglichkeiten. Diesmal machen wir es richtig, halten unser Repo sauber und vermeiden jeden Overhead. Wir haben so viele Ideen. Der grobe Tech-Stack ist schnell gefunden, auch auf einen Namen konnten wir uns mittlerweile einigen und ohne weitere Umschweife reiten wir auf unserer Wolke gen ersten MVP. Zack, Projekt angelegt, Zack, Readme pro forma befüllt, Zack – los geht’s.
npm install vue vuetify vuex vue-router && npm install -D vite @vitejs/plugin-vue @vue/test-utils eslint eslint-config-airbnb-base eslint-plugin-vue eslint-plugin-jest jest jest-environment-jsdom jest-serializer-vue @babel/core @babel/preset-env @babel/register babel-jest
Wir können ja nicht alles selber machen
Lassen Sie mich Sie direkt beruhigen: Dieser Artikel ist kein weiteres „Dude, y u bloat ur software?“. Die wenigsten Entwickler haben je versucht, jede Zeile Code selbst zu schreiben, die meisten dieser sind (wahrscheinlich) daran gescheitert und das ist absolut verständlich. Es ist nicht nur die Vorfreude darauf, endlich die ersten Schritte des Proofs of Concept zu erleben oder der warme Atem der wenn auch noch so fernen Deadline im Nacken, welche uns einen gepflegten Sack an externen Dependencies einbringen. Es ist vor allem sehr naheliegend. Die Open-Source-Welt ist voll von durchaus guten und zuverlässig funktionierenden Frameworks, Tools und anderen Hilfsmitteln, ohne die der Großteil der heute existierenden Projekte niemals zustande gekommen wäre. Unsere digitale Welt steht auf den Schultern einer meist in ihrer Freizeit codenden Community und das ist zumindest für uns als IONOS ein Segen.
Spielverderber Security
Was dieser Segen im aufziehenden Alltag bedeutet, ist uns dabei so wohlbekannt (und verdrängt) wie unser Hochzeitstag: Wir machen uns ab der ersten Sekunde abhängig von eben dieser Community, welche uns den Großteil unserer Arbeit abnimmt. Das klingt erst einmal härter als es ist, denn die meisten etablierten Open-Source-Projekte werden gewissenhaft gepflegt und bleiben auf lange Sicht in allen Belangen zuverlässig. Und doch erinnern wir uns noch mit leichtem Grusel an die Geschichten von Log4Shell oder dem FakerJS-Breakdown, die uns damals recht heitere Stunden beschert haben – von regelmäßigen OpenSSL-Meldungen mal abgesehen. Wir sind darauf angewiesen, dass die Maintainer riesiger Code-Bases ihre Hausaufgaben in Form von Bug- und Security-Fixes machen und vor allem sind wir in der Pflicht, diese auch bei uns einzuspielen. Unser SSDLC schreit uns entgegen „LC-22 Update Dependencies“ und damit hat es Recht. Die güldene Schleife an unserem Projekt fällt eben nicht auf, wenn einer unserer Pfeiler diverse Crypto-Scammer zur Zwischenmiete einlädt.
Dependency-Hygiene hält gesund (Ärzte hassen diesen Trick)
Das Aktualisieren von Dependencies war eine ganze Weile lang mein heimlicher Freitagnachmittag-Genuss, wenn sich ein neues Ticket vorm Wochenende nicht mehr lohnte und ich trotzdem eben dieses mit einem kleinen Erfolgserlebnis einläuten wollte. Kurz durch die Projekte gehüpft und die simplen Chores ausgeführt, fertig. Das war zwar schön, kam dann aber doch zu selten vor, um unsere ISMS-Kollegen zu beeindrucken. Die Disziplin bei solchen Aufgaben lässt in der echten Welt zumeist zu Wünschen übrig, vor allem sobald viel spannendere Dinge auf der Tagesordnung stehen. Ebenfalls lassen sich derartige Aufgaben nur ungenügend einplanen – es ist kein Feature, lässt sich weder intern noch extern vermarkten und muss vor allem ab der ersten Dependency dauerhaft gemacht werden. Kann man das nicht einfach weg automatisieren?
Dependency Automation Bots
Wir kennen Automatisierung an genügend Stellen: Automatisch testen, automatisch bauen (ab 01.04. sogar legal), automatisch ausrollen, mittlerweile sogar automatisch coden. Da liegt es absolut nahe, auch andere undankbare Aufgaben wie besagte Dependency-Updates automatisch durchführen zu lassen. Der geneigte Commit-Leser hat sich sicher einmal gefragt, wer diese Handvoll User eigentlich sind, welche in allen möglichen Projekten so fleißig die Dependencies pflegen und dabei meist irgendwas mit Bot im Namen tragen. Auf die Gefahr hin, dem einen oder anderen eine lieb gewonnene Illusion zu zerstören: Diese Wesen gibt es nicht, denn sie sind in der Tat Maschinen – welche dieser Art, die uns von unserem schlechten Gewissen ob mangelnder Disziplin befreien können (und mich von meinem Freitagnachmittag-Genuss ☹️). Die Idee ist einfach: So ein Dingsbums schaut regelmäßig im Weltnetz nach, ob es für eine oder mehrere unserer Dependencies eine neue Version gibt, welche wir noch nicht besitzen und trägt diese in unser Projekt ein. Fertig. Und wo wir vorhin bei Vermarktung waren: Mit KI hat das nichts zu tun.
Renovate
Womit ich endlich zum Punkt komme. Während dem begnadeten GitHub-Nutzer sicher einmal die Konfiguration eines gewissen Dependabots unter die Gleitsichtbrille gekommen ist, lassen wir heute den Lock-In hinter uns und schwingen die Trommel für den gewissermaßen coolen Bruder: Renovate
Lassen wir die Erfinder aus dem Hause mend.io selbst zu Wort kommen, beschreiben sie ihr Machwerk kurz und knackig als Tool für “Automated dependency updates. Multi-platform and multi-language”. Und so griffig wie vielversprechend diese Zusammenfassung schon klingt, ist sie in der Realität so treffend wie untertrieben. Automatisierte Dependency-Updates? Jawohl, danach suchen wir gerade. Unterstützung verschiedener Sprachen? Bitte, denn die verwenden wir. Support verschiedener Plattformen? Oh ja gerne, denn nur ein kleiner Teil unserer Projekte wohnt derzeit in GitHub und könnte in den Genuss des mitgelieferten Dependabots kommen. Was diese Beschreibung unterschlägt, ist die tatsächliche Fülle an zusätzlichen Möglichkeiten, die uns dieses Tool bietet.
Lassen Sie mich versuchen, die Interessantesten dieser einmal übersichtlich darzulegen:
- Sich selbst aktualisierende Branches & Pull Requests
- Auto Merge
- Lock File Maintenance
- Replacements von deprecated Dependencies
- Beliebiges Scheduling
- Support von Monorepos & individuelle Gruppierungen
- Support von Infrastructure as Code
- Support von privaten Packages
- Berücksichtigung der Repo-Settings
- Skip by Decline
- Extrem detaillierte, vererbbare Konfiguration
- Jede Menge mitgelieferte Presets
- Simples Setup
- Self Hosting
- Dashboard zum Gucken & Klicken
und das ganze Open Source.
In Worten:
Renovate liefert uns nach überschaubarem Aufwand in beliebigen Intervallen sich selbst pflegende, bei Bedarf eigenständig mergende Pull Requests mit Updates oder Replacements nahezu beliebiger Dependencies, welche die Regeln unserer Repositories befolgen und sich nach Belieben gruppieren, einschränken, anpassen und verwalten lassen. Unter anderem.
Und das schauen wir uns jetzt einmal genauer an.
Kaffeefahrt durch die Renovate-Doku
Bereits die antike Philosophie lehrte uns: Stehest Du am Fuße des Berges und siehst Du in seinem Schatten nicht Deinen Weg hinauf, RTFM. Wurd mir zumindest so erzählt. Und ob das nun stimmt oder nicht, so klingt es doch weise und daher klicken wir voller Anmut auf diesen Link zur Dokumentation und lassen auf uns wirken das Wort Renovates. Und Renovate sprach: Getting Started.
Lassen Sie sich so viel gesagt sein: Es gibt durchaus eine Menge zu lesen, doch die wichtigsten Punkte sind relativ einfach abgehandelt:
- Wird meine Plattform unterstützt?
- Werden meine Sprachen/Technologien unterstützt?
- Was mache ich, damit es läuft?
Die ersten beiden Punkte lassen sich mit Blick auf die Übersichten der Supported Platforms sowie der Supported Languages und Datasources schnell beantworten. Beim dritten Punkt wird es etwas kniffliger, denn da müssen wir uns entscheiden: Nehmen wir einen bestehenden Dienst oder hosten wir uns die Möhre doch lieber selber? Die Entscheidung kann ich Ihnen zwar nicht abnehmen, doch verrate ich so viel, dass wir uns in meiner ehemaligen Unit damals für letzteres entschieden haben und wir noch am selben Nachmittag den ersten Pull Request im Briefkasten hatten. Das hatte wenig mit unserem durchaus hohen Coolness-Faktor zu tun, sondern damit, dass Renovate uns für unser GitLab ein simples Beispielprojekt zum Diebstahl anbietet, mit welchem das erste Aufsetzen in Windeseile vonstatten geht. Die anderen Methoden haben wir dann zwar nicht mehr ausprobiert, jedoch ist es sehr wahrscheinlich, dass es damit ähnlich schnell geht.
An dieser Stelle bleibt dann tatsächlich nur noch folgendes zu tun: Nach geeigneter Konfiguration suchen und so lange mit dem Ding rumspielen, bis sich alles gut anfühlt. Und hier wird es spannend.
Und was haben wir dieses Quartal so gemacht?
In meinem Tech Talk zum Thema habe ich es bereits angeteasert und auch hier möchte ich es nicht unerwähnt lassen: Durch seine vielen Möglichkeiten bietet Renovate auch viele Möglichkeiten, im Welkprozess voranzuschreiten. Die Konfigurationsmöglichkeiten sind vielzählig und wer nach Wegen zum muskulösen Zeigefinger sucht, scrolle ein paar mal durch genau diese hindurch. Eine Menge der Konfigurationen werden zwar in verständlicherer Weise in mitgelieferten Presets angeboten, nur ist die Auswahl hier nicht gerade geringer. Es ist sicher ratsam, sich Gedanken über seinen gewünschten Prozess zu machen und dann nach den passenden Settings zu suchen als andersrum – genau so haben wir es damals auch gemacht.
Insgesamt ist es zudem empfehlenswert, sich mit der Etablierung von Renovate in den eigenen Repos ausreichend Zeit zu lassen und bestimmte Konfigurationen im Alltag auszuprobieren. Mit ausreichend Skepsis gegenüber “dem Neuen” haben wir uns zu Beginn erstmal nur getraut, Renovate pro Dependency einen dedizierten Pull Request anlegen zu lassen, welcher anschließend durch ein Teammitglied überprüft und manuell gemerged werden musste. Dafür reicht in größten Teilen tatsächlich Renovates Default-Config.
Bei vier Node-Projekten mit reichlich Dependencies im Nacken wurde aus dieser unschuldigen Freude jedoch schnell nerviger Overhead anderer Art, da täglich mindestens 5 Pull Requests durchgeschaut, rebased und gemerged werden wollten – von der Warterei auf die lahme Pipeline mal abgesehen. Also wurden wir mutiger und gruppierten sämtliche Dependency-Updates (pro Projekt) in einen Pull Request, was diese Arbeit deutlich erleichterte, jedoch auch den Nachteil mit sich brachte, fehlgeschlagene Pipelines nicht mehr so einfach auf eine spezifische Dependency zurückführen zu können.
Die letzte Eskalationsstufe war nach ausreichendem Testen und Konfigurieren schließlich, gänzlich auf Pull Requests zu verzichten und Renovate täglich seine gebündelten Updates selbst in die Mainlines mergen zu lassen. In den letzten acht Monaten haben wir diese Entscheidung nicht einmal bereut – vor allem, weil unser geliebter Bot fehlerhafte Updates dann doch in Form von Pull Requests meldet, statt einfach Amok zu laufen.
Mit Erfolg zum eigenen Bot
Da dieser Blog-Artikel nur den Anspruch hat, unsere Leserschaft auf das Tool aufmerksam zu machen und nicht, eine Konfigurationsanleitung zu liefern, möchte ich für Detailfragen auf die Doku verweisen. Bevor ich mich also weiter auf dröge Theorie beziehe, werfe ich an dieser Stelle lieber einmal die Best-Ofs meiner Erfahrungen und Empfehlungen aus der Praxis in den Ring, die womöglich nützlich sein könnten.
- Machen Sie das, was ich in den letzten beiden Abschnitten gesagt habe.
- Räumen Sie initial erstmal selbst Ihre Dependencies auf, sonst eskaliert es. Dann können Sie loslegen.
- Starten Sie mit einem einzelnen Projekt, bestenfalls mit einem Fork. Letzteres, weil Renovate “stateful” ist, sich also merkt, an welchen Dependencies es schon dran war. Schließen wir einen Pull Request, heißt das für Renovate “wurde abgelehnt, ich versuch’s für diese Dependency-Version nicht nochmal”. Das wird beim Testen schnell nervig und ist mit Wegwerfprojekten leichter.
- Lagern Sie Renovate in ein eigenes Projekt aus, d.h. CI-Templates, CI-Settings und Konfiguration. Einerseits müllen damit nicht die anderen Projekte voll und andererseits haben Sie eine zentrale Stelle für alles. Die anderen Projekte können sich alles Notwendige von dort einbinden.
- Verwenden Sie einen dedizierten User. Damit gibt es eine konkrete Zuordnung zu allem, was Renovate betrifft und Berechtigungen können besser verwaltet werden.
- Lassen Sie das Ding so oft es geht laufen, d.h. mindestens einmal täglich. Das bedeutet zwar mehr Commits, dafür kommen diese aber auch in der Mainline an. Es kann sonst je nach Settings durchaus passieren, dass Renovate-PRs bzw. -Branches Ewigkeiten in einer Update- oder Rebase-Schleife gefangen sind.
- Schalten Sie Major-Updates ab. Diese werden durch Breaking Changes höchst wahrscheinlich Ihr Projekt zerstören und sollten daher manuell durchgeführt werden.
- Aktivieren Sie für Node-Projekte die Regel minimumReleaseAge und setzen Sie diese auf drei Tage. Über diesen Zeitraum können NPM-Pakete zurückgezogen werden.
- Haben Sie automatisierte Tests. Dependencies sind Teil Ihrer Software und können sie daher auch kaputt machen. Je öfter es Änderungen gibt, desto mehr wird Sie die QA hassen, wenn sie manuell durchtesten muss.
Den Rest können Sie selber nachlesen.
Wer nutzt Renovate?
Quelle: https://docs.renovatebot.com
Also sind alle meine Probleme weg?
Nein, tut mir leid. Renovate nimmt uns zwar eine Menge dependency-bezogener Aufwände ab, ist aber auch nicht die Lösung für alles.
Der erste Grund dafür ist recht simpel: Es ist zwar toll, wenn die Mainline glänzt, jedoch kennen wir auch alle das Phänomen, dass Software nur zwei Zustände kennt – in Entwicklung und Legacy. Spätestens mit dem Eintritt in Phase 2 wird die beneidenswerte Mainline also nicht mehr im Livebetrieb ankommen und das wäre furchtbar schade. Daher basteln Sie sich bestenfalls so schnell wie möglich einen Prozess für automatisierte Deployments, welcher Ihre Software spätestens bei Eintritt ins Rentenalter regelmäßig an die frische Luft begleitet. Voraussetzung dafür ist selbstverständlich eine ausreichende Abdeckung durch automatisierte Tests, aber die haben Sie im vorherigen Abschnitt ja schon implementiert.
Der zweite Grund für den fehlenden Heilandstatus Renovates liegt in der Natur der Open Source Community: Nicht alle Dependencies werden auch gepflegt und vor allem sind neueste Dependencies nicht automatisch fehlerfrei – die Suche nach Security-Fixes wird daher recht eintönig, wenn ein solcher einfach nicht kommen mag. Daher die letzte Empfehlung für heute: Binden Sie Ihre Projekte zusätzlich an einen Dependency Tracker an und lassen Sie sich von diesem informieren, sobald eine neue Lücke in freier Wildbahn gesichtet wurde. Die ISMS-Dudes, Ihre Kunden, die PR-Abteilung und Ihre Nerven werden es Ihnen danken.