Pipeline 518 today was red with zero jobs - a push where no rule matched, which GitLab still turns into a pipeline and then marks failed. The standstill check reported it correctly, and it was the only finding standing between the scheduled checks and green. Acknowledging it would have been the wrong move twice over: the entry would name one pipeline id, so the next empty pipeline files a fresh finding, and the underlying behaviour - red without a fault - is exactly what management #0104 is about. Rules that prevent the empty pipeline remove the class instead. schedule comes first deliberately. The daily canonization changes no paths, so a changes-based rule alone would drop it.
225 lines
12 KiB
YAML
225 lines
12 KiB
YAML
# Leichter Verifikations-Job, portiert aus .gitea/workflows/deploy-on-push.yml
|
|
# (Gitea-CI-Rueckbau, CFGMON-11, erledigt 2026-08-01). Deployt nichts - Flux reconciled
|
|
# weiterhin selbststaendig aus dem Gitea-Mirror. Repo-Topologie: git.lab ist
|
|
# kanonisch, rohana/Gitea ist Push-Mirror und Flux-Quelle.
|
|
|
|
# Ohne workflow-Block legt GitLab auch dann eine Pipeline an, wenn KEIN Job auf sie
|
|
# passt - und fuehrt sie als "failed". Das ist rot ohne Fehler, und rot ohne Fehler
|
|
# gewoehnt einem das Hinsehen ab (management #0104). Real passiert am 2026-08-19,
|
|
# Pipeline 518. Diese Regeln verhindern die leere Pipeline, statt sie hinterher zu
|
|
# quittieren.
|
|
#
|
|
# Reihenfolge zaehlt: schedule zuerst, sonst faellt die taegliche Kanonisierung mit
|
|
# heraus - sie aendert keine Pfade und wuerde von der changes-Regel nie erfasst.
|
|
.pfade_mit_pipeline: &pfade_mit_pipeline
|
|
- apps/**/*
|
|
- clusters/**/*
|
|
- .gitlab-ci.yml
|
|
|
|
workflow:
|
|
rules:
|
|
- if: $CI_PIPELINE_SOURCE == "schedule"
|
|
- if: $CI_PIPELINE_SOURCE == "web"
|
|
- if: $CI_PIPELINE_SOURCE == "api"
|
|
- if: $CI_COMMIT_BRANCH == "main"
|
|
changes: *pfade_mit_pipeline
|
|
- when: never
|
|
|
|
verify:
|
|
image: alpine:3.20
|
|
rules:
|
|
- if: $CI_COMMIT_BRANCH == "main"
|
|
changes:
|
|
- apps/**/*
|
|
- clusters/**/*
|
|
- .gitlab-ci.yml
|
|
script:
|
|
- apk add --no-cache git >/dev/null
|
|
- |
|
|
echo "YAML-Manifest-Check..."
|
|
find apps clusters -name "*.yaml" -type f | while read f; do
|
|
grep -q "^apiVersion:" "$f" || echo "WARN: $f enthaelt kein apiVersion - evtl. kein K8s-Manifest"
|
|
done
|
|
- |
|
|
echo "SOPS-Check der in diesem Push geaenderten Secret-Dateien..."
|
|
for f in $(git diff --name-only HEAD~1..HEAD -- '**/secret*.yaml' '**/credentials*.yaml' 2>/dev/null || true); do
|
|
if grep -q "ENC\[" "$f"; then echo "OK: $f ist verschluesselt"; else echo "WARNUNG: $f ist moeglicherweise NICHT verschluesselt!"; fi
|
|
done
|
|
- echo "Flux reconciled die Aenderungen innerhalb ~1 Minute (Quelle Gitea-Mirror)."
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TURN-Rotation kanonisieren (laeuft NUR als Pipeline-Schedule)
|
|
#
|
|
# Der Rotations-CronJob laeuft im Cluster und erreicht git.lab nicht - er pusht
|
|
# seinen Branch deshalb nach Gitea. Von dort muss die Rotation ueber git.lab
|
|
# zurueck, sonst ueberschreibt der naechste Mirror-Push sie und Flux spielt still
|
|
# das ALTE Shared Secret wieder ein. Frueher war das ein monatlicher Handgriff;
|
|
# dieser Job erledigt ihn.
|
|
#
|
|
# Einrichtung:
|
|
# 1. OFFEN: Project Access Token, Rolle Maintainer, Scope write_repository
|
|
# -> CI/CD-Variable CANONIZE_TOKEN (masked + protected). Nur sorb legt das
|
|
# an; ohne das Token laeuft der Leerlauf gruen durch, aber die erste
|
|
# echte Rotation bricht ab. Siehe gitops#51.
|
|
# 2. ERLEDIGT: Schedule "TURN-Rotation kanonisieren (taeglich)", 17:05 Europe/
|
|
# Berlin, angelegt 2026-08-02. Probelaeufe: Pipeline 159 deckte die
|
|
# fehlende Lab-CA auf, 161 lief gruen durch ("Keine offene Rotation").
|
|
#
|
|
# Warum taeglich statt monatlich zum Rotationstermin: ein monatlicher Schedule
|
|
# hat genau einen Versuch. Faellt der Runner an dem Tag aus, faellt die
|
|
# Kanonisierung ein Monat lang aus. Taeglich holt der Job jede offene Rotation
|
|
# spaetestens am Folgetag nach und ist ansonsten in Sekunden fertig.
|
|
# ---------------------------------------------------------------------------
|
|
canonize_rotation:
|
|
image: alpine:3.20
|
|
rules:
|
|
- if: $CI_PIPELINE_SOURCE == "schedule"
|
|
variables:
|
|
GIT_DEPTH: "0" # volle Historie: wir brauchen merge-base
|
|
script:
|
|
- apk add --no-cache git ca-certificates >/dev/null
|
|
- |
|
|
# Lab-CA. Der Runner stellt sie nur seinem eigenen Klon-Schritt bereit, nicht
|
|
# dem Job-Script - ein 'git fetch origin' im Script scheiterte deshalb an
|
|
# "unable to get local issuer certificate" (live gesehen, Pipeline 159).
|
|
# Deshalb liegt die Kette hier im Repo; sie ist ein oeffentliches Zertifikat,
|
|
# kein Geheimnis. CI_SERVER_TLS_CA_FILE zuerst, falls der Runner sie doch liefert.
|
|
if [ -n "$CI_SERVER_TLS_CA_FILE" ] && [ -f "$CI_SERVER_TLS_CA_FILE" ]; then
|
|
export GIT_SSL_CAINFO="$CI_SERVER_TLS_CA_FILE"
|
|
else
|
|
export GIT_SSL_CAINFO="$CI_PROJECT_DIR/ci/lab-ca-chain.crt"
|
|
fi
|
|
echo "CA: $GIT_SSL_CAINFO"
|
|
test -f "$GIT_SSL_CAINFO" || { echo "ABBRUCH: CA-Datei fehlt."; exit 1; }
|
|
- git config --global user.email "ci@axion1337.chat"
|
|
- git config --global user.name "TURN-Rotation (automatische Kanonisierung)"
|
|
- git remote add gitea https://rohana.axion1337.de/sorb/axion1337.chat-gitops.git
|
|
# --prune ist Pflicht, nicht Kosmetik: der Runner recycelt seinen Workspace
|
|
# (GIT_STRATEGY=fetch), und ohne prune bleiben remote-tracking-Refs geloeschter
|
|
# Zweige stehen. Der Job saehe einen laengst entfernten Rotationszweig sonst
|
|
# dauerhaft weiter - live beobachtet am 2026-08-18, Pipeline 492: beide Remotes
|
|
# waren nachweislich leer, der Job meldete den Zweig trotzdem.
|
|
- git fetch --quiet --prune gitea
|
|
# Kein 'fetch origin' noetig: der Runner hat main schon ausgecheckt (GIT_DEPTH 0),
|
|
# CI_COMMIT_SHA ist der Stand von main beim Anlegen der Pipeline.
|
|
- git checkout -B main "$CI_COMMIT_SHA"
|
|
- |
|
|
# Zeitpunkt der Rotation aus dem SOPS-Metadatenblock lesen. 'lastmodified'
|
|
# ist Metadatum, kein Geheimnis - es steht im Klartext neben den ENC[]-Werten.
|
|
rotationszeit() {
|
|
git show "$1:apps/production/coturn-secret.yaml" 2>/dev/null \
|
|
| sed -n 's/^[[:space:]]*lastmodified:[[:space:]]*"\(.*\)".*/\1/p' | head -1
|
|
}
|
|
MERGED=0
|
|
UEBERHOLT=""
|
|
for ref in $(git for-each-ref --format='%(refname:short)' 'refs/remotes/gitea/turn-secret-rotation-*'); do
|
|
SHA=$(git rev-parse "$ref")
|
|
if git merge-base --is-ancestor "$SHA" HEAD; then
|
|
echo "bereits kanonisiert: $ref"
|
|
continue
|
|
fi
|
|
# Ueberholte Zweige NICHT mergen. Ein Zweig, dessen Rotation aelter ist als
|
|
# der Stand in main, bringt nichts Neues, konfliktiert aber mit allem, was
|
|
# seither passiert ist - inklusive coturn-secret.yaml und
|
|
# synapse-turn-secret.yaml. Eine unbedachte Hand-Aufloesung wuerde das
|
|
# TURN-Shared-Secret ZURUECKDREHEN; Synapse und coturn waeren dann uneins
|
|
# und TURN tot. Real passiert: der Zweig vom 2026-07-28 blieb nach dem Merge
|
|
# auf Gitea liegen und hat diesen Job neun Tage lang taeglich rot gefaerbt -
|
|
# womit die rote Pipeline als Alarm wertlos wurde (AGENTS.md: sie IST der
|
|
# einzige Meldeweg). Deshalb ist ein Ueberbleibsel hier Aufraeumarbeit und
|
|
# kein Vorfall: gemeldet ja, rot nein.
|
|
NEU=$(rotationszeit "$SHA"); IST=$(rotationszeit HEAD)
|
|
if [ -n "$NEU" ] && [ -n "$IST" ] \
|
|
&& [ "$(printf '%s\n%s\n' "$NEU" "$IST" | sort | head -1)" = "$NEU" ]; then
|
|
echo "ueberholt: $ref traegt die Rotation vom $NEU, main steht auf $IST."
|
|
UEBERHOLT="$UEBERHOLT ${ref#gitea/}"
|
|
continue
|
|
fi
|
|
echo "kanonisiere: $ref ($SHA)"
|
|
SUBJ="chore(coturn): TURN-Rotation aus ${ref#gitea/} uebernommen"
|
|
BODY="Automatisch kanonisiert: der Rotations-CronJob im Cluster erreicht git.lab nicht und pusht nach Gitea; dieser Commit bringt die Rotation auf den kanonischen Weg zurueck, bevor der Mirror sie ueberschreiben kann."
|
|
if ! git merge --no-ff -m "$SUBJ" -m "$BODY" "$SHA"; then
|
|
echo "MERGE-KONFLIKT in $ref - es wird nichts gepusht."
|
|
echo "⚠️ Beim Aufloesen NIEMALS die aeltere Fassung von coturn-secret.yaml oder"
|
|
echo " synapse-turn-secret.yaml uebernehmen: beide muessen dasselbe Shared"
|
|
echo " Secret tragen, sonst faellt TURN aus. Im Zweifel main behalten."
|
|
echo "Siehe CLAUDE.md (Abschnitt Repo Topology)."
|
|
exit 1
|
|
fi
|
|
MERGED=1
|
|
done
|
|
if [ -n "$UEBERHOLT" ]; then
|
|
echo
|
|
echo "AUFRAEUMEN: diese Zweige sind ueberholt und koennen auf Gitea geloescht werden:"
|
|
for b in $UEBERHOLT; do echo " - $b"; done
|
|
echo "(Der Job bleibt gruen - liegengebliebene Zweige sind kein Vorfall.)"
|
|
fi
|
|
echo "MERGED=$MERGED" > .canonize_state
|
|
- |
|
|
. ./.canonize_state
|
|
if [ "$MERGED" = "0" ]; then
|
|
echo "Keine offene Rotation - nichts zu tun."
|
|
exit 0
|
|
fi
|
|
# Token erst hier pruefen, nicht am Jobanfang: solange nichts zu pushen ist,
|
|
# soll der taegliche Lauf gruen durchgehen und die Gitea-Strecke verifizieren.
|
|
# Fehlt er, wenn es wirklich etwas zu tun gibt, ist Abbruch richtig.
|
|
if [ -z "$CANONIZE_TOKEN" ]; then
|
|
echo "ABBRUCH: Es gibt eine offene Rotation, aber CANONIZE_TOKEN ist nicht gesetzt."
|
|
echo "Siehe Kopf dieser Datei, Einrichtungsschritt 1."
|
|
exit 1
|
|
fi
|
|
# Sicherheitsnetz: die Rotation fasst genau diese Secrets an. Kaeme hier
|
|
# etwas Unverschluesseltes durch, waere das Shared Secret im Klartext in Git.
|
|
for f in apps/production/coturn-secret.yaml apps/production/synapse-turn-secret.yaml; do
|
|
if ! grep -q "ENC\[" "$f"; then
|
|
echo "ABBRUCH: $f ist nicht SOPS-verschluesselt - es wird nichts gepusht."
|
|
exit 1
|
|
fi
|
|
echo "OK: $f ist verschluesselt"
|
|
done
|
|
git push "https://oauth2:${CANONIZE_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" HEAD:main
|
|
echo "Gepusht. Der Mirror traegt es nach Gitea zurueck, der PR schliesst sich dort selbst;"
|
|
echo "der Rotations-Branch existiert dann nur noch auf Gitea und wird vom Mirror entfernt."
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Wiki-Inhalt kanonisieren (laeuft NUR als Pipeline-Schedule, ADR-0015)
|
|
#
|
|
# Wiki.js laeuft im Cluster und erreicht git.lab nicht - es pusht seinen Inhalt
|
|
# deshalb nach Gitea (sorb/ThreadNetWiki, oeffentlich lesbar). Dieser Job spiegelt
|
|
# den Stand von dort nach git.lab axion1337.chat/threadnet-wiki, damit der
|
|
# kanonische Speicher wieder im Lab liegt. Gleiche Umkehrung wie die TURN-Rotation.
|
|
#
|
|
# Einrichtung:
|
|
# 1. ERLEDIGT: Project Access Token (Rolle Maintainer, Scope write_repository) auf
|
|
# axion1337.chat/threadnet-wiki -> gitops-CI/CD-Variable WIKI_CANONIZE_TOKEN
|
|
# (masked + protected).
|
|
# 2. Schedule: laeuft im bestehenden Tages-Schedule mit (CI_PIPELINE_SOURCE ==
|
|
# "schedule"). threadnet-wiki wurde leer angelegt -> der erste Push legt main an,
|
|
# danach nur noch Fast-Forward (kein Force, Branch-Protection bleibt).
|
|
canonize_wiki:
|
|
image: alpine:3.20
|
|
rules:
|
|
- if: $CI_PIPELINE_SOURCE == "schedule"
|
|
variables:
|
|
GIT_DEPTH: "1" # nur fuer ci/lab-ca-chain.crt aus dem ausgecheckten Repo
|
|
script:
|
|
- apk add --no-cache git ca-certificates >/dev/null
|
|
- |
|
|
# Lab-CA fuer den git.lab-Push; der Gitea-Klon nutzt die System-CA (oeffentliches Zert).
|
|
if [ -n "$CI_SERVER_TLS_CA_FILE" ] && [ -f "$CI_SERVER_TLS_CA_FILE" ]; then
|
|
LABCA="$CI_SERVER_TLS_CA_FILE"
|
|
else
|
|
LABCA="$CI_PROJECT_DIR/ci/lab-ca-chain.crt"
|
|
fi
|
|
test -f "$LABCA" || { echo "ABBRUCH: Lab-CA-Datei fehlt."; exit 1; }
|
|
if [ -z "$WIKI_CANONIZE_TOKEN" ]; then
|
|
echo "ABBRUCH: WIKI_CANONIZE_TOKEN nicht gesetzt (siehe Kopf, Einrichtungsschritt 1)."
|
|
exit 1
|
|
fi
|
|
- git config --global user.email "ci@axion1337.chat"
|
|
- git config --global user.name "ThreadNet-Wiki Kanonisierung"
|
|
- git clone --bare --quiet https://rohana.axion1337.de/sorb/ThreadNetWiki.git /tmp/wiki.git
|
|
- GIT_SSL_CAINFO="$LABCA" git -C /tmp/wiki.git push "https://oauth2:${WIKI_CANONIZE_TOKEN}@${CI_SERVER_HOST}/axion1337.chat/threadnet-wiki.git" 'refs/heads/main:refs/heads/main'
|
|
- echo "git.lab threadnet-wiki spiegelt jetzt Gitea sorb/ThreadNetWiki (main)."
|