Mettre en place une intégration continue (CI) pour un site statique ne devrait pas être une corvée. Dans cet article, je vous guide pas à pas depuis la logique jusqu'à un workflow GitHub Actions opérationnel, adapté aux petits sites personnels comme aux sites de documentation. J'explique les choix pratiques, les erreurs que j'ai faites et comment automatiser le build, les tests simples et le déploiement (vers GitHub Pages ou un dossier de sortie). Le but : un CI simple, fiable et facile à maintenir.

Pourquoi ajouter une CI à un site statique ?

Quand on travaille sur un site statique (HTML/CSS/JS, Jekyll, Hugo, Eleventy...), on pense souvent que tout est local. Pourtant, une CI apporte plusieurs bénéfices concrets :

  • Automatisation du build : génération du site à chaque push sur la branche principale.
  • Contrôle qualité : exécution de linter CSS/JS, vérification de liens, tests d'accessibilité basiques.
  • Déploiement reproductible : déployer automatiquement sur GitHub Pages, Netlify, ou un bucket S3.
  • Traçabilité : logs et historique des déploiements en cas de régression.

Principe général du workflow

Mon workflow préféré pour un site statique suit ces étapes :

  • Déclenchement sur push/PR vers main (ou branche de production).
  • Checkout du dépôt.
  • Installation des dépendances (npm/pip/gem selon le générateur).
  • Exécution des linters/tests basiques.
  • Build du site (générer le dossier static/dist/_site).
  • Déploiement (actions-gh-pages, pages deploy, ou upload vers un hébergement).

Cette structure est volontairement simple : elle couvre la majorité des besoins tout en restant lisible pour quelqu'un qui débute avec GitHub Actions.

Exemple concret : site Node (Eleventy / Hugo via Node)

Pour rendre les choses palpables, voici un exemple que j'utilise souvent pour des sites basés sur Node (Eleventy, Astro, un SSG qui nécessite npm install et npm run build). L'idée :

1) Déclenchement sur push vers main et sur pull_request.

2) Setup de Node (version définie dans package.json ou .nvmrc).

3) Linting (ESLint, stylelint si présents).

4) Build (npm run build qui génère ./dist).

5) Déploiement sur GitHub Pages via peaceiris/actions-gh-pages.

Voici en clair les étapes YAML (présentées en texte) qu'il faudra placer dans .github/workflows/ci.yml :

- name: Checkout repository
- name: Setup Node.js (uses: actions/setup-node@v3)
- name: Install dependencies (run: npm ci)
- name: Lint (run: npm run lint) # optionnel
- name: Test (run: npm test) # optionnel
- name: Build (run: npm run build) # doit générer le dossier 'dist'
- name: Deploy (uses: peaceiris/actions-gh-pages@v3) # ou autre action de déploiement

Astuce pratique : préférez npm ci en CI pour une installation propre et rapidement reproductible.

Variables et secrets

Pour déployer vers GitHub Pages avec peaceiris/actions-gh-pages, vous aurez besoin d'un Personal Access Token (PAT) si vous déployez sur une branche gh-pages depuis Actions. Pour un déploiement sur la branche gh-pages du même dépôt depuis GitHub Actions, créez un secret nommé GH_TOKEN (ou utilisez le token prédéfini GITHUB_TOKEN pour des scénarios simples). Dans GitHub : Settings > Secrets and variables > Actions > New repository secret.

Nom du secretUtilité
GITHUB_TOKENToken automatique, souvent suffisant pour push sur gh-pages.
GH_PAT (ou GH_TOKEN personnalisé)Utilisez un PAT si vous avez besoin de permissions étendues (orga, forks).

Vérifications utiles à intégrer

Je recommande d'ajouter quelques vérifications simples au pipeline :

  • Vérifier que le site build sans erreur (exit code 0).
  • Executer un link checker : il existe des packages npm comme broken-link-checker ou des actions GitHub pour détecter les liens morts.
  • Linter CSS (stylelint) et JS (eslint) — parce qu'un site propre fait moins de surprises.
  • Un test d'accessibilité rapide via axe-core ou pa11y pour attraper les problèmes évidents.

Ces étapes allongent le temps du workflow, mais elles évitent de déployer des régressions visibles immédiatement.

Déploiement vers GitHub Pages : exemple de configuration

Mode d'emploi minimal pour peaceiris/actions-gh-pages (texte lisible) :

- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
  node-version: '18'
- run: npm ci
- run: npm run build # génère ./dist
- uses: peaceiris/actions-gh-pages@v3
with:
  github_token: ${{ secrets.GITHUB_TOKEN }}
  publish_dir: ./dist

Si vous avez besoin d'une configuration plus avancée (custom domain, cname, message de commit), peaceiris propose des options supplémentaires : publish_branch, publish_dir, cname, etc.

Déploiement sur Netlify / Vercel / S3

Si vous préférez Netlify ou Vercel, le déploiement automatique via leurs plates-formes se fait souvent en branch-based deploy sans GitHub Actions. Mais si vous voulez tout centraliser en Actions :

  • Netlify : utilisez netlify/actions-cli pour effectuer un deploy via NETLIFY_AUTH_TOKEN.
  • Vercel : utilisez vercel/action pour deploy avec VERCEL_TOKEN.
  • S3 : utilisez jakejarvis/s3-sync-action pour pousser le dossier dist dans un bucket, avec AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY.

Choisissez le service selon vos besoins (CDN, fonctions serverless, coût). Pour un site simple, GitHub Pages + CDN (Cloudflare) suffit souvent.

Erreurs courantes et comment les éviter

Quelques pièges que j'ai rencontrés et comment les corriger :

  • Le build fonctionne localement mais échoue en CI : vérifiez les versions de Node, variables d'environnement et dépendances natives. Fixez la version de Node dans le workflow.
  • Permissions refusées lors du push vers gh-pages : utilisez GITHUB_TOKEN ou un PAT avec les bonnes permissions, et vérifiez les règles de protection de branche.
  • Fichiers ignorés par .gitignore : si votre build dépend de fichiers générés ou de caches commit, assurez-vous que la CI installe tout proprement (npm ci) et ne repose pas sur des fichiers locaux.
  • Cache corrompu : utilisez l'action cache pour accélérer mais invalidez proprement lors de mises à jour majeures de dépendances.

Bonnes pratiques pour garder une CI légère

Voici ce que j'applique systématiquement :

  • Diviser les jobs : un job pour lint/test, un job pour build+deploy. Cela permet de ne pas déployer si le lint échoue.
  • Utiliser le cache pour node_modules (actions/cache) mais avec clé incluant package-lock.json pour invalidation.
  • Privilégier npm ci et scripts bien définis dans package.json (build, lint, test) pour que le workflow reste lisible.
  • Documenter le workflow dans le README pour que les contributeurs comprennent ce qui se passe à chaque push.

Si vous voulez, je peux générer un fichier YAML complet adapté à votre stack (Eleventy, Hugo, Jekyll, ou site purement statique) en me donnant : la branche cible, le dossier de build (dist/_site/public), le générateur et vos préférences de déploiement (GitHub Pages / Netlify / S3). Je peux aussi proposer une configuration qui inclut link-check et pa11y si vous voulez renforcer la qualité.