#!/usr/bin/bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" STACK_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" ENV_FILE="${ENV_FILE:-$STACK_DIR/.env}" DO_UP="${1:-}" if [[ ! -f "$ENV_FILE" ]]; then echo "Missing $ENV_FILE — copy .env.example to .env and fill in real values first." >&2 exit 1 fi set -a eval "$(python3 - "$ENV_FILE" <<'PY' from pathlib import Path import shlex import sys env_file = Path(sys.argv[1]) for raw_line in env_file.read_text(encoding='utf-8').splitlines(): line = raw_line.strip() if not line or line.startswith('#') or '=' not in line: continue key, value = line.split('=', 1) value = value.strip().strip('"').strip("'") print(f"{key.strip()}={shlex.quote(value)}") PY )" set +a required_vars=( APPDATA_PIHOLE_ROOT APPDATA_UNBOUND_ROOT LAN_INTERFACE VIP_CIDR KEEPALIVED_PASSWORD PD_PIHOLE_WEB_PORT ) for var in "${required_vars[@]}"; do if [[ -z "${!var:-}" ]]; then echo "Required variable $var is empty in $ENV_FILE" >&2 exit 1 fi done mkdir -p \ "$APPDATA_PIHOLE_ROOT/etc-pihole" \ "$APPDATA_PIHOLE_ROOT/etc-dnsmasq.d" \ "$APPDATA_UNBOUND_ROOT/var" UNBOUND_TARGET="$APPDATA_UNBOUND_ROOT/unbound.conf" UNBOUND_TMP="$APPDATA_UNBOUND_ROOT/.unbound.conf.tmp.$$" cp "$STACK_DIR/unbound.conf" "$UNBOUND_TMP" mv -f "$UNBOUND_TMP" "$UNBOUND_TARGET" if [[ ! -s "$APPDATA_UNBOUND_ROOT/var/root.hints" ]]; then curl -fsSL -o "$APPDATA_UNBOUND_ROOT/var/root.hints" https://www.internic.net/domain/named.cache fi ENV_FILE="$ENV_FILE" python3 "$STACK_DIR/bin/render_keepalived_conf.py" docker compose --env-file "$ENV_FILE" -f "$STACK_DIR/docker-compose.yaml" config >/dev/null echo "PD Pi-hole stack prepared at $STACK_DIR" echo "- unbound config copied to $APPDATA_UNBOUND_ROOT/unbound.conf" echo "- root hints present at $APPDATA_UNBOUND_ROOT/var/root.hints" echo "- keepalived.conf rendered" if [[ "$DO_UP" == "--up" ]]; then docker compose --env-file "$ENV_FILE" -f "$STACK_DIR/docker-compose.yaml" up -d docker compose --env-file "$ENV_FILE" -f "$STACK_DIR/docker-compose.yaml" ps fi