Files
ersatztv/scripts/e2e-local.sh
T
timothyandClaude Opus 4.8 0d7803079c
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 9s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 10s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m54s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 17s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 18s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 4m17s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 6m42s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m22s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 5m38s
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 6m2s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m41s
ci: add advisory functional-E2E curl harness (fixes #299)
Codify the manual live-E2E curl flows sessions have been re-running by hand
into a CI regression net: a new `functional-e2e` job boots the app from source
(via scripts/e2e-local.sh, parameterized for Release) and drives scripts/
e2e-functional.sh against it.

First-cut contracts (all curl-only, deterministic, no seeded media/ffmpeg/browser):
- legacy->SPA redirect sweep + the /api,/artwork never-redirect exemption
- auth/CSRF/security-stamp flow (setup-claim, read-gate, CSRF, login, logout+revoke)
- library-scan status contract (404/202/scan-status)
- optimistic-concurrency If-Match/412 round-trip

Advisory by design (separate job, not a `build` dependency, not a required
check) so a functional-E2E flake can't block the unit-test gate; promote once
proven, mirroring the migrations-job rollout. SQLite default -> no DB service.

Deferred to #299 follow-ups (need scanner+seeded media or a browser to be
deterministic): the racy 409 re-trigger, playout-build lock 409, Playwright UI
flows.

Assertions verified 30/30 green against a real Release-built instance; caught
/artwork/* returning 400 (not the 404 a static read suggested).

Docs updated same PR: docs/ci-cd.md (new job), docs/e2e-local.md (harness),
docs/decisions.md (append-only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 13:49:33 +02:00

88 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# scripts/e2e-local.sh — launch a local ErsatzTV instance for live E2E verification.
#
# See docs/e2e-local.md for the full recipe and the "why" behind each step. This script assumes
# `dotnet build ErsatzTV.sln` and (if the SPA changed) `cd web && npm run build` have ALREADY been
# run — it only does the wwwroot copy + launch + ready-wait, since rebuilding on every invocation
# is slow and this is meant to be re-run often during a debugging session.
#
# Usage:
# scripts/e2e-local.sh [CONFIG_DIR]
#
# CONFIG_DIR defaults to a fresh `mktemp -d` if omitted. NEVER reuse a config dir across runs.
#
# On success, prints:
# PID=<pid>
# PORT=<port>
# CONFIG_DIR=<dir>
# LOG=<log file path>
# and exits 0, leaving the server RUNNING in the background. The caller is responsible for
# stopping it (`kill <PID>`) and for freeing the port before starting another run.
set -euo pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
# ETV_BUILD_CONFIG selects which build output to launch (Debug for local dev; the CI functional-E2E
# job builds Release). Must match the `dotnet build --configuration` you ran beforehand.
BUILD_CONFIG="${ETV_BUILD_CONFIG:-Debug}"
BUILD_DIR="$REPO_ROOT/ErsatzTV/bin/$BUILD_CONFIG/net10.0"
PORT="${ETV_UI_PORT:-8409}"
READY_LINE="Done migrating search index"
TIMEOUT_SECS=120
CONFIG_DIR="${1:-$(mktemp -d)}"
mkdir -p "$CONFIG_DIR"
if [ ! -d "$BUILD_DIR" ]; then
echo "error: $BUILD_DIR does not exist — run 'dotnet build ErsatzTV.sln${BUILD_CONFIG:+ --configuration $BUILD_CONFIG}' first" >&2
exit 1
fi
if [ ! -d "$REPO_ROOT/ErsatzTV/wwwroot/app" ]; then
echo "warning: $REPO_ROOT/ErsatzTV/wwwroot/app does not exist — the SPA hasn't been built" \
"('cd web && npm run build'). The /app UI will 404 until it exists AND the server is" \
"(re)started after copying it in." >&2
fi
echo "Copying wwwroot into build output (static middleware resolves its file root at startup;" \
"a running process will never see files added later)..."
# rm first: with an existing destination dir, `cp -R src dst` copies INTO it (dst/wwwroot/...),
# silently leaving a previous run's stale assets in place.
rm -rf "$BUILD_DIR/wwwroot"
cp -R "$REPO_ROOT/ErsatzTV/wwwroot" "$BUILD_DIR/wwwroot"
LOG_FILE="$(mktemp)"
echo "Launching dotnet ErsatzTV.dll (log: $LOG_FILE, config: $CONFIG_DIR, port: $PORT)..."
(
cd "$BUILD_DIR"
ETV_CONFIG_FOLDER="$CONFIG_DIR" ETV_UI_PORT="$PORT" dotnet ErsatzTV.dll
) >"$LOG_FILE" 2>&1 &
PID=$!
echo "Waiting up to ${TIMEOUT_SECS}s for '$READY_LINE'..."
elapsed=0
until grep -q "$READY_LINE" "$LOG_FILE" 2>/dev/null; do
if ! kill -0 "$PID" 2>/dev/null; then
echo "error: process $PID exited before becoming ready. Log tail:" >&2
tail -n 40 "$LOG_FILE" >&2
exit 1
fi
if [ "$elapsed" -ge "$TIMEOUT_SECS" ]; then
echo "error: timed out after ${TIMEOUT_SECS}s waiting for readiness. Log tail:" >&2
tail -n 40 "$LOG_FILE" >&2
kill "$PID" 2>/dev/null || true
exit 1
fi
sleep 1
elapsed=$((elapsed + 1))
done
echo "Server ready."
echo "PID=$PID"
echo "PORT=$PORT"
echo "CONFIG_DIR=$CONFIG_DIR"
echo "LOG=$LOG_FILE"