name: Deploy Hostinger # Déploie Parenthèse sur le VPS quand le workflow « CI » a réussi sur un push sur main, ou à la main. # Le travail est fait sur le serveur par deploy/deploy.sh, incrémental et verrouillé : seules les parties # modifiées sont reconstruites, et un run sans changement ne reconstruit rien. # Le serveur déploie le commit exact que la CI a validé (head_sha du run), jamais la tête de main au # moment du run : un push arrivé entre-temps, CI rouge ou en cours, partait sinon en production sans contrôle. # Un run plus ancien qu'un déploiement déjà fait ne revient pas en arrière. on: workflow_run: workflows: - CI types: - completed branches: - main workflow_dispatch: inputs: full: description: 'Tout réinstaller et tout reconstruire' type: boolean default: false permissions: contents: read # Un déploiement à la fois. Un push arrivé pendant un déploiement attend son tour au lieu de le couper # (couper un npm ci en cours laisse node_modules à moitié vide). concurrency: group: hostinger-production cancel-in-progress: false jobs: deploy: # Uniquement sur le dépôt d'origine (jamais sur un fork), et après une CI réussie sur un vrai push sur main. if: >- github.repository == 'IliesAllali/parenthese' && ( github.event_name == 'workflow_dispatch' || ( github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main' && github.event.workflow_run.head_repository.full_name == 'IliesAllali/parenthese' ) ) runs-on: ubuntu-latest timeout-minutes: 25 steps: - name: Start SSH agent uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 with: ssh-private-key: ${{ secrets.HOSTINGER_SSH_PRIVATE_KEY }} # Clé d'hôte épinglée par le secret SSH_KNOWN_HOSTS (sortie de ssh-keyscan relue à la main, voir # deploy/HOSTINGER_GITHUB_SETUP.md). Sans ce secret, le déploiement s'arrête : faire confiance à la clé # présentée le jour du run enverrait la clé de déploiement à n'importe quel serveur intercalé. - name: Hostinger host key env: SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }} run: | set -euo pipefail if [ -z "$SSH_KNOWN_HOSTS" ]; then echo "::error::Secret SSH_KNOWN_HOSTS absent : ajoutez la clé d'hôte du VPS (voir deploy/HOSTINGER_GITHUB_SETUP.md)." exit 1 fi mkdir -p ~/.ssh chmod 700 ~/.ssh printf '%s\n' "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts chmod 600 ~/.ssh/known_hosts - name: Deploy on Hostinger env: HOSTINGER_HOST: ${{ secrets.HOSTINGER_HOST }} HOSTINGER_PORT: ${{ secrets.HOSTINGER_PORT }} HOSTINGER_USER: ${{ secrets.HOSTINGER_USER }} FORCE_FULL: ${{ inputs.full && '1' || '0' }} DEPLOY_SHA: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }} run: | set -euo pipefail PORT="${HOSTINGER_PORT:-22}" if ! printf '%s' "$DEPLOY_SHA" | grep -Eq '^[0-9a-f]{40}$'; then echo "::error::Commit à déployer invalide : $DEPLOY_SHA" exit 1 fi # backend/.env vit sur le serveur, jamais dans GitHub (dépôt public) ssh -p "$PORT" -o StrictHostKeyChecking=yes -o ServerAliveInterval=30 "$HOSTINGER_USER@$HOSTINGER_HOST" "FORCE_FULL=$FORCE_FULL DEPLOY_SHA=$DEPLOY_SHA bash -s" <<'EOF' set -euo pipefail cd /var/www/parenthese # Sous le même verrou que deploy.sh : récupération du code, puis déploiement flock -w 900 /tmp/parenthese-deploy.lock env PARENTHESE_DEPLOY_LOCKED=1 FORCE_FULL="$FORCE_FULL" DEPLOY_SHA="$DEPLOY_SHA" bash -c ' set -euo pipefail git fetch -q origin main # Le commit validé doit appartenir à main if ! git merge-base --is-ancestor "$DEPLOY_SHA" origin/main; then echo "Commit $DEPLOY_SHA absent de main : rien n est déployé" >&2 exit 1 fi # Un déploiement plus récent est déjà en ligne : pas de retour en arrière if [ "$(git rev-parse HEAD)" != "$DEPLOY_SHA" ] && git merge-base --is-ancestor "$DEPLOY_SHA" HEAD; then echo "Version plus récente déjà déployée, $DEPLOY_SHA ignoré" exit 0 fi git reset -q --hard "$DEPLOY_SHA" bash deploy/deploy.sh ' EOF