#!/usr/bin/env bash # scripts/_lib.sh — shared helpers sourced by all deploy scripts. # # This file is sourced (not executed); do not set -e at the top — leave error # handling to the caller. The helpers always check their own preconditions. # ----- logging -------------------------------------------------------------- log_info() { printf '\033[32m[deploy]\033[0m %s\n' "$*" >&2; } log_warn() { printf '\033[33m[deploy WARN]\033[0m %s\n' "$*" >&2; } log_error() { printf '\033[31m[deploy ERROR]\033[0m %s\n' "$*" >&2; } die() { log_error "$*"; exit 1; } # ----- input validation ----------------------------------------------------- require_env() { local var for var in "$@"; do if [[ -z "${!var:-}" ]]; then die "Required environment variable not set: $var" fi done } require_cmd() { local cmd for cmd in "$@"; do if ! command -v "$cmd" >/dev/null 2>&1; then die "Required command not found on PATH: $cmd" fi done } # ----- env overlay ---------------------------------------------------------- # load_env_overlay # 1. Sources scripts/_defaults.env if present (developer-friendly defaults). # 2. Sources secrets/.public.env (committed plain-text). # 3. Decrypts secrets/.env via sops + age and sources the result. # The decrypted intermediate is written to a mktemp file and removed on EXIT. load_env_overlay() { local env="$1" local script_dir repo_root public_file enc_file decrypted script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" repo_root="$(cd "$script_dir/.." && pwd)" if [[ -f "$repo_root/.env" ]]; then # Local dev convenience; harmless on a production host because the # production host should not have a .env in REPO_ROOT. log_info "Sourcing $repo_root/.env" set -a; . "$repo_root/.env"; set +a fi public_file="$repo_root/secrets/${env}.public.env" if [[ -f "$public_file" ]]; then log_info "Sourcing $public_file" set -a; . "$public_file"; set +a else log_warn "No $public_file — relying on environment / .env only" fi enc_file="$repo_root/secrets/${env}.env" if [[ -f "$enc_file" ]]; then require_cmd sops age decrypted="$(mktemp -t azaion-env.XXXXXX)" # shellcheck disable=SC2064 trap "rm -f '$decrypted'" EXIT INT TERM if ! SOPS_AGE_KEY_FILE="${SOPS_AGE_KEY_FILE:-/etc/azaion/age.key}" \ sops -d "$enc_file" > "$decrypted" 2>/tmp/sops.err; then log_error "sops decrypt failed for $enc_file" cat /tmp/sops.err >&2 die "Cannot continue without secrets" fi chmod 600 "$decrypted" log_info "Sourcing decrypted overlay (intermediate: $decrypted)" set -a; . "$decrypted"; set +a else log_warn "No $enc_file — secret values must already be in the environment" fi } # ----- container helpers ---------------------------------------------------- container_exists() { docker container inspect "$1" >/dev/null 2>&1 } container_running() { [[ "$(docker container inspect -f '{{.State.Running}}' "$1" 2>/dev/null || echo false)" == "true" ]] } current_image_revision() { # Returns the org.opencontainers.image.revision label of the running # container, or empty if the container does not exist. docker container inspect "$1" \ --format '{{ index .Config.Labels "org.opencontainers.image.revision" }}' 2>/dev/null || true }