Big visual overhaul docker compsoe file etc
Some checks failed
CI / test-and-quality (push) Failing after 4m4s

This commit is contained in:
Asger Geel Weirsøe
2026-03-23 14:11:30 +01:00
parent d86941fef8
commit a81bc1250c
92 changed files with 11584 additions and 1686 deletions

15
infra/env/.env.dev.example vendored Normal file
View File

@@ -0,0 +1,15 @@
DJANGO_SECRET_KEY=change-me-dev
DJANGO_DEBUG=true
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
DB_ENGINE=django.db.backends.mysql
DB_NAME=wpp_dev
DB_USER=wpp_dev
DB_PASSWORD=wpp_dev
DB_HOST=127.0.0.1
DB_PORT=3307
TEST_DB_NAME=wpp_test
CHANNEL_REDIS_HOST=127.0.0.1
CHANNEL_REDIS_PORT=6380
USE_SPA_UI=false
WPP_SPA_ASSET_BASE=http://localhost:4200/browser
WPP_SPA_ASSET_VERSION=dev

View File

@@ -10,3 +10,6 @@ DB_PORT=3306
TEST_DB_NAME=
CHANNEL_REDIS_HOST=127.0.0.1
CHANNEL_REDIS_PORT=6379
USE_SPA_UI=false
WPP_SPA_ASSET_BASE=/static/frontend/angular/browser
WPP_SPA_ASSET_VERSION=prod-dev

View File

@@ -10,3 +10,6 @@ DB_PORT=3306
TEST_DB_NAME=
CHANNEL_REDIS_HOST=127.0.0.1
CHANNEL_REDIS_PORT=6379
USE_SPA_UI=false
WPP_SPA_ASSET_BASE=/static/frontend/angular/browser
WPP_SPA_ASSET_VERSION=staging-dev

View File

@@ -10,3 +10,6 @@ DB_PORT=3306
TEST_DB_NAME=wpp_test
CHANNEL_REDIS_HOST=127.0.0.1
CHANNEL_REDIS_PORT=6379
USE_SPA_UI=false
WPP_SPA_ASSET_BASE=/static/frontend/angular/browser
WPP_SPA_ASSET_VERSION=test-dev

View File

@@ -9,6 +9,18 @@ Staging-miljø for WPP i Proxmox LXC, så release-klar kode kan deployes og smok
- Service: wpp-staging.service
- Health endpoint: GET /healthz
- Database: MySQL (staging må ikke bruge SQLite, issue #133)
- Aktuel MVP UI-path: legacy host/player UI (`USE_SPA_UI=false`)
## MVP env-kontrakt
Staging skal mindst have følgende release-relevante env vars sat:
- `DB_ENGINE=django.db.backends.mysql`
- `CHANNEL_REDIS_HOST` + `CHANNEL_REDIS_PORT`
- `USE_SPA_UI=false`
- `WPP_SPA_ASSET_BASE=/static/frontend/angular/browser`
- `WPP_SPA_ASSET_VERSION=<release-tag eller sha>`
`USE_SPA_UI=true` er ikke del af den primære MVP release-gate. Det hører til separat cutover-verifikation.
## Verifikation
Kør fra devops-shell med Proxmox-adgang:
@@ -24,6 +36,23 @@ Forventet:
Smoke-suite skriver nu et gameplay-artifact som JSON under `/opt/wpp-staging/app/artifacts/smoke/` (kan overrides via `ARTIFACT_DIR`/`ARTIFACT_FILE`).
Før manuel UI-smoke anbefales følgende bootstrap på staging-app'en:
python manage.py bootstrap_mvp
Det sikrer en host-bruger og aktiv demo-kategori/spørgsmål uden ad hoc admin-oprettelse.
For den automatiske MVP bootstrap + smoke artifact flow bruges den kanoniske kommando:
./infra/staging/run_mvp_smoke.sh
Kommandoen kører i staging-CT via Proxmox, loader staging-env, kører `bootstrap_mvp`, og derefter `smoke_staging --artifact ...`.
Som default håndhæver den MVP-pathen `USE_SPA_UI=false`. Brug kun `ALLOW_SPA_CUTOVER=1` ved separat SPA-cutover.
For release-lignende "én kommando" execution bruges wrapperen:
./infra/staging/deploy_and_smoke_staging.sh [ref] [artifact-path]
Efter deploy validerer scriptet, at `DB_ENGINE` ikke er `django.db.backends.sqlite3` før migrations køres.
Deploy-scriptet bruger en release-candidate mappe og promoverer først til `/opt/wpp-staging/app` efter succesfuld `migrate`. Det reducerer schema/code drift ved afbrudte deploys (issue #130) og understøtter release-readiness gate (issue #90).
@@ -35,6 +64,10 @@ Officiel kommando:
./infra/staging/deploy_staging.sh [ref]
Anbefalet release-wrapper:
./infra/staging/deploy_and_smoke_staging.sh [ref] [artifact-path]
Scriptet bruger default PROXMOX_HOST=proxmox-lan og kører sudo -n pct exec på hosten.
Eksempler:
@@ -42,9 +75,25 @@ Eksempler:
./infra/staging/deploy_staging.sh
./infra/staging/deploy_staging.sh v0.3.0
PROXMOX_HOST=proxmox-prod ./infra/staging/deploy_staging.sh main
./infra/staging/deploy_and_smoke_staging.sh main
./infra/staging/deploy_and_smoke_staging.sh v0.3.0 /opt/wpp-staging/app/artifacts/smoke/release-smoke.json
## Smoke (canonical execution context)
MVP smoke skal køres via Proxmox host over SSH, ligesom deploy:
./infra/staging/run_mvp_smoke.sh
Eksempler:
./infra/staging/run_mvp_smoke.sh
./infra/staging/run_mvp_smoke.sh /opt/wpp-staging/app/artifacts/smoke/manual-smoke.json
PROXMOX_HOST=proxmox-prod CT_ID=222 ./infra/staging/run_mvp_smoke.sh
ALLOW_SPA_CUTOVER=1 ./infra/staging/run_mvp_smoke.sh
## Policy-kobling
Før deploy:
1. Bekræft at tester ikke er aktiv (ingen aktiv smoke-run).
2. Deploy til staging skal lykkes.
3. Først derefter må release-tag oprettes (se docs/RELEASE_POLICY.md).
2. Kør helst `./infra/staging/deploy_and_smoke_staging.sh` for release-kandidater.
3. Hvis wrapper ikke bruges: deploy til staging og kør derefter `./infra/staging/run_mvp_smoke.sh`.
4. Bekræft MVP UI-smoke på legacy UI (`/lobby/ui/host` + `/lobby/ui/player`).
5. Først derefter må release-tag oprettes (se docs/RELEASE_POLICY.md).

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REF_NAME="${1:-main}"
ARTIFACT_FILE="${2:-}"
echo "[release] deploy + smoke start REF=${REF_NAME}"
"${SCRIPT_DIR}/deploy_staging.sh" "${REF_NAME}"
if [[ -n "${ARTIFACT_FILE}" ]]; then
"${SCRIPT_DIR}/run_mvp_smoke.sh" "${ARTIFACT_FILE}"
else
"${SCRIPT_DIR}/run_mvp_smoke.sh"
fi
echo "[release] deploy + smoke OK REF=${REF_NAME}"

97
infra/staging/run_mvp_smoke.sh Executable file
View File

@@ -0,0 +1,97 @@
#!/usr/bin/env bash
set -euo pipefail
CT_ID="${CT_ID:-143}"
PROXMOX_HOST="${PROXMOX_HOST:-proxmox-lan}"
APP_DIR="${APP_DIR:-/opt/wpp-staging/app}"
ARTIFACT_FILE="${1:-${ARTIFACT_FILE:-}}"
ARTIFACT_DIR_ARG="${ARTIFACT_DIR:-}"
BASE_URL_ARG="${BASE_URL:-}"
ISSUE_ON_FAIL="${ISSUE_ON_FAIL:-1}"
RUN_BOOTSTRAP_MVP="${RUN_BOOTSTRAP_MVP:-1}"
BOOTSTRAP_MVP_ARGS="${BOOTSTRAP_MVP_ARGS:-}"
ALLOW_SPA_CUTOVER="${ALLOW_SPA_CUTOVER:-0}"
ENV_FILE_ARG="${ENV_FILE:-}"
GITEA_BASE_ARG="${GITEA_BASE:-}"
GITEA_REPO_ARG="${GITEA_REPO:-}"
GITEA_USER_ARG="${GITEA_USER:-}"
GITEA_TOKEN_ARG="${GITEA_TOKEN:-}"
echo "[smoke] host=${PROXMOX_HOST} CT_ID=${CT_ID} APP_DIR=${APP_DIR}"
if [[ -n "${ARTIFACT_FILE}" ]]; then
echo "[smoke] artifact=${ARTIFACT_FILE}"
fi
ssh "${PROXMOX_HOST}" sudo -n /usr/sbin/pct exec "${CT_ID}" -- bash -s -- \
"${APP_DIR}" \
"${ARTIFACT_FILE}" \
"${ARTIFACT_DIR_ARG}" \
"${BASE_URL_ARG}" \
"${ISSUE_ON_FAIL}" \
"${RUN_BOOTSTRAP_MVP}" \
"${BOOTSTRAP_MVP_ARGS}" \
"${ALLOW_SPA_CUTOVER}" \
"${ENV_FILE_ARG}" \
"${GITEA_BASE_ARG}" \
"${GITEA_REPO_ARG}" \
"${GITEA_USER_ARG}" \
"${GITEA_TOKEN_ARG}" <<'REMOTE'
set -euo pipefail
APP_DIR="$1"
ARTIFACT_FILE="$2"
ARTIFACT_DIR_ARG="$3"
BASE_URL_ARG="$4"
ISSUE_ON_FAIL="$5"
RUN_BOOTSTRAP_MVP="$6"
BOOTSTRAP_MVP_ARGS="$7"
ALLOW_SPA_CUTOVER="$8"
ENV_FILE_ARG="$9"
GITEA_BASE_ARG="${10}"
GITEA_REPO_ARG="${11}"
GITEA_USER_ARG="${12}"
GITEA_TOKEN_ARG="${13}"
SCRIPT_PATH="${APP_DIR}/infra/staging/smoke_suite.sh"
if [[ ! -f "${SCRIPT_PATH}" ]]; then
echo "[smoke] ERROR: missing script ${SCRIPT_PATH}" >&2
exit 1
fi
ENV_VARS=(
"APP_DIR=${APP_DIR}"
"ISSUE_ON_FAIL=${ISSUE_ON_FAIL}"
"RUN_BOOTSTRAP_MVP=${RUN_BOOTSTRAP_MVP}"
"BOOTSTRAP_MVP_ARGS=${BOOTSTRAP_MVP_ARGS}"
"ALLOW_SPA_CUTOVER=${ALLOW_SPA_CUTOVER}"
)
if [[ -n "${ARTIFACT_FILE}" ]]; then
ENV_VARS+=("ARTIFACT_FILE=${ARTIFACT_FILE}")
fi
if [[ -n "${ARTIFACT_DIR_ARG}" ]]; then
ENV_VARS+=("ARTIFACT_DIR=${ARTIFACT_DIR_ARG}")
fi
if [[ -n "${BASE_URL_ARG}" ]]; then
ENV_VARS+=("BASE_URL=${BASE_URL_ARG}")
fi
if [[ -n "${ENV_FILE_ARG}" ]]; then
ENV_VARS+=("ENV_FILE=${ENV_FILE_ARG}")
fi
if [[ -n "${GITEA_BASE_ARG}" ]]; then
ENV_VARS+=("GITEA_BASE=${GITEA_BASE_ARG}")
fi
if [[ -n "${GITEA_REPO_ARG}" ]]; then
ENV_VARS+=("GITEA_REPO=${GITEA_REPO_ARG}")
fi
if [[ -n "${GITEA_USER_ARG}" ]]; then
ENV_VARS+=("GITEA_USER=${GITEA_USER_ARG}")
fi
if [[ -n "${GITEA_TOKEN_ARG}" ]]; then
ENV_VARS+=("GITEA_TOKEN=${GITEA_TOKEN_ARG}")
fi
runuser -u wpp -- env "${ENV_VARS[@]}" bash "${SCRIPT_PATH}"
REMOTE
echo "[smoke] OK: staging MVP smoke complete"

View File

@@ -4,6 +4,9 @@ set -euo pipefail
BASE_URL="${BASE_URL:-http://127.0.0.1:8000}"
APP_DIR="${APP_DIR:-/opt/wpp-staging/app}"
ISSUE_ON_FAIL="${ISSUE_ON_FAIL:-1}"
RUN_BOOTSTRAP_MVP="${RUN_BOOTSTRAP_MVP:-1}"
BOOTSTRAP_MVP_ARGS="${BOOTSTRAP_MVP_ARGS:-}"
ALLOW_SPA_CUTOVER="${ALLOW_SPA_CUTOVER:-0}"
fail() {
local message="$1"
@@ -50,10 +53,36 @@ PY
echo "[smoke] healthz check: ${BASE_URL}/healthz"
curl -fsS "${BASE_URL}/healthz" >/dev/null || { SMOKE_FAIL_MESSAGE="healthz check failed" fail "healthz check failed"; }
ENV_FILE="${ENV_FILE:-/etc/wpp/staging.env}"
resolve_env_file() {
if [[ -n "${ENV_FILE:-}" ]]; then
if [[ -f "${ENV_FILE}" ]]; then
printf '%s\n' "${ENV_FILE}"
return 0
fi
return 1
fi
local candidate
for candidate in \
/opt/wpp-staging/.env.staging \
/opt/wpp-staging/.env \
/opt/wpp-staging/env/wpp_staging.env \
/opt/wpp-staging/secrets/wpp_staging.env \
/etc/wpp/staging.env
do
if [[ -f "${candidate}" ]]; then
printf '%s\n' "${candidate}"
return 0
fi
done
return 1
}
ENV_FILE="$(resolve_env_file)" || { SMOKE_FAIL_MESSAGE="staging env file not found" fail "staging env file not found"; }
echo "[smoke] env file: ${ENV_FILE}"
run_manage() {
local cmd="$1"
(
cd "${APP_DIR}"
if [[ -f "${ENV_FILE}" ]]; then
@@ -62,18 +91,37 @@ run_manage() {
source "${ENV_FILE}"
set +a
fi
.venv/bin/python manage.py ${cmd}
.venv/bin/python manage.py "$@"
)
}
echo "[smoke] migration consistency check"
run_manage "migrate --check --noinput" || { SMOKE_FAIL_MESSAGE="schema drift: unapplied migrations in staging" fail "schema drift: unapplied migrations in staging"; }
run_manage migrate --check --noinput || { SMOKE_FAIL_MESSAGE="schema drift: unapplied migrations in staging" fail "schema drift: unapplied migrations in staging"; }
if [[ "${ALLOW_SPA_CUTOVER}" != "1" ]]; then
echo "[smoke] MVP UI mode check (expect USE_SPA_UI=false)"
run_manage shell -c "from django.conf import settings; import sys; print('USE_SPA_UI=' + ('true' if settings.USE_SPA_UI else 'false')); sys.exit(0 if not settings.USE_SPA_UI else 1)" \
|| { SMOKE_FAIL_MESSAGE="USE_SPA_UI=true is outside the canonical MVP smoke path" fail "USE_SPA_UI=true is outside the canonical MVP smoke path"; }
else
echo "[smoke] SPA cutover override enabled (ALLOW_SPA_CUTOVER=1)"
fi
if [[ "${RUN_BOOTSTRAP_MVP}" == "1" ]]; then
echo "[smoke] bootstrap MVP host + demo questions"
if [[ -n "${BOOTSTRAP_MVP_ARGS}" ]]; then
# shellcheck disable=SC2206
bootstrap_args=(${BOOTSTRAP_MVP_ARGS})
run_manage bootstrap_mvp "${bootstrap_args[@]}" || { SMOKE_FAIL_MESSAGE="manage.py bootstrap_mvp failed" fail "manage.py bootstrap_mvp failed"; }
else
run_manage bootstrap_mvp || { SMOKE_FAIL_MESSAGE="manage.py bootstrap_mvp failed" fail "manage.py bootstrap_mvp failed"; }
fi
fi
ARTIFACT_DIR="${ARTIFACT_DIR:-${APP_DIR}/artifacts/smoke}"
ARTIFACT_FILE="${ARTIFACT_FILE:-${ARTIFACT_DIR}/smoke-$(date -u +%Y%m%dT%H%M%SZ).json}"
echo "[smoke] gameplay flow via management command"
run_manage "smoke_staging --artifact ${ARTIFACT_FILE}" || { SMOKE_FAIL_MESSAGE="manage.py smoke_staging failed" fail "manage.py smoke_staging failed"; }
run_manage smoke_staging --artifact "${ARTIFACT_FILE}" || { SMOKE_FAIL_MESSAGE="manage.py smoke_staging failed" fail "manage.py smoke_staging failed"; }
echo "[smoke] artifact: ${ARTIFACT_FILE}"
echo "[smoke] OK"