137 行
3.3 KiB
Bash
137 行
3.3 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
|
|
AUDIT_LOG="$ROOT_DIR/logs/audit.log"
|
||
|
|
PROGRESS_FILE="$ROOT_DIR/.deploy-state/progress.md"
|
||
|
|
|
||
|
|
now() {
|
||
|
|
date +"%Y-%m-%dT%H:%M:%S%z"
|
||
|
|
}
|
||
|
|
|
||
|
|
audit() {
|
||
|
|
local action="$1"
|
||
|
|
local status="$2"
|
||
|
|
local message="${3:-}"
|
||
|
|
mkdir -p "$(dirname "$AUDIT_LOG")"
|
||
|
|
printf '{"time":"%s","action":"%s","status":"%s","message":"%s"}\n' \
|
||
|
|
"$(now)" "$action" "$status" "$message" >> "$AUDIT_LOG"
|
||
|
|
}
|
||
|
|
|
||
|
|
progress() {
|
||
|
|
local step="$1"
|
||
|
|
local status="$2"
|
||
|
|
local notes="${3:-}"
|
||
|
|
mkdir -p "$(dirname "$PROGRESS_FILE")"
|
||
|
|
if [ ! -f "$PROGRESS_FILE" ]; then
|
||
|
|
printf '# Deployment Progress\n\n| Time | Step | Status | Notes |\n|------|------|--------|-------|\n' > "$PROGRESS_FILE"
|
||
|
|
fi
|
||
|
|
printf '| %s | %s | %s | %s |\n' "$(now)" "$step" "$status" "$notes" >> "$PROGRESS_FILE"
|
||
|
|
}
|
||
|
|
|
||
|
|
fail_json() {
|
||
|
|
local code="$1"
|
||
|
|
local message="$2"
|
||
|
|
local step="${3:-unknown}"
|
||
|
|
printf '{"code":"%s","message":"%s","step":"%s","recoverable":true}\n' "$code" "$message" "$step" >&2
|
||
|
|
audit "$step" "FAILED" "$message"
|
||
|
|
progress "$step" "FAILED" "$message"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
load_env() {
|
||
|
|
set -a
|
||
|
|
[ -f "$ROOT_DIR/.env" ] && . "$ROOT_DIR/.env"
|
||
|
|
[ -f "$ROOT_DIR/config/infra.env" ] && . "$ROOT_DIR/config/infra.env"
|
||
|
|
[ -f "$ROOT_DIR/config/xuqm.env" ] && . "$ROOT_DIR/config/xuqm.env"
|
||
|
|
[ -f "$ROOT_DIR/config/secrets.env" ] && . "$ROOT_DIR/config/secrets.env"
|
||
|
|
set +a
|
||
|
|
}
|
||
|
|
|
||
|
|
require_cmd() {
|
||
|
|
command -v "$1" >/dev/null 2>&1 || fail_json "XUQM_PRIVATE_4001" "missing required command: $1" "preflight"
|
||
|
|
}
|
||
|
|
|
||
|
|
random_secret() {
|
||
|
|
if command -v openssl >/dev/null 2>&1; then
|
||
|
|
openssl rand -base64 32 | tr -d '\n'
|
||
|
|
else
|
||
|
|
printf '%s_%s' "$(date +%s)" "$RANDOM"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
ensure_secret_file() {
|
||
|
|
if [ ! -f "$ROOT_DIR/config/secrets.env" ]; then
|
||
|
|
cp "$ROOT_DIR/config/secrets.env.example" "$ROOT_DIR/config/secrets.env"
|
||
|
|
chmod 600 "$ROOT_DIR/config/secrets.env" || true
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
set_env_value() {
|
||
|
|
local file="$1"
|
||
|
|
local key="$2"
|
||
|
|
local value="$3"
|
||
|
|
mkdir -p "$(dirname "$file")"
|
||
|
|
touch "$file"
|
||
|
|
if grep -q "^${key}=" "$file"; then
|
||
|
|
sed -i.bak "s|^${key}=.*|${key}=${value}|" "$file"
|
||
|
|
rm -f "${file}.bak"
|
||
|
|
else
|
||
|
|
printf '%s=%s\n' "$key" "$value" >> "$file"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
ensure_env_value() {
|
||
|
|
local file="$1"
|
||
|
|
local key="$2"
|
||
|
|
local current="${3:-}"
|
||
|
|
local generated="$4"
|
||
|
|
if [ -z "$current" ] || [ "$current" = "change-me" ]; then
|
||
|
|
set_env_value "$file" "$key" "$generated"
|
||
|
|
printf '%s' "$generated"
|
||
|
|
else
|
||
|
|
printf '%s' "$current"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
profile_contains() {
|
||
|
|
local profiles="$1"
|
||
|
|
local target="$2"
|
||
|
|
case ",${profiles}," in
|
||
|
|
*,"${target}",*) return 0 ;;
|
||
|
|
*) return 1 ;;
|
||
|
|
esac
|
||
|
|
}
|
||
|
|
|
||
|
|
add_profile() {
|
||
|
|
local profiles="${1:-base}"
|
||
|
|
local target="$2"
|
||
|
|
if profile_contains "$profiles" "$target"; then
|
||
|
|
printf '%s' "$profiles"
|
||
|
|
else
|
||
|
|
printf '%s,%s' "$profiles" "$target"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
remove_profile() {
|
||
|
|
local profiles="$1"
|
||
|
|
local target="$2"
|
||
|
|
local result=""
|
||
|
|
local item
|
||
|
|
IFS=',' read -r -a items <<< "$profiles"
|
||
|
|
for item in "${items[@]}"; do
|
||
|
|
[ "$item" = "$target" ] && continue
|
||
|
|
[ -z "$item" ] && continue
|
||
|
|
if [ -z "$result" ]; then
|
||
|
|
result="$item"
|
||
|
|
else
|
||
|
|
result="$result,$item"
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
printf '%s' "${result:-base}"
|
||
|
|
}
|
||
|
|
|
||
|
|
compose() {
|
||
|
|
docker compose --env-file "$ROOT_DIR/.env" -f "$ROOT_DIR/docker-compose.yml" -f "$ROOT_DIR/docker-compose.infra.yml" "$@"
|
||
|
|
}
|