diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index f398469..074cb8d 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -24,8 +24,9 @@ This is intentional. For a private repository, server-side `git clone` adds unne ### Production - same deployment mechanism -- different server +- usually a different server - env file on server: `.env.prod` +- branch: `main` - should run behind real HTTPS - should keep `DEBUG=0` @@ -198,6 +199,44 @@ HEALTH_URL=http://192.168.2.55:8088/healthz/ \ ./scripts/deploy_test_from_mac.sh ``` +## Manual production deployment +For production, use a dedicated helper instead of the test script. + +### One-command production deployment from your Mac +Use: +```bash +./scripts/deploy_prod_from_mac.sh +``` + +What it does: +1. requires the current branch to be `main` +2. fast-forwards from `origin/main` +3. verifies that the server env file exists before syncing +4. syncs the repo to the production path via `rsync` +5. runs the remote deployment script with `RUN_DJANGO_CHECK=1` +6. verifies the production health endpoint +7. prints the deployed commit and branch + +Important: +- the production helper preserves server-local env files: + - `.env.test` + - `.env.prod` +- do not use the test helper for production + +Default assumptions: +- target host: `root@192.168.2.55` +- target path: `/opt/workdock` +- env file: `.env.prod` +- health URL: `https://workdock.bostame.de/healthz/` + +Optional overrides: +```bash +DEPLOY_HOST=root@192.168.2.55 \ +DEPLOY_PATH=/opt/workdock \ +HEALTH_URL=https://workdock.bostame.de/healthz/ \ +./scripts/deploy_prod_from_mac.sh +``` + ### Manual server-side deploy only If the latest code is already on the server: ```bash @@ -208,7 +247,7 @@ RUN_DJANGO_CHECK=0 DEPLOY_HEALTH_URL="http://127.0.0.1:8088/healthz/" ./scripts/ Manual production deployment: ```bash cd /opt/workdock -RUN_DJANGO_CHECK=1 ./scripts/deploy_stack.sh .env.prod docker-compose.prod.yml +RUN_DJANGO_CHECK=1 DEPLOY_HEALTH_URL="https://workdock.bostame.de/healthz/" ./scripts/deploy_stack.sh .env.prod docker-compose.prod.yml ``` ## Runtime config sync diff --git a/backend/workflows/templates/workflows/developer_handbook.html b/backend/workflows/templates/workflows/developer_handbook.html index f4b64ef..7ce2bb6 100644 --- a/backend/workflows/templates/workflows/developer_handbook.html +++ b/backend/workflows/templates/workflows/developer_handbook.html @@ -362,6 +362,7 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS
  • Stable branch: main.
  • GitHub Actions is used for CI.
  • Test deployment is done manually from a LAN-connected machine, not from GitHub-hosted runners.
  • +
  • Production deployment is also manual from the Mac today, but it uses main and a separate helper.
  • @@ -386,6 +387,7 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS
  • Run the local test deployment helper from the Mac.
  • Verify the updated version in the browser.
  • When the integration line is stable, merge develop into main.
  • +
  • Deploy production from the Mac only from main.
  • @@ -407,6 +409,24 @@ make backup-verify BACKUP_DIR=backups/backup_YYYYmmdd_HHMMSS +
    +

    One-command production deployment

    +

    From the Mac, only after the change has been promoted into main:

    +
    git checkout main
    +./scripts/deploy_prod_from_mac.sh
    +

    This helper script does all of the following:

    +
      +
    1. checks that the current branch is main
    2. +
    3. fast-forwards from origin/main
    4. +
    5. checks that the server env file exists
    6. +
    7. syncs the repository to /opt/workdock with rsync
    8. +
    9. preserves server-local env files like .env.test and .env.prod
    10. +
    11. runs the remote deployment script with RUN_DJANGO_CHECK=1
    12. +
    13. waits for the public health endpoint
    14. +
    15. prints the deployed commit and branch
    16. +
    +
    +

    Test server values

    +
    +

    Production deployment

    +
    ./scripts/deploy_prod_from_mac.sh
    +

    Sync the current main checkout to the production target and deploy it with production checks enabled.

    +

    Direct server deployment

    cd /opt/workdock
     RUN_DJANGO_CHECK=0 DEPLOY_HEALTH_URL="http://127.0.0.1:8088/healthz/" ./scripts/deploy_stack.sh .env.test docker-compose.prod.yml

    Deploy when code is already present on the server.

    +
    cd /opt/workdock
    +RUN_DJANGO_CHECK=1 DEPLOY_HEALTH_URL="https://workdock.bostame.de/healthz/" ./scripts/deploy_stack.sh .env.prod docker-compose.prod.yml
    +

    Production deploy when code is already present on the server.

    Config sync

    diff --git a/scripts/deploy_prod_from_mac.sh b/scripts/deploy_prod_from_mac.sh new file mode 100755 index 0000000..82bfb76 --- /dev/null +++ b/scripts/deploy_prod_from_mac.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +DEPLOY_HOST="${DEPLOY_HOST:-root@192.168.2.55}" +DEPLOY_PATH="${DEPLOY_PATH:-/opt/workdock}" +HEALTH_URL="${HEALTH_URL:-https://workdock.bostame.de/healthz/}" +REMOTE_ENV_FILE="${REMOTE_ENV_FILE:-.env.prod}" +COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.prod.yml}" +RUN_DJANGO_CHECK="${RUN_DJANGO_CHECK:-1}" +SSH_CMD="${SSH_CMD:-ssh -4}" +RSYNC_SSH="${RSYNC_SSH:-ssh -4}" + +cd "$REPO_ROOT" + +current_branch="$(git branch --show-current)" +if [[ "$current_branch" != "main" ]]; then + echo "Expected branch 'main' for production deployment, got '$current_branch'." >&2 + echo "Switch to main or override intentionally before deploying." >&2 + exit 1 +fi + +echo "Updating local branch from origin/main..." +git pull --ff-only origin main + +echo "Checking remote env file..." +$SSH_CMD "$DEPLOY_HOST" "test -f '$DEPLOY_PATH/$REMOTE_ENV_FILE'" || { + echo "Missing remote env file: $DEPLOY_PATH/$REMOTE_ENV_FILE" >&2 + echo "Create or restore the server env file before deploying." >&2 + exit 1 +} + +echo "Syncing repository to ${DEPLOY_HOST}:${DEPLOY_PATH} ..." +rsync -az --delete \ + --filter 'P .env.test' \ + --filter 'P .env.prod' \ + --exclude '.git' \ + --exclude '.github' \ + --exclude '.venv' \ + --exclude '__pycache__' \ + --exclude 'node_modules' \ + --exclude 'backend/media' \ + --exclude 'backend/staticfiles' \ + -e "$RSYNC_SSH" \ + "$REPO_ROOT"/ \ + "${DEPLOY_HOST}:${DEPLOY_PATH}/" + +echo "Running remote deployment..." +$SSH_CMD "$DEPLOY_HOST" \ + "cd '$DEPLOY_PATH' && RUN_DJANGO_CHECK='$RUN_DJANGO_CHECK' DEPLOY_HEALTH_URL='$HEALTH_URL' ./scripts/deploy_stack.sh '$REMOTE_ENV_FILE' '$COMPOSE_FILE'" + +echo "Verifying health endpoint..." +curl --fail --silent --show-error --max-time 10 "$HEALTH_URL" >/dev/null +commit_sha="$(git rev-parse --short HEAD)" +echo "Production deployment healthy: $HEALTH_URL" +echo "Deployed commit: $commit_sha" +echo "Branch: $current_branch"