#!/usr/bin/env bash # ──────────────────────────────────────────────────────────────────── # DECT relay agent — data-center install / upgrade helper. # # Run this inside the unzipped bundle directory on the DC host. It: # 1. Sanity-checks that Docker + Compose are installed. # 2. Loads the shipped image tarball into the local Docker daemon. # 3. Pins IMAGE_TAG in .env to whatever tag was baked into the # tarball (so compose can never fall back to a stale local # cache without you noticing). # 4. Verifies .env exists and has the required keys populated. # 5. Runs `docker compose up -d` and tails the last 40 lines. # # Safe to run repeatedly — it's a straightforward upgrade too: # unzip -o new-bundle.zip -d dect-relay-agent-bundle # cd dect-relay-agent-bundle # ./install.sh # ──────────────────────────────────────────────────────────────────── set -euo pipefail # Colors, only if stdout is a TTY. Corporate SSH sessions often are; # CI / pipe-to-file are not. if [[ -t 1 ]]; then BOLD=$'\033[1m'; DIM=$'\033[2m'; RED=$'\033[31m'; GREEN=$'\033[32m' YELLOW=$'\033[33m'; RESET=$'\033[0m' else BOLD=''; DIM=''; RED=''; GREEN=''; YELLOW=''; RESET='' fi log() { echo "${BOLD}[install]${RESET} $*"; } die() { echo "${RED}[install] ERROR:${RESET} $*" >&2; exit 1; } # Ensure we're running from the bundle dir (compose file must be here). cd "$(dirname "$0")" # ─── 1. Preflight ────────────────────────────────────────────────── log "Preflight checks" command -v docker >/dev/null 2>&1 || die "docker is not installed or not on PATH" # Compose v2 is `docker compose` (space); v1 is `docker-compose` (dash). # Prefer v2. bail if neither is available. if docker compose version >/dev/null 2>&1; then COMPOSE="docker compose" elif command -v docker-compose >/dev/null 2>&1; then COMPOSE="docker-compose" echo "${YELLOW}[install] Using legacy docker-compose v1. Consider upgrading to Compose v2.${RESET}" else die "docker compose (v2) not found and docker-compose (v1) not on PATH" fi if ! docker info >/dev/null 2>&1; then die "docker daemon is not reachable. Are you in the 'docker' group, or should you re-run with sudo?" fi [[ -f image.tar.gz ]] || die "image.tar.gz not found in $(pwd) — is the bundle complete?" [[ -f docker-compose.yml ]] || die "docker-compose.yml not found — is the bundle complete?" # ─── 2. Load image ───────────────────────────────────────────────── log "Loading Docker image from image.tar.gz (this is the only step that touches the docker daemon's image store)" # `docker load` prints "Loaded image: " for each tag in the archive. # We tee to stderr so the operator sees it, and grep the tag out for # use in the .env pin step below. LOAD_OUTPUT="$(gunzip -c image.tar.gz | docker load)" echo "$LOAD_OUTPUT" LOADED_TAG="$(echo "$LOAD_OUTPUT" | awk -F': ' '/Loaded image/ {print $2; exit}')" [[ -n "$LOADED_TAG" ]] || die "docker load did not report a loaded image tag" log "Loaded image: ${GREEN}${LOADED_TAG}${RESET}" # ─── 3. .env setup ───────────────────────────────────────────────── if [[ ! -f .env ]]; then cp .env.example .env echo "${YELLOW}[install] Created .env from .env.example. Edit it now with real values, then re-run this script.${RESET}" echo " Required: DECT_RELAY_BOT_URL, DECT_RELAY_AGENT_TOKEN, DECT_ADMIN_PASSWORD" exit 2 fi # Pin IMAGE_TAG in .env to the tag we just loaded. Idempotent — # rewrites the line each run so upgrades to a new tarball tag Just # Work without operator intervention. if grep -q '^IMAGE_TAG=' .env; then # Portable in-place sed (works on both GNU sed and BSD sed on macOS). # The `.bak` tempfile is removed at end. sed -i.bak "s|^IMAGE_TAG=.*|IMAGE_TAG=${LOADED_TAG}|" .env rm -f .env.bak else printf '\n# Pinned automatically by install.sh on %s\nIMAGE_TAG=%s\n' \ "$(date -u +%FT%TZ)" "$LOADED_TAG" >> .env fi log "Pinned IMAGE_TAG=${LOADED_TAG} in .env" # Validate the operator has actually filled in the required values — # .env.example ships with placeholders that would blow up at runtime # with a less friendly error. MISSING=() required_var() { local key="$1" val val="$(grep -E "^${key}=" .env | tail -1 | cut -d= -f2-)" # Strip surrounding quotes and whitespace so both bare and quoted # values validate the same. val="${val#\"}"; val="${val%\"}" val="${val#\'}"; val="${val%\'}" val="${val## }"; val="${val%% }" if [[ -z "$val" ]] || [[ "$val" == "replace-with-shared-secret" ]] \ || [[ "$val" == "replace-with-dect-serviceability-password" ]] \ || [[ "$val" == "wss://your-bot-host.example.com/dect-relay/ws" ]]; then MISSING+=("$key") fi } required_var DECT_RELAY_BOT_URL required_var DECT_RELAY_AGENT_TOKEN required_var DECT_ADMIN_PASSWORD if [[ ${#MISSING[@]} -gt 0 ]]; then echo "${RED}[install] .env is missing required values or still has placeholder text:${RESET}" for k in "${MISSING[@]}"; do echo " - $k"; done echo " Edit .env and re-run this script." exit 2 fi # ─── 4. Compose up ───────────────────────────────────────────────── log "Starting container via ${COMPOSE} up -d" $COMPOSE up -d log "Container started. Recent logs:" sleep 2 $COMPOSE logs --tail=40 dect-relay-agent || true echo log "${GREEN}Done.${RESET} Follow live logs with: ${DIM}${COMPOSE} logs -f dect-relay-agent${RESET}" log "Stop the agent with: ${DIM}${COMPOSE} down${RESET}"