Files
scrabble-game/deploy/write-prod-env.sh
T
Ilia Denisov 0a0a9e5a8d
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 27s
CI / ui (pull_request) Successful in 1m11s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m54s
fix(deploy): wire the Robokassa direct rail into the prod deploy + rollback
The Robokassa credentials reached only the test contour (ci.yaml → TEST_
secrets). The prod-deploy / prod-rollback workflows and write-prod-env.sh
never rendered BACKEND_ROBOKASSA_*, so on prod the shop login was empty and
the direct RUB rail stayed disabled — the PROD_BACKEND_ROBOKASSA_* secrets
went nowhere.

Export the shop login + Password1/Password2 (secrets) and a
BACKEND_ROBOKASSA_TEST flag (a variable, so go-live is a flag flip not a
secret rotation) from both prod workflows, and emit the four ROBOKASSA_*
vars from write-prod-env.sh (the shared deploy/rollback env renderer) so a
rollback keeps the rail up. The compose already maps ROBOKASSA_* →
BACKEND_ROBOKASSA_*. Document the new secrets/variable in the deploy README
and .env.example. Password3 (Robokassa's JWT-invoice API) is unused.
2026-07-10 11:59:20 +02:00

93 lines
4.4 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 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'
# 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