# Shared config loading. Sourced, never executed. Run from ctrl/. # # Precedence, weakest first: # ctrl/versions.env pinned toolchain (committed) # ctrl/env.d/.env provider shape: aws|gcp (committed) # ctrl/.env machine-local + secrets (gitignored) # the caller's env `make estate plan TARGET=gcp` (always wins) CONFIG_OVERRIDABLE="TARGET ESTATE CLOUD REGION INSTANCE_TYPE HOST HOST_ADMIN AWS_PROFILE AWS_HOSTED_ZONE_ID GCP_PROJECT GCP_ZONE TOFU_WORKSPACE" # rig's port formula, reproduced rather than imported. cksum because it is # POSIX and gives the same value on every machine. Used to verify, not allocate. derive_port_base() { local h; h=$(printf '%s' "$1" | cksum | awk '{print $1}') echo $((20000 + (h % 200) * 10)) } _config_restore() { local line while IFS= read -r line; do if [ -n "$line" ]; then eval "export $line" fi done <<< "$1" # A while loop returns its last body command's status; the trailing empty # line would otherwise make this return 1 and trip `set -e` in the caller. return 0 } load_config() { local k saved="" for k in $CONFIG_OVERRIDABLE; do # ${!k+x} distinguishes "set but empty" from "unset" — an explicit # FOO= on the command line is a real choice and must survive. if [ -n "${!k+x}" ]; then saved+="$k=$(printf '%q' "${!k}")"$'\n' fi done set -a source ./versions.env [ -f ./.env ] && source ./.env set +a # Re-apply overrides now so TARGET is the caller's before we pick the file. _config_restore "$saved" local target="${TARGET:-aws}" if [ ! -f "./env.d/${target}.env" ]; then echo "no such target: env.d/${target}.env" >&2 echo "available: $(ls env.d/*.env 2>/dev/null | xargs -n1 basename | sed 's/\.env$//' | tr '\n' ' ')" >&2 exit 1 fi set -a source "./env.d/${target}.env" [ -f ./.env ] && source ./.env set +a _config_restore "$saved" TARGET="$target" # Identity is explicit: berth never guesses which estate it is acting on. # The one convenience: a single estate/*.json is used without being asked. if [ -z "${ESTATE:-}" ]; then local n; n=$(ls ../estate/*.json 2>/dev/null | wc -l) if [ "$n" = "1" ]; then ESTATE=$(basename "$(ls ../estate/*.json)" .json) else echo "ESTATE is not set and estate/ holds $n candidates — refusing to guess." >&2 echo "available: $(ls ../estate/*.json 2>/dev/null | xargs -n1 basename | sed 's/\.json$//' | tr '\n' ' ')" >&2 echo "set it: make estate show ESTATE=, or ESTATE= in ctrl/.env" >&2 exit 1 fi fi ESTATE_FILE="../estate/${ESTATE}.json" if [ ! -f "$ESTATE_FILE" ]; then echo "no such estate: estate/${ESTATE}.json" >&2 echo "available: $(ls ../estate/*.json 2>/dev/null | xargs -n1 basename | sed 's/\.json$//' | tr '\n' ' ')" >&2 exit 1 fi # Facts come from the estate file, never restated in a target env. DOMAIN=$(estate_get "domain") HOST="${HOST:-$(estate_get "host")}" HOST_ADMIN="${HOST_ADMIN:-$(estate_get "host_admin")}" # Workspace == target, so the two can never mean different things. TOFU_WORKSPACE="${TOFU_WORKSPACE:-$TARGET}" }