14918cc94f
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 19s
CI / ui (pull_request) Successful in 1m12s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m50s
Land the offline-model redesign (ANDROID_PLAN.md O1-O7): replace the explicit offline toggle with a single detected net-state machine, unify the lobby, and add a two-tier client-version gate. Contour-safe: both version vars empty => dormant, the wire change is additive, so web/pwa/vk/tg behaviour is unchanged unless the gate is deliberately configured. O1 net-state reducer (test-first). O2 store + wiring (connection/offline shims; +@capacitor/network). O3 remove the offline toggle + migrate the pref. O4 two-tier gate: hard update_required degrades to an offline Update/Play-offline notice (not terminal); soft GATEWAY_RECOMMENDED_CLIENT_VERSION -> X-Update-Recommended nudge. O5 unified lobby (device-local + greyed-from-cache server games; self-set identity; closes G-step-0). O6 create flows (with-friends online/offline segment + offline dict guard). O7 docs. Deploy/CI: wire the version gate through the deploy (GATEWAY_MIN_CLIENT_VERSION + GATEWAY_RECOMMENDED_CLIENT_VERSION as plain unprefixed vars via compose + ci.yaml + prod-deploy.yaml + write-prod-env.sh + .env.example) so the gate is live when set (test-contour commit-hash version fails open; empty => dormant). Disable the manual android-build CI workflow for now (rename .disabled). Bump the app bundle budget 127->130 KB for the added always-loaded wiring. Fix the UI Docker stage (gateway/Dockerfile): install --ignore-scripts so the Alpine/musl SPA build no longer tries to native-build sharp (a local android:assets tool, unused in the image); esbuild's binary is an optional dep so Vite still builds. Tests: gateway go green (two-tier gate over a real HTTP handler); docker build of the landing + gateway targets green locally; compose --no-interpolate confirms the gateway env; svelte-check 0/0, vitest 617, e2e 122/122 (chromium + webkit).
100 lines
4.9 KiB
Bash
Executable File
100 lines
4.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Render the prod main-host runtime env.sh from the workflow job environment.
|
|
#
|
|
# Sourced identically by prod-deploy (deploy-main) and prod-rollback
|
|
# (rollback-main) so the two paths cannot drift: a rollback recreates the
|
|
# containers, so it must re-render the SAME runtime env a full deploy does —
|
|
# otherwise transactional email, VK login and Grafana alerts silently go dark
|
|
# after a rollback until the next full deploy.
|
|
#
|
|
# Usage: APP_VERSION=<tag> bash deploy/write-prod-env.sh <out-path>
|
|
#
|
|
# Every other value comes from the caller's environment (the job `env:` block,
|
|
# vars.* / secrets.*). Mirrors the compose interpolation contract in
|
|
# deploy/.env.example; keep in sync with deploy/docker-compose{,.prod}.yml.
|
|
out="${1:?usage: write-prod-env.sh <out-path>}"
|
|
|
|
# Derive the public URLs from the one canonical origin instead of storing each
|
|
# under its own variable. The path suffixes are structural (SPA routes / the
|
|
# Caddy /_gm sub-path), identical on every contour.
|
|
base="${PUBLIC_BASE_URL%/}"
|
|
TELEGRAM_MINIAPP_URL="$base/telegram/"
|
|
GRAFANA_ROOT_URL="$base/_gm/grafana/"
|
|
VITE_VK_ID_REDIRECT_URL="$base/app/"
|
|
|
|
# Grafana needs a BARE from-address (it rejects the "Name" <addr> form the backend
|
|
# go-mail accepts, and validates it even when SMTP is disabled — a bad value
|
|
# crash-loops Grafana). Split the display-format SERVICE From into address + name;
|
|
# the backend keeps the full form.
|
|
svc_from="${SMTP_RELAY_SERVICE_FROM:-}"
|
|
GRAFANA_SMTP_FROM_NAME=''
|
|
case "$svc_from" in
|
|
*"<"*">"*)
|
|
GRAFANA_SMTP_FROM_ADDRESS="$(printf '%s' "$svc_from" | sed -E 's/.*<([^>]+)>.*/\1/')"
|
|
GRAFANA_SMTP_FROM_NAME="$(printf '%s' "$svc_from" | sed -E 's/[[:space:]]*<[^>]*>.*$//; s/^"//; s/"$//')" ;;
|
|
*) GRAFANA_SMTP_FROM_ADDRESS="$svc_from" ;;
|
|
esac
|
|
|
|
cat > "$out" <<EOF
|
|
export REGISTRY='$REGISTRY'
|
|
export SCRABBLE_CONFIG_DIR='/opt/scrabble'
|
|
export POSTGRES_DB='${POSTGRES_DB:-scrabble}'
|
|
export POSTGRES_USER='${POSTGRES_USER:-scrabble}'
|
|
export POSTGRES_PASSWORD='$POSTGRES_PASSWORD'
|
|
export GM_BASICAUTH_USER='${GM_BASICAUTH_USER:-gm}'
|
|
export GM_BASICAUTH_HASH='$GM_BASICAUTH_HASH'
|
|
export GRAFANA_ADMIN_PASSWORD='$GRAFANA_ADMIN_PASSWORD'
|
|
export GRAFANA_ROOT_URL='$GRAFANA_ROOT_URL'
|
|
export CADDY_SITE_ADDRESS='$CADDY_SITE_ADDRESS'
|
|
export LOG_LEVEL='${LOG_LEVEL:-info}'
|
|
export DICT_VERSION='$DICT_VERSION'
|
|
export APP_VERSION='$APP_VERSION'
|
|
export GATEWAY_MIN_CLIENT_VERSION='$GATEWAY_MIN_CLIENT_VERSION'
|
|
export GATEWAY_RECOMMENDED_CLIENT_VERSION='$GATEWAY_RECOMMENDED_CLIENT_VERSION'
|
|
export TELEGRAM_BOT_TOKEN='$TELEGRAM_BOT_TOKEN'
|
|
export TELEGRAM_MINIAPP_URL='$TELEGRAM_MINIAPP_URL'
|
|
export GATEWAY_VK_APP_SECRET='$GATEWAY_VK_APP_SECRET'
|
|
export ROBOKASSA_MERCHANT_LOGIN='$ROBOKASSA_MERCHANT_LOGIN'
|
|
export ROBOKASSA_PASSWORD1='$ROBOKASSA_PASSWORD1'
|
|
export ROBOKASSA_PASSWORD2='$ROBOKASSA_PASSWORD2'
|
|
export ROBOKASSA_TEST='$ROBOKASSA_TEST'
|
|
export VITE_VK_APP_ID='$VITE_VK_APP_ID'
|
|
export VITE_VK_ID_REDIRECT_URL='$VITE_VK_ID_REDIRECT_URL'
|
|
export GATEWAY_VK_ID_CLIENT_SECRET='$GATEWAY_VK_ID_CLIENT_SECRET'
|
|
export EXPORT_SIGN_KEY='$EXPORT_SIGN_KEY'
|
|
export SMTP_RELAY_HOST='$SMTP_RELAY_HOST'
|
|
export SMTP_RELAY_PORT='$SMTP_RELAY_PORT'
|
|
export SMTP_RELAY_TLS='$SMTP_RELAY_TLS'
|
|
export SMTP_RELAY_USER='$SMTP_RELAY_USER'
|
|
export SMTP_RELAY_PASS='$SMTP_RELAY_PASS'
|
|
export SMTP_RELAY_FROM='$SMTP_RELAY_FROM'
|
|
export SMTP_RELAY_ADMIN_FROM='$SMTP_RELAY_ADMIN_FROM'
|
|
export ADMIN_EMAIL='$ADMIN_EMAIL'
|
|
export SMTP_RELAY_SERVICE_FROM='$SMTP_RELAY_SERVICE_FROM'
|
|
export GRAFANA_SMTP_FROM_ADDRESS='$GRAFANA_SMTP_FROM_ADDRESS'
|
|
export GRAFANA_SMTP_FROM_NAME='$GRAFANA_SMTP_FROM_NAME'
|
|
export SERVICE_EMAIL='$SERVICE_EMAIL'
|
|
export GRAFANA_SMTP_PORT='$GRAFANA_SMTP_PORT'
|
|
export GF_SMTP_ENABLED='$GF_SMTP_ENABLED'
|
|
export PUBLIC_BASE_URL='$PUBLIC_BASE_URL'
|
|
export GATEWAY_HONEYTOKEN='$GATEWAY_HONEYTOKEN'
|
|
export GATEWAY_ABUSE_BAN_ENABLED='true'
|
|
# Community IP blocklist (Spamhaus DROP): opt-in — the operator enables it and sets the feed URL +
|
|
# allowlist via PROD_GATEWAY_BLOCKLIST_* vars once the feed is verified. Unset ⇒ off (safe).
|
|
export GATEWAY_BLOCKLIST_ENABLED='${GATEWAY_BLOCKLIST_ENABLED:-false}'
|
|
export GATEWAY_BLOCKLIST_URL='$GATEWAY_BLOCKLIST_URL'
|
|
export GATEWAY_BLOCKLIST_ALLOW='$GATEWAY_BLOCKLIST_ALLOW'
|
|
# Continuous WAL archiving (pgBackRest -> S3) for point-in-time recovery. The artifact
|
|
# ships disarmed: PGBACKREST_ARCHIVE_MODE defaults off, so archiving stays inert until the
|
|
# operator sets the S3 repository values + secrets, creates the stanza and flips the switch
|
|
# on (deploy/README.md). Empty repository values are harmless while the mode is off.
|
|
export PGBACKREST_ARCHIVE_MODE='${PGBACKREST_ARCHIVE_MODE:-off}'
|
|
export PGBACKREST_S3_ENDPOINT='$PGBACKREST_S3_ENDPOINT'
|
|
export PGBACKREST_S3_PORT='${PGBACKREST_S3_PORT:-443}'
|
|
export PGBACKREST_S3_BUCKET='$PGBACKREST_S3_BUCKET'
|
|
export PGBACKREST_S3_REGION='$PGBACKREST_S3_REGION'
|
|
export PGBACKREST_S3_KEY='$PGBACKREST_S3_KEY'
|
|
export PGBACKREST_S3_KEY_SECRET='$PGBACKREST_S3_KEY_SECRET'
|
|
export PGBACKREST_CIPHER_PASS='$PGBACKREST_CIPHER_PASS'
|
|
EOF
|