safe-redeploy-service.sh 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #!/usr/bin/env bash
  2. # Safe service-scoped redeploy for Briven France (compose project briven-brivenfrance-uilsk6).
  3. #
  4. # Why: bare `docker compose up --build` without the Dokploy-managed env blanks
  5. # secrets and can take down api.briven.tech. This script always loads durable env
  6. # first, then rebuilds only the services you name.
  7. #
  8. # On France:
  9. # /etc/dokploy/compose/briven-brivenfrance-uilsk6/code/scripts/safe-redeploy-service.sh api
  10. # /etc/dokploy/compose/briven-brivenfrance-uilsk6/code/scripts/safe-redeploy-service.sh api web docs
  11. #
  12. # Env sources (first that exists wins, then layered):
  13. # 1) /etc/dokploy/compose/briven-brivenfrance-uilsk6/.env.prod (durable secrets)
  14. # 2) /opt/briven_deploy/infra/dokploy/.env (bootstrap secrets)
  15. # Live Doltgres password always overrides from the running doltgres container.
  16. set -euo pipefail
  17. COMPOSE_DIR="${BRIVEN_COMPOSE_DIR:-/etc/dokploy/compose/briven-brivenfrance-uilsk6/code}"
  18. PROJECT="${BRIVEN_COMPOSE_PROJECT:-briven-brivenfrance-uilsk6}"
  19. FILE="${BRIVEN_COMPOSE_FILE:-infra/dokploy/compose.dokploy.yml}"
  20. DURABLE_ENV="${BRIVEN_DURABLE_ENV:-/etc/dokploy/compose/briven-brivenfrance-uilsk6/.env.prod}"
  21. BOOTSTRAP_ENV="${BRIVEN_BOOTSTRAP_ENV:-/opt/briven_deploy/infra/dokploy/.env}"
  22. if [[ $# -lt 1 ]]; then
  23. echo "usage: $0 <service> [service...]"
  24. echo "examples: $0 api | $0 api web | $0 docs"
  25. exit 1
  26. fi
  27. if [[ ! -d "$COMPOSE_DIR" ]]; then
  28. echo "error: compose dir not found: $COMPOSE_DIR (run on France)"
  29. exit 1
  30. fi
  31. cd "$COMPOSE_DIR"
  32. load_env_file() {
  33. local f="$1"
  34. [[ -f "$f" ]] || return 0
  35. set -a
  36. # shellcheck disable=SC1090
  37. . "$f"
  38. set +a
  39. echo "loaded env: $f"
  40. }
  41. load_env_file "$BOOTSTRAP_ENV"
  42. load_env_file "$DURABLE_ENV"
  43. # Live Doltgres password (container is source of truth)
  44. if docker ps --format '{{.Names}}' | grep -qx 'briven-brivenfrance-uilsk6-doltgres-1'; then
  45. LIVE_PW="$(
  46. docker inspect briven-brivenfrance-uilsk6-doltgres-1 \
  47. --format '{{range .Config.Env}}{{println .}}{{end}}' \
  48. | sed -n 's/^DOLTGRES_PASSWORD=//p'
  49. )"
  50. if [[ -n "${LIVE_PW:-}" ]]; then
  51. export BRIVEN_DOLTGRES_PASSWORD="$LIVE_PW"
  52. echo "loaded live DOLTGRES password from container"
  53. fi
  54. fi
  55. # Live MinIO root password is source of truth for S3 access.
  56. # Compose wires BOTH minio.MINIO_ROOT_PASSWORD and api.BRIVEN_MINIO_SECRET_KEY
  57. # from ${BRIVEN_MINIO_ROOT_PASSWORD}. If durable/bootstrap has a different
  58. # value than the already-running MinIO volume, logo uploads fail with
  59. # SignatureDoesNotMatch (and older UI surfaces a confusing 410 on fallback).
  60. if docker ps --format '{{.Names}}' | grep -qx 'briven-brivenfrance-uilsk6-minio-1'; then
  61. LIVE_MINIO_PW="$(
  62. docker inspect briven-brivenfrance-uilsk6-minio-1 \
  63. --format '{{range .Config.Env}}{{println .}}{{end}}' \
  64. | sed -n 's/^MINIO_ROOT_PASSWORD=//p'
  65. )"
  66. if [[ -n "${LIVE_MINIO_PW:-}" ]]; then
  67. export BRIVEN_MINIO_ROOT_PASSWORD="$LIVE_MINIO_PW"
  68. export BRIVEN_MINIO_SECRET_KEY="$LIVE_MINIO_PW"
  69. # Keep durable env in lockstep so the next redeploy does not drift again.
  70. if [[ -f "$DURABLE_ENV" ]]; then
  71. if grep -q '^BRIVEN_MINIO_ROOT_PASSWORD=' "$DURABLE_ENV"; then
  72. tmp=$(mktemp)
  73. while IFS= read -r line || [[ -n "$line" ]]; do
  74. case "$line" in
  75. BRIVEN_MINIO_ROOT_PASSWORD=*) echo "BRIVEN_MINIO_ROOT_PASSWORD=$LIVE_MINIO_PW" ;;
  76. BRIVEN_MINIO_SECRET_KEY=*) echo "BRIVEN_MINIO_SECRET_KEY=$LIVE_MINIO_PW" ;;
  77. *) printf '%s\n' "$line" ;;
  78. esac
  79. done < "$DURABLE_ENV" > "$tmp"
  80. mv "$tmp" "$DURABLE_ENV"
  81. else
  82. printf '\nBRIVEN_MINIO_ROOT_PASSWORD=%s\nBRIVEN_MINIO_SECRET_KEY=%s\n' \
  83. "$LIVE_MINIO_PW" "$LIVE_MINIO_PW" >> "$DURABLE_ENV"
  84. fi
  85. chmod 600 "$DURABLE_ENV"
  86. fi
  87. echo "loaded live MINIO root password from container (len=${#LIVE_MINIO_PW})"
  88. fi
  89. fi
  90. export BRIVEN_DOMAIN="${BRIVEN_DOMAIN:-briven.tech}"
  91. export BRIVEN_DOMAIN="${BRIVEN_DOMAIN#https://}"
  92. export BRIVEN_DOMAIN="${BRIVEN_DOMAIN#http://}"
  93. export BRIVEN_DOMAIN="${BRIVEN_DOMAIN%%/*}"
  94. if [[ -z "${BRIVEN_DOLTGRES_PASSWORD:-}" ]]; then
  95. echo "error: BRIVEN_DOLTGRES_PASSWORD empty — fix $DURABLE_ENV or doltgres container"
  96. exit 1
  97. fi
  98. if [[ -z "${BRIVEN_ENCRYPTION_KEY:-}" || -z "${BRIVEN_BETTER_AUTH_SECRET:-}" ]]; then
  99. echo "error: missing core secrets — copy a full env into $DURABLE_ENV"
  100. exit 1
  101. fi
  102. # Branding + OAuth secrets live in the encrypted tenant-secret store and need
  103. # BRIVEN_AUTH_MASTER_KEY (64 hex chars). Without it, dashboard Auth → branding
  104. # save returns "master key not configured for service: auth" and nothing sticks.
  105. if [[ -z "${BRIVEN_AUTH_MASTER_KEY:-}" || ! "${BRIVEN_AUTH_MASTER_KEY}" =~ ^[0-9a-fA-F]{64}$ ]]; then
  106. if [[ -f "$DURABLE_ENV" ]]; then
  107. GEN=$(openssl rand -hex 32)
  108. if grep -q '^BRIVEN_AUTH_MASTER_KEY=' "$DURABLE_ENV"; then
  109. tmp=$(mktemp)
  110. while IFS= read -r line || [[ -n "$line" ]]; do
  111. case "$line" in
  112. BRIVEN_AUTH_MASTER_KEY=*) echo "BRIVEN_AUTH_MASTER_KEY=$GEN" ;;
  113. *) printf '%s\n' "$line" ;;
  114. esac
  115. done < "$DURABLE_ENV" > "$tmp"
  116. mv "$tmp" "$DURABLE_ENV"
  117. else
  118. printf '\nBRIVEN_AUTH_MASTER_KEY=%s\n' "$GEN" >> "$DURABLE_ENV"
  119. fi
  120. chmod 600 "$DURABLE_ENV"
  121. export BRIVEN_AUTH_MASTER_KEY="$GEN"
  122. echo "generated BRIVEN_AUTH_MASTER_KEY into $DURABLE_ENV (was missing/invalid)"
  123. else
  124. echo "error: BRIVEN_AUTH_MASTER_KEY missing and no durable env at $DURABLE_ENV"
  125. exit 1
  126. fi
  127. fi
  128. export BRIVEN_AUTH_MASTER_KEY
  129. echo "BRIVEN_AUTH_MASTER_KEY loaded (len=${#BRIVEN_AUTH_MASTER_KEY})"
  130. echo "redeploying: $* (project=$PROJECT domain=$BRIVEN_DOMAIN)"
  131. docker compose -p "$PROJECT" -f "$FILE" build "$@"
  132. docker compose -p "$PROJECT" -f "$FILE" up -d --force-recreate --no-deps "$@"
  133. echo "done. verify:"
  134. echo " curl -sS https://api.briven.tech/info | head -c 200"
  135. echo " curl -sS -o /dev/null -w '%{http_code}\\n' https://docs.briven.tech/auth/parity"
  136. if [[ " $* " == *" api "* ]] || [[ "$*" == "api" ]]; then
  137. echo " docker exec briven-brivenfrance-uilsk6-api-1 sh -c 'echo AUTH_MASTER=\${BRIVEN_AUTH_MASTER_KEY:+SET}'"
  138. fi