feat: configure Gitea deployment and environment-managed WorldQuant credentials
Deploy production / deploy (push) Has been cancelled

This commit is contained in:
yuxuanhui
2026-09-09 09:58:09 +08:00
parent 20645d6d17
commit b8429efa3d
18 changed files with 430 additions and 45 deletions
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
# Deploy on the target Linux Docker host with configuration injected by Gitea.
# Returns nonzero on configuration/build/migration/health failure. Never removes data.
set -Eeuo pipefail
umask 077
cd "$(dirname "${BASH_SOURCE[0]}")/.."
# Fail before touching the host; never read a checkout's local .env file.
for key in WQ_EMAIL WQ_PASSWORD DATABASE_URL ADMIN_PASSWORD ENCRYPTION_KEY DATABASE_NETWORK PUBLIC_ORIGIN; do
if [[ -z "${!key:-}" ]]; then
echo "Missing required Gitea configuration: $key" >&2
exit 1
fi
done
state_dir="${DEPLOY_STATE_DIR:-/opt/wq-alpha}"
if [[ "$state_dir" != /* || ! -d "$state_dir" || ! -w "$state_dir" ]]; then
echo 'DEPLOY_STATE_DIR must be an existing writable absolute directory.' >&2
exit 1
fi
command -v flock >/dev/null
# Stable across checkouts; stores only a lock and release metadata, never credentials.
exec 9>"$state_dir/deploy.lock"
flock -n 9 || { echo 'Another production deployment is running.' >&2; exit 1; }
export DEPLOY_TAG="${DEPLOY_TAG:-$(git rev-parse HEAD)}"
compose=(docker compose --env-file /dev/null -p wq-alpha-production -f compose.production.yaml)
trap 'rc=$?; "${compose[@]}" --profile jobs ps -a || true; exit "$rc"' EXIT
"${compose[@]}" config --quiet
"${compose[@]}" --profile jobs config --quiet
"${compose[@]}" build backend web
# Validate secrets and DB connectivity before interrupting the running version.
"${compose[@]}" run --rm --no-deps backend python -c '
import asyncio
from app.config import Settings
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine
async def check():
settings = Settings()
engine = create_async_engine(settings.database_url)
try:
async with engine.connect() as connection:
await connection.execute(text("SELECT 1"))
finally:
await engine.dispose()
try:
asyncio.run(check())
except Exception:
raise SystemExit("Production configuration/database preflight failed; check env and database access.") from None
'
# Record immutable image references before switching; do not prune old images.
previous_images=$("${compose[@]}" images --quiet)
if [[ -n "$previous_images" ]]; then
docker image inspect --format '{{.Id}} {{json .RepoTags}}' $previous_images > "$state_dir/previous-images.txt"
fi
"${compose[@]}" stop web backend
"${compose[@]}" --profile jobs run --rm --no-deps migrate
"${compose[@]}" up -d --no-build --remove-orphans --wait --wait-timeout 180 backend web
printf '%s\n' "$DEPLOY_TAG" > "$state_dir/current-release.txt"
echo "Production is healthy; release $DEPLOY_TAG"
+1
View File
@@ -14,6 +14,7 @@ content = "\n".join([
f"ADMIN_PASSWORD={secrets.token_urlsafe(24)}",
f"POSTGRES_PASSWORD={secrets.token_hex(24)}",
f"ENCRYPTION_KEY={base64.urlsafe_b64encode(secrets.token_bytes(32)).decode()}",
"WQ_EMAIL=", "WQ_PASSWORD=",
"LOCAL_PORT=8080", "DOMAIN=alpha.example.com", "",
])
try: