#!/usr/bin/env bash set -euo pipefail log() { printf '[redeploy] %s\n' "$*" } read_env_value() { local key="$1" local line="" if [[ ! -f .env ]]; then return 1 fi while IFS= read -r line || [[ -n "$line" ]]; do if [[ "$line" == "${key}="* ]]; then printf '%s\n' "${line#*=}" return 0 fi done < .env return 1 } upsert_env_value() { local key="$1" local value="$2" local temp_file="" local line="" local found=0 temp_file="$(mktemp)" while IFS= read -r line || [[ -n "$line" ]]; do if [[ "$line" == "${key}="* ]]; then printf '%s=%s\n' "$key" "$value" >> "$temp_file" found=1 else printf '%s\n' "$line" >> "$temp_file" fi done < .env if [[ "$found" -eq 0 ]]; then printf '%s=%s\n' "$key" "$value" >> "$temp_file" fi chmod --reference=.env "$temp_file" 2>/dev/null || chmod 600 "$temp_file" mv "$temp_file" .env } parse_route_source_ip() { local route="$1" local previous="" local token for token in $route; do if [[ "$previous" == "src" ]]; then printf '%s\n' "$token" return 0 fi previous="$token" done return 1 } detect_ad_dns_ip() { local lookup_name="$1" local target_ip="" local route="" if ! command -v ip >/dev/null 2>&1; then return 1 fi if [[ -n "$lookup_name" ]] && command -v getent >/dev/null 2>&1; then while read -r target_ip _; do if [[ ! "$target_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then continue fi route="$(ip -o -4 route get "$target_ip" 2>/dev/null || true)" parse_route_source_ip "$route" && return 0 done < <(getent ahostsv4 "$lookup_name" 2>/dev/null) fi route="$(ip -o -4 route get 1.1.1.1 2>/dev/null || true)" parse_route_source_ip "$route" } refresh_ad_dns_env() { local auto="" local domain="" local detected_ip="" local samba_hostname="" local netbios_name="" local host_part="" if [[ ! -f .env ]]; then log '.env missing; skipping AD DNS IP refresh' return fi auto="$(read_env_value AD_DNS_IP_AUTO || true)" case "${auto,,}" in 0|false|no) log 'AD_DNS_IP_AUTO disabled; keeping configured AD_DNS_IP' return ;; esac domain="$(read_env_value DOMAIN || true)" if [[ -z "$domain" ]]; then log 'DOMAIN missing in .env; skipping AD DNS IP refresh' return fi detected_ip="$(detect_ad_dns_ip "$domain" || true)" if [[ -z "$detected_ip" ]]; then log 'Unable to detect host LAN IP; keeping configured AD_DNS_IP' return fi upsert_env_value AD_DNS_IP "$detected_ip" if ! read_env_value AD_DNS_NAME >/dev/null; then samba_hostname="$(read_env_value SAMBA_HOSTNAME || true)" netbios_name="$(read_env_value NETBIOS_NAME || true)" host_part="${samba_hostname:-${netbios_name,,}}" host_part="${host_part:-adsambafsrv}" upsert_env_value AD_DNS_NAME "${host_part}.${domain}" fi upsert_env_value AD_DNS_IP_AUTO "1" log "Updated AD_DNS_IP=${detected_ip}" } if [[ ! -d .git ]]; then printf '[redeploy] ERROR: run this script from the repository root.\n' >&2 exit 1 fi refresh_ad_dns_env OLD_COMPOSE_FILE="$(mktemp)" cleanup() { if [[ -f "$OLD_COMPOSE_FILE" ]]; then rm -f "$OLD_COMPOSE_FILE" fi } trap cleanup EXIT log 'Capturing current compose configuration' docker compose config > "$OLD_COMPOSE_FILE" log 'Pulling latest git changes' git pull log 'Building updated images while current stack is running' docker compose build --no-cache log 'Stopping previous stack using captured configuration' docker compose --project-directory "$PWD" -f "$OLD_COMPOSE_FILE" down log 'Starting stack' docker compose up -d log 'Redeploy complete'