#!/usr/bin/env bash # GPS-Denied Onboard — main deployment orchestrator. # # Runs: pull-images → flight-state-check → stop-services → start-services # → health-check. Honours the /run/azaion/in-flight flag on # airborne targets; refuses to restart mid-flight unless --force is # passed. See deployment_procedures.md § Production Deployment. # # Usage: # scripts/deploy.sh [--target dev|airborne|operator-workstation] # [--branch ] [--arch ] # [--compose-file ] # [--wait-secs N] # [--rollback] [--force] [--help] # # Defaults: # target = operator-workstation # branch = main # arch = arm # wait-secs = 120 # # --rollback restores the image set saved by the most recent # stop-services.sh run (.previous-tags.env at the repo root). # # Exit codes: # 0 deploy or rollback succeeded # 64 missing prerequisite or invalid argument # 65 SSH unreachable when DEPLOY_HOST is set # 66 registry login failed (forwarded from pull-images.sh) # 67 image pull failed (forwarded from pull-images.sh) # 68 refused — /run/azaion/in-flight is set and --force was not passed # 69 timed out waiting for healthchecks (forwarded from start-services.sh) # 70 rollback requested but .previous-tags.env not found set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" usage() { cat <<'EOF' GPS-Denied Onboard — main deployment orchestrator. Runs: pull-images → flight-state-check → stop-services → start-services → health-check. Honours the /run/azaion/in-flight flag on airborne targets; refuses to restart mid-flight unless --force is passed. See deployment_procedures.md § Production Deployment. Usage: scripts/deploy.sh [--target dev|airborne|operator-workstation] [--branch ] [--arch ] [--compose-file ] [--wait-secs N] [--rollback] [--force] [--help] Defaults: target = operator-workstation branch = main arch = arm wait-secs = 120 --rollback restores the image set saved by the most recent stop-services.sh run (.previous-tags.env at the repo root). Exit codes: 0 deploy or rollback succeeded 64 missing prerequisite or invalid argument 65 SSH unreachable when DEPLOY_HOST is set 66 registry login failed (forwarded from pull-images.sh) 67 image pull failed (forwarded from pull-images.sh) 68 refused — /run/azaion/in-flight is set and --force was not passed 69 timed out waiting for healthchecks (forwarded from start-services.sh) 70 rollback requested but .previous-tags.env not found EOF exit 0 } TARGET="operator-workstation" BRANCH="${BRANCH:-main}" ARCH="${ARCH:-arm}" COMPOSE_FILE="" WAIT_SECS="${WAIT_SECS:-120}" ROLLBACK=0 FORCE=0 while [ $# -gt 0 ]; do case "$1" in --target) TARGET="$2"; shift 2 ;; --branch) BRANCH="$2"; shift 2 ;; --arch) ARCH="$2"; shift 2 ;; --compose-file) COMPOSE_FILE="$2"; shift 2 ;; --wait-secs) WAIT_SECS="$2"; shift 2 ;; --rollback) ROLLBACK=1; shift ;; --force) FORCE=1; shift ;; --help|-h) usage ;; *) echo "ERROR: unknown argument: $1" >&2; usage ;; esac done if [ -f "${REPO_ROOT}/.env" ]; then set -a # shellcheck disable=SC1091 . "${REPO_ROOT}/.env" set +a fi case "${TARGET}" in dev|airborne|operator-workstation) ;; *) echo "ERROR: invalid --target: ${TARGET}" >&2; exit 64 ;; esac PREV_TAGS_FILE="${REPO_ROOT}/.previous-tags.env" # Pass-through args common to pull/start/stop/health. common_args=( --target "${TARGET}" ) [ -n "${COMPOSE_FILE}" ] && common_args+=( --compose-file "${COMPOSE_FILE}" ) force_args=() [ "${FORCE}" = "1" ] && force_args+=( --force ) echo "═══════════════════════════════════════════════════" echo " GPS-Denied Onboard — deploy" echo "═══════════════════════════════════════════════════" echo " target: ${TARGET}" echo " branch: ${BRANCH}" echo " arch: ${ARCH}" echo " rollback: ${ROLLBACK}" if [ -n "${DEPLOY_HOST:-}" ]; then echo " remote: ${DEPLOY_HOST}" fi echo "═══════════════════════════════════════════════════" if [ "${ROLLBACK}" = "1" ]; then if [ ! -f "${PREV_TAGS_FILE}" ]; then echo "ERROR: --rollback requested but ${PREV_TAGS_FILE} not found." >&2 echo " Rollback requires a prior stop-services.sh run." >&2 exit 70 fi echo "[deploy] rollback: restoring image set from ${PREV_TAGS_FILE}" cat "${PREV_TAGS_FILE}" echo echo "[deploy] re-pulling the saved digests via docker pull" set +e grep -E '^PREV_[A-Z0-9_]+_IMAGE=' "${PREV_TAGS_FILE}" | while IFS='=' read -r _ image; do ${DEPLOY_HOST:+ssh ${DEPLOY_HOST}} docker pull "${image}" done rc=$? set -e if [ "${rc}" != "0" ]; then echo "ERROR: rollback image pull failed (rc=${rc})" >&2 exit "${rc}" fi else echo "[deploy] step 1/4 — pull-images" "${SCRIPT_DIR}/pull-images.sh" \ --target "${TARGET}" \ --branch "${BRANCH}" \ --arch "${ARCH}" fi echo "[deploy] step 2/4 — stop-services" "${SCRIPT_DIR}/stop-services.sh" "${common_args[@]}" "${force_args[@]}" echo "[deploy] step 3/4 — start-services" "${SCRIPT_DIR}/start-services.sh" \ "${common_args[@]}" \ --wait-secs "${WAIT_SECS}" \ "${force_args[@]}" echo "[deploy] step 4/4 — health-check" if ! "${SCRIPT_DIR}/health-check.sh" "${common_args[@]}"; then echo "ERROR: health-check failed after deploy. See deployment_procedures.md § Rollback." >&2 echo " To roll back: scripts/deploy.sh --rollback ${common_args[*]}" >&2 exit 1 fi echo "═══════════════════════════════════════════════════" echo " DEPLOY OK — ${TARGET} @ ${BRANCH}-${ARCH}" if [ "${ROLLBACK}" = "1" ]; then echo " (rollback to previously-saved image digests)" fi echo "═══════════════════════════════════════════════════"