From da8305c12d933552aaa5779a8fe5e9db49a95167 Mon Sep 17 00:00:00 2001 From: cschlaefke Date: Tue, 3 Nov 2026 10:53:32 +0100 Subject: [PATCH] ci(e2e): address review findings Refs #38 Co-Authored-By: Claude Sonnet 5.5 --- .forgejo/scripts/e2e-run.sh | 20 +++++++++++--- .forgejo/workflows/ci.yml | 40 +++++++++++++++++++++------- e2e/support/start-throttle-gotrue.sh | 14 +++++++--- 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/.forgejo/scripts/e2e-run.sh b/.forgejo/scripts/e2e-run.sh index cd2812c..2caaecb 100755 --- a/.forgejo/scripts/e2e-run.sh +++ b/.forgejo/scripts/e2e-run.sh @@ -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 diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 0bf098e..810790b 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -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. diff --git a/e2e/support/start-throttle-gotrue.sh b/e2e/support/start-throttle-gotrue.sh index 963e0dd..19da8f4 100755 --- a/e2e/support/start-throttle-gotrue.sh +++ b/e2e/support/start-throttle-gotrue.sh @@ -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= 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