#!/usr/bin/env bash # ═══════════════════════════════════════════════════════════════ # sync-env.sh — Ensure .env has every key from .env.example # # Reads .env.example as the source of truth. For any key that is # missing in the local .env, appends the .env.example value (or # a placeholder) under the same section comment. Existing keys in # .env are preserved (never overwritten) so secrets stay intact. # # Usage: # ./scripts/sync-env.sh # sync into .env # ./scripts/sync-env.sh --check # report missing keys only # ./scripts/sync-env.sh --target .env.production # ═══════════════════════════════════════════════════════════════ set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" EXAMPLE="${REPO_ROOT}/.env.example" COOLIFY="${REPO_ROOT}/.env.example.coolify" target=".env" mode="sync" for arg in "$@"; do case "$arg" in --check) mode="check" ;; --target) shift; target="${1:-.env}" ;; *) ;; esac done TARGET="${REPO_ROOT}/${target}" if [[ ! -f "$EXAMPLE" ]]; then echo "ERROR: $EXAMPLE not found" >&2 exit 1 fi if [[ ! -f "$TARGET" ]]; then echo "Creating $TARGET from $EXAMPLE template" cp "$EXAMPLE" "$TARGET" exit 0 fi # Extract existing keys (handle values that span lines) declare -A existing while IFS= read -r line; do # Skip comments / blanks [[ "$line" =~ ^[[:space:]]*# ]] && continue [[ -z "$line" ]] && continue if [[ "$line" =~ ^[[:space:]]*([A-Z_][A-Z_0-9]*)= ]]; then key="${BASH_REMATCH[1]}" existing["$key"]=1 fi done < "$TARGET" missing=() total=0 while IFS= read -r line; do [[ "$line" =~ ^[[:space:]]*# ]] && continue [[ -z "$line" ]] && continue if [[ "$line" =~ ^[[:space:]]*([A-Z_][A-Z_0-9]*)= ]]; then key="${BASH_REMATCH[1]}" total=$((total + 1)) if [[ -z "${existing[$key]:-}" ]]; then missing+=("$line") fi fi done < "$EXAMPLE" if [[ "$mode" == "check" ]]; then echo "Checked $target against .env.example" echo " Total keys in .env.example: $total" echo " Already present in $target: $((total - ${#missing[@]}))" echo " Missing keys: ${#missing[@]}" if [[ ${#missing[@]} -gt 0 ]]; then echo "" echo "Missing keys (add these to $target):" printf ' %s\n' "${missing[@]}" exit 1 fi exit 0 fi if [[ ${#missing[@]} -eq 0 ]]; then echo "✓ $target already in sync with .env.example ($total keys)" exit 0 fi { echo "" echo "# ─── Auto-synced from .env.example ($(date -u +%Y-%m-%dT%H:%M:%SZ)) ──" printf '%s\n' "${missing[@]}" } >> "$TARGET" echo "✓ Added ${#missing[@]} missing keys to $target" echo " Total keys in $target: $total"