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
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>
88 lines
3.1 KiB
Bash
Executable File
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"
|