From 300c9dc69485182564a1fc3a75040aa8871e3cc6 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 29 Jul 2026 16:25:03 -0400 Subject: [PATCH] fix build issue --- .gitea/workflows/build-and-deploy.yml | 100 +++------------------- docs/DOCKER.md | 2 + scripts/preserve-env-values.sh | 117 ++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 89 deletions(-) create mode 100644 scripts/preserve-env-values.sh diff --git a/.gitea/workflows/build-and-deploy.yml b/.gitea/workflows/build-and-deploy.yml index 37b143f..5f6bf5d 100644 --- a/.gitea/workflows/build-and-deploy.yml +++ b/.gitea/workflows/build-and-deploy.yml @@ -447,101 +447,18 @@ jobs: ENV_DOCKER_PRODUCTION_RAW_B64="$(printf '%s' "$ENV_DOCKER_PRODUCTION" | base64 | tr -d '\n')" STRIPE_API_KEY_B64="$(printf '%s' "$STRIPE_API_KEY" | base64 | tr -d '\n')" STRIPE_WEBHOOK_SECRET_B64="$(printf '%s' "$STRIPE_WEBHOOK_SECRET" | base64 | tr -d '\n')" - validate_stripe_secret_inputs() { - local env_file stripe_api_key stripe_webhook_secret - - env_file="$(mktemp)" - trap 'rm -f "$env_file"' EXIT - - if [ -n "$ENV_DOCKER_PRODUCTION_B64_CLEAN" ]; then - if ! printf '%s' "$ENV_DOCKER_PRODUCTION_B64_CLEAN" | base64 -d > "$env_file"; then - echo "::error::ENV_DOCKER_PRODUCTION_B64 is not valid base64" - exit 1 - fi - elif [ -n "$ENV_DOCKER_PRODUCTION" ]; then - printf '%s' "$ENV_DOCKER_PRODUCTION" > "$env_file" - else - : > "$env_file" - fi - - read_env_key() { - local key="$1" - awk -F= -v key="$key" ' - /^[[:space:]]*#/ || index($0, "=") == 0 { - next - } - { - name = $1 - sub(/^[[:space:]]*export[[:space:]]+/, "", name) - gsub(/^[[:space:]]+|[[:space:]]+$/, "", name) - if (name == key) { - value = substr($0, index($0, "=") + 1) - } - } - END { - print value - } - ' "$env_file" - } - - normalize_secret() { - local value="$1" - value="${value%$'\r'}" - value="${value#"${value%%[![:space:]]*}"}" - value="${value%"${value##*[![:space:]]}"}" - if [[ "$value" == \"*\" && "$value" == *\" ]]; then - value="${value#\"}" - value="${value%\"}" - elif [[ "$value" == \'*\' && "$value" == *\' ]]; then - value="${value#\'}" - value="${value%\'}" - fi - printf '%s' "$value" - } - - stripe_api_key="$(normalize_secret "${STRIPE_API_KEY:-$(read_env_key STRIPE_API_KEY)}")" - stripe_webhook_secret="$(normalize_secret "${STRIPE_WEBHOOK_SECRET:-$(read_env_key STRIPE_WEBHOOK_SECRET)}")" - - case "$stripe_api_key" in - sk_live_*|rk_live_*) ;; - "") - echo "::error::STRIPE_API_KEY is missing. Add a live sk_live_ or restricted rk_live_ key to the STRIPE_API_KEY secret, ENV_DOCKER_PRODUCTION, or ENV_DOCKER_PRODUCTION_B64." - exit 1 - ;; - placeholder|replace-with-*|*changeme*|*change-me*) - echo "::error::STRIPE_API_KEY is still a placeholder. Replace it with a live sk_live_ or restricted rk_live_ key in Gitea Actions secrets." - exit 1 - ;; - *) - echo "::error::STRIPE_API_KEY must start with sk_live_ or rk_live_ for production billing." - exit 1 - ;; - esac - - case "$stripe_webhook_secret" in - whsec_*) ;; - "") - echo "::error::STRIPE_WEBHOOK_SECRET is missing. Add the Stripe webhook signing secret to the STRIPE_WEBHOOK_SECRET secret, ENV_DOCKER_PRODUCTION, or ENV_DOCKER_PRODUCTION_B64." - exit 1 - ;; - placeholder|replace-with-*|*changeme*|*change-me*) - echo "::error::STRIPE_WEBHOOK_SECRET is still a placeholder. Replace it with the Stripe webhook signing secret in Gitea Actions secrets." - exit 1 - ;; - *) - echo "::error::STRIPE_WEBHOOK_SECRET must start with whsec_." - exit 1 - ;; - esac - } - - validate_stripe_secret_inputs REGISTRY_USERNAME="${REGISTRY_USERNAME:-${REGISTRY_USER:-}}" REGISTRY_PASSWORD="${REGISTRY_PASSWORD:-${REGISTRY_TOKEN:-}}" REGISTRY_PASSWORD_B64="$(printf '%s' "$REGISTRY_PASSWORD" | base64 | tr -d '\n')" ssh $SSH_OPTIONS "${{ secrets.VPS_USER }}@$VPS_HOST" " set -e mkdir -p '$DEPLOY_ROOT' '$REMOTE_BUILD_DIR' + previous_env_file='' + if [ -f '$DEPLOY_ROOT/.env.docker.production' ]; then + previous_env_file=\"\$(mktemp)\" + cp '$DEPLOY_ROOT/.env.docker.production' \"\$previous_env_file\" + trap 'rm -f \"\$previous_env_file\"' EXIT + fi if [ -n '$ENV_DOCKER_PRODUCTION_B64_CLEAN' ]; then printf '%s' '$ENV_DOCKER_PRODUCTION_B64_CLEAN' | base64 -d > '$DEPLOY_ROOT/.env.docker.production' chmod 600 '$DEPLOY_ROOT/.env.docker.production' @@ -557,9 +474,14 @@ jobs: cp '$DEPLOY_ROOT/.env.docker.production' '$REMOTE_BUILD_DIR/.env.docker.production' chmod 600 '$REMOTE_BUILD_DIR/.env.docker.production' cd '$REMOTE_BUILD_DIR' + if [ -n \"\$previous_env_file\" ]; then + bash scripts/preserve-env-values.sh \"\$previous_env_file\" .env.docker.production STRIPE_API_KEY STRIPE_WEBHOOK_SECRET + fi export STRIPE_API_KEY_B64='$STRIPE_API_KEY_B64' export STRIPE_WEBHOOK_SECRET_B64='$STRIPE_WEBHOOK_SECRET_B64' bash scripts/apply-env-secret-overrides.sh .env.docker.production STRIPE_API_KEY STRIPE_WEBHOOK_SECRET + cp .env.docker.production '$DEPLOY_ROOT/.env.docker.production' + chmod 600 '$DEPLOY_ROOT/.env.docker.production' export APP_IMAGE='$DEPLOY_REGISTRY_HOST/$IMAGE_REPOSITORY' export IMAGE_TAG='$IMAGE_TAG' export REGISTRY_HOST='$DEPLOY_REGISTRY_HOST' diff --git a/docs/DOCKER.md b/docs/DOCKER.md index f17d85e..5a2e49f 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -174,6 +174,8 @@ Paste that single-line output into `ENV_DOCKER_PRODUCTION_B64` when using the ba You can also store `STRIPE_API_KEY` and `STRIPE_WEBHOOK_SECRET` as separate Gitea Actions secrets. When those secrets are present, the deploy workflow overwrites the Stripe values from `ENV_DOCKER_PRODUCTION`/`ENV_DOCKER_PRODUCTION_B64` before deploying. This is useful when the production env file secret still contains placeholders for billing secrets. +If `/opt/rentaldrivego/.env.docker.production` already exists on the VPS and a newly supplied env-file secret omits `STRIPE_API_KEY` or `STRIPE_WEBHOOK_SECRET`, the deploy workflow preserves the existing VPS values for those keys. Final production validation still fails if the merged env file does not contain a live `sk_live_`/`rk_live_` key and a `whsec_` webhook signing secret. + Production now derives `DATABASE_URL` inside the app container from `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_DB`, `POSTGRES_USER`, and `POSTGRES_PASSWORD` when `DATABASE_URL_FROM_POSTGRES=true`. That avoids Prisma auth failures when the database password contains reserved URL characters such as `@`, `:`, or `/`. The example file uses `rentaldrivego.ma` for the carplace and public site. The dashboard and admin panel are routed under that same host at `/dashboard` and `/admin`. diff --git a/scripts/preserve-env-values.sh b/scripts/preserve-env-values.sh new file mode 100644 index 0000000..8b641a8 --- /dev/null +++ b/scripts/preserve-env-values.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash + +set -euo pipefail + +usage() { + cat <<'EOF' +Usage: + preserve-env-values.sh [KEY...] + +For each KEY, copy the value from into only +when the target value is missing or still looks like a placeholder. +EOF +} + +if [[ $# -lt 3 ]]; then + usage >&2 + exit 1 +fi + +source_env_file="$1" +target_env_file="$2" +shift 2 + +for env_file in "${source_env_file}" "${target_env_file}"; do + if [[ ! -f "${env_file}" ]]; then + echo "Missing env file: ${env_file}" >&2 + exit 1 + fi +done + +read_env_value() { + local env_file="$1" + local key="$2" + local value + + value="$( + awk -F= -v key="${key}" ' + /^[[:space:]]*#/ || index($0, "=") == 0 { + next + } + { + name = $1 + sub(/^[[:space:]]*export[[:space:]]+/, "", name) + gsub(/^[[:space:]]+|[[:space:]]+$/, "", name) + if (name == key) { + value = substr($0, index($0, "=") + 1) + } + } + END { + print value + } + ' "${env_file}" + )" + + value="${value%$'\r'}" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + if [[ "${value}" == \"*\" && "${value}" == *\" ]]; then + value="${value#\"}" + value="${value%\"}" + elif [[ "${value}" == \'*\' && "${value}" == *\' ]]; then + value="${value#\'}" + value="${value%\'}" + fi + printf '%s' "${value}" +} + +should_preserve_value() { + local value="$1" + + [[ -z "${value}" || "${value}" == placeholder || "${value}" == replace-with-* || "${value}" == *changeme* || "${value}" == *change-me* ]] +} + +upsert_env_value() { + local key="$1" + local value="$2" + local tmp_file + + tmp_file="$(mktemp "${target_env_file}.tmp.XXXXXX")" + awk -v key="${key}" -v value="${value}" ' + BEGIN { + updated = 0 + } + /^[[:space:]]*#/ || index($0, "=") == 0 { + print + next + } + { + name = $1 + sub(/=.*/, "", name) + sub(/^[[:space:]]*export[[:space:]]+/, "", name) + gsub(/^[[:space:]]+|[[:space:]]+$/, "", name) + if (name == key) { + print key "=" value + updated = 1 + next + } + print + } + END { + if (!updated) { + print key "=" value + } + } + ' "${target_env_file}" > "${tmp_file}" + + mv "${tmp_file}" "${target_env_file}" +} + +for key in "$@"; do + source_value="$(read_env_value "${source_env_file}" "${key}")" + target_value="$(read_env_value "${target_env_file}" "${key}")" + + if [[ -n "${source_value}" ]] && should_preserve_value "${target_value}"; then + upsert_env_value "${key}" "${source_value}" + fi +done