Kurzfassung Die bisherigen Beiträge dieser Serie zeigen, wie ein TwinCAT-Agent zuverlässig arbeitet: Tools, Branches, Sperren und Restore. Sie beantworten jedoch noch nicht die Frage, wann aus einem erfolgreichen Build ein Paket wird, das andere Projekte nutzen dürfen. Wenn Build und Veröffentlichung im selben Job stattfinden, wird jeder erfolgreiche Kompiliervorgang schnell zur Auslieferung. Besser ist eine klare Trennung in drei Jobs: Build liefert schnelles Feedback und kann optional einen Snapshot veröffentlichen. Staging erstellt daraus einen versionierten Release-Kandidaten, der noch verworfen werden kann. Deploy übernimmt schließlich den bewussten Schritt, diesen Kandidaten für andere verfügbar zu machen. Genau diese Struktur verwenden wir für unser eigenes Framework und setzen sie auch bei Kunden ein, wenn CI/CD Bibliotheken zuverlässig ausliefern soll – und nicht nur dafür sorgen soll, dass sie kompiliert werden.
Dies ist der sechste und letzte Beitrag unserer Serie zu CI/CD für TwinCAT. Die vorherigen Artikel behandeln Build-Tooling, CLI-Verteilung mit Scoop, Branching-Strategien für Maschinensoftware, exklusives Sperren auf einer Windows-Farm und Abhängigkeitswiederherstellung mit Twinpack. Hier geht es darum, wie diese Komponenten in einer echten Release-Pipeline zusammenwirken.
Die Falle: Ein grüner Build gilt als „veröffentlicht“
Sobald Restore und Compile auf einem gesperrten Agenten funktionieren, liegt die nächste Versuchung nahe: zkmake build, .library archivieren und twinpack push im selben Job ausführen. Push auf main. Auf Grün warten. Bibliothek liegt auf dem Server. Fertig.
Das funktioniert. Bis zu dem Tag, an dem ein instabiler Test übersprungen wurde, eine falsche TwinCAT-Version verwendet wurde oder jemand nur „kompiliert das noch?“ meinte und versehentlich Version 1.4.0.87 an alle Projekte auslieferte, die diesen Branch verfolgen. Bei Maschinensoftware ist der Preis eines versehentlichen Publish kein Rollback-Banner. Es ist eine Bibliothek, die bereits im Restore eines anderen Projekts gelandet ist – oder schlimmer: auf einer Maschine mitten in der Inbetriebnahme.
Die Lösung ist kein längeres Build-Skript. Es ist eine andere Struktur: getrennte Jobs mit klaren Aufgaben.
Drei Jobs, drei Verantwortlichkeiten
| Job | Verantwortung | Zielgruppe |
|---|---|---|
| Build | Wiederverwendbarer, parametrisierter Job: schnelles Kompilieren bei Push/PR und die Einheit, die Staging über TwinCAT-Versionen und Patches aufteilt. Optional ein Snapshot. | Entwickler, PR-Reviewer; Staging als Aufrufer |
| Staging | Einen versionierten Release-Kandidaten erstellen: Versionierung, mehrfaches Ausführen des Build-Jobs, Dokumentation und weitere Nebenaufgaben, Artefakte speichern, standardmäßig noch nicht öffentlich. | Release-Verantwortliche, QA, Freigeber |
| Deploy | Ein bestimmtes Staging-Ergebnis auswählen und verfügbar machen: Twinpack-Push in die echte Konfiguration, Tags, Installer – alles, was bei euch „öffentlich“ bedeutet. | Berechtigte für die Auslieferung |
An den Namen liegt nichts Mystisches. Wichtig ist: Veröffentlichung ist kein Nebeneffekt des Kompilierens.
Genau dieses Muster nutzen wir für das Zeugwerk Framework mit Jenkins: ein parametrisierter Build-Job (auch für Snapshots), eine Staging-Pipeline, die diesen Job mehrfach mit verschiedenen Parametern aufruft und Kandidaten sammelt (und Deploy auslösen kann, wenn gewünscht), sowie ein Deploy-Job, der nur Artefakte aus einem ausgewählten Staging-Build kopiert und veröffentlicht.
So rufen die drei Jobs einander auf:
Build ist eine Job-Definition. Staging ruft sie mehrfach mit verschiedenen Parametern auf, zusätzlich zu Nebenjobs, die keine Kompilierungen sind. Deploy kompiliert nicht erneut; es macht öffentlich, was Staging bereits bereithält.
Build: Ein wiederverwendbarer Job, nicht nur die PR-Pipeline
Build ist ein parametrisierter Job, den andere Pipelines aufrufen können – nicht nur „das, was bei jedem Push läuft“.
Bei jedem Push und jedem PR möchte man dennoch eine schnelle Instanz davon: Tools per Scoop, Restore aus .Zeugwerk/config.json, Kompilieren, Tests der inneren Schleife, .library/.compiled-library für diesen Commit archivieren. Diese Instanz sollte kurz genug sein, dass Entwickler sie nicht ignorieren.
Derselbe Job wird später von Staging mehrfach mit anderen Parametern aufgerufen: TwinCAT-Version, Compiled-Library-Patch, Tests überspringen oder ausführen usw. Eine Implementierung, viele Aufrufe. Deshalb bleibt Build einfach, und die komplexe Matrix landet nicht im PR-Pfad.
Falls andere Projekte auf demselben Branch die Arbeit vor einem Release benötigen, kann ein erfolgreicher Build diese Artefakte zusätzlich als Twinpack-Snapshot-Konfiguration veröffentlichen – nicht als die Konfiguration, aus der Feldmaschinen wiederhergestellt werden. Ein Snapshot bedeutet: „Letzter erfolgreicher Build auf diesem Branch für diejenigen, die danach fragen.“ Es bedeutet nicht: „Hinter dieser Version stehen wir.“
Diese Trennung gilt für Release-Linien. Ein Maschinenprojekt auf release/1.x sollte standardmäßig freigegebene 1.x-Bibliotheken wiederherstellen. Wer einen Hotfix auf derselben Linie integriert, kann den Snapshot wählen. Die Pipeline erzwingt diese Wahl nicht für jeden, der nur ein grünes Kompilat wollte.
Staging: Einen Kandidaten orchestrieren, der noch verworfen werden kann
Staging ist der Ort, an dem ein Commit zum versionierten Kandidaten wird. Es ist vor allem ein Orchestrator, keine zweite Kopie des Build-Skripts.
In der Praxis:
- Versionierung: Die vierstellige TwinCAT-Version für diesen Kandidaten ableiten oder erhöhen. Staging vergibt die Nummer, die Deploy später auf Twinpack und Git-Tags überträgt. Ein PR-Build reicht eine Arbeitsversion; Staging ist das, was tatsächlich ausgeliefert werden könnte.
- Aufteilung: Den Build-Job parallel mehrfach ausführen: einmal pro TwinCAT-Version, einmal pro Patch (Source-Library vs. Compiled-Library), oft ein eigener Testdurchlauf, der denselben Job mit übersprungenem Kompilieren nutzt. Jedes Artefakt wird zurück in den Staging-Workspace geholt. So deckt ein Staging eine Matrix ab, ohne diese Matrix in jeden Entwickler-Push zu stopfen.
- Nebenaufgaben: Weitere Release-Arbeiten im selben Fenster ausführen: Changelog, Dokumentationsbuilds (
zkdoc/Parse-Jobs), Installer – alles, was sonst noch existieren muss, bevor Menschen einen Kandidaten prüfen. Das sind keine TwinCAT-Kompilierungen, gehört aber genauso zu „Ist das auslieferbar?“ - Identität: Aufzeichnen, was gebaut wurde: Commit-SHAs jedes Repos, das in den Kandidaten eingeflossen ist (
references.jsonoder gleichwertig). Deploy darf nicht neu aus „wasmaingerade ist“ bauen. Es muss dieses Staging-Ergebnis erneut veröffentlichen. - Speicherung: Die gesammelten Artefakte im CI-System archivieren. Noch nicht auf den öffentlichen Feed pushen.
Staging darf scheitern. Das ist der Sinn. Ein fehlgeschlagenes Staging ist günstig. Eine veröffentlichte fehlerhafte Bibliothek nicht.
Bei Erfolg hält Staging entweder an (Benachrichtigung, auf manuelle Freigabe warten) oder löst – bei erlaubter Policy für diesen Branch – Deploy mit einem expliziten Flag aus. In unseren Framework-Pipelines ist Deploy aus Staging ein Parameter, der auf main standardmäßig aktiviert ist, sonst deaktiviert. Andere Teams möchten auf jeder Linie einen manuellen „Promote“-Klick. Beides ist in Ordnung. Automatisches, stilles Veröffentlichen aus Staging ist wieder dieselbe Ein-Job-Falle – nur versteckter.
Deploy: Diesen Kandidaten verfügbar machen
Deploy ist absichtlich schlank und „unbequem“.
Es kompiliert PLC-Code nicht erneut, wenn es sich vermeiden lässt. Stattdessen:
- Wählt einen erfolgreichen Staging-Build aus (meist den letzten erfolgreichen auf diesem Branch oder eine gepinnte Build-Nummer).
- Kopiert dessen Artefakte und die protokollierten Revisionen.
- Veröffentlicht sie auf Twinpack unter der echten Konfiguration (Distribution, Debug – wie auch immer der Nicht-Snapshot-Kanal heißt), für den Branch, aus dem andere Projekte wiederherstellen.
- Taggt die Git-Repos, lädt Zips hoch, aktualisiert „latest“-Zeiger, benachrichtigt die Dokumentation: alles, was bei euch „für alle verfügbar“ bedeutet.
Geht in Deploy etwas schief, wird Deploy repariert oder gegen dieselben Staging-Artefakte erneut gestartet. Es wird kein neues Binary erstellt, indem erneut kompiliert und gehofft wird, dass es dem entspricht, was die QA getestet hat.
Hier gehört auch die Zugriffskontrolle hin. Nicht jeder, der auf feature/foo pushen darf, sollte Deploy auf release/1.x auslösen können. Jenkins-Berechtigungen, geschützte Branches und eine kurze Liste von Personen, die wissen, wann eine Bibliothek veröffentlicht werden darf, sind Teil des Designs – kein nachträglich hinzugefügtes Protokoll.
Wie der Rest der Serie hier einfließt
Diese Topologie funktioniert nur, wenn die vorherigen Beiträge keine offenen Fragen mehr lassen.
| Beitrag | Rolle in Build / Staging / Deploy |
|---|---|
| Tooling-Landschaft | Cloud oder Self-Hosted klären, bevor man drei Jenkins-Jobs entwirft, die niemand betreiben kann |
| Scoop | Dieselbe zkmake-/Twinpack-Version auf jedem Agenten in jeder Stufe |
| Branching | Staging und Deploy laufen pro Release-Linie; Snapshot und Freigegebenes folgen diesen Linien |
| Sperren | Die Aufteilung in Staging kollidiert ohne exklusive Agenten |
| Twinpack Restore | Jede Build-Instanz stellt aus der Projekt-Konfiguration wieder her; Deploy veröffentlicht, was andere wiederherstellen |
TcPkg bleibt Maschinen-Setup auf dem Agenten, nicht der Projekt-Publish-Pfad. Twinpack (oder euer privater NuGet-Feed) ist das, was Deploy aktualisiert, wenn „öffentlich“ bedeutet: „Andere PLC-Projekte können das wiederherstellen.“
Was ihr trotzdem entscheiden müsst
Die Struktur ist wiederverwendbar. Die Richtlinien nicht.
- Wann wird Deploy ausgelöst? Nur bei
main? Bei jedemrelease/*nach manueller Freigabe? Nie automatisch? - Wer nutzt Snapshots? Nur interne Schwester-Repos oder auch befreundete Kunden auf einem Pre-Release-Feed?
- Wie viel muss Staging beweisen? Nur Kompilieren reicht nicht für einen Kandidaten. Unit-Tests auf UMRT, statische Analyse, Multi-TC: Die Latte so hoch legen, wie der Schaden einer fehlerhaften Bibliothek teuer ist.
- Wer darf veröffentlichen? Benannte Personen, Freigabe im Chat, ein geschützter Jenkins-Job. Einmal festhalten.
Teams, die diese Entscheidungen überspringen, treffen sie meist unter Druck – mit einem wartenden Kunden.
Wo die Serie damit steht
Über sechs Beiträge hinweg zeigt sich der Weg: Klären, wie TwinCAT überhaupt gebaut wird, Tools und Abhängigkeiten ehrlich auf den Agenten bringen, so branchen, wie Maschinen im Feld tatsächlich leben, die Windows-Boxen sperren, damit parallele Arbeit sie nicht korrumpiert, und schließlich jeden erfolgreichen Kompiliervorgang nicht mehr als Release behandeln.
Das könnt ihr selbst auf Jenkins oder GitLab umsetzen. Viele Maschinenbauer tun das. Es ist auch ein großes Feld: Nodes, Sperren, Scoop-Tokens, Package-Server, Staging-Identität, Deploy-Berechtigungen. Die Teams, die dabei den Überblick behalten, widmen diesem Feld Leute oder hören auf, so zu tun, als wäre es ein Nebenprojekt.
On-Prem bedeutet nicht, bei null anzufangen. zkmake und der Rest der DevTools sind das, was Build- und Staging-Jobs tatsächlich ausführen: Restore über Twinpack, Kompilieren, Testen, Snapshot-Updates – dieselben CLIs, die wir per Scoop auf die Nodes bringen. Die Windows-Farm und die Jenkins-Topologie bleiben bei euch; ihr kauft das Tooling, das TwinCAT bereits kennt, statt einen privaten Automatisierungs-Stack zu pflegen. Das ist der Weg für air-gapped Betriebe und Teams, die die Infrastruktur bereits haben, aber nicht jedes Binary selbst verwalten möchten.
Zeugwerk CI/CD, die gemanagte Variante, ist das andere Extrem: TwinCAT-Build-Nodes als Service, ohne dass ihr die Farm überhaupt betreibt. Und wenn das fehlende Stück die Topologie selbst ist (welche Jobs, welche Gates, wie Twinpack-Publish verdrahtet wird), ist das die Beratungsarbeit, die wir mit Teams machen: Build / Staging / Deploy entwerfen und einen Publish-Schritt hinterlassen, der Absicht ist.
Der Vertrag ist in jedem Fall derselbe: Build beantwortet: „Funktioniert dieser Commit?“ Staging beantwortet: „Ist das ein Kandidat?“ Deploy beantwortet: „Dürfen alle anderen ihn nutzen?“
Wer die CLIs auf den Agenten, einen gemanagten Node oder die Jobs für Bibliotheken und Maschinenlinien einrichten möchte: einfach melden.
