feat(deploy): Staging-Umgebung und einheitliches Deploy-Skript
Fügt docker-compose.staging.yml, Staging-Dokumentation und -dest prod|stage in update-prod.sh hinzu, damit Prod und Staging über ein Skript deploybar sind. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -15,6 +15,11 @@ DeepLAPIKey=
|
||||
# Production (kapteins-daagbok.eu):
|
||||
# RP_ID=kapteins-daagbok.eu
|
||||
# ORIGIN=https://kapteins-daagbok.eu
|
||||
# Staging (staging.kapteins-daagbok.eu):
|
||||
# RP_ID=staging.kapteins-daagbok.eu
|
||||
# ORIGIN=https://staging.kapteins-daagbok.eu
|
||||
# POSTGRES_DB=daagbox_staging
|
||||
# NTFY_TOPIC=kapteins-daagbok-staging-feedback
|
||||
RP_ID=localhost
|
||||
# Must match the frontend URL exactly (Vite dev: http://localhost:5173; Docker: http://localhost)
|
||||
ORIGIN=http://localhost:5173
|
||||
|
||||
@@ -251,15 +251,25 @@ Produktions-Update auf den Server (konfigurierbar via Umgebungsvariablen). Führ
|
||||
|
||||
|
||||
```bash
|
||||
./scripts/update-prod.sh
|
||||
./scripts/update-prod.sh -dest prod
|
||||
```
|
||||
|
||||
Standard-Ziel: `root@10.0.0.25:/opt/kapteins-daagbok` — per `REMOTE_HOST`, `REMOTE_USER`, `REMOTE_DIR` überschreibbar.
|
||||
Standard-Ziel Prod: `root@10.0.0.25:/opt/kapteins-daagbok` — per `REMOTE_HOST`, `REMOTE_USER`, `REMOTE_DIR` überschreibbar.
|
||||
|
||||
Auf dem Server müssen `.env` u. a. `POSTGRES_PASSWORD`, `RP_ID`, `ORIGIN` (`https://kapteins-daagbok.eu`), `SESSION_SECRET` (≥ 32 Zeichen), `TRUST_PROXY` (NPM, z. B. `172.16.10.10` oder `1`) und bei Push `VAPID_*` enthalten. Optional `NTFY_*` für Feedback. Nach Schema-Änderungen: `npx prisma db push` im Backend-Container.
|
||||
|
||||
Hinter **Nginx Proxy Manager**: [docs/deployment/npm-security.md](docs/deployment/npm-security.md).
|
||||
|
||||
### Staging
|
||||
|
||||
Testumgebung unter [staging.kapteins-daagbok.eu](https://staging.kapteins-daagbok.eu) — Deploy ohne Release-Tag:
|
||||
|
||||
```bash
|
||||
./scripts/update-prod.sh -dest stage
|
||||
```
|
||||
|
||||
Standard-Ziel Staging: `root@10.0.0.27:/opt/kapteins-daagbok-staging` — per `REMOTE_HOST`, `REMOTE_DIR`, `DEPLOY_BRANCH` überschreibbar. Details: [docs/deployment/staging.md](docs/deployment/staging.md).
|
||||
|
||||
## Dokumentation
|
||||
|
||||
| Dokument | Inhalt |
|
||||
@@ -267,6 +277,7 @@ Hinter **Nginx Proxy Manager**: [docs/deployment/npm-security.md](docs/deploymen
|
||||
| [docs/deployment/npm-security.md](docs/deployment/npm-security.md) | NPM, TLS, `trust proxy`, Security-Header |
|
||||
| [docs/deployment/predeploy.md](docs/deployment/predeploy.md) | Pre-Deploy-Checks ohne CI |
|
||||
| [docs/deployment/postgres-password.md](docs/deployment/postgres-password.md) | PostgreSQL-Passwort rotieren / App-Rolle |
|
||||
| [docs/deployment/staging.md](docs/deployment/staging.md) | Staging-VM, Deploy, `.env` |
|
||||
| [docs/plausible-events.md](docs/plausible-events.md) | Custom Events für Plausible Analytics |
|
||||
| [docs/push-notifications-plan.md](docs/push-notifications-plan.md) | Web Push: Architektur, API, Testplan |
|
||||
| [docs/plan-compass-course-dial.md](docs/plan-compass-course-dial.md) | Kompass-Dial: UX- und Implementierungsplan |
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
services:
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
container_name: daagbox-staging-db
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-daagbox_staging}
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U \"$$POSTGRES_USER\" -d \"$$POSTGRES_DB\""]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: ./server
|
||||
dockerfile: Dockerfile
|
||||
container_name: daagbox-staging-backend
|
||||
restart: always
|
||||
environment:
|
||||
PORT: 5000
|
||||
DATABASE_URL: "postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-daagbox_staging}?schema=public"
|
||||
RP_ID: ${RP_ID:-localhost}
|
||||
ORIGIN: ${ORIGIN:-http://localhost}
|
||||
TRUST_PROXY: ${TRUST_PROXY:-1}
|
||||
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-}
|
||||
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-}
|
||||
VAPID_SUBJECT: ${VAPID_SUBJECT:-mailto:support@kapteins-daagbok.eu}
|
||||
OpenWeatherMapAPIKey: ${OpenWeatherMapAPIKey:-}
|
||||
OpenRouterAPIKey: ${OpenRouterAPIKey:-}
|
||||
OpenRouterModel: ${OpenRouterModel:-anthropic/claude-3.5-haiku}
|
||||
SESSION_SECRET: ${SESSION_SECRET:-}
|
||||
ADMIN_USER_IDS: ${ADMIN_USER_IDS:-}
|
||||
NTFY_SERVER: ${NTFY_SERVER:-https://ntfy.sh}
|
||||
NTFY_TOPIC: ${NTFY_TOPIC:-}
|
||||
NTFY_TOKEN: ${NTFY_TOKEN:-}
|
||||
command: sh -c "npx prisma db push && node dist/index.js"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: client/Dockerfile
|
||||
args:
|
||||
APP_VERSION: ${APP_VERSION:-0.1.0.0-dev}
|
||||
container_name: daagbox-staging-frontend
|
||||
restart: always
|
||||
ports:
|
||||
- "80:80"
|
||||
depends_on:
|
||||
backend:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
name: daagbox-staging-pgdata
|
||||
@@ -1,14 +1,14 @@
|
||||
# Deployment: Nginx Proxy Manager & Security (Sprint 1)
|
||||
|
||||
Kapteins Daagbok läuft öffentlich unter **https://kapteins-daagbok.eu/** hinter **Nginx Proxy Manager** (NPM, z. B. `172.16.10.10`) mit Upstream auf den App-Stack (`172.16.10.110`).
|
||||
Kapteins Daagbok läuft öffentlich unter **https://kapteins-daagbok.eu/** (Produktion) und **https://staging.kapteins-daagbok.eu/** (Staging) hinter **Nginx Proxy Manager** (NPM, z. B. `172.16.10.10`) mit Upstream auf die App-VMs (`10.0.0.25` Prod, `10.0.0.27` Staging).
|
||||
|
||||
## NPM Proxy Host
|
||||
|
||||
| Einstellung | Wert |
|
||||
|-------------|------|
|
||||
| Domain | `kapteins-daagbok.eu` |
|
||||
| Domain | `kapteins-daagbok.eu` / `staging.kapteins-daagbok.eu` |
|
||||
| Scheme | `https` |
|
||||
| Forward Hostname / IP | `172.16.10.110` (oder Container-Port auf dem Host) |
|
||||
| Forward Hostname / IP | `10.0.0.25` (Prod) / `10.0.0.27` (Staging) |
|
||||
| Forward Port | `80` (Frontend-Nginx) |
|
||||
| Websockets | an, falls genutzt |
|
||||
| Block Common Exploits | an |
|
||||
|
||||
@@ -34,9 +34,9 @@ cd server && npm test
|
||||
[`scripts/update-prod.sh`](../../scripts/update-prod.sh) führt `predeploy-check.sh` **automatisch** aus (nach Release-Vorbereitung, vor dem SSH-Deploy).
|
||||
|
||||
```bash
|
||||
./scripts/update-prod.sh
|
||||
./scripts/update-prod.sh -dest prod
|
||||
```
|
||||
|
||||
Notfall ohne Checks (nur wenn nötig): `SKIP_PREDEPLOY_CHECK=1 ./scripts/update-prod.sh`
|
||||
Notfall ohne Checks (nur wenn nötig): `SKIP_PREDEPLOY_CHECK=1 ./scripts/update-prod.sh -dest prod`
|
||||
|
||||
Manuell auf dem Server: `git pull`, `docker compose build`, `docker compose up -d` (siehe [npm-security.md](npm-security.md)).
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# Staging-Umgebung
|
||||
|
||||
Staging läuft auf **VM3** (`10.0.0.27`) unter **https://staging.kapteins-daagbok.eu/** — hinter Nginx Proxy Manager wie Produktion.
|
||||
|
||||
## Unterschiede zu Produktion
|
||||
|
||||
| | Staging | Produktion |
|
||||
|---|---------|------------|
|
||||
| Host | `10.0.0.27` | `10.0.0.25` |
|
||||
| Verzeichnis | `/opt/kapteins-daagbok-staging` | `/opt/kapteins-daagbok` |
|
||||
| Compose | `docker-compose.staging.yml` | `docker-compose.yml` |
|
||||
| Deploy-Skript | `./scripts/update-prod.sh -dest stage` | `./scripts/update-prod.sh -dest prod` |
|
||||
| Release-Tag | nein | ja (`v*`) |
|
||||
| Datenbank-Volume | `daagbox-staging-pgdata` | `daagbox-prod-pgdata` |
|
||||
|
||||
Staging ist **vollständig isoliert**: eigene DB, Session-Secrets, Passkeys (`RP_ID=staging.kapteins-daagbok.eu`) und optional eigene VAPID-/Ntfy-Konfiguration.
|
||||
|
||||
## Erstinstallation (VM3)
|
||||
|
||||
```bash
|
||||
ssh root@10.0.0.27
|
||||
|
||||
git clone https://gitea.elpatron.me/elpatron/kapteins-daagbok.git /opt/kapteins-daagbok-staging
|
||||
cd /opt/kapteins-daagbok-staging
|
||||
git checkout master
|
||||
|
||||
# .env anlegen — Secrets neu generieren, nicht von Prod kopieren
|
||||
openssl rand -hex 24 # POSTGRES_PASSWORD
|
||||
openssl rand -base64 48 # SESSION_SECRET
|
||||
|
||||
nano .env
|
||||
docker compose -f docker-compose.staging.yml up -d --build
|
||||
```
|
||||
|
||||
### `.env` (Staging)
|
||||
|
||||
```env
|
||||
ORIGIN=https://staging.kapteins-daagbok.eu
|
||||
RP_ID=staging.kapteins-daagbok.eu
|
||||
TRUST_PROXY=1
|
||||
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=<generiert>
|
||||
POSTGRES_DB=daagbox_staging
|
||||
|
||||
SESSION_SECRET=<generiert>
|
||||
|
||||
NTFY_SERVER=https://ntfy.sh
|
||||
NTFY_TOPIC=kapteins-daagbok-staging-feedback
|
||||
```
|
||||
|
||||
Optional: `VAPID_*`, `OpenWeatherMapAPIKey`, `OpenRouterAPIKey`, `ADMIN_USER_IDS`, `NTFY_TOKEN`.
|
||||
|
||||
## Deploy vom Entwicklungsrechner
|
||||
|
||||
Führt `npm run check` aus, dann SSH-Deploy ohne Release-Tag:
|
||||
|
||||
```bash
|
||||
./scripts/update-prod.sh -dest stage
|
||||
```
|
||||
|
||||
Konfiguration via Umgebungsvariablen:
|
||||
|
||||
```bash
|
||||
REMOTE_HOST=10.0.0.27 \
|
||||
REMOTE_DIR=/opt/kapteins-daagbok-staging \
|
||||
DEPLOY_BRANCH=master \
|
||||
./scripts/update-prod.sh -dest stage
|
||||
```
|
||||
|
||||
Notfall ohne Checks: `SKIP_PREDEPLOY_CHECK=1 ./scripts/update-prod.sh -dest stage`
|
||||
|
||||
## NPM (VM1)
|
||||
|
||||
| Einstellung | Wert |
|
||||
|-------------|------|
|
||||
| Domain | `staging.kapteins-daagbok.eu` |
|
||||
| Forward Hostname / IP | `10.0.0.27` |
|
||||
| Forward Port | `80` |
|
||||
| SSL | Let's Encrypt |
|
||||
|
||||
Empfohlen: Custom Header `X-Robots-Tag: noindex, nofollow` (Staging nicht indexieren).
|
||||
|
||||
Details zu Proxy-Headern und Security: [npm-security.md](npm-security.md).
|
||||
|
||||
## Nach Deploy prüfen
|
||||
|
||||
1. https://staging.kapteins-daagbok.eu/api/health — `status: ok`
|
||||
2. Neuen Test-Account registrieren (Prod-Passkeys funktionieren nicht auf Staging)
|
||||
3. Passkey Login
|
||||
4. Cookie `daagbok_session`: `Secure`, `HttpOnly`, `SameSite=Lax`
|
||||
|
||||
## Daten zurücksetzen
|
||||
|
||||
Staging-Daten sind wegwerfbar:
|
||||
|
||||
```bash
|
||||
cd /opt/kapteins-daagbok-staging
|
||||
docker compose -f docker-compose.staging.yml down
|
||||
docker volume rm daagbox-staging-pgdata
|
||||
docker compose -f docker-compose.staging.yml up -d
|
||||
```
|
||||
+128
-32
@@ -2,28 +2,102 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Remote deployment configuration
|
||||
# Override any of these via environment variables if needed, e.g.:
|
||||
# REMOTE_HOST=192.168.1.10 ./scripts/update-prod.sh
|
||||
REMOTE_USER="${REMOTE_USER:-root}"
|
||||
REMOTE_HOST="${REMOTE_HOST:-10.0.0.25}"
|
||||
REMOTE_DIR="${REMOTE_DIR:-/opt/kapteins-daagbok}"
|
||||
REMOTE_TARGET="${REMOTE_USER}@${REMOTE_HOST}"
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") [-dest prod|stage]
|
||||
|
||||
# Configuration
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
BACKEND_CONTAINER="daagbox-prod-backend"
|
||||
MAX_WAIT=35
|
||||
Deploy Kapteins Daagbok to production or staging.
|
||||
|
||||
-dest prod Production (default): release tag, bump VERSION, deploy to 10.0.0.25
|
||||
-dest stage Staging: no release tag, deploy branch to 10.0.0.27
|
||||
|
||||
Environment overrides (optional):
|
||||
REMOTE_HOST, REMOTE_USER, REMOTE_DIR, COMPOSE_FILE, BACKEND_CONTAINER
|
||||
DEPLOY_BRANCH (stage only, default: master)
|
||||
SKIP_PREDEPLOY_CHECK=1
|
||||
|
||||
Examples:
|
||||
$(basename "$0") -dest prod
|
||||
$(basename "$0") -dest stage
|
||||
DEPLOY_BRANCH=feature/foo $(basename "$0") -dest stage
|
||||
EOF
|
||||
}
|
||||
|
||||
DEST="prod"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-dest)
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "Error: -dest requires an argument (prod or stage)." >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
DEST="$2"
|
||||
shift 2
|
||||
;;
|
||||
-dest=*)
|
||||
DEST="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unknown argument: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$DEST" in
|
||||
prod|stage) ;;
|
||||
*)
|
||||
echo "Error: Invalid -dest '$DEST' (use prod or stage)." >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
VERSION_FILE="$REPO_ROOT/VERSION"
|
||||
DEFAULT_VERSION="0.1.0.0"
|
||||
MAX_WAIT=35
|
||||
|
||||
REMOTE_USER="${REMOTE_USER:-root}"
|
||||
|
||||
if [[ "$DEST" == "stage" ]]; then
|
||||
REMOTE_HOST="${REMOTE_HOST:-10.0.0.27}"
|
||||
REMOTE_DIR="${REMOTE_DIR:-/opt/kapteins-daagbok-staging}"
|
||||
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.staging.yml}"
|
||||
BACKEND_CONTAINER="${BACKEND_CONTAINER:-daagbox-staging-backend}"
|
||||
APP_URL="${APP_URL:-https://staging.kapteins-daagbok.eu}"
|
||||
DEPLOY_BRANCH="${DEPLOY_BRANCH:-master}"
|
||||
ENV_LABEL="Staging"
|
||||
else
|
||||
REMOTE_HOST="${REMOTE_HOST:-10.0.0.25}"
|
||||
REMOTE_DIR="${REMOTE_DIR:-/opt/kapteins-daagbok}"
|
||||
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
|
||||
BACKEND_CONTAINER="${BACKEND_CONTAINER:-daagbox-prod-backend}"
|
||||
APP_URL="${APP_URL:-https://kapteins-daagbok.eu}"
|
||||
DEPLOY_BRANCH=""
|
||||
ENV_LABEL="Production"
|
||||
fi
|
||||
|
||||
REMOTE_TARGET="${REMOTE_USER}@${REMOTE_HOST}"
|
||||
|
||||
echo "=================================================="
|
||||
echo " Kapteins Daagbok Prod Environment Update "
|
||||
echo " Kapteins Daagbok ${ENV_LABEL} Update"
|
||||
echo "=================================================="
|
||||
echo "Destination: ${DEST}"
|
||||
echo "Target: ${REMOTE_TARGET}:${REMOTE_DIR}"
|
||||
if [[ "$DEST" == "stage" ]]; then
|
||||
echo "Branch: ${DEPLOY_BRANCH}"
|
||||
fi
|
||||
echo "URL: ${APP_URL}"
|
||||
echo "=================================================="
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
@@ -123,7 +197,11 @@ prepare_release() {
|
||||
export APP_VERSION="$release_version"
|
||||
}
|
||||
|
||||
if [[ "$DEST" == "prod" ]]; then
|
||||
prepare_release
|
||||
else
|
||||
APP_VERSION="$(read_current_version)"
|
||||
fi
|
||||
|
||||
if [[ "${SKIP_PREDEPLOY_CHECK:-}" == "1" ]]; then
|
||||
echo "Skipping pre-deploy checks (SKIP_PREDEPLOY_CHECK=1)."
|
||||
@@ -135,23 +213,42 @@ else
|
||||
fi
|
||||
|
||||
echo "=================================================="
|
||||
echo "Deploying ${APP_VERSION} to ${REMOTE_TARGET}:${REMOTE_DIR}"
|
||||
echo "Deploying v${APP_VERSION} to ${REMOTE_TARGET}:${REMOTE_DIR}"
|
||||
echo "=================================================="
|
||||
|
||||
# Run the whole update procedure remotely over SSH.
|
||||
ssh -o ConnectTimeout=10 "$REMOTE_TARGET" 'bash -s' -- \
|
||||
"$REMOTE_DIR" "$COMPOSE_FILE" "$BACKEND_CONTAINER" "$MAX_WAIT" "$REMOTE_HOST" "$APP_VERSION" <<'REMOTE_SCRIPT'
|
||||
"$REMOTE_DIR" "$COMPOSE_FILE" "$BACKEND_CONTAINER" "$MAX_WAIT" "$APP_URL" "$APP_VERSION" "$DEST" "$DEPLOY_BRANCH" <<'REMOTE_SCRIPT'
|
||||
set -uo pipefail
|
||||
|
||||
REMOTE_DIR="$1"
|
||||
COMPOSE_FILE="$2"
|
||||
BACKEND_CONTAINER="$3"
|
||||
MAX_WAIT="$4"
|
||||
REMOTE_HOST="$5"
|
||||
APP_URL="$5"
|
||||
APP_VERSION="$6"
|
||||
DEST="$7"
|
||||
DEPLOY_BRANCH="$8"
|
||||
|
||||
cd "$REMOTE_DIR" || { echo "Error: Remote directory '$REMOTE_DIR' not found."; exit 1; }
|
||||
|
||||
if ! git diff-index --quiet HEAD -- || [ -n "$(git status --porcelain)" ]; then
|
||||
echo "Warning: Local changes on deployment host will be discarded."
|
||||
fi
|
||||
|
||||
if [[ "$DEST" == "stage" ]]; then
|
||||
echo "Syncing repository from origin/${DEPLOY_BRANCH}..."
|
||||
git fetch origin
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Git fetch failed."
|
||||
exit 1
|
||||
fi
|
||||
git checkout "$DEPLOY_BRANCH" 2>/dev/null || git checkout -b "$DEPLOY_BRANCH" "origin/${DEPLOY_BRANCH}"
|
||||
git reset --hard "origin/${DEPLOY_BRANCH}"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Git reset to origin/${DEPLOY_BRANCH} failed."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Syncing repository from origin..."
|
||||
CURRENT_BRANCH="$(git branch --show-current)"
|
||||
if [ -z "$CURRENT_BRANCH" ]; then
|
||||
@@ -159,10 +256,6 @@ if [ -z "$CURRENT_BRANCH" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! git diff-index --quiet HEAD -- || [ -n "$(git status --porcelain)" ]; then
|
||||
echo "Warning: Local changes on deployment host will be discarded."
|
||||
fi
|
||||
|
||||
git fetch --tags origin
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Git fetch failed."
|
||||
@@ -180,11 +273,17 @@ if [ "$REMOTE_VERSION" != "$APP_VERSION" ]; then
|
||||
echo "Note: Remote VERSION file already points to next release (v${REMOTE_VERSION})."
|
||||
echo " Building deployed release v${APP_VERSION}."
|
||||
fi
|
||||
fi
|
||||
|
||||
export APP_VERSION="$APP_VERSION"
|
||||
|
||||
if [[ "$DEST" == "prod" ]]; then
|
||||
echo "Rebuilding Docker images without cache (APP_VERSION=${APP_VERSION})..."
|
||||
docker compose -f "$COMPOSE_FILE" build --no-cache
|
||||
else
|
||||
echo "Rebuilding Docker images (APP_VERSION=${APP_VERSION})..."
|
||||
docker compose -f "$COMPOSE_FILE" build
|
||||
fi
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Docker compose build failed."
|
||||
exit 1
|
||||
@@ -198,10 +297,7 @@ if [ $? -ne 0 ]; then
|
||||
fi
|
||||
|
||||
echo "Cleaning up old/unused Docker resources..."
|
||||
docker system prune -f
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Warning: Docker system prune failed to run completely."
|
||||
fi
|
||||
docker system prune -f || echo "Warning: Docker system prune failed."
|
||||
|
||||
echo "Waiting for services to become healthy..."
|
||||
COUNTER=0
|
||||
@@ -227,15 +323,15 @@ docker compose -f "$COMPOSE_FILE" ps
|
||||
echo "=================================================="
|
||||
|
||||
if [ "$IS_READY" = true ]; then
|
||||
echo "SUCCESS: Production environment updated and healthy!"
|
||||
echo "SUCCESS: ${DEST} environment updated and healthy!"
|
||||
echo " -> Version: v${APP_VERSION}"
|
||||
echo " -> App Frontend (Nginx): http://${REMOTE_HOST}"
|
||||
echo " -> Backend API Health: http://${REMOTE_HOST}/api/health"
|
||||
echo " -> App Frontend: ${APP_URL}"
|
||||
echo " -> Backend API Health: ${APP_URL}/api/health"
|
||||
echo "=================================================="
|
||||
else
|
||||
echo "WARNING: Backend did not transition to healthy in time."
|
||||
echo "Check backend container logs for details:"
|
||||
echo " -> docker compose logs backend"
|
||||
echo "Check backend container logs:"
|
||||
echo " -> docker compose -f ${COMPOSE_FILE} logs backend"
|
||||
echo "=================================================="
|
||||
exit 3
|
||||
fi
|
||||
@@ -244,11 +340,11 @@ REMOTE_SCRIPT
|
||||
REMOTE_EXIT=$?
|
||||
echo "=================================================="
|
||||
if [ $REMOTE_EXIT -eq 0 ]; then
|
||||
echo "Remote update completed successfully on ${REMOTE_TARGET} (v${APP_VERSION})."
|
||||
echo "${ENV_LABEL} update completed successfully on ${REMOTE_TARGET} (v${APP_VERSION})."
|
||||
elif [ $REMOTE_EXIT -eq 3 ]; then
|
||||
echo "Remote update finished, but the backend was not healthy in time on ${REMOTE_TARGET}."
|
||||
echo "${ENV_LABEL} update finished, but the backend was not healthy in time on ${REMOTE_TARGET}."
|
||||
else
|
||||
echo "Remote update FAILED on ${REMOTE_TARGET} (exit code: ${REMOTE_EXIT})."
|
||||
echo "${ENV_LABEL} update FAILED on ${REMOTE_TARGET} (exit code: ${REMOTE_EXIT})."
|
||||
fi
|
||||
echo "=================================================="
|
||||
exit $REMOTE_EXIT
|
||||
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Backward-compatible wrapper — prefer: ./scripts/update-prod.sh -dest stage
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec "$SCRIPT_DIR/update-prod.sh" -dest stage "$@"
|
||||
Reference in New Issue
Block a user