feat(deploy): webhook auto-deploy Forgejo → VPS Paris (TD-04)
Some checks are pending
CI / quality (push) Waiting to run

This commit is contained in:
Hermann_Kitio 2026-07-01 12:34:39 +03:00
parent 85c760abee
commit 0ae2db3d8c
6 changed files with 333 additions and 37 deletions

98
deploy/deploy.sh Normal file
View file

@ -0,0 +1,98 @@
#!/usr/bin/env bash
# Expria auto-deploy script (VPS Paris).
#
# Runs as the non-root `deploy` user, invoked by deploy/webhook-listener.mjs on
# a verified push to origin/main. Fast-forwards the checkout, installs, builds,
# and restarts expria-backend.service via ONE restricted sudo rule:
# deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart expria-backend.service
#
# On a build or health-check failure it auto-rolls back to the previous commit.
# All output goes to stdout -> journald (journalctl -u expria-deploy).
set -euo pipefail
REPO_DIR="/opt/expria/expria-backend"
BRANCH="main"
SERVICE="expria-backend.service"
HEALTH_URL="${HEALTH_URL:-http://127.0.0.1:4000/}"
LOCK_FILE="/tmp/expria-deploy.lock"
log() { echo "$(date --iso-8601=seconds) [deploy] $*"; }
# Serialize concurrent deploys: a second webhook waits here, or bails out.
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
log "another deploy is already in progress — aborting this run"
exit 0
fi
cd "$REPO_DIR"
PREV="$(git rev-parse HEAD)"
log "start (current=${PREV:0:8})"
restart_backend() {
sudo /usr/bin/systemctl restart "$SERVICE"
}
# Poll the liveness endpoint (GET / always returns 200 when the process is up).
health_check() {
for _ in $(seq 1 10); do
if curl -fsS --max-time 3 "$HEALTH_URL" >/dev/null 2>&1; then
return 0
fi
sleep 2
done
return 1
}
rollback() {
log "ROLLBACK to ${PREV:0:8}"
if git reset --hard "$PREV" && npm ci && npm run build; then
if restart_backend && health_check; then
log "ROLLBACK ok — service healthy on ${PREV:0:8}"
return 0
fi
fi
log "ROLLBACK FAILED — manual intervention required (was on ${PREV:0:8})"
return 1
}
# 1. Fetch + fast-forward only (refuses a diverged history, no destructive pull).
git fetch --prune origin "$BRANCH"
if ! git merge --ff-only "origin/$BRANCH"; then
log "fast-forward failed (diverged history) — aborting, no changes applied"
exit 1
fi
NEW="$(git rev-parse HEAD)"
log "pulled ${PREV:0:8} -> ${NEW:0:8}"
if [ "$PREV" = "$NEW" ]; then
log "already up to date — nothing to deploy"
exit 0
fi
# 2. Install + build (rollback on failure).
if ! npm ci; then
log "npm ci FAILED"
rollback
exit 1
fi
if ! npm run build; then
log "npm run build FAILED"
rollback
exit 1
fi
# 3. Restart + health check (rollback on failure).
if ! restart_backend; then
log "systemctl restart FAILED"
rollback
exit 1
fi
if health_check; then
log "SUCCESS — deployed ${NEW:0:8}"
else
log "health check FAILED after restart"
rollback
exit 1
fi