#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SSH_KEY_DEFAULT="${HOME}/.ssh/vps_ovh_cd035e9a_ed25519" SSH_USER_DEFAULT="ubuntu" SSH_HOST_DEFAULT="142.44.210.180" usage() { cat <<'EOF' Usage: ./scripts/deploy-vps.sh [--skip-check] [--skip-build] Examples: ./scripts/deploy-vps.sh preprod ./scripts/deploy-vps.sh prod Behavior: - synchronise les sources utiles vers le VPS - exécute le check TypeScript local (sauf --skip-check) - exécute le build local (sauf --skip-build) - reconstruit et redémarre l'application distante - utilise explicitement le bon fichier d'environnement par cible EOF } if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then usage exit 0 fi TARGET="${1:-}" if [[ -z "${TARGET}" ]]; then usage exit 1 fi shift || true RUN_CHECK=1 RUN_BUILD=1 USE_MAINTENANCE_FLAG="${USE_MAINTENANCE_FLAG:-0}" while [[ $# -gt 0 ]]; do case "$1" in --skip-check) RUN_CHECK=0 ;; --skip-build) RUN_BUILD=0 ;; -h|--help) usage exit 0 ;; *) echo "Option inconnue: $1" >&2 usage exit 1 ;; esac shift done case "${TARGET}" in prod) REMOTE_DIR="/home/${SSH_USER_DEFAULT}/portail-associations" REMOTE_DEPLOY_CMD='cd /home/ubuntu/portail-associations && docker compose --env-file .env up -d --build app' POST_CHECK_URL='https://www.portail-association973.com' ;; preprod) REMOTE_DIR="/home/${SSH_USER_DEFAULT}/portail-associations-preprod" REMOTE_DEPLOY_CMD='cd /home/ubuntu/portail-associations-preprod/deployments/preprod && docker compose --env-file .env up -d --build app' POST_CHECK_URL='https://preprod.portail-association973.com' ;; *) echo "Cible invalide: ${TARGET}" >&2 usage exit 1 ;; esac SSH_KEY="${SSH_KEY:-${SSH_KEY_DEFAULT}}" SSH_USER="${SSH_USER:-${SSH_USER_DEFAULT}}" SSH_HOST="${SSH_HOST:-${SSH_HOST_DEFAULT}}" SSH_TARGET="${SSH_USER}@${SSH_HOST}" SSH_CMD=(ssh -i "${SSH_KEY}" -o BatchMode=yes -o ConnectTimeout=10 "${SSH_TARGET}") RSYNC_SSH="ssh -i ${SSH_KEY} -o BatchMode=yes -o ConnectTimeout=10" POST_CHECK_HEALTH_URL="${POST_CHECK_URL}/health/ready" SYNC_ITEMS=( "${ROOT_DIR}/client" "${ROOT_DIR}/server" "${ROOT_DIR}/shared" "${ROOT_DIR}/drizzle" "${ROOT_DIR}/docs" "${ROOT_DIR}/deployments" "${ROOT_DIR}/scripts" "${ROOT_DIR}/patches" "${ROOT_DIR}/package.json" "${ROOT_DIR}/pnpm-lock.yaml" "${ROOT_DIR}/Dockerfile" "${ROOT_DIR}/docker-compose.yml" "${ROOT_DIR}/vite.config.ts" "${ROOT_DIR}/tsconfig.json" "${ROOT_DIR}/components.json" "${ROOT_DIR}/drizzle.config.ts" "${ROOT_DIR}/MAIL_PROVIDERS.md" ) echo "==> Vérification SSH (${SSH_TARGET})" "${SSH_CMD[@]}" 'echo SSH_OK >/dev/null' remote_maintenance() { local mode="$1" "${SSH_CMD[@]}" "cd ${REMOTE_DIR} && MAINTENANCE_FLAG_PATH=${REMOTE_DIR}/uploads/system/maintenance.flag ./scripts/maintenance-flag.sh ${mode}" } wait_for_http_200() { local url="$1" local label="$2" local max_attempts="${3:-20}" local delay_seconds="${4:-3}" local attempt=1 while [[ "${attempt}" -le "${max_attempts}" ]]; do local code code="$(curl -k -s -o /dev/null -w "%{http_code}" -m 20 "${url}" || true)" echo " - ${label} tentative ${attempt}/${max_attempts} -> ${code}" if [[ "${code}" == "200" ]]; then return 0 fi sleep "${delay_seconds}" attempt=$((attempt + 1)) done return 1 } if [[ "${RUN_CHECK}" -eq 1 ]]; then echo "==> TypeScript check local" (cd "${ROOT_DIR}" && corepack pnpm check) fi if [[ "${RUN_BUILD}" -eq 1 ]]; then echo "==> Build local" (cd "${ROOT_DIR}" && corepack pnpm build) fi echo "==> Synchronisation des sources vers ${REMOTE_DIR}" rsync -az --delete \ --exclude '.git/' \ --exclude 'node_modules/' \ --exclude 'dist/' \ --exclude 'uploads/' \ --exclude '.env' \ -e "${RSYNC_SSH}" \ "${SYNC_ITEMS[@]}" \ "${SSH_TARGET}:${REMOTE_DIR}/" if [[ "${USE_MAINTENANCE_FLAG}" == "1" ]]; then echo "==> Activation du mode maintenance" remote_maintenance on fi echo "==> Déploiement ${TARGET}" "${SSH_CMD[@]}" "${REMOTE_DEPLOY_CMD}" echo "==> Attente de disponibilité applicative (${POST_CHECK_HEALTH_URL})" wait_for_http_200 "${POST_CHECK_HEALTH_URL}" "health" 25 3 if [[ "${USE_MAINTENANCE_FLAG}" == "1" ]]; then echo "==> Désactivation du mode maintenance" remote_maintenance off fi echo "==> Contrôle HTTP ${POST_CHECK_URL}" wait_for_http_200 "${POST_CHECK_URL}" "home" 10 2 curl -I -k -m 20 "${POST_CHECK_HEALTH_URL}" echo "==> Déploiement ${TARGET} terminé"