Administration Serveur angularforall.com

- CI/CD GitHub Actions vers serveur Linux Angular

Ci/Cd Github Actions Deploiement Angular Ssh Linux Automatisation Rollback
CI/CD GitHub Actions vers serveur Linux Angular

Automatisez le deploiement Angular avec GitHub Actions : workflow YAML, cle SSH dediee, secrets, deploiement atomique avec symlinks, rollback rapide et hardening complet.

Pourquoi automatiser avec GitHub Actions

Deployer manuellement une application Angular se passe bien... jusqu'au jour ou ca deraille. Quelqu'un oublie une etape, fait le `git pull` sur la mauvaise branche, oublie le `npm ci` apres une montee de version, ou execute le build sur sa machine locale et pousse des bundles qui ne correspondent pas au serveur.

Une CI/CD bien faite remplace ces gestes humains par un workflow reproductible. Chaque push sur `main` declenche les memes etapes : tests → build → transfert → reload. Si une etape echoue, le deploiement est stoppe automatiquement. Le serveur reste donc toujours dans un etat coherent, et l'historique de deploiement est tracable directement dans GitHub.

GitHub Actions est l'outil le plus accessible pour cela en 2026 : integration native avec le depot, gratuit jusqu'a 2000 minutes/mois en prive, et syntaxe YAML lisible. Pour une app Angular sur un VPS Linux classique, c'est le bon choix. Cet article montre la configuration exacte pour passer d'un deploiement manuel a une release sur push, en moins d'une heure.

Approche choisie : on declenche le build sur le serveur via SSH (pas dans GitHub Actions). C'est plus rapide pour les petits projets et garantit un build coherent avec l'environnement de production.

Creer une cle SSH dediee au deploy

Premiere regle de securite : ne reutilisez jamais votre cle SSH personnelle pour la CI. Generez une paire dediee, qui pourra etre revoquee independamment si elle fuit, et qui sera restreinte aux operations de deploiement.

# Sur votre machine locale
ssh-keygen -t ed25519 -C "github-deploy-angular" -f ~/.ssh/deploy_angular_ed25519

# Le fichier ~/.ssh/deploy_angular_ed25519.pub contient la cle publique
cat ~/.ssh/deploy_angular_ed25519.pub

Sur le serveur, creez un utilisateur dedie au deploiement (separe de votre compte sudo) et ajoutez la cle publique :

# Sur le serveur
sudo adduser --disabled-password --gecos "" deploy
sudo mkdir -p /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh

# Ajout de la cle publique (copier-coller le contenu du .pub)
sudo nano /home/deploy/.ssh/authorized_keys

sudo chmod 600 /home/deploy/.ssh/authorized_keys
sudo chown -R deploy:deploy /home/deploy/.ssh

Donnez ensuite a `deploy` les droits stricts necessaires sur le repertoire de l'app et l'autorisation de recharger PM2 sans mot de passe :

# Droits sur le repertoire de deploy
sudo mkdir -p /var/www/angular-app
sudo chown deploy:deploy /var/www/angular-app

# Autoriser le reload PM2 sans password (sudoers minimal)
echo "deploy ALL=(angularapp) NOPASSWD: /usr/bin/pm2 reload angular-ssr" \
  | sudo tee /etc/sudoers.d/deploy-angular
sudo chmod 440 /etc/sudoers.d/deploy-angular

Testez la connexion avec la nouvelle cle depuis votre poste :

ssh -i ~/.ssh/deploy_angular_ed25519 deploy@votre-serveur.com "echo OK"
# OK
Hardening : dans `/home/deploy/.ssh/authorized_keys`, prefixez la cle avec `command="..."` pour restreindre les commandes executables. C'est utile pour limiter encore plus le perimetre.

Configurer les GitHub Secrets

Stockez la cle privee et les autres infos sensibles dans Settings > Secrets and variables > Actions du depot GitHub. Ne les ecrivez jamais dans le YAML du workflow.

Secrets minimum a creer :

  • `SSH_PRIVATE_KEY` : contenu de `~/.ssh/deploy_angular_ed25519` (cle privee, lignes BEGIN/END comprises)
  • `SSH_HOST` : adresse du serveur (ex: `votre-domaine.com` ou IP)
  • `SSH_USER` : utilisateur de deploy (ex: `deploy`)
  • `SSH_KNOWN_HOSTS` : empreinte du serveur (recuperee via `ssh-keyscan votre-serveur.com`)

Pour generer la valeur de `SSH_KNOWN_HOSTS` :

# Sur votre machine locale
ssh-keyscan -t ed25519 votre-serveur.com

# Output a copier dans GitHub Secrets
# votre-serveur.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...

Cette etape est cruciale : sans `known_hosts`, GitHub Actions affiche une erreur "Host key verification failed". Avec, vous evitez egalement les attaques MITM (man-in-the-middle).

Premier workflow : build et tests

Avant le deploiement, creez un premier workflow CI qui valide chaque push : install, lint, tests, build. Si ces etapes echouent, on ne deploie pas.

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js 20
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Lint
        run: npm run lint

      - name: Tests unitaires
        run: npm test -- --watch=false --browsers=ChromeHeadless

      - name: Build production
        run: npm run build

Pushez ce fichier sur la branche `main`. Dans l'onglet "Actions" du depot, vous voyez immediatement le workflow s'executer. En cas d'erreur, GitHub envoie un email automatique au commiter.

# Test local equivalent (simule la CI)
npm ci
npm run lint
npm test -- --watch=false
npm run build

Pour les pull requests, ajoutez une protection de branche dans Settings > Branches : exigez que ce workflow passe avant tout merge sur `main`.

Workflow de deploiement SSH

Le deuxieme workflow declenche le deploiement reel. Il s'execute uniquement sur les push vers `main` (jamais sur les pull requests) et utilise les secrets SSH definis precedemment.

# .github/workflows/deploy.yml
name: Deploy Production

on:
  push:
    branches: [main]
  workflow_dispatch:  # Permet le declenchement manuel

jobs:
  deploy:
    needs: []
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      - name: Setup SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
          chmod 644 ~/.ssh/known_hosts

      - name: Deploy via SSH
        env:
          SSH_HOST: ${{ secrets.SSH_HOST }}
          SSH_USER: ${{ secrets.SSH_USER }}
        run: |
          ssh -i ~/.ssh/id_ed25519 $SSH_USER@$SSH_HOST 'bash -s' < .github/scripts/deploy.sh

      - name: Notification Slack
        if: always()
        run: |
          STATUS="${{ job.status }}"
          curl -X POST -H 'Content-type: application/json' \
            --data "{\"text\":\":rocket: Deploy ${STATUS} sur production - SHA ${{ github.sha }}\"}" \
            ${{ secrets.SLACK_WEBHOOK }}

Le script de deploiement reel reste sur le serveur (ou versionne dans `.github/scripts/deploy.sh`) :

#!/usr/bin/env bash
# .github/scripts/deploy.sh
set -euo pipefail

APP_DIR="/var/www/angular-app"
LOG_FILE="/var/log/angular-ssr/deploy.log"

log() {
  printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" | tee -a "$LOG_FILE"
}

cd "$APP_DIR"

log "=== Debut deploy SHA $(git rev-parse --short HEAD)"

log "Pull du code"
git fetch origin main
git reset --hard origin/main

log "Installation dependances"
npm ci

log "Build production"
npm run build

log "Reload PM2"
sudo -u angularapp pm2 reload angular-ssr

log "=== Deploy termine"

A chaque push sur `main`, GitHub Actions se connecte au serveur et execute ces etapes. Le statut du job remonte dans GitHub avec les logs complets, accessibles meme apres l'execution.

Deploiement atomique avec symlinks

Le script precedent fait un deploiement "in-place" : pendant le `npm ci` + `npm run build`, l'app peut etre dans un etat incoherent. Pour eviter cela, utilisez le pattern Capistrano : chaque deploy creee un nouveau dossier `releases/timestamp`, et un symlink `current` pointe vers la release active.

# Structure cible sur le serveur
# /var/www/angular-app/
# ├── current -> releases/20260506-220000
# ├── releases/
# │   ├── 20260506-220000/
# │   ├── 20260506-180000/
# │   └── 20260505-220000/
# └── shared/
#     └── .env

Script de deploy atomique :

#!/usr/bin/env bash
# /home/deploy/deploy-atomic.sh
set -euo pipefail

APP_DIR="/var/www/angular-app"
RELEASES="$APP_DIR/releases"
SHARED="$APP_DIR/shared"
TIMESTAMP=$(date '+%Y%m%d-%H%M%S')
NEW_RELEASE="$RELEASES/$TIMESTAMP"
KEEP_RELEASES=5

mkdir -p "$NEW_RELEASE"
cd "$NEW_RELEASE"

# Recuperation du code
git clone --depth 1 https://github.com/votre-org/mon-app.git .

# Lien vers fichiers partages (env, uploads, etc.)
ln -s "$SHARED/.env" .env

# Build
npm ci --omit=dev=false
npm run build

# Switch atomique du symlink
ln -sfn "$NEW_RELEASE" "$APP_DIR/current"

# Reload PM2 (les workers prennent le nouveau code)
sudo -u angularapp pm2 reload angular-ssr

# Cleanup des anciennes releases
cd "$RELEASES"
ls -t | tail -n +$((KEEP_RELEASES + 1)) | xargs -r rm -rf

echo "Deploy $TIMESTAMP termine"

L'avantage : pendant que le nouveau dossier se prepare, l'ancien continue de servir le trafic. Le switch est instantane (un seul symlink) et n'interrompt aucune requete en cours.

Bonus : avec ce pattern, le rollback ne demande aucun rebuild. Il suffit de changer la cible du symlink `current`.

Strategie de rollback automatique

Un deploy peut sembler reussir techniquement mais casser une fonctionnalite cote utilisateur. Avoir une commande de rollback rapide est essentiel : c'est ce qui transforme un incident de 30 minutes en incident de 30 secondes.

#!/usr/bin/env bash
# /home/deploy/rollback.sh
set -euo pipefail

APP_DIR="/var/www/angular-app"
RELEASES="$APP_DIR/releases"

# Trouver la release precedente (la 2e plus recente)
PREVIOUS=$(ls -t "$RELEASES" | sed -n '2p')

if [ -z "$PREVIOUS" ]; then
    echo "Aucune release precedente disponible"
    exit 1
fi

echo "Rollback vers $PREVIOUS"

ln -sfn "$RELEASES/$PREVIOUS" "$APP_DIR/current"
sudo -u angularapp pm2 reload angular-ssr

echo "Rollback termine"

Pour automatiser le rollback en cas d'echec du healthcheck post-deploy, ajoutez une etape de validation :

# Ajout dans deploy-atomic.sh, apres le pm2 reload
sleep 5
if ! curl -sf https://votre-domaine.com/health > /dev/null; then
    echo "Healthcheck KO, rollback automatique"
    /home/deploy/rollback.sh
    exit 1
fi

echo "Healthcheck OK"

Ce filet de securite garantit qu'une release qui casse le `/health` ne reste pas active plus de quelques secondes.

Bonnes pratiques et hardening

Quelques regles a respecter pour que votre CI/CD reste fiable et secure dans la duree :

  • Cle SSH limitee : utilisez `command="..."` dans `authorized_keys` pour restreindre les commandes executables. La cle de deploy ne doit JAMAIS pouvoir lancer `bash -i` ou `rm -rf`.
  • Environnement protege : dans GitHub > Environments, ajoutez une protection "Required reviewers" sur l'environnement `production`. Cela force une approbation humaine avant chaque deploy.
  • Secrets segregues : stockez les secrets sensibles au niveau Environment (pas Repository), ce qui evite qu'un workflow sur une branche autre que `main` puisse les lire.
  • Logs centralises : configurez le workflow pour publier les logs de deploy dans Slack ou Discord. Cela cree un historique parallele a celui de GitHub Actions.
  • Tests post-deploy : integrez un smoke test (curl + verif HTTP 200 sur 3-4 routes critiques) apres le `pm2 reload`. Ne considerez pas le deploy reussi tant que ce test ne passe pas.
  • Rotation cles : renouvelez la cle SSH de deploy une fois par an minimum. Plus si l'equipe change.

Pour aller plus loin, vous pouvez decouper le workflow en plusieurs jobs avec dependances :

# .github/workflows/deploy.yml (version avancee)
jobs:
  test:
    # ... lint + tests + build
  deploy-staging:
    needs: test
    if: github.ref == 'refs/heads/develop'
    # ... deploy vers staging
  deploy-prod:
    needs: test
    if: github.ref == 'refs/heads/main'
    environment:
      name: production
      url: https://votre-domaine.com
    # ... deploy vers prod
Conseil terrain : commencez simple. Un workflow `ci.yml` + un `deploy.yml` basique suffit largement pour 95 % des projets. Ajoutez la complexite quand le besoin reel apparait.

Conclusion

Mettre en place une CI/CD pour Angular avec GitHub Actions tient en quatre etapes : creer une cle SSH dediee, stocker les secrets dans GitHub, ecrire un workflow CI puis un workflow de deploy SSH. Une fois en place, chaque merge sur `main` declenche automatiquement la sequence test → build → deploy → reload, sans intervention humaine.

Le vrai gain n'est pas seulement technique. Il est organisationnel. L'equipe peut pousser plus souvent, avec moins de stress, parce que chaque release suit le meme parcours et qu'un rollback prend 30 secondes. Les regressions sont plus rares parce que les tests bloquent les deploys avant qu'ils atteignent la prod.

Si vous demarrez aujourd'hui, suivez l'ordre exact de cet article. Validez chaque etape avant de passer a la suivante : SSH manuel d'abord, puis workflow build, puis workflow deploy. C'est cette progression qui evite les erreurs de configuration silencieuses qui ne se voient que lors du premier vrai incident.

Suite logique : couplez ce pipeline avec le deploiement Angular SSR + PM2, le monitoring production et le hardening SSH pour une chaine de delivery complete et fiable.

Partager