Ein Versions-Tag (v*) löst den Bau aus, ein Push auf main nicht - so bleibt das Zusammenführen von Branches folgenlos und ein Release ist ein bewusster Schritt. Die CI baut, testet das FERTIGE Image und veröffentlicht erst danach; "latest" nur bei echten Tags. Beim Durchspielen des Prüfschritts zwei Fehler gefunden, die beide erst in der CI aufgefallen wären - und einer davon gar nicht: 1. "docker exec" ohne -i hängt kein stdin an. Das Here-Dokument lief damit ins Leere, python bekam ein leeres Programm, tat nichts und meldete Erfolg. Ein Test, der nie hätte fehlschlagen können. Lokal nachgestellt und mit -i behoben. 2. Die YAML war ungültig: eingebettete Python-Blöcke standen links vom Einzug und brachen den run-Block auf. Das Prüfskript liegt jetzt als scripts/image-pruefen.py vor - dadurch lässt es sich auch lokal gegen ein gebautes Image laufen lassen - und die Warteschleife ist einzeilig. Aus der Kantone-App übernommen: github.ref_name geht über env in die Shell statt direkt als Ausdruck (Tag-Namen dürfen " und $ enthalten, und der Job hält ein Registry-Token), Anmeldung per --password-stdin, docker logout mit if: always(), Rechte auf contents:read und packages:write. docker-compose.betrieb.yml liefert bewusst KEIN Passwort mit: ohne eigenes bleibt der Erfassungsbereich gesperrt. Genau das war die schwerste Lücke der Kantone-App. Ausserdem fehlte eine .dockerignore - der ganze Kontext samt .git und .env ging an den Docker-Daemon, obwohl das Dockerfile nur gezielt kopiert. 81 Tests grün, der CI-Prüfschritt lokal gegen das gebaute Image durchgespielt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GR4bNaj9GtRu57J4Niii8o
58 lines
1.9 KiB
Python
58 lines
1.9 KiB
Python
"""Prüft das fertige Image, bevor es veröffentlicht wird.
|
|
|
|
Wird von der CI in den Container gepipt:
|
|
docker exec -i "$CID" python - < scripts/image-pruefen.py
|
|
|
|
Bewusst eine eigene Datei statt eines Here-Dokuments im Workflow: ein
|
|
unindentierter Block bricht den YAML-Block auf, und als Datei lässt sich
|
|
das Ganze auch lokal gegen ein gebautes Image laufen lassen.
|
|
|
|
Die Untergrenzen liegen bewusst unter dem Ist-Stand, damit der Test nicht
|
|
bei jeder zusätzlichen Kategorie anschlägt.
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
import urllib.error
|
|
import urllib.request
|
|
|
|
BASIS = "http://localhost:8000"
|
|
|
|
|
|
def hole(pfad: str):
|
|
with urllib.request.urlopen(BASIS + pfad) as antwort:
|
|
return json.load(antwort)
|
|
|
|
|
|
def main() -> None:
|
|
kategorien = hole("/api/v1/categories")
|
|
groessen = hole("/api/v1/sizes")
|
|
print(f"Kategorien: {len(kategorien)}, Grössen: {len(groessen)}")
|
|
assert len(kategorien) >= 5, "zu wenige Kategorien - Stammdaten fehlen?"
|
|
assert len(groessen) >= 20, "zu wenige Grössen - Stammdaten fehlen?"
|
|
|
|
with urllib.request.urlopen(BASIS + "/") as antwort:
|
|
seite = antwort.read().decode()
|
|
assert "Kleider-Börse" in seite, "Galerie wird nicht ausgeliefert"
|
|
# Das CSS entsteht in einer eigenen Build-Stufe und würde bei einem
|
|
# Fehler dort still fehlen - die Seite käme dann ohne Gestaltung.
|
|
assert "/static/style.css" in seite, "kein CSS eingebunden"
|
|
print("Galerie und CSS: ok")
|
|
|
|
# Ohne Anmeldung darf nichts geschrieben werden.
|
|
try:
|
|
urllib.request.urlopen(urllib.request.Request(
|
|
BASIS + "/api/v1/items", method="POST",
|
|
data=b'{"size":"80"}',
|
|
headers={"Content-Type": "application/json"},
|
|
))
|
|
except urllib.error.HTTPError as fehler:
|
|
assert fehler.code == 401, f"ergab {fehler.code}, erwartet 401"
|
|
else:
|
|
sys.exit("Schreiben war ohne Anmeldung möglich!")
|
|
print("Schreibschutz greift.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|