#!/bin/bash set -euo pipefail ENV_FILE="${1:-.env}" PROJECT_ROOT="${2:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" MIGRATIONS_DIR="${UPDATE_ENV_MIGRATIONS_DIR:-$PROJECT_ROOT/scripts/update-env.d}" if [ ! -f "$ENV_FILE" ]; then echo "❌ 环境配置文件不存在: $ENV_FILE" >&2 exit 1 fi if [ ! -d "$MIGRATIONS_DIR" ]; then echo "ℹ️ 未发现环境配置迁移目录,跳过" exit 0 fi read_env_value() { local file="$1" local key="$2" local value value="$(awk -v key="$key" 'index($0, key "=") == 1 { print substr($0, length(key) + 2); exit }' "$file")" value="$(printf '%s' "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" case "$value" in \"*\") value="${value#\"}"; value="${value%\"}" ;; \'*\') value="${value#\'}"; value="${value%\'}" ;; esac printf '%s' "$value" } write_env_value() { local file="$1" local key="$2" local value="$3" local temporary temporary="$(mktemp "${file}.tmp.XXXXXX")" awk -v key="$key" -v value="$value" ' BEGIN { replaced = 0 } index($0, key "=") == 1 { if (!replaced) { print key "=" value replaced = 1 } next } { print } END { if (!replaced) print key "=" value } ' "$file" > "$temporary" chmod 600 "$temporary" mv "$temporary" "$file" } apply_default() { local key="$1" local value="$2" if [ -n "$(read_env_value "$ENV_FILE" "$key")" ]; then return 0 fi write_env_value "$ENV_FILE" "$key" "$value" echo " ✓ 已补充环境配置: $key" } ensure_list_item() { local key="$1" local item="$2" local current normalized current="$(read_env_value "$ENV_FILE" "$key")" normalized="$(printf '%s' "$current" | tr -d '[:space:]')" case ",${normalized}," in *",${item},"*) return 0 ;; esac if [ -n "$current" ]; then current="${current},${item}" else current="$item" fi write_env_value "$ENV_FILE" "$key" "$current" echo " ✓ 已补充环境配置项: $key" } validate_manifest() { local manifest="$1" local line line_number=0 while IFS= read -r line || [ -n "$line" ]; do line_number=$((line_number + 1)) line="${line%$'\r'}" case "$line" in ""|'#'*) continue ;; esac if [[ "$line" =~ ^@skip-if[[:space:]]+[A-Z][A-Z0-9_]*=.+$ ]]; then continue fi if ! [[ "$line" =~ ^[A-Z][A-Z0-9_]*(\+)?=.+$ ]]; then echo "❌ 无效的环境配置迁移: ${manifest}:${line_number}" >&2 return 1 fi done < "$manifest" } apply_manifest() { local manifest="$1" local line key value current while IFS= read -r line || [ -n "$line" ]; do line="${line%$'\r'}" if [[ "$line" =~ ^@skip-if[[:space:]]+([A-Z][A-Z0-9_]*)=(.+)$ ]]; then key="${BASH_REMATCH[1]}" value="${BASH_REMATCH[2]}" current="$(read_env_value "$ENV_FILE" "$key")" if [ "$current" = "$value" ]; then echo "ℹ️ 保留现有更新模式,跳过环境配置迁移: $(basename "$manifest")" return 0 fi fi done < "$manifest" echo "📝 应用环境配置迁移: $(basename "$manifest")" while IFS= read -r line || [ -n "$line" ]; do line="${line%$'\r'}" case "$line" in ""|'#'*) continue ;; esac if [[ "$line" == @skip-if* ]]; then continue fi if [[ "$line" == *'+='* ]]; then key="${line%%+=*}" value="${line#*+=}" ensure_list_item "$key" "$value" else key="${line%%=*}" value="${line#*=}" apply_default "$key" "$value" fi done < "$manifest" } shopt -s nullglob manifests=("$MIGRATIONS_DIR"/*.env) if [ "${#manifests[@]}" -eq 0 ]; then echo "ℹ️ 未发现环境配置迁移文件,跳过" exit 0 fi for manifest in "${manifests[@]}"; do validate_manifest "$manifest" done for manifest in "${manifests[@]}"; do apply_manifest "$manifest" done chmod 600 "$ENV_FILE"