#!/usr/bin/env bash # Shared constants and helpers for all run scripts # Ensure cargo is in PATH (rustup installs to ~/.cargo/bin) [[ -f "$HOME/.cargo/env" ]] && source "$HOME/.cargo/env" # ── Dotenv loader (Next.js/Vite precedence) ───────────────────────── # Source order (later wins, so secrets override base): # 1. .env (tracked, base) # 2. .env.local (gitignored, user overrides) # 3. .env. (tracked, mode defaults — development/production) # 4. .env..local (gitignored, mode+user) # Mode is $NODE_ENV, else "development". # # Variables already set in the shell ALWAYS win (never clobber). Lines # are plain `KEY=VALUE`; inline comments (`#`) are stripped; surrounding # single/double quotes on values are stripped. _load_envfile() { local f="$1" [[ -r "$f" ]] || return 0 local line key val while IFS= read -r line || [[ -n "$line" ]]; do # Strip leading whitespace + skip comments + blanks line="${line#"${line%%[![:space:]]*}"}" [[ -z "$line" || "$line" == \#* ]] && continue # Split once on =, tolerate `export KEY=VAL` line="${line#export }" [[ "$line" != *=* ]] && continue key="${line%%=*}" val="${line#*=}" # Strip surrounding quotes [[ "$val" == \"*\" && "$val" == *\" ]] && val="${val:1:${#val}-2}" [[ "$val" == \'*\' && "$val" == *\' ]] && val="${val:1:${#val}-2}" # Don't clobber vars already set in the shell [[ -z "${!key+x}" ]] && export "$key=$val" done < "$f" } _load_env_cascade() { local mode="${NODE_ENV:-development}" _load_envfile "$REPO_ROOT/.env" _load_envfile "$REPO_ROOT/.env.local" _load_envfile "$REPO_ROOT/.env.${mode}" _load_envfile "$REPO_ROOT/.env.${mode}.local" } # Require one or more env vars; fail the current command with a helpful # message when any are missing. Usage: `require_env FORGEJO_HOST FORGEJO_RUNNER_TOKEN`. require_env() { local missing=() for v in "$@"; do [[ -z "${!v:-}" ]] && missing+=("$v") done if (( ${#missing[@]} > 0 )); then echo -e "${RED}Missing required env:${NC} ${missing[*]}" >&2 echo -e "${DIM} Set in .env.local (gitignored) — see .env.example for documented keys.${NC}" >&2 return 2 fi return 0 } # Run cascade at source time so every subcommand has vars available. _load_env_cascade RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' DIM='\033[2m' NC='\033[0m' case "$(uname -s)" in Darwin) GODOT_BIN="godot" ;; *) GODOT_BIN="flatpak run --user org.godotengine.Godot" ;; esac GAME_DIR="$REPO_ROOT/src/game" SIMULATOR_DIR="$REPO_ROOT/src/simulator" GUIDE_DIR="$REPO_ROOT/public/games/age-of-dwarves/guide" # ── Optional-tool detection helper ────────────────────────────────── # Used across lint.sh / test.sh / verify.sh. Returns 0 if available, # 1 + yellow warning if not. _have_tool() { local tool="$1" local hint="${2:-}" if command -v "$tool" >/dev/null 2>&1; then return 0 fi if [ -n "$hint" ]; then echo -e "${YELLOW}SKIP: '$tool' not installed — install with: $hint${NC}" else echo -e "${YELLOW}SKIP: '$tool' not installed${NC}" fi return 1 }