test(auth): KTO-01 Playwright-E2E (api, web, throttle) und CI-Job e2e #42

Merged
cschlaefke merged 9 commits from feature/38-auth-e2e into main 2026-11-03 10:42:02 +00:00
3 changed files with 57 additions and 17 deletions
Showing only changes of commit da8305c12d - Show all commits

View file

@ -13,12 +13,24 @@ node e2e/support/tcp-forward.mjs "$DOCKER_GW" 54321 54322 54324 9998 &
relay=$!
trap 'kill "$relay" 2>/dev/null || true' EXIT
for port in 54321 54322 54324 9998; do
for _ in $(seq 1 20); do
(exec 3<>"/dev/tcp/127.0.0.1/${port}") 2>/dev/null && break
sleep 0.5
# Warten, bis die Dienste HINTER dem Relay antworten (nicht nur der lokale Port); Timeout = Fehler.
wait_http() {
for _ in $(seq 1 60); do
curl -fsS -o /dev/null --max-time 3 -H "apikey: ${SUPABASE_ANON_KEY}" "$1" 2>/dev/null && return 0
sleep 1
done
echo "Timeout: $1 nicht erreichbar (Relay zu ${DOCKER_GW})" >&2
return 1
}
wait_http "http://127.0.0.1:54321/auth/v1/health" || exit 1 # Kong -> GoTrue
wait_http "http://127.0.0.1:54324/api/v1/info" || exit 1
wait_http "http://127.0.0.1:9998/health" || exit 1
pg_ok=""
for _ in $(seq 1 60); do
if (exec 3<>/dev/tcp/127.0.0.1/54322) 2>/dev/null; then pg_ok=1; break; fi
sleep 1
done
[ -n "$pg_ok" ] || { echo "Timeout: Postgres 54322 nicht erreichbar" >&2; exit 1; }
export SUPABASE_URL=http://127.0.0.1:54321
export MAILPIT_URL=http://127.0.0.1:54324

View file

@ -155,6 +155,9 @@ jobs:
# Nutzt die eingecheckte supabase/config.toml. Ohne passenden Runner Job bei Bedarf deaktivieren.
supabase-tests:
runs-on: docker-host
concurrency:
group: docker-host-supabase
cancel-in-progress: false
env:
DOCKER_CLI_VERSION: 27.3.1
DIND_PORT: 2375
@ -187,12 +190,20 @@ jobs:
# Läuft wie supabase-tests auf "docker-host" (Docker-CLI + dind per TCP). Der Stack liegt im dind; ein
# TCP-Relay (e2e/support/tcp-forward.mjs) macht ihn im Job unter 127.0.0.1 erreichbar.
# SICHERHEIT: Der Job führt PR-Code mit Zugriff auf den dind-Daemon des Runners aus (Daemon = Root-Äquivalent).
# Deshalb nicht für Fork-PRs (Bedingung unten); Fork-Beiträge nur nach Review und Übernahme in einen Branch dieses Repos.
# Gleiches Risiko hat supabase-tests; dort ist es (noch) nicht eingeschränkt.
# needs: supabase-tests, weil beide Jobs dieselben festen Container-Namen/Ports (project_id shuffle, 54321 ...) im dind belegen.
# Der `if:` unten ist KEIN Schutz vor böswilligen Forks: Bei pull_request kommt der Workflow aus dem PR-Head,
# ein Fork kann die Bedingung einfach entfernen. Er verhindert nur versehentliche Läufe. Die echte Absicherung
# gehört in Repo-/Runner-Einstellungen (Freigabe für Fork-PRs, Runner nur für vertrauenswürdige Repos; Folge-Issue).
# Gleiches Risiko hat supabase-tests.
# needs + concurrency: Beide Jobs belegen dieselben festen Container-Namen/Ports (project_id shuffle, 54321 ...) im dind.
# `needs` serialisiert innerhalb eines Laufs; die concurrency-Gruppe (auch an supabase-tests) soll Läufe verschiedener
# PRs hintereinander schalten. Ob Forgejo 8.0.3 / Runner v5.0.4 Job-concurrency auswertet, ist hier nicht verifiziert;
# sonst wird der Schlüssel ignoriert und Kollisionen bleiben möglich (dann Runner mit capacity 1 verwenden).
# Der Spike (SPIKE_GOTRUE_URL, Issue #37) läuft nicht in CI.
e2e:
runs-on: docker-host
concurrency:
group: docker-host-supabase
cancel-in-progress: false
needs: [supabase-tests]
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
env:
@ -206,9 +217,11 @@ jobs:
- name: Werkzeuge prüfen
run: |
missing=""
for t in git curl unzip xz jq; do command -v "$t" >/dev/null || missing="$missing $t"; done
for t in git curl unzip xz jq; do
command -v "$t" >/dev/null || { [ "$t" = xz ] && missing="$missing xz-utils" || missing="$missing $t"; }
done
if [ -n "$missing" ]; then
apt-get update -qq && apt-get install -y -qq --no-install-recommends $missing xz-utils ca-certificates
apt-get update -qq && apt-get install -y -qq --no-install-recommends $missing ca-certificates
fi
node --version
@ -224,20 +237,27 @@ jobs:
DOCKER_HOST="tcp://${gw}:${DIND_PORT}" docker version --format 'dind {{.Server.Version}}'
- name: Supabase starten (mit Mailpit, ohne Studio u. a.)
run: npx --yes supabase@${{ env.SUPABASE_CLI_VERSION }} start -x studio,imgproxy,edge-runtime,logflare,vector
# Ausgabe nicht ins Log: Die Zusammenfassung von `start` enthält die lokalen Schlüssel (noch nicht maskiert).
run: |
if ! npx --yes supabase@${{ env.SUPABASE_CLI_VERSION }} start -x studio,imgproxy,edge-runtime,logflare,vector >"${RUNNER_TEMP:-/tmp}/supabase-start.log" 2>&1; then
echo "supabase start fehlgeschlagen, letzte Zeilen (Schlüssel gefiltert):"
tail -n 40 "${RUNNER_TEMP:-/tmp}/supabase-start.log" | grep -viE 'key|secret|token|jwt'
exit 1
fi
echo "supabase start ok"
- name: Datenbank zurücksetzen
run: npx --yes supabase@${{ env.SUPABASE_CLI_VERSION }} db reset
- name: Lokale Schlüssel lesen
# Die Werte sind die öffentlichen Demo-Schlüssel der CLI, trotzdem nur maskiert weiterreichen.
# Öffentliche Demo-Schlüssel der CLI; trotzdem wird die Maske gesetzt, bevor sie irgendwo ausgegeben werden.
run: |
status=$(npx --yes supabase@${{ env.SUPABASE_CLI_VERSION }} status -o env)
status=$(npx --yes supabase@${{ env.SUPABASE_CLI_VERSION }} status -o env 2>/dev/null)
get() { printf '%s\n' "$status" | sed -n "s/^$1=\"\{0,1\}\([^\"]*\)\"\{0,1\}$/\1/p" | head -n1; }
anon=$(get ANON_KEY); service=$(get SERVICE_ROLE_KEY)
[ -z "$anon" ] || echo "::add-mask::$anon"
[ -z "$service" ] || echo "::add-mask::$service"
test -n "$anon" && test -n "$service" || { echo "ANON_KEY/SERVICE_ROLE_KEY nicht in 'supabase status -o env' gefunden"; exit 1; }
echo "::add-mask::$anon"
echo "::add-mask::$service"
echo "SUPABASE_ANON_KEY=${anon}" >> "$GITHUB_ENV"
echo "SUPABASE_SERVICE_ROLE_KEY=${service}" >> "$GITHUB_ENV"
# Env-Datei für den Web-Build: Platzhalter-Datei bleibt unverändert, der Schlüssel kommt zur Laufzeit.

View file

@ -17,6 +17,8 @@
# Docker-in-Docker (CI, DOCKER_HOST=tcp://...): Der Port wird im dind veroeffentlicht, nicht im Job-Container.
# Dann BIND_ADDR=0.0.0.0 und HEALTH_HOST=<dind-Gateway> setzen (siehe .forgejo/workflows/ci.yml, Job e2e);
# die Tests erreichen den Container ueber e2e/support/tcp-forward.mjs auf 127.0.0.1.
# Bindung: lokal nur 127.0.0.1. Unter dind muss 0.0.0.0 gelten (der Job-Container erreicht den dind nur ueber
# das Gateway; eine feinere Bindung ist dort nicht moeglich). Der Port ist dann im Netz des Runners sichtbar.
# Nur lokal/CI benutzen: Die kopierte Umgebung enthaelt die lokalen Demo-Secrets der CLI.
set -euo pipefail
@ -24,7 +26,9 @@ BIND_ADDR="${BIND_ADDR:-127.0.0.1}"
HEALTH_HOST="${HEALTH_HOST:-127.0.0.1}"
ACTION="${1:-start}"
PROFILE="${2:-throttle}"
SOURCE="${SOURCE_CONTAINER:-$(docker ps --format '{{.Names}}' | grep -E '^supabase_auth_' | head -n1 || true)}"
# Auth-Container gezielt ueber die Projekt-ID (project_id in supabase/config.toml), nicht "erster Treffer"
PROJECT_ID="${SUPABASE_PROJECT_ID:-shuffle}"
SOURCE="${SOURCE_CONTAINER:-supabase_auth_${PROJECT_ID}}"
case "$PROFILE" in
throttle) NAME="${CONTAINER_NAME:-shuffle_auth_throttle}"; HOST_PORT="${HOST_PORT:-9998}" ;;
@ -39,8 +43,8 @@ if [ "$ACTION" = "stop" ]; then
fi
[ "$ACTION" = "start" ] || { echo "Aktion: start|stop" >&2; exit 2; }
if [ -z "$SOURCE" ]; then
echo "Kein laufender supabase_auth_*-Container gefunden. Zuerst 'supabase start'." >&2
if ! docker inspect "$SOURCE" >/dev/null 2>&1; then
echo "Container $SOURCE nicht gefunden. Zuerst 'supabase start' (oder SUPABASE_PROJECT_ID setzen)." >&2
exit 1
fi
@ -52,6 +56,7 @@ ENV_ARGS=()
while IFS= read -r -d '' entry; do
case "$entry" in
GOTRUE_RATE_LIMIT_HEADER=*) continue ;; # wird unten gezielt gesetzt
API_EXTERNAL_URL=*) continue ;; # wird unten explizit gesetzt
GOTRUE_SMTP_PORT=*) [ "$PROFILE" = "smtp-down" ] && continue ;;
esac
ENV_ARGS+=(-e "$entry")
@ -62,6 +67,9 @@ case "$PROFILE" in
smtp-down) ENV_ARGS+=(-e "GOTRUE_SMTP_PORT=1") ;; # Host bleibt Mailpit, Port 1 lehnt die Verbindung ab
esac
# API_EXTERNAL_URL explizit (Spec D2): Kong-URL der CLI, nur Basis fuer Links in Mails/Redirects.
ENV_ARGS+=(-e "API_EXTERNAL_URL=${API_EXTERNAL_URL:-http://127.0.0.1:54321}")
docker run -d --name "$NAME" --network "$NETWORK" -p "${BIND_ADDR}:${HOST_PORT}:9999" \
"${ENV_ARGS[@]}" "$IMAGE" auth >/dev/null