Verweise der Form #123 im folgenden Text beziehen sich auf die GitHub-Nummerierung, nicht auf die Nummern in diesem Repo. Geschlossene Issues wurden nicht mitgezogen und sind weiterhin nur auf GitHub lesbar.
Repository, Workflows, Issues und Renovate ziehen von GitHub auf die Gitea-Instanz (JLab-Git) um. GitHub bleibt danach als Push-Spiegel ohne Action-Läufe bestehen.
Stand
Der Code ist bereits drüben und deckungsgleich: JLab-Git/main und github/main zeigen beide auf 757e4be. Das lokale Remote origin heißt inzwischen github, daneben existiert JLab-Git.
Alles andere fehlt. Aus der Repo-API der Instanz:
has_actions = false — keine CI, keine Publish-Pipeline
open_issues_count = 0 — alle offenen Issues liegen weiterhin nur auf GitHub
mirror = false, fork = false — ein echtes Repo, das Pushes und Issues annehmen kann
Entschieden
Das bestehende Gitea-Repo bleibt. Giteas eigene GitHub-Migration kommt damit nicht in Frage, sie legt das Repo selbst an.
Die Issues werden später über die API nachgezogen, nicht als Teil der ersten Stufen.
GitHub wird Push-Spiegel, Actions dort abgeschaltet.
Die Instanz darf Actions von github.com nachladen — actions/checkout, azure/setup-helm und das 1Password-Action müssen also nicht ersetzt werden.
Runner werden gerade eingerichtet.
Die Falle, die die Reihenfolge bestimmt
Spiegelt Gitea nach GitHub, während dort noch Actions laufen, löst jeder gespiegelte Push die 23 Publish-Workflows auf GitHub erneut aus. Die veröffentlichen ein zweites Mal in dieselbe Registry — und an dieser Instanz ist gemessen (siehe #160), dass ein Republish still angenommen wird, das Artefakt ersetzt und den Zeitstempel des ersten Uploads stehen lässt. Das Repo überschriebe sich also unbemerkt selbst.
Daraus folgt zwingend: erst CI auf Gitea nachweislich lauffähig, dann Actions auf GitHub abschalten, erst danach den Spiegel einschalten. Jede andere Reihenfolge lässt ein Fenster offen, in dem entweder keine CI existiert oder zwei gleichzeitig arbeiten.
Zu prüfen, bevor Workflows umgeschrieben werden
Zwei Fragen entscheiden über den Zuschnitt der Arbeit und sind zu messen, nicht anzunehmen:
Beherrscht Gitea 1.27 wiederverwendbare Workflows mit secrets: inherit? Daran hängt die gesamte Publish-Architektur: 23 dünne Workflows rufen publish-helm-chart.yml auf. Fällt das weg, ist es kein Anpassen, sondern ein Umbau.
Trägt ein Runner den Install-Test? Der startet einen k3d-Cluster im Runner; heute steht dafür ein Läufer mit 4 vCPU bereit. Ein zu kleiner Runner erzeugt einen sporadisch roten Test — die schlechteste aller Varianten, weil man ihm dann nicht mehr glaubt.
Beides gehört in einen Wegwerf-Workflow als Probelauf, bevor irgendein bestehender Workflow angefasst wird.
Stufen
Runner steht → Actions für das Repo einschalten, OP_CONNECT_TOKEN als Gitea-Secret anlegen.
Probelauf, der die beiden Fragen oben beantwortet.
Workflows anpassen, so weit der Probelauf es verlangt. Erst hier entsteht ein PR am Repo.
Branch-Schutz nachbauen: erforderliche Checks, Signaturpflicht für Commits. Die Merge-Queue gibt es in Gitea nicht — an ihre Stelle tritt Auto-Merge bei grünen Checks. Für einen einzelnen Maintainer ausreichend, aber der Ablauf ändert sich.
Renovate selbst betreiben: gegen Gitea gibt es keine App, das läuft als geplanter Lauf mit eigenem Token. Dort gilt doppelt, was in diesem Repo schon einmal teuer war — jede Regel im Trockenlauf belegen, Fehlschläge sind still.
Actions auf GitHub abschalten, dann den Spiegel einschalten.
CLAUDE.md umschreiben: der dokumentierte Arbeitsablauf ist vollständig um gh und GitHub-PRs herum formuliert und wird durch den Umzug falsch.
Vorwarnung zu den Issue-Nummern
Weil das bestehende Repo bleibt, müssen die Issues über die API neu angelegt werden, und Gitea vergibt die Nummern fortlaufend. Issues und Pull Requests teilen sich auf beiden Systemen einen Zähler — ein heutiges #218 landet also nicht wieder auf 218, es sei denn, vorher werden 217 Einträge erzeugt.
Dieses Repo ist dicht mit Verweisen durchsetzt: CLAUDE.md, README.md, mehrere NOTES.txt, Kommentare in values.yaml, dazu Commit-Botschaften, die sich gar nicht mehr ändern lassen. Nach einer Neuvergabe zeigen sie still auf das Falsche.
Mögliche Auswege — keiner davon entschieden, und die Entscheidung gehört vor den Beginn der Migration:
Nummern auffüllen, damit die alten Verweise weiter stimmen (unschön, aber dauerhaft korrekt)
eine Umsetzungstabelle alt → neu im Repo hinterlegen und die lebenden Dokumente umschreiben
Issues auf GitHub belassen und nur den Rest umziehen
Offen
Die Labels, auf die die Runner hören. Die Workflows fordern heute durchgehend ubuntu-latest.
> Migriert aus [`jk-cluster/helm-charts#225`](https://github.com/jk-cluster/helm-charts/issues/225) — urspruenglich angelegt von **jk-cluster-bot** am 2026-09-02.
>
> Verweise der Form `#123` im folgenden Text beziehen sich auf die **GitHub-Nummerierung**, nicht auf die Nummern in diesem Repo. Geschlossene Issues wurden nicht mitgezogen und sind weiterhin nur auf GitHub lesbar.
---
Repository, Workflows, Issues und Renovate ziehen von GitHub auf die Gitea-Instanz (JLab-Git) um. GitHub bleibt danach als **Push-Spiegel ohne Action-Läufe** bestehen.
## Stand
Der Code ist bereits drüben und deckungsgleich: `JLab-Git/main` und `github/main` zeigen beide auf `757e4be`. Das lokale Remote `origin` heißt inzwischen `github`, daneben existiert `JLab-Git`.
Alles andere fehlt. Aus der Repo-API der Instanz:
- `has_actions = false` — keine CI, keine Publish-Pipeline
- `open_issues_count = 0` — alle offenen Issues liegen weiterhin nur auf GitHub
- `mirror = false`, `fork = false` — ein echtes Repo, das Pushes und Issues annehmen kann
## Entschieden
- **Das bestehende Gitea-Repo bleibt.** Giteas eigene GitHub-Migration kommt damit nicht in Frage, sie legt das Repo selbst an.
- **Die Issues werden später über die API nachgezogen**, nicht als Teil der ersten Stufen.
- **GitHub wird Push-Spiegel**, Actions dort abgeschaltet.
- **Die Instanz darf Actions von github.com nachladen** — `actions/checkout`, `azure/setup-helm` und das 1Password-Action müssen also nicht ersetzt werden.
- Runner werden gerade eingerichtet.
## Die Falle, die die Reihenfolge bestimmt
Spiegelt Gitea nach GitHub, während dort noch Actions laufen, löst **jeder gespiegelte Push die 23 Publish-Workflows auf GitHub erneut aus**. Die veröffentlichen ein zweites Mal in dieselbe Registry — und an dieser Instanz ist gemessen (siehe #160), dass ein Republish still angenommen wird, das Artefakt ersetzt und den Zeitstempel des ersten Uploads stehen lässt. Das Repo überschriebe sich also unbemerkt selbst.
Daraus folgt zwingend: **erst CI auf Gitea nachweislich lauffähig, dann Actions auf GitHub abschalten, erst danach den Spiegel einschalten.** Jede andere Reihenfolge lässt ein Fenster offen, in dem entweder keine CI existiert oder zwei gleichzeitig arbeiten.
## Zu prüfen, bevor Workflows umgeschrieben werden
Zwei Fragen entscheiden über den Zuschnitt der Arbeit und sind zu **messen, nicht anzunehmen**:
1. **Beherrscht Gitea 1.27 wiederverwendbare Workflows mit `secrets: inherit`?** Daran hängt die gesamte Publish-Architektur: 23 dünne Workflows rufen `publish-helm-chart.yml` auf. Fällt das weg, ist es kein Anpassen, sondern ein Umbau.
2. **Trägt ein Runner den Install-Test?** Der startet einen k3d-Cluster im Runner; heute steht dafür ein Läufer mit 4 vCPU bereit. Ein zu kleiner Runner erzeugt einen sporadisch roten Test — die schlechteste aller Varianten, weil man ihm dann nicht mehr glaubt.
Beides gehört in einen **Wegwerf-Workflow als Probelauf**, bevor irgendein bestehender Workflow angefasst wird.
## Stufen
1. Runner steht → Actions für das Repo einschalten, `OP_CONNECT_TOKEN` als Gitea-Secret anlegen.
2. Probelauf, der die beiden Fragen oben beantwortet.
3. Workflows anpassen, so weit der Probelauf es verlangt. Erst hier entsteht ein PR am Repo.
4. Branch-Schutz nachbauen: erforderliche Checks, Signaturpflicht für Commits. **Die Merge-Queue gibt es in Gitea nicht** — an ihre Stelle tritt Auto-Merge bei grünen Checks. Für einen einzelnen Maintainer ausreichend, aber der Ablauf ändert sich.
5. Renovate selbst betreiben: gegen Gitea gibt es keine App, das läuft als geplanter Lauf mit eigenem Token. Dort gilt doppelt, was in diesem Repo schon einmal teuer war — jede Regel im Trockenlauf belegen, Fehlschläge sind still.
6. Actions auf GitHub abschalten, dann den Spiegel einschalten.
7. `CLAUDE.md` umschreiben: der dokumentierte Arbeitsablauf ist vollständig um `gh` und GitHub-PRs herum formuliert und wird durch den Umzug falsch.
## Vorwarnung zu den Issue-Nummern
Weil das bestehende Repo bleibt, müssen die Issues über die API neu angelegt werden, und Gitea vergibt die Nummern fortlaufend. Issues und Pull Requests teilen sich auf beiden Systemen **einen** Zähler — ein heutiges #218 landet also nicht wieder auf 218, es sei denn, vorher werden 217 Einträge erzeugt.
Dieses Repo ist dicht mit Verweisen durchsetzt: `CLAUDE.md`, `README.md`, mehrere `NOTES.txt`, Kommentare in `values.yaml`, dazu Commit-Botschaften, die sich gar nicht mehr ändern lassen. Nach einer Neuvergabe zeigen sie still auf das Falsche.
Mögliche Auswege — keiner davon entschieden, und die Entscheidung gehört **vor** den Beginn der Migration:
- Nummern auffüllen, damit die alten Verweise weiter stimmen (unschön, aber dauerhaft korrekt)
- eine Umsetzungstabelle alt → neu im Repo hinterlegen und die lebenden Dokumente umschreiben
- Issues auf GitHub belassen und nur den Rest umziehen
## Offen
Die Labels, auf die die Runner hören. Die Workflows fordern heute durchgehend `ubuntu-latest`.
Kommentar von jk-cluster-bot am 2026-09-04 (migriert aus GitHub).
Korrektur am Ausgangsbefund
Der Rumpf dieses Issues nennt has_actions = false für jlab-cluster/helm-charts. Das stimmt nicht mehr — heute gemessen über die Gitea-API:
has_actions = True
mirror = False
empty = False
Aufgefallen ist es der Nachbar-Session, die den Wert nachgeprüft hat, statt meine Auskunft zu übernehmen. Ob die Einheit zwischen dem 2. und dem 4. September eingeschaltet wurde oder ob meine erste Lesung falsch war, kann ich nicht belegen; updated_at steht unverändert auf dem 1. September, was aber nur Inhalts-Änderungen nachhält, keine Einstellungen.
Folge für Stufe 1: Die Actions-Einheit muss nicht mehr eingeschaltet werden. Es bleibt: OP_CONNECT_TOKEN als Gitea-Secret anlegen und der Probelauf.
Weiterhin offen ist die Runner-Frage. Dort lief nach Auskunft der Nachbar-Session noch nie ein Auftrag (total_count = 0), und ich kann die registrierten Runner nicht abfragen — das tea-Token bekommt „user should be the owner of the repo". Ob Runner registriert sind und auf welche Labels sie hören, muss der Maintainer nachsehen; die Workflows fordern heute durchgehend ubuntu-latest.
Nachtrag zur Wiederverwendbarkeit
Aus demselben Austausch: Es gibt Interesse, publish-helm-chart.yml aus einem anderen Repo heraus per uses: aufzurufen (konkret suer/suer-configurator auf derselben Gitea-Instanz). Dafür fehlen zwei Dinge, unabhängig vom Umzug:
Die Ziel-URL kommt ausschließlich aus op://GitHub-Actions/jlab-registry-access/charts-url. Ein fremder Aufrufer würde damit unter unserem Owner veröffentlichen, mit unserem Token und unserem Namen im Paket-Protokoll. Der saubere Schnitt ist ein Input für den 1Password-Item-Pfad, nicht für die URL — dann reisen username, password und charts-url gemeinsam und passen zueinander.
Der Schritt „Fetch chart dependencies" ruft ci/helm-dependencies.sh, das im aufrufenden Repo nicht existiert. Ein Guard sollte dabei nicht bloß überspringen: fehlt das Skript und deklariert der Chart Abhängigkeiten, gehört ein lauter Abbruch mit genau dieser Begründung hin — sonst scheitert zwei Schritte später helm package mit einer Meldung, von der niemand auf die Ursache kommt.
Beides ist nicht entschieden und gehört ohnehin erst nach diesen Umzug.
> Kommentar von **jk-cluster-bot** am 2026-09-04 (migriert aus GitHub).
---
## Korrektur am Ausgangsbefund
Der Rumpf dieses Issues nennt `has_actions = false` für `jlab-cluster/helm-charts`. **Das stimmt nicht mehr** — heute gemessen über die Gitea-API:
```
has_actions = True
mirror = False
empty = False
```
Aufgefallen ist es der Nachbar-Session, die den Wert nachgeprüft hat, statt meine Auskunft zu übernehmen. Ob die Einheit zwischen dem 2. und dem 4. September eingeschaltet wurde oder ob meine erste Lesung falsch war, kann ich nicht belegen; `updated_at` steht unverändert auf dem 1. September, was aber nur Inhalts-Änderungen nachhält, keine Einstellungen.
**Folge für Stufe 1:** Die Actions-Einheit muss nicht mehr eingeschaltet werden. Es bleibt: `OP_CONNECT_TOKEN` als Gitea-Secret anlegen und der Probelauf.
**Weiterhin offen ist die Runner-Frage.** Dort lief nach Auskunft der Nachbar-Session noch nie ein Auftrag (`total_count = 0`), und ich kann die registrierten Runner nicht abfragen — das `tea`-Token bekommt „user should be the owner of the repo". Ob Runner registriert sind und auf welche Labels sie hören, muss der Maintainer nachsehen; die Workflows fordern heute durchgehend `ubuntu-latest`.
## Nachtrag zur Wiederverwendbarkeit
Aus demselben Austausch: Es gibt Interesse, `publish-helm-chart.yml` aus einem anderen Repo heraus per `uses:` aufzurufen (konkret `suer/suer-configurator` auf derselben Gitea-Instanz). Dafür fehlen zwei Dinge, unabhängig vom Umzug:
1. **Die Ziel-URL kommt ausschließlich aus `op://GitHub-Actions/jlab-registry-access/charts-url`.** Ein fremder Aufrufer würde damit unter *unserem* Owner veröffentlichen, mit *unserem* Token und unserem Namen im Paket-Protokoll. Der saubere Schnitt ist ein Input für den **1Password-Item-Pfad**, nicht für die URL — dann reisen `username`, `password` und `charts-url` gemeinsam und passen zueinander.
2. **Der Schritt „Fetch chart dependencies" ruft `ci/helm-dependencies.sh`**, das im aufrufenden Repo nicht existiert. Ein Guard sollte dabei nicht bloß überspringen: fehlt das Skript **und** deklariert der Chart Abhängigkeiten, gehört ein lauter Abbruch mit genau dieser Begründung hin — sonst scheitert zwei Schritte später `helm package` mit einer Meldung, von der niemand auf die Ursache kommt.
Beides ist nicht entschieden und gehört ohnehin erst nach diesen Umzug.
Kommentar von jk-cluster-bot am 2026-09-04 (migriert aus GitHub).
Runner-Labels — und eine Falle, die sie mitbringen
Die Nachbar-Session (suer/suer-configurator, dieselbe Gitea-Instanz) berichtet zwei registrierte Labels:
ubuntu-latest — ohne Docker-Socket
ubuntu-docker — mit Docker-Socket
Nicht von mir gemessen. Mein tea-Token bekommt auf /repos/jlab-cluster/helm-charts/actions/runners ein „user should be the owner of the repo", ihres bei jlab-cluster schon auf Repo-Variablen ein 403. Der Maintainer kann es in einem Blick bestätigen; bis dahin ist es eine Fremdauskunft, keine Messung.
Wenn es zutrifft: das Label lügt nicht, aber es bedeutet etwas anderes
Alle unsere Workflows fordern runs-on: ubuntu-latest. Auf GitHub sind das gehostete Runner mit Docker. Auf dieser Instanz ist dasselbe Label ein Runner ohne Docker-Socket.
Für Lint, Paketieren, den Quantity-Render-Test und das Hochladen ist das folgenlos — die brauchen nur Helm und curl.
Der Install-Test bricht. Er legt einen k3d-Cluster im Runner an; ohne Docker-Socket scheitert der Schritt „Create k3d cluster". Und die Meldung wird auf k3d zeigen, nicht auf den Runner — niemand kommt von dort auf ein fehlendes Label. Das ist genau die Sorte stiller Fehlleitung, die dieses Repo sonst abstellt: der Workflow ist gültig, der Job startet, und die Ursache steht nicht in der Fehlermeldung.
Folge für den Plan
Stufe 3 bekommt einen konkreten Punkt: die Jobs, die einen Docker-Socket brauchen, müssen auf ubuntu-docker umgestellt werden — nach heutigem Stand nur der Install-Test. Das ist beim Probelauf (Stufe 2) mitzuprüfen und nicht anzunehmen:
build-chart (helm lint/package) — vermutlich kein Docker
Quantity-Render-Test — reines helm template, kein Docker
Trivy-Config-Scan — scannt gerenderte Dateien, vermutlich kein Docker
Install-Test — braucht Docker, sicher
Wer das umstellt, sollte den Grund als Kommentar an das runs-on schreiben. Ein ubuntu-docker ohne Erklärung sieht auf GitHub wie ein Fehler aus und wird beim nächsten Aufräumen „korrigiert".
Was sich dadurch am Aufwand ändert
Stufe 1 schrumpft weiter: die Actions-Einheit ist an (siehe Kommentar oben), das Label ubuntu-latest existiert und ist bedient. Es bleibt das Gitea-Secret für OP_CONNECT_TOKEN und der Probelauf — der jetzt eine Frage mehr zu beantworten hat.
> Kommentar von **jk-cluster-bot** am 2026-09-04 (migriert aus GitHub).
---
## Runner-Labels — und eine Falle, die sie mitbringen
Die Nachbar-Session (`suer/suer-configurator`, dieselbe Gitea-Instanz) berichtet zwei registrierte Labels:
- **`ubuntu-latest`** — **ohne** Docker-Socket
- **`ubuntu-docker`** — **mit** Docker-Socket
**Nicht von mir gemessen.** Mein `tea`-Token bekommt auf `/repos/jlab-cluster/helm-charts/actions/runners` ein „user should be the owner of the repo", ihres bei `jlab-cluster` schon auf Repo-Variablen ein 403. Der Maintainer kann es in einem Blick bestätigen; bis dahin ist es eine Fremdauskunft, keine Messung.
## Wenn es zutrifft: das Label lügt nicht, aber es bedeutet etwas anderes
Alle unsere Workflows fordern `runs-on: ubuntu-latest`. Auf GitHub sind das gehostete Runner **mit** Docker. Auf dieser Instanz ist dasselbe Label ein Runner **ohne** Docker-Socket.
Für Lint, Paketieren, den Quantity-Render-Test und das Hochladen ist das folgenlos — die brauchen nur Helm und curl.
**Der Install-Test bricht.** Er legt einen k3d-Cluster im Runner an; ohne Docker-Socket scheitert der Schritt „Create k3d cluster". Und die Meldung wird auf k3d zeigen, nicht auf den Runner — niemand kommt von dort auf ein fehlendes Label. Das ist genau die Sorte stiller Fehlleitung, die dieses Repo sonst abstellt: der Workflow ist gültig, der Job startet, und die Ursache steht nicht in der Fehlermeldung.
## Folge für den Plan
**Stufe 3 bekommt einen konkreten Punkt:** die Jobs, die einen Docker-Socket brauchen, müssen auf `ubuntu-docker` umgestellt werden — nach heutigem Stand nur der Install-Test. Das ist beim Probelauf (Stufe 2) mitzuprüfen und **nicht anzunehmen**:
- `build-chart` (helm lint/package) — vermutlich kein Docker
- Quantity-Render-Test — reines `helm template`, kein Docker
- Trivy-Config-Scan — scannt gerenderte Dateien, vermutlich kein Docker
- **Install-Test — braucht Docker, sicher**
Wer das umstellt, sollte den Grund als Kommentar an das `runs-on` schreiben. Ein `ubuntu-docker` ohne Erklärung sieht auf GitHub wie ein Fehler aus und wird beim nächsten Aufräumen „korrigiert".
## Was sich dadurch am Aufwand ändert
Stufe 1 schrumpft weiter: die Actions-Einheit ist an (siehe Kommentar oben), das Label `ubuntu-latest` existiert und ist bedient. Es bleibt das Gitea-Secret für `OP_CONNECT_TOKEN` und der Probelauf — der jetzt eine Frage mehr zu beantworten hat.
Kommentar von jk-cluster-bot am 2026-09-14 (migriert aus GitHub).
Stand korrigiert, und ein Plan für die Vorbereitung
Die Ursache der neun roten Läufe war das fehlende OP_CONNECT_TOKEN, nicht die wiederverwendbaren Workflows. Der Maintainer hat das Secret angelegt; publish-voucher-vault Lauf 9 ist grün. Meine Vermutung im vorigen Kommentar — kein Job-Start, keine Schritte, also vermutlich ein nicht auflösbares uses: — war falsch. Gitea fällt offenbar schon bei der Job-Erzeugung aus, wenn ein über secrets: inherit erwartetes Secret fehlt, statt den Job zu starten und am 1Password-Schritt zu scheitern.
Frage 1 aus dem Rumpf ist damit positiv beantwortet: Gitea beherrscht wiederverwendbare Workflows mit secrets: inherit. Die Publish-Architektur aus 23 dünnen Hüllen überlebt den Umzug unverändert.
Frage 2 ist unberührt. Gelaufen sind ausschließlich Publish-Workflows. validate-charts.yml ist on: pull_request, und auf Gitea gibt es bislang keinen einzigen Pull Request — ob ein Runner den Install-Test mit seinem k3d-Cluster trägt, ist weiterhin ungemessen. Das ist der Gatepunkt für alles Weitere.
Ein Fenster ist jetzt offen
Seit die Gitea-Pipeline funktioniert, veröffentlichen beide Seiten. Solange GitHub-Actions aktiv sind und Commits nach Gitea gelangen, publizieren zwei CIs in dieselbe Registry — und an dieser Instanz ist gemessen (#160), dass ein Republish still angenommen wird, das Artefakt ersetzt und den Zeitstempel des ersten Uploads stehen lässt.
Solange dieses Fenster offen ist, gilt: nicht nach Gitea pushen, außer für den Probelauf unten. Der Schaden ist heute gering, weil beide Seiten denselben Inhalt bauen, aber die Eigenschaft, auf die man sich dabei verlässt, ist nirgends zugesichert.
Nebenbefund aus derselben Messung: Der Push vom 14.09. hat auf Gitea sechs Publish-Workflows für einen Commit ausgelöst, der keinen Chart anfasst (der Merge von #240). Auf GitHub feuerte für denselben Commit korrekt keiner. Die plausible Erklärung ist ein Nachhol-Push über mehrere Commits, bei dem der paths:-Filter über die ganze Spanne greift. Genau das entsteht beim Einschalten des Spiegels — ein weiterer Grund, die Reihenfolge aus dem Rumpf nicht aufzuweichen.
Vorbereitung — was jetzt gebaut wird
Probelauf zuerst, Anpassung danach. Ein Wegwerf-PR auf Gitea bringt validate-charts.yml zum Laufen und beantwortet Frage 2. Welche Jobs einen Docker-Socket brauchen, wird dabei gemessen, nicht angenommen — Vermutung bisher: nur der Install-Test, während Build, Quantity-Render-Test und Trivy ohne auskommen.
Install-Test auf ubuntu-docker, sofern der Probelauf das bestätigt, mit der Begründung als Kommentar direkt am runs-on. Ohne Kommentar sieht das Label auf GitHub wie ein Fehler aus und wird beim nächsten Aufräumen „korrigiert".
Renovate im Eigenbetrieb als geplanter Lauf mit eigenem Token. Jede Regel im Trockenlauf belegen — Fehlschläge sind still, und das war in diesem Repo schon zweimal teuer.
CLAUDE.md und README.md auf den neuen Ablauf umschreiben: Gitea statt GitHub, Auto-Merge bei grünen Checks statt Merge-Queue.
Skript für die Issue-Migration über die API, lauffähig aber noch nicht ausgeführt.
Was beim Maintainer bleibt
Drei Schritte sind nicht delegierbar, zwei davon weil mein Token dort 403 bekommt:
Branch-Schutz und Signaturpflicht auf dem Gitea-Repo einrichten.
Runner-Labels bestätigen — weiterhin Fremdauskunft.
GitHub-Actions abschalten, danach den Spiegel einschalten. In dieser Reihenfolge, und erst wenn der Probelauf grün ist.
> Kommentar von **jk-cluster-bot** am 2026-09-14 (migriert aus GitHub).
---
## Stand korrigiert, und ein Plan für die Vorbereitung
**Die Ursache der neun roten Läufe war das fehlende `OP_CONNECT_TOKEN`, nicht die wiederverwendbaren Workflows.** Der Maintainer hat das Secret angelegt; `publish-voucher-vault` Lauf 9 ist grün. Meine Vermutung im vorigen Kommentar — kein Job-Start, keine Schritte, also vermutlich ein nicht auflösbares `uses:` — war falsch. Gitea fällt offenbar schon bei der Job-Erzeugung aus, wenn ein über `secrets: inherit` erwartetes Secret fehlt, statt den Job zu starten und am 1Password-Schritt zu scheitern.
**Frage 1 aus dem Rumpf ist damit positiv beantwortet:** Gitea beherrscht wiederverwendbare Workflows mit `secrets: inherit`. Die Publish-Architektur aus 23 dünnen Hüllen überlebt den Umzug unverändert.
**Frage 2 ist unberührt.** Gelaufen sind ausschließlich Publish-Workflows. `validate-charts.yml` ist `on: pull_request`, und auf Gitea gibt es bislang keinen einzigen Pull Request — ob ein Runner den Install-Test mit seinem k3d-Cluster trägt, ist weiterhin ungemessen. Das ist der Gatepunkt für alles Weitere.
## Ein Fenster ist jetzt offen
Seit die Gitea-Pipeline funktioniert, veröffentlichen **beide Seiten**. Solange GitHub-Actions aktiv sind und Commits nach Gitea gelangen, publizieren zwei CIs in dieselbe Registry — und an dieser Instanz ist gemessen (#160), dass ein Republish still angenommen wird, das Artefakt ersetzt und den Zeitstempel des ersten Uploads stehen lässt.
Solange dieses Fenster offen ist, gilt: **nicht nach Gitea pushen**, außer für den Probelauf unten. Der Schaden ist heute gering, weil beide Seiten denselben Inhalt bauen, aber die Eigenschaft, auf die man sich dabei verlässt, ist nirgends zugesichert.
Nebenbefund aus derselben Messung: Der Push vom 14.09. hat auf Gitea **sechs** Publish-Workflows für einen Commit ausgelöst, der **keinen** Chart anfasst (der Merge von #240). Auf GitHub feuerte für denselben Commit korrekt keiner. Die plausible Erklärung ist ein Nachhol-Push über mehrere Commits, bei dem der `paths:`-Filter über die ganze Spanne greift. Genau das entsteht beim Einschalten des Spiegels — ein weiterer Grund, die Reihenfolge aus dem Rumpf nicht aufzuweichen.
## Vorbereitung — was jetzt gebaut wird
1. **Probelauf zuerst, Anpassung danach.** Ein Wegwerf-PR auf Gitea bringt `validate-charts.yml` zum Laufen und beantwortet Frage 2. Welche Jobs einen Docker-Socket brauchen, wird dabei **gemessen**, nicht angenommen — Vermutung bisher: nur der Install-Test, während Build, Quantity-Render-Test und Trivy ohne auskommen.
2. **Install-Test auf `ubuntu-docker`**, sofern der Probelauf das bestätigt, mit der Begründung als Kommentar direkt am `runs-on`. Ohne Kommentar sieht das Label auf GitHub wie ein Fehler aus und wird beim nächsten Aufräumen „korrigiert".
3. **Renovate im Eigenbetrieb** als geplanter Lauf mit eigenem Token. Jede Regel im Trockenlauf belegen — Fehlschläge sind still, und das war in diesem Repo schon zweimal teuer.
4. **`CLAUDE.md` und `README.md`** auf den neuen Ablauf umschreiben: Gitea statt GitHub, Auto-Merge bei grünen Checks statt Merge-Queue.
5. **Skript für die Issue-Migration** über die API, lauffähig aber noch nicht ausgeführt.
## Was beim Maintainer bleibt
Drei Schritte sind nicht delegierbar, zwei davon weil mein Token dort 403 bekommt:
- **Branch-Schutz und Signaturpflicht** auf dem Gitea-Repo einrichten.
- **Runner-Labels bestätigen** — weiterhin Fremdauskunft.
- **GitHub-Actions abschalten, danach den Spiegel einschalten.** In dieser Reihenfolge, und erst wenn der Probelauf grün ist.
Kommentar von jk-cluster-bot am 2026-09-14 (migriert aus GitHub).
Probelauf auf Gitea — gemessen, nicht übernommen
Ein Wegwerf-PR auf der Instanz (vier Runden, danach geschlossen und Branch gelöscht) hat validate-charts.yml dort erstmals zum Laufen gebracht. Vorbereitung dazu in #253.
Frage 1 des Rumpfes war schon beantwortet. Frage 2 ist es jetzt auch, und sie hat eine unerwartete dritte Antwort mitgebracht.
Läuft der Workflow an?
Ja. on: pull_request greift, die Jobs werden erzeugt, needs, strategy.matrix, if, fromJSON, GITHUB_OUTPUT, GITHUB_STEP_SUMMARY und actions/checkout@v7 mit fetch-depth: 0 verhalten sich wie auf GitHub. origin/${{ github.base_ref }} existiert im Checkout, die Chart-Erkennung funktioniert unverändert.
Welche Jobs brauchen einen Docker-Socket?
Nur der Install-Test. Die Vermutung aus dem vorigen Kommentar trifft zu, hier mit Belegen:
Job
Label
Ergebnis
Docker
detect-changed-charts
ubuntu-latest
grün, 3 s
nein
build-chart (lint, package, Version)
ubuntu-latest
grün, 4 s
nein
quantity-render (alle Charts)
ubuntu-latest
grün, 8 s
nein
trivy-config
ubuntu-latest
grün (nach dem Fix unten)
nein
install-test
ubuntu-latest
rot, Schritt „Create k3d cluster"
ja
Die Meldung auf ubuntu-latest lautet wörtlich:
Failed Cluster Preparation: Failed Network Preparation: failed to create cluster network:
failed to check for duplicate docker networks: docker failed to list networks:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Sie zeigt auf k3d, nie auf das Runner-Label — wie vorhergesagt.
Nebenbefund zur Labelfrage: Die Runner sind jetzt indirekt belegt, ohne actions/runners (dort weiterhin 403). Aus den gelaufenen Jobs: jlab-git-actions-runner-0 bedient ubuntu-latest, jlab-git-actions-docker-runner-0 bedient ubuntu-docker. Die Fremdauskunft war richtig.
Trägt der Runner den k3d-Cluster? Nein — und das ist der neue Gatepunkt.
ubuntu-docker hat reichlich Eisen: 8 vCPU, 64 GB RAM, Docker 29.5.2. k3d kommt dort weit — Netz angelegt, rancher/k3s und k3d-proxy geladen, Node erzeugt, LoadBalancer erzeugt. Dann:
Failed to start server k3d-install-test-server-0: Node k3d-install-test-server-0 failed to get ready:
error waiting for log line `k3s is up and running` ... node ... is running=true in status=restarting
Mit --no-rollback nachgesehen: docker ps -a zeigt Restarting (1), und docker logs ist leer — der Einstiegspunkt stirbt sofort, ohne eine Zeile. Die Ursache steht im docker info desselben Laufs:
Cgroup Driver: none
Cgroup Version: 2
Security Options: rootless
Docker Root Dir: /home/rootless/.local/share/docker
WARNING: Running in rootless-mode without cgroups. Systemd is required to enable cgroups in rootless-mode.
Das Docker hinter ubuntu-docker ist ein rootless Docker-in-Docker ohne Cgroup-Delegation. k3s braucht Cgroups, um überhaupt zu starten. ubuntu-docker ist also notwendig, aber nicht hinreichend.
Damit ist genau die Sorte Runner entstanden, vor der der Rumpf warnt — nur schlimmer als „sporadisch rot": er ist immer rot, und die Meldung nennt weder Cgroups noch rootless. Wer nur die k3d-Zeile liest, sucht am falschen Ende.
Für den Maintainer, nicht delegierbar: dem DinD Cgroup-Delegation geben (rootful, oder rootless mit systemd-Delegation). Erst danach ist Frage 3 beantwortbar — auch die Laufzeit des Install-Tests dort ist bis dahin ungemessen.
Dritter Befund, im Plan nicht vorgesehen: aquasecurity/setup-trivy ist auf Gitea unbenutzbar
trivy-config war in Runde 1 rot, ohne jeden Docker-Bezug. Die Action führt intern actions/checkout gegen aquasecurity/trivy auf github.com aus und übergibt dafür github.token — auf Gitea ist das ein Gitea-Token. github.com antwortet 401, checkout wiederholt zweimal und endet mit:
fatal: could not read Username for 'https://github.com': terminal prompts disabled
Eine Meldung, die weder die Action noch das Token nennt. Dazu: ihr actions/cache-Schritt ist auf der Instanz tot (Cache action is only supported on GHES version >= 3.5), und das Nachladen der verschachtelten Actions kostete 4 min 30 s, bevor der Job anfing. #253 ersetzt sie durch den Release-Tarball; damit ist trivy-config auf ubuntu-latest grün.
Die verallgemeinerbare Regel: Actions von github.com nachladen funktioniert (anonym, und actions/checkout, azure/setup-helm und das 1Password-Action brauchen keinen Ersatz). Was bricht, ist eine Action, die sich selbst gegen github.com authentifiziert.
Vierter Befund, wichtig für Stufe 4 (Branch-Schutz)
Gitea expandiert ${{ matrix.chart }} im Job-name:nicht, sondern setzt den Matrix-Ausdruck ein. Der Commit-Status heißt dort:
und alle Zweige einer Matrix teilen sich diesen einen Kontext. Folgen: ein Pflicht-Check pro Chart ist auf Gitea nicht einrichtbar, und die Namen der Pflicht-Checks lassen sich nicht von GitHub übernehmen — sie müssen von einem echten Lauf abgelesen werden. (Die Probe hatte nur einen Matrix-Zweig; ob sich zwei Zweige gegenseitig im Status überschreiben, ist damit offen.)
Was runs-on daraus macht
Ein blankes runs-on: ubuntu-docker wäre auf GitHub ein Bremsklotz: Das Label existiert dort nicht, also bliebe jeder Chart-PR unbegrenzt in der Warteschlange, ohne Fehlermeldung, solange dieses Repo noch auf github.com baut. #253 setzt deshalb
Gemessen: Gitea wertet den Ausdruck aus — der Probe-Job wurde vom Docker-Runner mit labels: ["ubuntu-docker"] genommen. Begründung steht als Kommentar direkt an der Zeile, samt dem Hinweis, dass sie nicht „korrigiert" werden darf.
Stand der Stufen
Stufe 2 ist abgeschlossen, mit einem Ergebnis mehr als erwartet.
Stufe 3 liegt in #253 (Workflow-Anpassungen, Renovate im Eigenbetrieb mit belegten Trockenläufen, CLAUDE.md/README.md, Issue-Migrationsskript — lauffähig, nicht ausgeführt).
Neu vor Stufe 6: das Cgroup-Problem des Docker-Runners. Solange es besteht, gibt es auf Gitea keinen Install-Test — und der ist es, der Renovates Automerge absichert. GitHub-Actions abzuschalten, bevor das gelöst ist, nähme dem Automerge sein Netz.
> Kommentar von **jk-cluster-bot** am 2026-09-14 (migriert aus GitHub).
---
## Probelauf auf Gitea — gemessen, nicht übernommen
Ein Wegwerf-PR auf der Instanz (vier Runden, danach geschlossen und Branch gelöscht) hat `validate-charts.yml` dort erstmals zum Laufen gebracht. Vorbereitung dazu in #253.
**Frage 1 des Rumpfes** war schon beantwortet. **Frage 2 ist es jetzt auch, und sie hat eine unerwartete dritte Antwort mitgebracht.**
### Läuft der Workflow an?
Ja. `on: pull_request` greift, die Jobs werden erzeugt, `needs`, `strategy.matrix`, `if`, `fromJSON`, `GITHUB_OUTPUT`, `GITHUB_STEP_SUMMARY` und `actions/checkout@v7` mit `fetch-depth: 0` verhalten sich wie auf GitHub. `origin/${{ github.base_ref }}` existiert im Checkout, die Chart-Erkennung funktioniert unverändert.
### Welche Jobs brauchen einen Docker-Socket?
**Nur der Install-Test.** Die Vermutung aus dem vorigen Kommentar trifft zu, hier mit Belegen:
| Job | Label | Ergebnis | Docker |
|---|---|---|---|
| `detect-changed-charts` | `ubuntu-latest` | grün, 3 s | nein |
| `build-chart` (lint, package, Version) | `ubuntu-latest` | grün, 4 s | nein |
| `quantity-render` (alle Charts) | `ubuntu-latest` | grün, 8 s | nein |
| `trivy-config` | `ubuntu-latest` | grün (nach dem Fix unten) | nein |
| `install-test` | `ubuntu-latest` | **rot**, Schritt „Create k3d cluster" | **ja** |
Die Meldung auf `ubuntu-latest` lautet wörtlich:
```
Failed Cluster Preparation: Failed Network Preparation: failed to create cluster network:
failed to check for duplicate docker networks: docker failed to list networks:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
```
Sie zeigt auf k3d, nie auf das Runner-Label — wie vorhergesagt.
Nebenbefund zur Labelfrage: Die Runner sind jetzt indirekt belegt, ohne `actions/runners` (dort weiterhin 403). Aus den gelaufenen Jobs: `jlab-git-actions-runner-0` bedient `ubuntu-latest`, `jlab-git-actions-docker-runner-0` bedient `ubuntu-docker`. Die Fremdauskunft war richtig.
### Trägt der Runner den k3d-Cluster? **Nein — und das ist der neue Gatepunkt.**
`ubuntu-docker` hat reichlich Eisen: 8 vCPU, 64 GB RAM, Docker 29.5.2. k3d kommt dort weit — Netz angelegt, `rancher/k3s` und `k3d-proxy` geladen, Node erzeugt, LoadBalancer erzeugt. Dann:
```
Failed to start server k3d-install-test-server-0: Node k3d-install-test-server-0 failed to get ready:
error waiting for log line `k3s is up and running` ... node ... is running=true in status=restarting
```
Mit `--no-rollback` nachgesehen: `docker ps -a` zeigt `Restarting (1)`, und **`docker logs` ist leer** — der Einstiegspunkt stirbt sofort, ohne eine Zeile. Die Ursache steht im `docker info` desselben Laufs:
```
Cgroup Driver: none
Cgroup Version: 2
Security Options: rootless
Docker Root Dir: /home/rootless/.local/share/docker
WARNING: Running in rootless-mode without cgroups. Systemd is required to enable cgroups in rootless-mode.
```
Das Docker hinter `ubuntu-docker` ist ein **rootless Docker-in-Docker ohne Cgroup-Delegation**. k3s braucht Cgroups, um überhaupt zu starten. **`ubuntu-docker` ist also notwendig, aber nicht hinreichend.**
Damit ist genau die Sorte Runner entstanden, vor der der Rumpf warnt — nur schlimmer als „sporadisch rot": er ist *immer* rot, und die Meldung nennt weder Cgroups noch rootless. Wer nur die k3d-Zeile liest, sucht am falschen Ende.
**Für den Maintainer, nicht delegierbar:** dem DinD Cgroup-Delegation geben (rootful, oder rootless mit systemd-Delegation). Erst danach ist Frage 3 beantwortbar — auch die Laufzeit des Install-Tests dort ist bis dahin ungemessen.
### Dritter Befund, im Plan nicht vorgesehen: `aquasecurity/setup-trivy` ist auf Gitea unbenutzbar
`trivy-config` war in Runde 1 rot, **ohne jeden Docker-Bezug**. Die Action führt intern `actions/checkout` gegen `aquasecurity/trivy` auf github.com aus und übergibt dafür `github.token` — auf Gitea ist das ein *Gitea*-Token. github.com antwortet 401, checkout wiederholt zweimal und endet mit:
```
fatal: could not read Username for 'https://github.com': terminal prompts disabled
```
Eine Meldung, die weder die Action noch das Token nennt. Dazu: ihr `actions/cache`-Schritt ist auf der Instanz tot (`Cache action is only supported on GHES version >= 3.5`), und das Nachladen der verschachtelten Actions kostete **4 min 30 s**, bevor der Job anfing. #253 ersetzt sie durch den Release-Tarball; damit ist `trivy-config` auf `ubuntu-latest` grün.
Die verallgemeinerbare Regel: Actions von github.com **nachladen** funktioniert (anonym, und `actions/checkout`, `azure/setup-helm` und das 1Password-Action brauchen keinen Ersatz). Was bricht, ist eine Action, die sich **selbst** gegen github.com authentifiziert.
### Vierter Befund, wichtig für Stufe 4 (Branch-Schutz)
Gitea expandiert `${{ matrix.chart }}` im Job-`name:` **nicht**, sondern setzt den Matrix-*Ausdruck* ein. Der Commit-Status heißt dort:
```
Validate Helm Charts / Install-test ${{ fromJSON(needs.detect-changed-charts.outputs.install-charts) }} (pull_request)
```
und **alle Zweige einer Matrix teilen sich diesen einen Kontext**. Folgen: ein Pflicht-Check *pro Chart* ist auf Gitea nicht einrichtbar, und die Namen der Pflicht-Checks lassen sich nicht von GitHub übernehmen — sie müssen von einem echten Lauf abgelesen werden. (Die Probe hatte nur einen Matrix-Zweig; ob sich zwei Zweige gegenseitig im Status überschreiben, ist damit offen.)
### Was `runs-on` daraus macht
Ein blankes `runs-on: ubuntu-docker` wäre auf GitHub ein Bremsklotz: Das Label existiert dort nicht, also bliebe jeder Chart-PR **unbegrenzt in der Warteschlange**, ohne Fehlermeldung, solange dieses Repo noch auf github.com baut. #253 setzt deshalb
```yaml
runs-on: ${{ github.server_url == 'https://github.com' && 'ubuntu-latest' || 'ubuntu-docker' }}
```
Gemessen: Gitea wertet den Ausdruck aus — der Probe-Job wurde vom Docker-Runner mit `labels: ["ubuntu-docker"]` genommen. Begründung steht als Kommentar direkt an der Zeile, samt dem Hinweis, dass sie nicht „korrigiert" werden darf.
### Stand der Stufen
- **Stufe 2 ist abgeschlossen**, mit einem Ergebnis mehr als erwartet.
- **Stufe 3** liegt in #253 (Workflow-Anpassungen, Renovate im Eigenbetrieb mit belegten Trockenläufen, `CLAUDE.md`/`README.md`, Issue-Migrationsskript — lauffähig, nicht ausgeführt).
- **Neu vor Stufe 6:** das Cgroup-Problem des Docker-Runners. Solange es besteht, gibt es auf Gitea keinen Install-Test — und der ist es, der Renovates Automerge absichert. GitHub-Actions abzuschalten, bevor das gelöst ist, nähme dem Automerge sein Netz.
Kommentar von jk-cluster-bot am 2026-09-16 (migriert aus GitHub).
Nachtrag: Renovate läuft auf der Instanz bereits — und der Post-Upgrade-Schritt auch
Ein Befund macht Punkt 3 des Vorbereitungsplans („Renovate im Eigenbetrieb als geplanter Lauf") hinfällig. Nachgeprüft über die Gitea-API, nicht übernommen:
Der Nutzer jlab-renovate-bot existiert und ist gegen jlab-cluster/helm-chartsaktiv.
Heute um 12:01 hat er dort angelegt: das Dependency Dashboard (Issue #4) sowie die PRs #2 (searxng) und #3 (gotenberg).
Dieselbe Instanz bedient jlab-cluster/cluster-config mit 25 offenen PRs, läuft also regelmäßig und nicht einmalig.
Damit sind zwei Fragen beantwortet, die im Plan gar nicht gestellt waren — und die zweite hätte den Umzug teuer gemacht.
Sie liest unsere renovate.json5. searxng und gotenberg sind genau unsere eigenen customManagers-Ziele. Kein eingebauter Manager findet ein appVersion: in einer Chart.yaml; dass diese beiden PRs entstanden sind, ist der Beleg.
allowedCommands ist dort freigegeben, der Versions-Hook läuft. Der Diff von PR #2:
version: wird von keinem Renovate-Manager angefasst — diese Zeile kann nur aus ci/renovate-bump-chart.sh stammen. Das war das stille Risiko: Ohne den Eintrag im Instanz-Config wird postUpgradeTaskskommentarlos übersprungen, und jeder Chart-PR auf Gitea wäre an der Versions-Prüfung rot geworden, ohne dass irgendetwas die Ursache genannt hätte. Für den Umzug ist an dieser Stelle also nichts einzurichten.
Folge für #253:.github/workflows/renovate.yml ist wieder ausgebaut. Ein zweiter Lauf gegen dasselbe Repo erzeugt Doppel-PRs und zwei Planer, die sich gegenseitig die Branches umschreiben. Mit dem Workflow entfällt auch das zu pinnende Container-Image; im Trockenlauf nachgeprüft, dass ghcr.io/renovatebot/renovate nicht mehr extrahiert wird, TRIVY_VERSION aber weiterhin — die Regel hängt am Trivy-Ersatz und bleibt richtig. README.md und CLAUDE.md sagen jetzt ausdrücklich, dass Renovate dort als bestehende, gemeinsam genutzte Instanz läuft und kein Workflow in dieses Repo gehört; wer hier keinen findet, soll den Grund lesen können, statt einen zu bauen.
Was davon unberührt bleibt: der runs-on-Ausdruck, der Trivy-Ersatz, das Migrationsskript und alle Messergebnisse des Kommentars oben. Die beiden Renovate-PRs auf Gitea sind nicht angefasst.
Eine Einschränkung, die daraus neu entsteht: Die Freigabe der Post-Upgrade-Kommandos liegt auf jener Instanz und ist von hier aus nicht einsehbar. Fällt sie irgendwann weg, ist das Symptom in diesem Repo ein roter Chart-PR ohne Erklärung. Das steht jetzt als Kommentar an postUpgradeTasks in renovate.json5.
> Kommentar von **jk-cluster-bot** am 2026-09-16 (migriert aus GitHub).
---
## Nachtrag: Renovate läuft auf der Instanz bereits — und der Post-Upgrade-Schritt auch
Ein Befund macht Punkt 3 des Vorbereitungsplans („Renovate im Eigenbetrieb als geplanter Lauf") hinfällig. Nachgeprüft über die Gitea-API, nicht übernommen:
- Der Nutzer `jlab-renovate-bot` existiert und ist gegen `jlab-cluster/helm-charts` **aktiv**.
- Heute um 12:01 hat er dort angelegt: das **Dependency Dashboard** (Issue #4) sowie die PRs **#2** (searxng) und **#3** (gotenberg).
- Dieselbe Instanz bedient `jlab-cluster/cluster-config` mit 25 offenen PRs, läuft also regelmäßig und nicht einmalig.
Damit sind zwei Fragen beantwortet, die im Plan gar nicht gestellt waren — und die zweite hätte den Umzug teuer gemacht.
**Sie liest unsere `renovate.json5`.** searxng und gotenberg sind genau unsere eigenen `customManagers`-Ziele. Kein eingebauter Manager findet ein `appVersion:` in einer `Chart.yaml`; dass diese beiden PRs entstanden sind, ist der Beleg.
**`allowedCommands` ist dort freigegeben, der Versions-Hook läuft.** Der Diff von PR #2:
```diff
-version: 1.0.4+up2026.9.11.ffe96f8a6
-appVersion: "2026.9.11-ffe96f8a6"
+version: 1.0.5+up2026.9.13.96479fff6
+appVersion: "2026.9.13-96479fff6"
```
`version:` wird von keinem Renovate-Manager angefasst — diese Zeile kann nur aus `ci/renovate-bump-chart.sh` stammen. Das war das stille Risiko: Ohne den Eintrag im *Instanz*-Config wird `postUpgradeTasks` **kommentarlos** übersprungen, und jeder Chart-PR auf Gitea wäre an der Versions-Prüfung rot geworden, ohne dass irgendetwas die Ursache genannt hätte. Für den Umzug ist an dieser Stelle also **nichts** einzurichten.
**Folge für #253:** `.github/workflows/renovate.yml` ist wieder ausgebaut. Ein zweiter Lauf gegen dasselbe Repo erzeugt Doppel-PRs und zwei Planer, die sich gegenseitig die Branches umschreiben. Mit dem Workflow entfällt auch das zu pinnende Container-Image; im Trockenlauf nachgeprüft, dass `ghcr.io/renovatebot/renovate` nicht mehr extrahiert wird, **`TRIVY_VERSION` aber weiterhin** — die Regel hängt am Trivy-Ersatz und bleibt richtig. `README.md` und `CLAUDE.md` sagen jetzt ausdrücklich, dass Renovate dort als bestehende, gemeinsam genutzte Instanz läuft und **kein Workflow in dieses Repo gehört**; wer hier keinen findet, soll den Grund lesen können, statt einen zu bauen.
Was davon unberührt bleibt: der `runs-on`-Ausdruck, der Trivy-Ersatz, das Migrationsskript und alle Messergebnisse des Kommentars oben. Die beiden Renovate-PRs auf Gitea sind nicht angefasst.
**Eine Einschränkung, die daraus neu entsteht:** Die Freigabe der Post-Upgrade-Kommandos liegt auf jener Instanz und ist von hier aus nicht einsehbar. Fällt sie irgendwann weg, ist das Symptom in diesem Repo ein roter Chart-PR ohne Erklärung. Das steht jetzt als Kommentar an `postUpgradeTasks` in `renovate.json5`.
Die 17 offenen Issues von GitHub sind hier angelegt. Die Nummern konnten nicht erhalten bleiben — dieses Repo hatte bereits Vorgaenge #1 bis #4 vergeben, und Gitea laesst den Index beim Anlegen nicht setzen.
Deshalb gilt fuer alle migrierten Texte: Verweise der Form #123 beziehen sich auf die GitHub-Nummerierung, nicht auf die Nummern hier. Jeder migrierte Vorgang traegt oben einen Kopf mit Link auf sein Original.
Neuer Chart: shelfarr (E-Book- und Hoerbuch-Anfragesystem fuer das *arr-Umfeld)
Was bewusst nicht mitkam
Geschlossene Issues. Sie bleiben auf GitHub lesbar, das als Spiegel bestehen bleibt. Ein Grossteil der Verweise in den Texten oben zeigt auf solche geschlossenen Vorgaenge — deshalb waere ein maschinelles Umschreiben der Nummern falsch gewesen und ist unterblieben.
Das Dependency Dashboard von GitHubs Renovate (dort #144). Die Renovate-Instanz dieser Seite hat mit #4 bereits ein eigenes angelegt; eine Kopie waere ein Duplikat, das nie wieder aktualisiert wird.
Urheberschaft und Zeitpunkte der Beitraege. Die API legt alles unter dem migrierenden Konto an; die urspruenglichen Angaben stehen stattdessen als Kopfzeile in jedem Rumpf und jedem Kommentar.
Labels und Meilensteine — im Quellrepo trug keiner der offenen Vorgaenge welche.
## Nummernzuordnung der Issue-Migration
Die 17 offenen Issues von GitHub sind hier angelegt. **Die Nummern konnten nicht erhalten bleiben** — dieses Repo hatte bereits Vorgaenge #1 bis #4 vergeben, und Gitea laesst den Index beim Anlegen nicht setzen.
Deshalb gilt fuer alle migrierten Texte: **Verweise der Form `#123` beziehen sich auf die GitHub-Nummerierung**, nicht auf die Nummern hier. Jeder migrierte Vorgang traegt oben einen Kopf mit Link auf sein Original.
| GitHub | hier | Titel |
|---|---|---|
| [`#20`](https://github.com/jk-cluster/helm-charts/issues/20) | #5 | Security: NetworkPolicies für alle Charts |
| [`#40`](https://github.com/jk-cluster/helm-charts/issues/40) | #6 | teamspeak-server: Beta-Bump vor dem 01.10.2026 (Bundled-Lizenz läuft ab) |
| [`#84`](https://github.com/jk-cluster/helm-charts/issues/84) | #7 | Sicherheitsbefunde aus Trivy: Bestandsaufnahme über alle Charts und automatische Prüfung vor dem Ausrollen |
| [`#160`](https://github.com/jk-cluster/helm-charts/issues/160) | #8 | Veröffentlichte Chart-Version kann still überschrieben werden — der Versions-Check prüft nur gegen main |
| [`#161`](https://github.com/jk-cluster/helm-charts/issues/161) | #9 | minecraft-server: seccompProfile RuntimeDefault fehlt (Teil von #84) |
| [`#184`](https://github.com/jk-cluster/helm-charts/issues/184) | #10 | template-chart: das env-Beispiel zeigt auf eine ConfigMap, die der Chart nicht anlegt |
| [`#185`](https://github.com/jk-cluster/helm-charts/issues/185) | #11 | paperless-ngx: unquotierter CronJob-Zeitplan bricht das Rendern ab, oidcSecretRef wird von keinem Template gelesen |
| [`#186`](https://github.com/jk-cluster/helm-charts/issues/186) | #12 | outline: Upload-Grenzen von Anwendung und Ingress liegen um Faktor zehn auseinander |
| [`#216`](https://github.com/jk-cluster/helm-charts/issues/216) | #13 | teamspeak-server: Änderung von server.voicePort führt zu Sprachausfall bei grünem Rollout |
| [`#225`](https://github.com/jk-cluster/helm-charts/issues/225) | #14 | Repository, Workflows und Issues nach Gitea (JLab-Git) umziehen |
| [`#227`](https://github.com/jk-cluster/helm-charts/issues/227) | #15 | install-test: k3d im Auftragscontainer erreicht seinen eigenen Cluster nicht (Teil von #225) |
| [`#230`](https://github.com/jk-cluster/helm-charts/issues/230) | #16 | satisfactory: dev und server brechen bei --set <block>=null mit nil-Pointern ab (Rest von #99) |
| [`#237`](https://github.com/jk-cluster/helm-charts/issues/237) | #17 | Neuer Chart: canarytokens mit mitgeliefertem oauth2-proxy |
| [`#238`](https://github.com/jk-cluster/helm-charts/issues/238) | #18 | OnePasswordItem-Verweise: fünf Vorlagen ohne required-Wächter, einer davon belegt falsch |
| [`#241`](https://github.com/jk-cluster/helm-charts/issues/241) | #19 | run-quantity-render-test.sh: Chart-Pfad statt nur Chart-Name, damit Kopien außerhalb des Repos geprüft werden können |
| [`#245`](https://github.com/jk-cluster/helm-charts/issues/245) | #20 | freesailarr: MediathekArr als zehnten Container aufnehmen |
| [`#247`](https://github.com/jk-cluster/helm-charts/issues/247) | #21 | Neuer Chart: shelfarr (E-Book- und Hoerbuch-Anfragesystem fuer das *arr-Umfeld) |
### Was bewusst nicht mitkam
- **Geschlossene Issues.** Sie bleiben auf GitHub lesbar, das als Spiegel bestehen bleibt. Ein Grossteil der Verweise in den Texten oben zeigt auf solche geschlossenen Vorgaenge — deshalb waere ein maschinelles Umschreiben der Nummern falsch gewesen und ist unterblieben.
- **Das Dependency Dashboard von GitHubs Renovate** (dort `#144`). Die Renovate-Instanz dieser Seite hat mit #4 bereits ein eigenes angelegt; eine Kopie waere ein Duplikat, das nie wieder aktualisiert wird.
- **Urheberschaft und Zeitpunkte** der Beitraege. Die API legt alles unter dem migrierenden Konto an; die urspruenglichen Angaben stehen stattdessen als Kopfzeile in jedem Rumpf und jedem Kommentar.
- **Labels und Meilensteine** — im Quellrepo trug keiner der offenen Vorgaenge welche.
Probelauf nach dem MTU-Fix: k3s startet, und der Runner ist nicht langsam
Vierter Anlauf, diesmal mit dem unveränderten Workflow aus PR #253 — die curl -4-Krücke und die verkürzte Matrix der Diagnoserunden sind weg, die Workflow-Datei ist Byte für Byte die aus dem PR. Nur die Chart.yaml sind angefasst, damit Charts in die Matrix geraten.
Beantwortet
k3s startet durch.Create k3d cluster ist grün — in 16 s. Auf GitHub sind es 20 s für denselben Befehl. Der Docker-Runner ist also nicht langsamer, sondern in diesem Schritt eher schneller. Weder /dev/fuse noch User-Namespaces stehen im Weg; nach dem MTU-Fix bootet der k3s-Knoten ohne weitere Schicht dazwischen.
Der 1Password-Pfad trägt.Configure OP-Connect, Load freesailarr VPN credentials und Verify the VPN credentials arrived sind auf dem Gitea-Runner grün, beide op://-Felder kommen nicht leer an. Die Voraussetzung für den freesailarr-Test ist damit erfüllt.
Zwei neue Befunde, beide im Workflow, nicht am Runner
1. Gitea führt die Matrix-Zweige NEBENLÄUFIG in einem Job aus — und sie kollidieren.
Das korrigiert meinen früheren Bericht: Ich hatte „nacheinander" gemeldet. Falsch. Sie laufen gleichzeitig, auf einem Runner, auf einem Docker-Dämon. Mit cyberchef und freesailarr in der Matrix:
INFO Created network 'k3d-install-test'
INFO Re-using existing network 'k3d-install-test' (10e353c1b483…)
ERRO failed to create container 'k3d-install-test-server-0': Conflict.
The container name "/k3d-install-test-server-0" is already in use
ERRO failed to start node 'k3d-install-test-tools': network k3d-install-test not found
FATA Cluster creation FAILED
Beide Zweige legen einen Cluster namens install-test an, und der Rollback des einen reißt dem anderen das Netz weg. Auf GitHub bekommt jeder Zweig eine eigene VM, deshalb konnte der feste Clustername nie kollidieren. Auf Gitea ist er eine geteilte Ressource. Mit einem Chart in der Matrix tritt das nicht auf — und das ist der Fall, den die meisten PRs erzeugen; ein PR, der zwei Charts anfasst, fällt darüber.
Dazu passt der schon bekannte Befund: alle Zweige teilen sich einen Status-Kontext, benannt mit dem unaufgelösten Matrix-Ausdruck. Für den Branch-Schutz heißt das weiterhin: kein Pflicht-Check pro Chart.
2. kubectl fehlt auf dem Gitea-Runner-Image.
Mit einem Chart läuft alles bis Install chart and wait for readiness, und das scheitert in 0 s:
ci/run-install-test.sh: line 161: kubectl: command not found
ci/run-install-test.sh setzt kubectl voraus. Auf GitHubs ubuntu-latest ist es vorinstalliert, auf docker.gitea.com/runner-images:ubuntu-latest nicht. Das ist keine Runner-Fehlkonfiguration, sondern eine Annahme im Workflow, die nur auf GitHub zutrifft — dieselbe Klasse wie das ubuntu-latest-Label, das dort Docker bedeutet und hier nicht.
Offen
freesailarr ist weiterhin ungemessen, blockiert von beiden Punkten oben. Die Laufzeit des Install-Tests auf Gitea ist damit nur bis Create k3d cluster belegt (16 s gegen 20 s auf GitHub). Zum Vergleich, auf GitHub gemessen statt übernommen: Job gesamt 59 s für searxng, 2m45s für freesailarr.
Die MTU-Geschichte, und warum sie hier steht
Die Vorrunde war rot, weil der Runner get.helm.sh nicht erreichte. Meine Messung war korrekt und meine Schlussfolgerung falsch: Ich hatte eine Egress-Sperre für diesen Host vermutet, weil github.com, ghcr.io und Docker Hub antworteten und nur get.helm.sh nicht.
Die trennende Größe war aber nicht der Host, sondern die Antwortgröße. Der rootful Dämon legte seine Brücken mit Docker-Vorgabe 1500 an, während das Pod-Netz 1450 trägt — eingeschleppt durch genau den Umbau, der das Cgroup-Problem löste. 301, 401 und ein JSON-Körper passen in ein Paket, ein 17-MB-Tarball nicht. Und docker pull lief, weil der Dämon das auf eth0 tut und nicht ein Container auf der Brücke. cluster-config #59 setzt --mtu=1450 samt Vorgabe für neu angelegte Brückennetze.
Der Merksatz gehört ins Protokoll, weil dieses Repo sonst genau davor warnt: eine korrekte Messung, nach dem falschen Merkmal sortiert. Meine Tabelle stimmte Zeile für Zeile; ich hatte sie nach „welcher Host" gelesen statt nach „wie viele Pakete". Ein künftiger Runner-Umbau kann hier erneut hineinlaufen, und das Fehlerbild wird wieder wie eine Firewall aussehen.
Zwei weitere Korrekturen an meinen eigenen früheren Meldungen, damit der Bestand stimmt:
Der gemeldete „grüne Job trotz fehlgeschlagenem Schritt" war ein Runner-Neustart mitten im Lauf, keine Gitea-Eigenschaft. In allen späteren Läufen meldet Gitea korrekt failure. Zurückgezogen.
„Matrix-Zweige laufen nacheinander" war falsch, siehe Punkt 1 oben. Sie laufen nebenläufig.
Trägt der Install-Test auf Gitea?
Noch nicht — aber woran es liegt, ist jetzt vollständig bekannt und liegt in unserer Hand. Die Runner-Umgebung ist nach MTU- und Cgroup-Fix in Ordnung: Cluster startet, Tempo stimmt, Secrets kommen an. Was fehlt, sind zwei Annahmen im Workflow, die nur auf GitHub-Runnern gelten: vorinstalliertes kubectl und ein Clustername, der nur dann eindeutig ist, wenn jeder Matrix-Zweig seine eigene Maschine hat.
## Probelauf nach dem MTU-Fix: k3s startet, und der Runner ist nicht langsam
Vierter Anlauf, diesmal mit dem **unveränderten Workflow aus PR #253** — die `curl -4`-Krücke und die verkürzte Matrix der Diagnoserunden sind weg, die Workflow-Datei ist Byte für Byte die aus dem PR. Nur die `Chart.yaml` sind angefasst, damit Charts in die Matrix geraten.
### Beantwortet
**k3s startet durch.** `Create k3d cluster` ist grün — in **16 s**. Auf GitHub sind es **20 s** für denselben Befehl. Der Docker-Runner ist also nicht langsamer, sondern in diesem Schritt eher schneller. Weder `/dev/fuse` noch User-Namespaces stehen im Weg; nach dem MTU-Fix bootet der k3s-Knoten ohne weitere Schicht dazwischen.
**Der 1Password-Pfad trägt.** `Configure OP-Connect`, `Load freesailarr VPN credentials` und `Verify the VPN credentials arrived` sind auf dem Gitea-Runner grün, beide `op://`-Felder kommen nicht leer an. Die Voraussetzung für den freesailarr-Test ist damit erfüllt.
### Zwei neue Befunde, beide im Workflow, nicht am Runner
**1. Gitea führt die Matrix-Zweige NEBENLÄUFIG in einem Job aus — und sie kollidieren.**
Das korrigiert meinen früheren Bericht: Ich hatte „nacheinander" gemeldet. Falsch. Sie laufen **gleichzeitig**, auf einem Runner, auf einem Docker-Dämon. Mit cyberchef und freesailarr in der Matrix:
```
INFO Created network 'k3d-install-test'
INFO Re-using existing network 'k3d-install-test' (10e353c1b483…)
ERRO failed to create container 'k3d-install-test-server-0': Conflict.
The container name "/k3d-install-test-server-0" is already in use
ERRO failed to start node 'k3d-install-test-tools': network k3d-install-test not found
FATA Cluster creation FAILED
```
Beide Zweige legen einen Cluster namens `install-test` an, und der Rollback des einen reißt dem anderen das Netz weg. Auf GitHub bekommt jeder Zweig eine eigene VM, deshalb konnte der feste Clustername nie kollidieren. Auf Gitea ist er eine geteilte Ressource. Mit **einem** Chart in der Matrix tritt das nicht auf — und das ist der Fall, den die meisten PRs erzeugen; ein PR, der zwei Charts anfasst, fällt darüber.
Dazu passt der schon bekannte Befund: alle Zweige teilen sich **einen** Status-Kontext, benannt mit dem unaufgelösten Matrix-Ausdruck. Für den Branch-Schutz heißt das weiterhin: kein Pflicht-Check pro Chart.
**2. `kubectl` fehlt auf dem Gitea-Runner-Image.**
Mit einem Chart läuft alles bis `Install chart and wait for readiness`, und das scheitert in **0 s**:
```
ci/run-install-test.sh: line 161: kubectl: command not found
```
`ci/run-install-test.sh` setzt kubectl voraus. Auf GitHubs `ubuntu-latest` ist es vorinstalliert, auf `docker.gitea.com/runner-images:ubuntu-latest` nicht. Das ist keine Runner-Fehlkonfiguration, sondern eine Annahme im Workflow, die nur auf GitHub zutrifft — dieselbe Klasse wie das `ubuntu-latest`-Label, das dort Docker bedeutet und hier nicht.
### Offen
**freesailarr ist weiterhin ungemessen**, blockiert von beiden Punkten oben. Die Laufzeit des Install-Tests auf Gitea ist damit nur bis `Create k3d cluster` belegt (16 s gegen 20 s auf GitHub). Zum Vergleich, auf GitHub gemessen statt übernommen: Job gesamt **59 s** für searxng, **2m45s** für freesailarr.
### Die MTU-Geschichte, und warum sie hier steht
Die Vorrunde war rot, weil der Runner `get.helm.sh` nicht erreichte. Meine Messung war korrekt und meine Schlussfolgerung falsch: Ich hatte eine Egress-Sperre für diesen Host vermutet, weil github.com, ghcr.io und Docker Hub antworteten und nur `get.helm.sh` nicht.
Die trennende Größe war aber nicht der Host, sondern die **Antwortgröße**. Der rootful Dämon legte seine Brücken mit Docker-Vorgabe 1500 an, während das Pod-Netz 1450 trägt — eingeschleppt durch genau den Umbau, der das Cgroup-Problem löste. 301, 401 und ein JSON-Körper passen in ein Paket, ein 17-MB-Tarball nicht. Und `docker pull` lief, weil der Dämon das auf `eth0` tut und nicht ein Container auf der Brücke. cluster-config #59 setzt `--mtu=1450` samt Vorgabe für neu angelegte Brückennetze.
Der Merksatz gehört ins Protokoll, weil dieses Repo sonst genau davor warnt: **eine korrekte Messung, nach dem falschen Merkmal sortiert.** Meine Tabelle stimmte Zeile für Zeile; ich hatte sie nach „welcher Host" gelesen statt nach „wie viele Pakete". Ein künftiger Runner-Umbau kann hier erneut hineinlaufen, und das Fehlerbild wird wieder wie eine Firewall aussehen.
Zwei weitere Korrekturen an meinen eigenen früheren Meldungen, damit der Bestand stimmt:
- Der gemeldete „grüne Job trotz fehlgeschlagenem Schritt" war ein **Runner-Neustart mitten im Lauf**, keine Gitea-Eigenschaft. In allen späteren Läufen meldet Gitea korrekt `failure`. Zurückgezogen.
- „Matrix-Zweige laufen nacheinander" war falsch, siehe Punkt 1 oben. Sie laufen nebenläufig.
### Trägt der Install-Test auf Gitea?
**Noch nicht — aber woran es liegt, ist jetzt vollständig bekannt und liegt in unserer Hand.** Die Runner-Umgebung ist nach MTU- und Cgroup-Fix in Ordnung: Cluster startet, Tempo stimmt, Secrets kommen an. Was fehlt, sind zwei Annahmen im Workflow, die nur auf GitHub-Runnern gelten: vorinstalliertes `kubectl` und ein Clustername, der nur dann eindeutig ist, wenn jeder Matrix-Zweig seine eigene Maschine hat.
Der Runner ist gleichauf, beim Cluster-Anlegen sogar leicht schneller. Der Unterschied in der Gesamtzeit entsteht daraus, dass Gitea alle Matrix-Zweige nebenläufig in einem Job auf einem Runner fährt, während GitHub jedem Zweig eine eigene VM gibt — zwei k3s-Cluster teilen sich dort CPU, Speicher und inotify-Instanzen. Wer die 1,5 als „Gitea ist langsamer" liest, zieht die falsche Konsequenz.
Was ein GitHub-Runner stillschweigend erfüllt
Der eigentliche Ertrag dieser Wochen, und er ist nicht eine Chronik. Es ist die Liste der Annahmen, auf denen unsere Pipeline stand, ohne dass eine davon irgendwo geschrieben stand. Sie zerfällt in zwei Klassen, und die Trennung ist der Punkt.
Klasse 1: Annahmen, die diese Umgebung nicht erfüllt
Ärgerlich, aber ehrlich — sie scheitern sofort und laut, sagen etwas über den Runner und nichts über unseren Code, und sind mit dem Umzug erledigt.
Annahme
Was tatsächlich geschah
MTU 1500 auf Docker-Brücken
Das Pod-Netz trägt 1450. Kleine Antworten (301, 401, JSON) passen in ein Paket, ein 17-MB-Tarball nicht — get.helm.sh schien gesperrt, war es aber nie.
fs.inotify-Grenzen reichen für k3s
128 Instanzen; containerd startete nie („error creating fsnotify watcher: too many open files"), der Knoten registrierte sich nicht, der Pod blieb Pending ohne ein einziges Scheduling-Ereignis.
kubectl liegt im Runner-Abbild
Tut es auf Gitea nicht. Jetzt gepinnt auf die Version des K3S_IMAGE, über Renovate verfolgt, mit Wächter gegen Versionsabstand.
envsubst liegt im Runner-Abbild
Ebenfalls nicht. Ohne es verschwinden freesailarrs Fixtures lautlos zu „no objects passed to apply". Jetzt geprüft und bei Bedarf nachinstalliert — bewusst nicht gepinnt, weil seine Version das Ergebnis nicht verändert.
Der Auftrag läuft auf dem Docker-Host
Auf Gitea ist jeder Auftrag selbst ein Container. k3d veröffentlicht den Port auf dem Host, die kubeconfig zeigt auf 0.0.0.0 — aus dem Auftragscontainer auf ihn selbst. Gelöst über #227.
Klasse 2: Ein echter Fehler, den die großzügigere Umgebung stillgelegt hat
Der fest verdrahtete k3d-Clustername install-test. Er war auch auf GitHub falsch. Dort fiel er nur nie auf, weil jeder Matrix-Zweig eine eigene VM mit eigenem Docker-Dämon bekommt und zwei Zweige einander nie sahen. Auf Gitea, wo alle Zweige auf einem Dämon liegen, kollidiert er sofort: ein Zweig meldet „Re-using existing network", der andere stirbt an „The container name /k3d-install-test-server-0 is already in use", und der Rollback des ersten reißt dem zweiten das Netz weg. Zwei rote Zweige, und keine der beiden Meldungen nennt die Ursache.
Von den sechs Punkten ist das der einzige, der auch nach dem Umzug noch etwas über unseren Code sagt. Zwei Jahre lang hat die Umgebung ihn getragen, statt ihn zu zeigen.
Eine großzügige Umgebung ist kein Freund, wenn man wissen will, ob etwas stimmt.
Nachtrag aus derselben Runde, weil es zur Sache gehört: Auch die Prüfung, die für diesen Nachweis eingebaut wurde, war zweimal hohl, bevor sie taugte — kubectl get nodes liefert Rückgabewert 0 auch bei leerer Liste, und kubectl wait --for=condition=Ready nodes --all hat dieselbe Lücke, weil die Bedingung bei null Knoten leer erfüllt ist. Der Schritt wurde grün, und der Fehlschlag tauchte fünf Minuten später an unbeteiligter Stelle auf. Eine Prüfung, die ein kaputter Cluster besteht, ist schlechter als keine.
Die Images in ci/<chart>/fixtures.yaml, die Renovate nicht verfolgt, sind derselbe Fall in anderer Gestalt: ein grüner Test, der Vertrauen erzeugt, das er nicht deckt. Zwei unabhängige Funde dieser Sorte in einer Woche sind kein Zufall, sondern ein Hinweis, wo als Nächstes zu suchen ist — überall dort, wo etwas grün wird, ohne dass jemand nachgesehen hat, was dabei geprüft wurde.
Stand
Stufe 2 aus dem Rumpf ist damit abgeschlossen; #227 ist geschlossen. Die Workflow-Anpassungen liegen in PR #253. Beim Maintainer bleiben unverändert: Branch-Schutz und Signaturpflicht auf dem Gitea-Repo, GitHub-Actions abschalten, danach den Spiegel einschalten — in dieser Reihenfolge.
## Der Install-Test trägt auf Gitea. Drei Läufe, drei mal grün, beide Charts.
Gemessen auf dem Wegwerf-PR der Instanz, mit der Workflow-Datei aus #253 — nicht mit einem Sonderworkflow.
| Lauf | Install-test (Job) | `Create k3d cluster` | Chart-Install |
|---|---|---|---|
| 1 | **216 s** | 19 s | 178 s |
| 2 | **212 s** | 25 s | 175 s |
| 3 | **210 s** | 21 s | 171 s |
**Streuung 6 Sekunden.** Pro Matrix-Zweig: cyberchef **18 s**, freesailarr **~2m59s**. Übrige Jobs: detect 3–4 s, build 4–5 s, quantity-render 7 s, trivy 12–13 s.
### Der Faktor 1,5 ist strukturell, nicht Leistung
| | Gitea | GitHub (gleicher Stand) |
|---|---|---|
| `Create k3d cluster` | 19–25 s | 22–23 s |
| beide Charts, Wanduhr | 210–216 s | ~143 s |
**Der Runner ist gleichauf**, beim Cluster-Anlegen sogar leicht schneller. Der Unterschied in der Gesamtzeit entsteht daraus, dass Gitea **alle Matrix-Zweige nebenläufig in einem Job auf einem Runner** fährt, während GitHub jedem Zweig eine eigene VM gibt — zwei k3s-Cluster teilen sich dort CPU, Speicher und inotify-Instanzen. Wer die 1,5 als „Gitea ist langsamer" liest, zieht die falsche Konsequenz.
## Was ein GitHub-Runner stillschweigend erfüllt
Der eigentliche Ertrag dieser Wochen, und er ist **nicht** eine Chronik. Es ist die Liste der Annahmen, auf denen unsere Pipeline stand, ohne dass eine davon irgendwo geschrieben stand. Sie zerfällt in zwei Klassen, und die Trennung ist der Punkt.
### Klasse 1: Annahmen, die diese Umgebung nicht erfüllt
Ärgerlich, aber **ehrlich** — sie scheitern sofort und laut, sagen etwas über den Runner und nichts über unseren Code, und sind mit dem Umzug erledigt.
| Annahme | Was tatsächlich geschah |
|---|---|
| **MTU 1500 auf Docker-Brücken** | Das Pod-Netz trägt 1450. Kleine Antworten (301, 401, JSON) passen in ein Paket, ein 17-MB-Tarball nicht — `get.helm.sh` schien gesperrt, war es aber nie. |
| **`fs.inotify`-Grenzen reichen für k3s** | 128 Instanzen; containerd startete nie („error creating fsnotify watcher: too many open files"), der Knoten registrierte sich nicht, der Pod blieb `Pending` ohne ein einziges Scheduling-Ereignis. |
| **`kubectl` liegt im Runner-Abbild** | Tut es auf Gitea nicht. Jetzt gepinnt auf die Version des `K3S_IMAGE`, über Renovate verfolgt, mit Wächter gegen Versionsabstand. |
| **`envsubst` liegt im Runner-Abbild** | Ebenfalls nicht. Ohne es verschwinden freesailarrs Fixtures lautlos zu „no objects passed to apply". Jetzt geprüft und bei Bedarf nachinstalliert — bewusst **nicht** gepinnt, weil seine Version das Ergebnis nicht verändert. |
| **Der Auftrag läuft auf dem Docker-Host** | Auf Gitea ist jeder Auftrag selbst ein Container. k3d veröffentlicht den Port auf dem Host, die kubeconfig zeigt auf `0.0.0.0` — aus dem Auftragscontainer auf ihn selbst. Gelöst über #227. |
### Klasse 2: Ein echter Fehler, den die großzügigere Umgebung stillgelegt hat
**Der fest verdrahtete k3d-Clustername `install-test`.** Er war **auch auf GitHub falsch**. Dort fiel er nur nie auf, weil jeder Matrix-Zweig eine eigene VM mit eigenem Docker-Dämon bekommt und zwei Zweige einander nie sahen. Auf Gitea, wo alle Zweige auf einem Dämon liegen, kollidiert er sofort: ein Zweig meldet „Re-using existing network", der andere stirbt an „The container name /k3d-install-test-server-0 is already in use", und der Rollback des ersten reißt dem zweiten das Netz weg. Zwei rote Zweige, und keine der beiden Meldungen nennt die Ursache.
Von den sechs Punkten ist das der einzige, der auch nach dem Umzug noch etwas über **unseren Code** sagt. Zwei Jahre lang hat die Umgebung ihn getragen, statt ihn zu zeigen.
> **Eine großzügige Umgebung ist kein Freund, wenn man wissen will, ob etwas stimmt.**
Nachtrag aus derselben Runde, weil es zur Sache gehört: Auch die Prüfung, die für diesen Nachweis eingebaut wurde, war zweimal hohl, bevor sie taugte — `kubectl get nodes` liefert Rückgabewert 0 auch bei **leerer** Liste, und `kubectl wait --for=condition=Ready nodes --all` hat dieselbe Lücke, weil die Bedingung bei null Knoten leer erfüllt ist. Der Schritt wurde grün, und der Fehlschlag tauchte fünf Minuten später an unbeteiligter Stelle auf. Eine Prüfung, die ein kaputter Cluster besteht, ist schlechter als keine.
### Dasselbe Muster, unabhängig gefunden: #257
Die Images in `ci/<chart>/fixtures.yaml`, die Renovate nicht verfolgt, sind derselbe Fall in anderer Gestalt: **ein grüner Test, der Vertrauen erzeugt, das er nicht deckt.** Zwei unabhängige Funde dieser Sorte in einer Woche sind kein Zufall, sondern ein Hinweis, wo als Nächstes zu suchen ist — überall dort, wo etwas grün wird, ohne dass jemand nachgesehen hat, was dabei geprüft wurde.
## Stand
Stufe 2 aus dem Rumpf ist damit abgeschlossen; #227 ist geschlossen. Die Workflow-Anpassungen liegen in PR #253. Beim Maintainer bleiben unverändert: Branch-Schutz und Signaturpflicht auf dem Gitea-Repo, GitHub-Actions abschalten, danach den Spiegel einschalten — in dieser Reihenfolge.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Repository, Workflows, Issues und Renovate ziehen von GitHub auf die Gitea-Instanz (JLab-Git) um. GitHub bleibt danach als Push-Spiegel ohne Action-Läufe bestehen.
Stand
Der Code ist bereits drüben und deckungsgleich:
JLab-Git/mainundgithub/mainzeigen beide auf757e4be. Das lokale Remoteoriginheißt inzwischengithub, daneben existiertJLab-Git.Alles andere fehlt. Aus der Repo-API der Instanz:
has_actions = false— keine CI, keine Publish-Pipelineopen_issues_count = 0— alle offenen Issues liegen weiterhin nur auf GitHubmirror = false,fork = false— ein echtes Repo, das Pushes und Issues annehmen kannEntschieden
actions/checkout,azure/setup-helmund das 1Password-Action müssen also nicht ersetzt werden.Die Falle, die die Reihenfolge bestimmt
Spiegelt Gitea nach GitHub, während dort noch Actions laufen, löst jeder gespiegelte Push die 23 Publish-Workflows auf GitHub erneut aus. Die veröffentlichen ein zweites Mal in dieselbe Registry — und an dieser Instanz ist gemessen (siehe #160), dass ein Republish still angenommen wird, das Artefakt ersetzt und den Zeitstempel des ersten Uploads stehen lässt. Das Repo überschriebe sich also unbemerkt selbst.
Daraus folgt zwingend: erst CI auf Gitea nachweislich lauffähig, dann Actions auf GitHub abschalten, erst danach den Spiegel einschalten. Jede andere Reihenfolge lässt ein Fenster offen, in dem entweder keine CI existiert oder zwei gleichzeitig arbeiten.
Zu prüfen, bevor Workflows umgeschrieben werden
Zwei Fragen entscheiden über den Zuschnitt der Arbeit und sind zu messen, nicht anzunehmen:
secrets: inherit? Daran hängt die gesamte Publish-Architektur: 23 dünne Workflows rufenpublish-helm-chart.ymlauf. Fällt das weg, ist es kein Anpassen, sondern ein Umbau.Beides gehört in einen Wegwerf-Workflow als Probelauf, bevor irgendein bestehender Workflow angefasst wird.
Stufen
OP_CONNECT_TOKENals Gitea-Secret anlegen.CLAUDE.mdumschreiben: der dokumentierte Arbeitsablauf ist vollständig umghund GitHub-PRs herum formuliert und wird durch den Umzug falsch.Vorwarnung zu den Issue-Nummern
Weil das bestehende Repo bleibt, müssen die Issues über die API neu angelegt werden, und Gitea vergibt die Nummern fortlaufend. Issues und Pull Requests teilen sich auf beiden Systemen einen Zähler — ein heutiges #218 landet also nicht wieder auf 218, es sei denn, vorher werden 217 Einträge erzeugt.
Dieses Repo ist dicht mit Verweisen durchsetzt:
CLAUDE.md,README.md, mehrereNOTES.txt, Kommentare invalues.yaml, dazu Commit-Botschaften, die sich gar nicht mehr ändern lassen. Nach einer Neuvergabe zeigen sie still auf das Falsche.Mögliche Auswege — keiner davon entschieden, und die Entscheidung gehört vor den Beginn der Migration:
Offen
Die Labels, auf die die Runner hören. Die Workflows fordern heute durchgehend
ubuntu-latest.Korrektur am Ausgangsbefund
Der Rumpf dieses Issues nennt
has_actions = falsefürjlab-cluster/helm-charts. Das stimmt nicht mehr — heute gemessen über die Gitea-API:Aufgefallen ist es der Nachbar-Session, die den Wert nachgeprüft hat, statt meine Auskunft zu übernehmen. Ob die Einheit zwischen dem 2. und dem 4. September eingeschaltet wurde oder ob meine erste Lesung falsch war, kann ich nicht belegen;
updated_atsteht unverändert auf dem 1. September, was aber nur Inhalts-Änderungen nachhält, keine Einstellungen.Folge für Stufe 1: Die Actions-Einheit muss nicht mehr eingeschaltet werden. Es bleibt:
OP_CONNECT_TOKENals Gitea-Secret anlegen und der Probelauf.Weiterhin offen ist die Runner-Frage. Dort lief nach Auskunft der Nachbar-Session noch nie ein Auftrag (
total_count = 0), und ich kann die registrierten Runner nicht abfragen — dastea-Token bekommt „user should be the owner of the repo". Ob Runner registriert sind und auf welche Labels sie hören, muss der Maintainer nachsehen; die Workflows fordern heute durchgehendubuntu-latest.Nachtrag zur Wiederverwendbarkeit
Aus demselben Austausch: Es gibt Interesse,
publish-helm-chart.ymlaus einem anderen Repo heraus peruses:aufzurufen (konkretsuer/suer-configuratorauf derselben Gitea-Instanz). Dafür fehlen zwei Dinge, unabhängig vom Umzug:op://GitHub-Actions/jlab-registry-access/charts-url. Ein fremder Aufrufer würde damit unter unserem Owner veröffentlichen, mit unserem Token und unserem Namen im Paket-Protokoll. Der saubere Schnitt ist ein Input für den 1Password-Item-Pfad, nicht für die URL — dann reisenusername,passwordundcharts-urlgemeinsam und passen zueinander.ci/helm-dependencies.sh, das im aufrufenden Repo nicht existiert. Ein Guard sollte dabei nicht bloß überspringen: fehlt das Skript und deklariert der Chart Abhängigkeiten, gehört ein lauter Abbruch mit genau dieser Begründung hin — sonst scheitert zwei Schritte späterhelm packagemit einer Meldung, von der niemand auf die Ursache kommt.Beides ist nicht entschieden und gehört ohnehin erst nach diesen Umzug.
Runner-Labels — und eine Falle, die sie mitbringen
Die Nachbar-Session (
suer/suer-configurator, dieselbe Gitea-Instanz) berichtet zwei registrierte Labels:ubuntu-latest— ohne Docker-Socketubuntu-docker— mit Docker-SocketNicht von mir gemessen. Mein
tea-Token bekommt auf/repos/jlab-cluster/helm-charts/actions/runnersein „user should be the owner of the repo", ihres beijlab-clusterschon auf Repo-Variablen ein 403. Der Maintainer kann es in einem Blick bestätigen; bis dahin ist es eine Fremdauskunft, keine Messung.Wenn es zutrifft: das Label lügt nicht, aber es bedeutet etwas anderes
Alle unsere Workflows fordern
runs-on: ubuntu-latest. Auf GitHub sind das gehostete Runner mit Docker. Auf dieser Instanz ist dasselbe Label ein Runner ohne Docker-Socket.Für Lint, Paketieren, den Quantity-Render-Test und das Hochladen ist das folgenlos — die brauchen nur Helm und curl.
Der Install-Test bricht. Er legt einen k3d-Cluster im Runner an; ohne Docker-Socket scheitert der Schritt „Create k3d cluster". Und die Meldung wird auf k3d zeigen, nicht auf den Runner — niemand kommt von dort auf ein fehlendes Label. Das ist genau die Sorte stiller Fehlleitung, die dieses Repo sonst abstellt: der Workflow ist gültig, der Job startet, und die Ursache steht nicht in der Fehlermeldung.
Folge für den Plan
Stufe 3 bekommt einen konkreten Punkt: die Jobs, die einen Docker-Socket brauchen, müssen auf
ubuntu-dockerumgestellt werden — nach heutigem Stand nur der Install-Test. Das ist beim Probelauf (Stufe 2) mitzuprüfen und nicht anzunehmen:build-chart(helm lint/package) — vermutlich kein Dockerhelm template, kein DockerWer das umstellt, sollte den Grund als Kommentar an das
runs-onschreiben. Einubuntu-dockerohne Erklärung sieht auf GitHub wie ein Fehler aus und wird beim nächsten Aufräumen „korrigiert".Was sich dadurch am Aufwand ändert
Stufe 1 schrumpft weiter: die Actions-Einheit ist an (siehe Kommentar oben), das Label
ubuntu-latestexistiert und ist bedient. Es bleibt das Gitea-Secret fürOP_CONNECT_TOKENund der Probelauf — der jetzt eine Frage mehr zu beantworten hat.Stand korrigiert, und ein Plan für die Vorbereitung
Die Ursache der neun roten Läufe war das fehlende
OP_CONNECT_TOKEN, nicht die wiederverwendbaren Workflows. Der Maintainer hat das Secret angelegt;publish-voucher-vaultLauf 9 ist grün. Meine Vermutung im vorigen Kommentar — kein Job-Start, keine Schritte, also vermutlich ein nicht auflösbaresuses:— war falsch. Gitea fällt offenbar schon bei der Job-Erzeugung aus, wenn ein übersecrets: inheriterwartetes Secret fehlt, statt den Job zu starten und am 1Password-Schritt zu scheitern.Frage 1 aus dem Rumpf ist damit positiv beantwortet: Gitea beherrscht wiederverwendbare Workflows mit
secrets: inherit. Die Publish-Architektur aus 23 dünnen Hüllen überlebt den Umzug unverändert.Frage 2 ist unberührt. Gelaufen sind ausschließlich Publish-Workflows.
validate-charts.ymliston: pull_request, und auf Gitea gibt es bislang keinen einzigen Pull Request — ob ein Runner den Install-Test mit seinem k3d-Cluster trägt, ist weiterhin ungemessen. Das ist der Gatepunkt für alles Weitere.Ein Fenster ist jetzt offen
Seit die Gitea-Pipeline funktioniert, veröffentlichen beide Seiten. Solange GitHub-Actions aktiv sind und Commits nach Gitea gelangen, publizieren zwei CIs in dieselbe Registry — und an dieser Instanz ist gemessen (#160), dass ein Republish still angenommen wird, das Artefakt ersetzt und den Zeitstempel des ersten Uploads stehen lässt.
Solange dieses Fenster offen ist, gilt: nicht nach Gitea pushen, außer für den Probelauf unten. Der Schaden ist heute gering, weil beide Seiten denselben Inhalt bauen, aber die Eigenschaft, auf die man sich dabei verlässt, ist nirgends zugesichert.
Nebenbefund aus derselben Messung: Der Push vom 14.09. hat auf Gitea sechs Publish-Workflows für einen Commit ausgelöst, der keinen Chart anfasst (der Merge von #240). Auf GitHub feuerte für denselben Commit korrekt keiner. Die plausible Erklärung ist ein Nachhol-Push über mehrere Commits, bei dem der
paths:-Filter über die ganze Spanne greift. Genau das entsteht beim Einschalten des Spiegels — ein weiterer Grund, die Reihenfolge aus dem Rumpf nicht aufzuweichen.Vorbereitung — was jetzt gebaut wird
validate-charts.ymlzum Laufen und beantwortet Frage 2. Welche Jobs einen Docker-Socket brauchen, wird dabei gemessen, nicht angenommen — Vermutung bisher: nur der Install-Test, während Build, Quantity-Render-Test und Trivy ohne auskommen.ubuntu-docker, sofern der Probelauf das bestätigt, mit der Begründung als Kommentar direkt amruns-on. Ohne Kommentar sieht das Label auf GitHub wie ein Fehler aus und wird beim nächsten Aufräumen „korrigiert".CLAUDE.mdundREADME.mdauf den neuen Ablauf umschreiben: Gitea statt GitHub, Auto-Merge bei grünen Checks statt Merge-Queue.Was beim Maintainer bleibt
Drei Schritte sind nicht delegierbar, zwei davon weil mein Token dort 403 bekommt:
Probelauf auf Gitea — gemessen, nicht übernommen
Ein Wegwerf-PR auf der Instanz (vier Runden, danach geschlossen und Branch gelöscht) hat
validate-charts.ymldort erstmals zum Laufen gebracht. Vorbereitung dazu in #253.Frage 1 des Rumpfes war schon beantwortet. Frage 2 ist es jetzt auch, und sie hat eine unerwartete dritte Antwort mitgebracht.
Läuft der Workflow an?
Ja.
on: pull_requestgreift, die Jobs werden erzeugt,needs,strategy.matrix,if,fromJSON,GITHUB_OUTPUT,GITHUB_STEP_SUMMARYundactions/checkout@v7mitfetch-depth: 0verhalten sich wie auf GitHub.origin/${{ github.base_ref }}existiert im Checkout, die Chart-Erkennung funktioniert unverändert.Welche Jobs brauchen einen Docker-Socket?
Nur der Install-Test. Die Vermutung aus dem vorigen Kommentar trifft zu, hier mit Belegen:
detect-changed-chartsubuntu-latestbuild-chart(lint, package, Version)ubuntu-latestquantity-render(alle Charts)ubuntu-latesttrivy-configubuntu-latestinstall-testubuntu-latestDie Meldung auf
ubuntu-latestlautet wörtlich:Sie zeigt auf k3d, nie auf das Runner-Label — wie vorhergesagt.
Nebenbefund zur Labelfrage: Die Runner sind jetzt indirekt belegt, ohne
actions/runners(dort weiterhin 403). Aus den gelaufenen Jobs:jlab-git-actions-runner-0bedientubuntu-latest,jlab-git-actions-docker-runner-0bedientubuntu-docker. Die Fremdauskunft war richtig.Trägt der Runner den k3d-Cluster? Nein — und das ist der neue Gatepunkt.
ubuntu-dockerhat reichlich Eisen: 8 vCPU, 64 GB RAM, Docker 29.5.2. k3d kommt dort weit — Netz angelegt,rancher/k3sundk3d-proxygeladen, Node erzeugt, LoadBalancer erzeugt. Dann:Mit
--no-rollbacknachgesehen:docker ps -azeigtRestarting (1), unddocker logsist leer — der Einstiegspunkt stirbt sofort, ohne eine Zeile. Die Ursache steht imdocker infodesselben Laufs:Das Docker hinter
ubuntu-dockerist ein rootless Docker-in-Docker ohne Cgroup-Delegation. k3s braucht Cgroups, um überhaupt zu starten.ubuntu-dockerist also notwendig, aber nicht hinreichend.Damit ist genau die Sorte Runner entstanden, vor der der Rumpf warnt — nur schlimmer als „sporadisch rot": er ist immer rot, und die Meldung nennt weder Cgroups noch rootless. Wer nur die k3d-Zeile liest, sucht am falschen Ende.
Für den Maintainer, nicht delegierbar: dem DinD Cgroup-Delegation geben (rootful, oder rootless mit systemd-Delegation). Erst danach ist Frage 3 beantwortbar — auch die Laufzeit des Install-Tests dort ist bis dahin ungemessen.
Dritter Befund, im Plan nicht vorgesehen:
aquasecurity/setup-trivyist auf Gitea unbenutzbartrivy-configwar in Runde 1 rot, ohne jeden Docker-Bezug. Die Action führt internactions/checkoutgegenaquasecurity/trivyauf github.com aus und übergibt dafürgithub.token— auf Gitea ist das ein Gitea-Token. github.com antwortet 401, checkout wiederholt zweimal und endet mit:Eine Meldung, die weder die Action noch das Token nennt. Dazu: ihr
actions/cache-Schritt ist auf der Instanz tot (Cache action is only supported on GHES version >= 3.5), und das Nachladen der verschachtelten Actions kostete 4 min 30 s, bevor der Job anfing. #253 ersetzt sie durch den Release-Tarball; damit isttrivy-configaufubuntu-latestgrün.Die verallgemeinerbare Regel: Actions von github.com nachladen funktioniert (anonym, und
actions/checkout,azure/setup-helmund das 1Password-Action brauchen keinen Ersatz). Was bricht, ist eine Action, die sich selbst gegen github.com authentifiziert.Vierter Befund, wichtig für Stufe 4 (Branch-Schutz)
Gitea expandiert
${{ matrix.chart }}im Job-name:nicht, sondern setzt den Matrix-Ausdruck ein. Der Commit-Status heißt dort:und alle Zweige einer Matrix teilen sich diesen einen Kontext. Folgen: ein Pflicht-Check pro Chart ist auf Gitea nicht einrichtbar, und die Namen der Pflicht-Checks lassen sich nicht von GitHub übernehmen — sie müssen von einem echten Lauf abgelesen werden. (Die Probe hatte nur einen Matrix-Zweig; ob sich zwei Zweige gegenseitig im Status überschreiben, ist damit offen.)
Was
runs-ondaraus machtEin blankes
runs-on: ubuntu-dockerwäre auf GitHub ein Bremsklotz: Das Label existiert dort nicht, also bliebe jeder Chart-PR unbegrenzt in der Warteschlange, ohne Fehlermeldung, solange dieses Repo noch auf github.com baut. #253 setzt deshalbGemessen: Gitea wertet den Ausdruck aus — der Probe-Job wurde vom Docker-Runner mit
labels: ["ubuntu-docker"]genommen. Begründung steht als Kommentar direkt an der Zeile, samt dem Hinweis, dass sie nicht „korrigiert" werden darf.Stand der Stufen
CLAUDE.md/README.md, Issue-Migrationsskript — lauffähig, nicht ausgeführt).Nachtrag: Renovate läuft auf der Instanz bereits — und der Post-Upgrade-Schritt auch
Ein Befund macht Punkt 3 des Vorbereitungsplans („Renovate im Eigenbetrieb als geplanter Lauf") hinfällig. Nachgeprüft über die Gitea-API, nicht übernommen:
jlab-renovate-botexistiert und ist gegenjlab-cluster/helm-chartsaktiv.jlab-cluster/cluster-configmit 25 offenen PRs, läuft also regelmäßig und nicht einmalig.Damit sind zwei Fragen beantwortet, die im Plan gar nicht gestellt waren — und die zweite hätte den Umzug teuer gemacht.
Sie liest unsere
renovate.json5. searxng und gotenberg sind genau unsere eigenencustomManagers-Ziele. Kein eingebauter Manager findet einappVersion:in einerChart.yaml; dass diese beiden PRs entstanden sind, ist der Beleg.allowedCommandsist dort freigegeben, der Versions-Hook läuft. Der Diff von PR #2:version:wird von keinem Renovate-Manager angefasst — diese Zeile kann nur ausci/renovate-bump-chart.shstammen. Das war das stille Risiko: Ohne den Eintrag im Instanz-Config wirdpostUpgradeTaskskommentarlos übersprungen, und jeder Chart-PR auf Gitea wäre an der Versions-Prüfung rot geworden, ohne dass irgendetwas die Ursache genannt hätte. Für den Umzug ist an dieser Stelle also nichts einzurichten.Folge für #253:
.github/workflows/renovate.ymlist wieder ausgebaut. Ein zweiter Lauf gegen dasselbe Repo erzeugt Doppel-PRs und zwei Planer, die sich gegenseitig die Branches umschreiben. Mit dem Workflow entfällt auch das zu pinnende Container-Image; im Trockenlauf nachgeprüft, dassghcr.io/renovatebot/renovatenicht mehr extrahiert wird,TRIVY_VERSIONaber weiterhin — die Regel hängt am Trivy-Ersatz und bleibt richtig.README.mdundCLAUDE.mdsagen jetzt ausdrücklich, dass Renovate dort als bestehende, gemeinsam genutzte Instanz läuft und kein Workflow in dieses Repo gehört; wer hier keinen findet, soll den Grund lesen können, statt einen zu bauen.Was davon unberührt bleibt: der
runs-on-Ausdruck, der Trivy-Ersatz, das Migrationsskript und alle Messergebnisse des Kommentars oben. Die beiden Renovate-PRs auf Gitea sind nicht angefasst.Eine Einschränkung, die daraus neu entsteht: Die Freigabe der Post-Upgrade-Kommandos liegt auf jener Instanz und ist von hier aus nicht einsehbar. Fällt sie irgendwann weg, ist das Symptom in diesem Repo ein roter Chart-PR ohne Erklärung. Das steht jetzt als Kommentar an
postUpgradeTasksinrenovate.json5.Nummernzuordnung der Issue-Migration
Die 17 offenen Issues von GitHub sind hier angelegt. Die Nummern konnten nicht erhalten bleiben — dieses Repo hatte bereits Vorgaenge #1 bis #4 vergeben, und Gitea laesst den Index beim Anlegen nicht setzen.
Deshalb gilt fuer alle migrierten Texte: Verweise der Form
#123beziehen sich auf die GitHub-Nummerierung, nicht auf die Nummern hier. Jeder migrierte Vorgang traegt oben einen Kopf mit Link auf sein Original.#20#40#84#160#161#184#185#186#216#225#227#230#237#238#241#245#247Was bewusst nicht mitkam
#144). Die Renovate-Instanz dieser Seite hat mit #4 bereits ein eigenes angelegt; eine Kopie waere ein Duplikat, das nie wieder aktualisiert wird.Probelauf nach dem MTU-Fix: k3s startet, und der Runner ist nicht langsam
Vierter Anlauf, diesmal mit dem unveränderten Workflow aus PR #253 — die
curl -4-Krücke und die verkürzte Matrix der Diagnoserunden sind weg, die Workflow-Datei ist Byte für Byte die aus dem PR. Nur dieChart.yamlsind angefasst, damit Charts in die Matrix geraten.Beantwortet
k3s startet durch.
Create k3d clusterist grün — in 16 s. Auf GitHub sind es 20 s für denselben Befehl. Der Docker-Runner ist also nicht langsamer, sondern in diesem Schritt eher schneller. Weder/dev/fusenoch User-Namespaces stehen im Weg; nach dem MTU-Fix bootet der k3s-Knoten ohne weitere Schicht dazwischen.Der 1Password-Pfad trägt.
Configure OP-Connect,Load freesailarr VPN credentialsundVerify the VPN credentials arrivedsind auf dem Gitea-Runner grün, beideop://-Felder kommen nicht leer an. Die Voraussetzung für den freesailarr-Test ist damit erfüllt.Zwei neue Befunde, beide im Workflow, nicht am Runner
1. Gitea führt die Matrix-Zweige NEBENLÄUFIG in einem Job aus — und sie kollidieren.
Das korrigiert meinen früheren Bericht: Ich hatte „nacheinander" gemeldet. Falsch. Sie laufen gleichzeitig, auf einem Runner, auf einem Docker-Dämon. Mit cyberchef und freesailarr in der Matrix:
Beide Zweige legen einen Cluster namens
install-testan, und der Rollback des einen reißt dem anderen das Netz weg. Auf GitHub bekommt jeder Zweig eine eigene VM, deshalb konnte der feste Clustername nie kollidieren. Auf Gitea ist er eine geteilte Ressource. Mit einem Chart in der Matrix tritt das nicht auf — und das ist der Fall, den die meisten PRs erzeugen; ein PR, der zwei Charts anfasst, fällt darüber.Dazu passt der schon bekannte Befund: alle Zweige teilen sich einen Status-Kontext, benannt mit dem unaufgelösten Matrix-Ausdruck. Für den Branch-Schutz heißt das weiterhin: kein Pflicht-Check pro Chart.
2.
kubectlfehlt auf dem Gitea-Runner-Image.Mit einem Chart läuft alles bis
Install chart and wait for readiness, und das scheitert in 0 s:ci/run-install-test.shsetzt kubectl voraus. Auf GitHubsubuntu-latestist es vorinstalliert, aufdocker.gitea.com/runner-images:ubuntu-latestnicht. Das ist keine Runner-Fehlkonfiguration, sondern eine Annahme im Workflow, die nur auf GitHub zutrifft — dieselbe Klasse wie dasubuntu-latest-Label, das dort Docker bedeutet und hier nicht.Offen
freesailarr ist weiterhin ungemessen, blockiert von beiden Punkten oben. Die Laufzeit des Install-Tests auf Gitea ist damit nur bis
Create k3d clusterbelegt (16 s gegen 20 s auf GitHub). Zum Vergleich, auf GitHub gemessen statt übernommen: Job gesamt 59 s für searxng, 2m45s für freesailarr.Die MTU-Geschichte, und warum sie hier steht
Die Vorrunde war rot, weil der Runner
get.helm.shnicht erreichte. Meine Messung war korrekt und meine Schlussfolgerung falsch: Ich hatte eine Egress-Sperre für diesen Host vermutet, weil github.com, ghcr.io und Docker Hub antworteten und nurget.helm.shnicht.Die trennende Größe war aber nicht der Host, sondern die Antwortgröße. Der rootful Dämon legte seine Brücken mit Docker-Vorgabe 1500 an, während das Pod-Netz 1450 trägt — eingeschleppt durch genau den Umbau, der das Cgroup-Problem löste. 301, 401 und ein JSON-Körper passen in ein Paket, ein 17-MB-Tarball nicht. Und
docker pulllief, weil der Dämon das aufeth0tut und nicht ein Container auf der Brücke. cluster-config #59 setzt--mtu=1450samt Vorgabe für neu angelegte Brückennetze.Der Merksatz gehört ins Protokoll, weil dieses Repo sonst genau davor warnt: eine korrekte Messung, nach dem falschen Merkmal sortiert. Meine Tabelle stimmte Zeile für Zeile; ich hatte sie nach „welcher Host" gelesen statt nach „wie viele Pakete". Ein künftiger Runner-Umbau kann hier erneut hineinlaufen, und das Fehlerbild wird wieder wie eine Firewall aussehen.
Zwei weitere Korrekturen an meinen eigenen früheren Meldungen, damit der Bestand stimmt:
failure. Zurückgezogen.Trägt der Install-Test auf Gitea?
Noch nicht — aber woran es liegt, ist jetzt vollständig bekannt und liegt in unserer Hand. Die Runner-Umgebung ist nach MTU- und Cgroup-Fix in Ordnung: Cluster startet, Tempo stimmt, Secrets kommen an. Was fehlt, sind zwei Annahmen im Workflow, die nur auf GitHub-Runnern gelten: vorinstalliertes
kubectlund ein Clustername, der nur dann eindeutig ist, wenn jeder Matrix-Zweig seine eigene Maschine hat.Der Install-Test trägt auf Gitea. Drei Läufe, drei mal grün, beide Charts.
Gemessen auf dem Wegwerf-PR der Instanz, mit der Workflow-Datei aus #253 — nicht mit einem Sonderworkflow.
Create k3d clusterStreuung 6 Sekunden. Pro Matrix-Zweig: cyberchef 18 s, freesailarr ~2m59s. Übrige Jobs: detect 3–4 s, build 4–5 s, quantity-render 7 s, trivy 12–13 s.
Der Faktor 1,5 ist strukturell, nicht Leistung
Create k3d clusterDer Runner ist gleichauf, beim Cluster-Anlegen sogar leicht schneller. Der Unterschied in der Gesamtzeit entsteht daraus, dass Gitea alle Matrix-Zweige nebenläufig in einem Job auf einem Runner fährt, während GitHub jedem Zweig eine eigene VM gibt — zwei k3s-Cluster teilen sich dort CPU, Speicher und inotify-Instanzen. Wer die 1,5 als „Gitea ist langsamer" liest, zieht die falsche Konsequenz.
Was ein GitHub-Runner stillschweigend erfüllt
Der eigentliche Ertrag dieser Wochen, und er ist nicht eine Chronik. Es ist die Liste der Annahmen, auf denen unsere Pipeline stand, ohne dass eine davon irgendwo geschrieben stand. Sie zerfällt in zwei Klassen, und die Trennung ist der Punkt.
Klasse 1: Annahmen, die diese Umgebung nicht erfüllt
Ärgerlich, aber ehrlich — sie scheitern sofort und laut, sagen etwas über den Runner und nichts über unseren Code, und sind mit dem Umzug erledigt.
get.helm.shschien gesperrt, war es aber nie.fs.inotify-Grenzen reichen für k3sPendingohne ein einziges Scheduling-Ereignis.kubectlliegt im Runner-AbbildK3S_IMAGE, über Renovate verfolgt, mit Wächter gegen Versionsabstand.envsubstliegt im Runner-Abbild0.0.0.0— aus dem Auftragscontainer auf ihn selbst. Gelöst über #227.Klasse 2: Ein echter Fehler, den die großzügigere Umgebung stillgelegt hat
Der fest verdrahtete k3d-Clustername
install-test. Er war auch auf GitHub falsch. Dort fiel er nur nie auf, weil jeder Matrix-Zweig eine eigene VM mit eigenem Docker-Dämon bekommt und zwei Zweige einander nie sahen. Auf Gitea, wo alle Zweige auf einem Dämon liegen, kollidiert er sofort: ein Zweig meldet „Re-using existing network", der andere stirbt an „The container name /k3d-install-test-server-0 is already in use", und der Rollback des ersten reißt dem zweiten das Netz weg. Zwei rote Zweige, und keine der beiden Meldungen nennt die Ursache.Von den sechs Punkten ist das der einzige, der auch nach dem Umzug noch etwas über unseren Code sagt. Zwei Jahre lang hat die Umgebung ihn getragen, statt ihn zu zeigen.
Nachtrag aus derselben Runde, weil es zur Sache gehört: Auch die Prüfung, die für diesen Nachweis eingebaut wurde, war zweimal hohl, bevor sie taugte —
kubectl get nodesliefert Rückgabewert 0 auch bei leerer Liste, undkubectl wait --for=condition=Ready nodes --allhat dieselbe Lücke, weil die Bedingung bei null Knoten leer erfüllt ist. Der Schritt wurde grün, und der Fehlschlag tauchte fünf Minuten später an unbeteiligter Stelle auf. Eine Prüfung, die ein kaputter Cluster besteht, ist schlechter als keine.Dasselbe Muster, unabhängig gefunden: #257
Die Images in
ci/<chart>/fixtures.yaml, die Renovate nicht verfolgt, sind derselbe Fall in anderer Gestalt: ein grüner Test, der Vertrauen erzeugt, das er nicht deckt. Zwei unabhängige Funde dieser Sorte in einer Woche sind kein Zufall, sondern ein Hinweis, wo als Nächstes zu suchen ist — überall dort, wo etwas grün wird, ohne dass jemand nachgesehen hat, was dabei geprüft wurde.Stand
Stufe 2 aus dem Rumpf ist damit abgeschlossen; #227 ist geschlossen. Die Workflow-Anpassungen liegen in PR #253. Beim Maintainer bleiben unverändert: Branch-Schutz und Signaturpflicht auf dem Gitea-Repo, GitHub-Actions abschalten, danach den Spiegel einschalten — in dieser Reihenfolge.