Wasserdicht-Runde: universelle CI-Ampel fuer ALLE Lucy/MC2-Repos
- deploy/ampel-ci.yml: sprachneutrale Actions-Ampel (erkennt Python/Node selbst; Doku-Repo=gruen, Code ohne Tests=ROT) - gitea-repo-create.sh: pflanzt die Ampel bei JEDER Repo-Anlage ein (Contents-API, Push-Token, defensiv) - projektstart-SOUL: drei harte AGENTS-Regeln (Ampel-Pflicht, Spec-Abweichung=STOPP, Deploy nur via deploy.sh) — rippy-Lehren Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||||
@@ -74,6 +74,31 @@ case "$CODE" in
|
|||||||
-H 'Content-Type: application/json' \
|
-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")" \
|
--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
|
>/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 "Repo '$FULL' angelegt (privat, main initialisiert)."
|
||||||
echo "CLONE ${CLONE}"
|
echo "CLONE ${CLONE}"
|
||||||
exit 0 ;;
|
exit 0 ;;
|
||||||
|
|||||||
@@ -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`**
|
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."
|
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.
|
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.
|
- **`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/
|
- **`SAVEPOINT.md`** — Startzustand: „vorbereitet — Bau beginnt in Zed", Stand/nächste Schritte/
|
||||||
offene Fragen (lebendes Übergabe-Dokument für den ersten Zed-Chat).
|
offene Fragen (lebendes Übergabe-Dokument für den ersten Zed-Chat).
|
||||||
|
|||||||
Reference in New Issue
Block a user