#!/usr/bin/env bash # Build the app image on this machine, copy it and Caddy to the host, and restart both. # Uses the diverter-emu entry in ~/.ssh/config (user and key). # Usage: ./deploy/ship.sh [ssh-host] set -euo pipefail HOST="${1:-diverter-emu}" IMAGE=diverter-device-emulator REMOTE_DIR=/opt/diverter-device-emulator ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT" if [[ ! -f .env ]]; then echo "Missing $ROOT/.env — copy .env.example and fill it in." >&2 exit 1 fi if ! command -v docker >/dev/null 2>&1; then echo "docker is not on PATH. Start Docker Desktop, then run this again." >&2 exit 1 fi env_value() { local key="$1" grep -E "^${key}=" .env | head -n 1 | cut -d= -f2- | tr -d "\"'" | tr -d '[:space:]' } PORTAL_HOST="$(env_value PORTAL_HOST)" if [[ -z "$PORTAL_HOST" ]]; then echo "Set PORTAL_HOST in .env to the public hostname. Its DNS A record must point at the Lightsail IP before the first deploy, or Caddy cannot get a certificate." >&2 exit 1 fi ARCH="$(ssh "$HOST" "uname -m")" case "$ARCH" in x86_64) PLATFORM=linux/amd64 ;; aarch64 | arm64) PLATFORM=linux/arm64 ;; *) echo "Unsupported server architecture: $ARCH" >&2 exit 1 ;; esac TAR="$(mktemp -t diverter-image.XXXXXX.tar)" cleanup() { rm -f "$TAR"; } trap cleanup EXIT if ! ssh "$HOST" "sudo docker compose version" >/dev/null 2>&1; then echo "The server has no Docker Compose. On the instance: sudo apt-get install -y docker-compose-v2" >&2 exit 1 fi # Attestation indexes from Docker Desktop make `docker save` fail, so both images are single-platform. echo "Building $IMAGE for $PLATFORM" docker build --platform "$PLATFORM" --provenance=false --sbom=false -t "$IMAGE" . echo "Preparing caddy:2 for $PLATFORM" docker build --platform "$PLATFORM" --provenance=false --sbom=false -t caddy:2 - <<'EOF' FROM caddy:2 EOF echo "Saving images" docker save -o "$TAR" "$IMAGE" caddy:2 echo "Copying images, .env, and Caddy config to $HOST" ssh "$HOST" "sudo mkdir -p '$REMOTE_DIR/data' && sudo chown 1000:1000 '$REMOTE_DIR/data'" scp "$TAR" "$HOST:/tmp/${IMAGE}.tar" scp .env "$HOST:/tmp/${IMAGE}.env" scp "$ROOT/deploy/docker-compose.yml" "$ROOT/deploy/Caddyfile" "$HOST:/tmp/" ssh "$HOST" "sudo mv /tmp/${IMAGE}.env '$REMOTE_DIR/.env' && sudo chmod 600 '$REMOTE_DIR/.env' && sudo mv /tmp/docker-compose.yml /tmp/Caddyfile '$REMOTE_DIR/'" ssh "$HOST" "sudo docker load -i /tmp/${IMAGE}.tar && rm -f /tmp/${IMAGE}.tar" ssh "$HOST" "sudo docker rm -f '$IMAGE' >/dev/null 2>&1 || true" ssh "$HOST" "sudo bash -c 'cd \"$REMOTE_DIR\" && docker compose up -d --pull never --remove-orphans'" echo "Containers on $HOST:" ssh "$HOST" "sudo docker ps --filter name='diverter-' --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'"