#!/usr/bin/env bash # deploy.sh — deploy to production via SSH # Called by: just deploy # # Prerequisites: # - DEPLOY_HOST and DEPLOY_PATH set in .env # - SSH key configured for passwordless access # - Docker image already pushed to GHCR (done by CI) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # Load env if [ -f "$SCRIPT_DIR/../../.env" ]; then # shellcheck disable=SC1091 set -a; source "$SCRIPT_DIR/../../.env"; set +a fi DEPLOY_HOST="${DEPLOY_HOST:?Set DEPLOY_HOST in .env}" DEPLOY_PATH="${DEPLOY_PATH:-/opt/colectivo}" IMAGE_TAG="${IMAGE_TAG:-latest}" echo "==> Deploying colectivo to $DEPLOY_HOST:$DEPLOY_PATH (tag: $IMAGE_TAG)" # 1. Copy compose file and scripts to server rsync -az --delete \ "$SCRIPT_DIR/../docker-compose.prod.yml" \ "$SCRIPT_DIR/../kong.yml" \ "$DEPLOY_HOST:$DEPLOY_PATH/infra/" # 2. SSH: pull image, restart services with zero downtime ssh "$DEPLOY_HOST" bash -s << EOF set -euo pipefail cd "$DEPLOY_PATH" echo "--- Pulling latest image (tag: $IMAGE_TAG)" IMAGE_TAG="$IMAGE_TAG" docker compose -f infra/docker-compose.prod.yml pull app echo "--- Rolling restart of app service" IMAGE_TAG="$IMAGE_TAG" docker compose -f infra/docker-compose.prod.yml up -d --no-deps app echo "--- Running pending DB migrations" docker compose -f infra/docker-compose.prod.yml exec -T db \ psql -U postgres -f /docker-entrypoint-initdb.d/migrations.sql 2>/dev/null || true echo "--- Pruning old images" docker image prune -f echo "--- Deploy done" EOF echo "==> Deployment complete"