diff --git a/deploy/ampel-ci.yml b/deploy/ampel-ci.yml new file mode 100644 index 0000000..11036f4 --- /dev/null +++ b/deploy/ampel-ci.yml @@ -0,0 +1,68 @@ +# Universelle CI-Ampel — wird bei der Repo-Anlage automatisch eingepflanzt +# (gitea-repo-create.sh, Wasserdicht-Runde 22.07.2026) und läuft auf dem +# Gitea-Actions-Runner (arcane-VM). +# GRUNDSATZ: Rot ist ein Ergebnis („nicht bewiesen"), kein Ärgernis. +# • Reines Doku-Repo (noch kein Code) → GRÜN mit Vermerk. +# • Code ohne Tests → ROT. Tests sind Pflicht, kein Deko. +# Der Workflow erkennt selbst, was das Projekt ist (Python / Node / beides). +name: Ampel +on: [push, pull_request] + +jobs: + ampel: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Ampel — erkennt Projekt-Typ selbst und prüft entsprechend + shell: bash + run: | + set -uo pipefail + rot=0 + py_datei=$(find . -name '*.py' -not -path './.git/*' -not -path '*/node_modules/*' -print -quit) + pkg_dateien=$(find . -name package.json -not -path '*/node_modules/*' -not -path './.git/*') + + if [ -z "$py_datei" ] && [ -z "$pkg_dateien" ]; then + echo "✅ Doku-Repo (noch kein Code) — Ampel GRÜN mit Vermerk." + exit 0 + fi + + if [ -n "$py_datei" ]; then + echo "== Python erkannt ==" + python3 -m venv /tmp/ampel-venv && . /tmp/ampel-venv/bin/activate + pip install -q ruff pytest + echo "-- Ruff (Linter: toter Code, kaputte Imports, Schlampereien)" + ruff check . || rot=1 + echo "-- Abhängigkeiten installierbar? (halluzinierte Pakete fliegen hier auf)" + while IFS= read -r req; do + [ -n "$req" ] || continue + pip install -q -r "$req" || { echo "❌ $req nicht installierbar"; rot=1; } + done < <(find . -name 'requirements*.txt' -not -path '*/node_modules/*' -not -path './.git/*') + echo "-- Importierbar? (kaputte Modul-Struktur fliegt hier auf)" + python -m compileall -q . || rot=1 + echo "-- Pytest (keine Tests gefunden = ROT)" + pytest -q; ec=$? + if [ $ec -eq 5 ]; then + echo "❌ KEINE TESTS GEFUNDEN — Tests sind Pflicht, kein Deko (AGENTS.md)." + rot=1 + elif [ $ec -ne 0 ]; then + rot=1 + fi + fi + + if [ -n "$pkg_dateien" ]; then + echo "== Node/TypeScript erkannt ==" + while IFS= read -r pkg; do + [ -n "$pkg" ] || continue + d=$(dirname "$pkg") + echo "-- $d" + (cd "$d" && npm ci --no-audit --no-fund) || { rot=1; continue; } + if grep -q '"build"' "$pkg"; then (cd "$d" && npm run build) || rot=1; fi + if grep -q '"test"' "$pkg"; then (cd "$d" && npm test --silent) || rot=1; fi + done <<< "$pkg_dateien" + fi + + if [ $rot -ne 0 ]; then + echo "❌ AMPEL ROT — nichts heißt ‚fertig', solange das rot ist." + fi + exit $rot diff --git a/deploy/gitea-repo-create.sh b/deploy/gitea-repo-create.sh index 64180d8..464532c 100644 --- a/deploy/gitea-repo-create.sh +++ b/deploy/gitea-repo-create.sh @@ -74,6 +74,31 @@ case "$CODE" in -H 'Content-Type: application/json' \ --data "$(python3 -c 'import json,sys; print(json.dumps({"text":"Neues PRIVATES Projekt-Repo angelegt: "+sys.argv[1],"subject":"Gitea","source":"gitea-repo-create","priority":"silent"}))' "$FULL")" \ >/dev/null 2>&1 || true + # CI-Ampel einpflanzen (Wasserdicht-Runde 22.07.): JEDES neue Repo wird mit der + # universellen Actions-Ampel geboren (deploy/ampel-ci.yml). Defensiv: fehlt die + # Vorlage oder scheitert der Seed, wird die Anlage NICHT abgebrochen, nur gewarnt. + # WICHTIG: Contents-API braucht write:repository → PUSH-Token nutzen (das + # dedizierte Anlage-Token hat u.U. nur write:user). + AMPEL="$REALHOME/mission-control-v2/deploy/ampel-ci.yml" + PUSHTOKEN="$(printf '%s' "$LINE" | sed -nE 's#https://[^:]+:([^@]+)@.*#\1#p')"; PUSHTOKEN="${PUSHTOKEN:-$TOKEN}" + if [ -r "$AMPEL" ]; then + SEED_CODE="$(curl -s -o /dev/null -w '%{http_code}' -X POST "$BASE/api/v1/repos/$FULL/contents/.gitea/workflows/ci.yml" \ + -H "Authorization: token $PUSHTOKEN" -H "Content-Type: application/json" \ + --data "$(python3 - "$AMPEL" <<'PY' +import base64, json, sys +inhalt = open(sys.argv[1], "rb").read() +print(json.dumps({"content": base64.b64encode(inhalt).decode(), + "message": "CI-Ampel (automatisch bei Repo-Anlage eingepflanzt)"})) +PY +)")" + if [ "$SEED_CODE" = "201" ]; then + echo "CI-Ampel eingepflanzt (.gitea/workflows/ci.yml)." + else + echo "WARNUNG: CI-Ampel-Seed antwortete HTTP $SEED_CODE (Repo ist trotzdem da)." >&2 + fi + else + echo "WARNUNG: Ampel-Vorlage fehlt ($AMPEL) — Repo ohne CI-Ampel angelegt." >&2 + fi echo "Repo '$FULL' angelegt (privat, main initialisiert)." echo "CLONE ${CLONE}" exit 0 ;; diff --git a/deploy/projektstart-SOUL.md b/deploy/projektstart-SOUL.md index cb541d3..dc4fbd0 100644 --- a/deploy/projektstart-SOUL.md +++ b/deploy/projektstart-SOUL.md @@ -91,6 +91,15 @@ genau diese Dateien — sonst **nichts**, kein Code, kein Gerüst: bereits gehärtet, ROADMAP.md ist der Plan. Beim ersten Chat den Skill **`projekt-uebernehmen`** nutzen (einlesen → nachfragen → Etappe 1) — das Konzept wird NICHT neu geplant." Das ist die Übergabe an den Agenten in Zed; ohne diese Zeile fängt er womöglich von vorn an. + **Dazu diese drei HARTEN Regeln — wörtlich in die AGENTS.md (Wasserdicht-Runde 22.07., + Lehre aus rippy: Prosa-Regeln wurden komplett ignoriert, darum stehen sie ab jetzt überall):** + „1. Die **CI-Ampel** (Gitea Actions, liegt seit der Repo-Anlage unter `.gitea/workflows/ci.yml` — + NICHT löschen) muss **GRÜN** sein, bevor eine Etappe ‚fertig' heißt. Keine Tests = rot = nicht fertig. + 2. Weicht etwas vom KONZEPT ab (anderes Werkzeug, andere Bibliothek, gestrichenes Muss-Feature): + **STOPP und den Commander fragen** — niemals still ersetzen. (rippy-Lehre: Muss-Feature + ‚MakeMKV lossless' wurde still durch lossy HandBrake ersetzt.) + 3. Deployment NUR über `./deploy.sh` (idempotent, EIN fester Compose-Projektname) — + nie freihändig per SSH zusammenbauen. (rippy-Lehre: Doppel-Anlage `Rippy`+`rippy` auf der VM.)" - **`README.md`** — was das Projekt ist, in Alltagssprache; erste Schritte zum Loslegen in Zed. - **`SAVEPOINT.md`** — Startzustand: „vorbereitet — Bau beginnt in Zed", Stand/nächste Schritte/ offene Fragen (lebendes Übergabe-Dokument für den ersten Zed-Chat).