179 lines
4.5 KiB
Bash
Executable file
179 lines
4.5 KiB
Bash
Executable file
#!/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 <prod|preprod> [--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é"
|