= 🚀 DEPLOY — Mise en production et CI/CD :toc: left :toc-title: Sommaire :toclevels: 3 Ce document dĂ©crit la mise en production complĂšte d'*ANNU KUTE CED* sur un serveur fraĂźchement installĂ©, puis l'activation du dĂ©ploiement continu via *Gitea Actions*. Il est complĂ©mentaire au link:README.adoc[README] (installation manuelle, configuration de l'application). == 🧭 Vue d'ensemble L'architecture retenue (identique Ă  celle de pawol.nu) : [source] ---- ┌─────────────┐ push main ┌──────────────────┐ SSH (git pull) ┌─────────────┐ │ DĂ©pĂŽt git │ ─────────────▶│ Gitea Actions │ ─────────────────▶│ Serveur │ │ (LaBola) │ │ check → deploy │ │ production │ └─────────────┘ └──────────────────┘ └─────────────┘ ---- . *VĂ©rification* (`check-pr.yml` + job `check` de `deploy-prod.yml`) : lint PHP/JS et tests unitaires PHP/JS. Les validations AsciiDoc, JSON, XML et shellcheck ne sont pas exĂ©cutĂ©es dans le CI : elles doivent ĂȘtre passĂ©es en local avec `scripts/check.sh`. . *DĂ©ploiement* (`deploy-prod.yml`) : le runner se connecte en SSH au serveur, qui tient *un clone du dĂ©pĂŽt*, fait `git pull --ff-only`, puis *bumpe la version des caches* du Service Worker (`sw.js`) pour dĂ©clencher le modal de mise Ă  jour PWA chez les visiteurs. Pourquoi un `git pull` sur le serveur plutĂŽt qu'un rsync : tous les fichiers propres Ă  l'instance (`config.local.php`, `.htaccess`, `sitemap.xml`, `robots.txt`, `site.webmanifest`, `mentions-legales.php`, `dons.php`, `uploads/`, `cache/`) sont ignorĂ©s par Git — un pull ne les Ă©crase jamais. Le dĂ©ploiement est ainsi sans risque pour la configuration de production. == 📋 PrĂ©requis - Un serveur *Debian 12* ou *Ubuntu 24.04* fraĂźchement installĂ©, avec accĂšs `root` (ou `sudo`) - Un nom de domaine dont le *DNS pointe vers le serveur* (enregistrement A/AAAA) - Le dĂ©pĂŽt Gitea : `git@labola.o-k-i.net:cedric/annu-kute-ced.git` - Un *runner Gitea Actions* opĂ©rationnel sur l'instance LaBola (dĂ©jĂ  le cas pour pawol.nu) NOTE: Sur un hĂ©bergement mutualisĂ© (pas d'accĂšs root), adaptez : les fichiers sont dĂ©ployĂ©s dans le docroot fourni par l'hĂ©bergeur, et la clĂ© SSH s'ajoute via le panneau de contrĂŽle (o2switch : *ClĂ©s SSH* dans cPanel). == 1ïžâƒŁ Installation des paquets [source,bash] ---- # Nginx + PHP-FPM (choix recommandĂ© ; Apache possible, voir §5 option B) apt update && apt upgrade -y apt install -y nginx php-fpm \ php-curl php-intl php-mbstring php-xml \ git curl unzip # VĂ©rifier les extensions requises php -m | grep -E 'curl|intl|mbstring|SimpleXML|json' ---- == 2ïžâƒŁ Utilisateur de dĂ©ploiement Le runner CI se connectera en SSH avec cet utilisateur. Ne *pas* utiliser root. [source,bash] ---- adduser --disabled-password --gecos "Deploy annu-kute-ced" deploy usermod -aG www-data deploy # RĂ©pertoire de l'application mkdir -p /var/www/annu-kute-ced chown -R deploy:www-data /var/www/annu-kute-ced # RĂ©pertoire SSH de l'utilisateur deploy (prĂ©parĂ© pour les clĂ©s publiques) mkdir -p /home/deploy/.ssh chmod 700 /home/deploy/.ssh chown -R deploy:deploy /home/deploy/.ssh ---- == 3ïžâƒŁ Clone du dĂ©pĂŽt Le serveur de production tient un clone du dĂ©pĂŽt. Le runner y exĂ©cutera `git pull` Ă  chaque dĂ©ploiement. [source,bash] ---- # ClĂ© SSH du serveur pour lire le dĂ©pĂŽt (deploy key Gitea, lecture seule) sudo -u deploy ssh-keygen -t ed25519 -f /home/deploy/.ssh/id_ed25519 -N "" cat /home/deploy/.ssh/id_ed25519.pub ---- . Sur LaBola : *Settings du dĂ©pĂŽt → Deploy Keys → Add deploy key* (coller la clĂ© publique, lecture seule suffit). . Puis : + [source,bash] ---- sudo -u deploy git clone git@labola.o-k-i.net:cedric/annu-kute-ced.git /var/www/annu-kute-ced cd /var/www/annu-kute-ced sudo -u deploy git config pull.ff only # sĂ©curitĂ© : refuser tout pull non fast-forward ---- == 4ïžâƒŁ Fichiers d'instance Ces fichiers sont ignorĂ©s par Git : ils vivent *uniquement sur le serveur* et ne seront jamais Ă©crasĂ©s par les dĂ©ploiements. [source,bash] ---- cd /var/www/annu-kute-ced sudo -u deploy cp includes/config.local.php.sample includes/config.local.php sudo -u deploy cp site.webmanifest.sample site.webmanifest sudo -u deploy cp robots.txt.sample robots.txt sudo -u deploy cp sitemap.xml.sample sitemap.xml sudo -u deploy cp mentions-legales.php.sample mentions-legales.php # Uniquement si vous utilisez Apache (option B de l'Ă©tape 5) : # sudo -u deploy cp conf/.htaccess.sample .htaccess # Facultatif (page de dons) : # sudo -u deploy cp dons.php.sample dons.php ---- Éditer ensuite les fichiers copiĂ©s : - `includes/config.local.php` : `APP_HOST_NAME`, sources (PeerTube/Castopod/Mastodon), `CACHE_ENABLED=true` (recommandĂ© pour Castopod), etc. — voir la link:README.adoc#fr-configuration[rĂ©fĂ©rence de configuration] - `sitemap.xml`, `robots.txt`, `site.webmanifest` : remplacer `example.com` par le domaine rĂ©el - `mentions-legales.php` : remplacer le placeholder `VOTRE-DATE-MAJ` [source,bash] ---- # Le cache de l'API doit ĂȘtre accessible en Ă©criture par PHP mkdir -p /var/www/annu-kute-ced/cache chown -R www-data:www-data /var/www/annu-kute-ced/cache ---- == 5ïžâƒŁ VirtualHost === Option A — Nginx + PHP-FPM (recommandĂ©e, configuration fournie) Le fichier `conf/nginx.conf.sample` est l'Ă©quivalent Nginx complet du `.htaccess` : mĂȘmes protections (fichiers de configuration, rĂ©pertoires sensibles, dotfiles, pas de listing), masquage de l'extension `.php`, `no-cache` pour `sw.js` et `site.webmanifest`, plus le cache des assets et gzip. Il est conçu pour un dĂ©ploiement *en deux temps* : le bloc `:80` sert immĂ©diatement le site en HTTP (prĂ©requis de la validation Certbot, Ă©tape 6), et `certbot --nginx` crĂ©era ensuite le bloc `443` avec la redirection HTTPS. Aucun certificat n'est donc requis Ă  cette Ă©tape — `nginx -t` doit passer tel quel. [source,bash] ---- # Adapter conf/nginx.conf.sample : # - server_name : votre domaine # - root : /var/www/annu-kute-ced # - fastcgi_pass : socket de votre version PHP (ex. /var/run/php/php8.3-fpm.sock) cp conf/nginx.conf.sample /etc/nginx/sites-available/annu-kute-ced ln -s /etc/nginx/sites-available/annu-kute-ced /etc/nginx/sites-enabled/ nginx -t && systemctl reload nginx ---- === Option B — Apache [source,bash] ---- apt install -y apache2 php libapache2-mod-php a2enmod rewrite headers cat > /etc/apache2/sites-available/annu-kute-ced.conf <<'EOF' ServerName example.com ServerAlias www.example.com DocumentRoot /var/www/annu-kute-ced AllowOverride All Require all granted EOF a2ensite annu-kute-ced systemctl reload apache2 ---- Le `.htaccess` copiĂ© Ă  l'Ă©tape 4 applique les rĂšgles de sĂ©curitĂ©, le HTTPS forcĂ© (actif aprĂšs l'Ă©tape 6) et le `no-cache` de `sw.js`. == 6ïžâƒŁ HTTPS (obligatoire pour la PWA) MĂ©thode recommandĂ©e par l'EFF : *Certbot via snap* (https://certbot.eff.org/instructions?ws=nginx&os=snap[instructions officielles^]). PrĂ©requis : le domaine pointe vers le serveur et le site rĂ©pond dĂ©jĂ  en HTTP sur le port 80 (Ă©tape 5 terminĂ©e). [source,bash] ---- # 1. Installer snapd (Ubuntu : dĂ©jĂ  prĂ©sent. Debian : apt + support classic) apt install -y snapd snap install core && snap refresh core # 2. Retirer tout certbot installĂ© via apt (Ă©vite les conflits de commande) apt-get remove -y certbot || true # 3. Installer Certbot et prĂ©parer la commande snap install --classic certbot ln -s /snap/bin/certbot /usr/local/bin/certbot # 4. Obtenir le certificat ET laisser Certbot configurer Nginx automatiquement certbot --nginx -d example.com -d www.example.com # Variante prudente (ne fait qu'Ă©mettre le certificat, vhost Ă©ditĂ© Ă  la main) : # certbot certonly --nginx -d example.com -d www.example.com # 5. VĂ©rifier le renouvellement automatique (timer systemd/cron inclus avec le snap) certbot renew --dry-run ---- NOTE: Pour Apache (option B), la mĂ©thode snap est identique, avec `certbot --apache` Ă  l'Ă©tape 4. Ouvrez ensuite `https://example.com` dans un navigateur : le cadenas doit apparaĂźtre dans la barre d'URL. == 7ïžâƒŁ VĂ©rification du site [source,bash] ---- curl -I https://example.com # Attendu : HTTP/2 200, en-tĂȘtes de sĂ©curitĂ© (CSP, X-Frame-Options
) curl -I https://example.com/sw.js # Attendu : Cache-Control: no-cache ---- Ouvrir le site dans un navigateur : vidĂ©os, podcasts, timeline et le bouton d'installation PWA doivent fonctionner. == 8ïžâƒŁ ClĂ© SSH du CI C'est la clĂ© utilisĂ©e par le runner Gitea Actions pour se connecter au serveur et dĂ©ployer. Elle est *diffĂ©rente* de la deploy key de lecture (Ă©tape 3) : celle-ci doit pouvoir Ă©crire dans le clone. GĂ©nĂ©rez la paire de clĂ©s *sur votre poste local* (pas sur le serveur), puis transfĂ©rez la clĂ© publique sur le serveur pour l'autoriser. [source,bash] ---- # Sur votre poste local ssh-keygen -t ed25519 -f annu-kute-ced-deploy -C "ci-gitea-annu-kute-ced" # Affichez la clĂ© publique (elle doit ĂȘtre copiĂ©e sur le serveur) cat annu-kute-ced-deploy.pub ---- === Transfert de la clĂ© publique sur le serveur Option A — copie directe avec `scp` (depuis votre poste) : [source,bash] ---- # Sur votre poste local (adapter user@serveur si vous ne vous connectez pas en root) scp annu-kute-ced-deploy.pub root@:/tmp/annu-kute-ced-deploy.pub # Puis sur le serveur, ajoutez-la au fichier authorized_keys de deploy sudo -u deploy mkdir -p /home/deploy/.ssh sudo -u deploy tee -a /home/deploy/.ssh/authorized_keys < /tmp/annu-kute-ced-deploy.pub ---- Option B — connexion SSH sur le serveur, puis crĂ©ation manuelle du fichier : [source,bash] ---- # Sur le serveur, connectĂ© en root ou avec sudo sudo -u deploy mkdir -p /home/deploy/.ssh sudo -u deploy tee -a /home/deploy/.ssh/authorized_keys # Coller le contenu de annu-kute-ced-deploy.pub, puis Ctrl+D ---- Quelle que soit la mĂ©thode, vĂ©rifiez les permissions : [source,bash] ---- chown -R deploy:deploy /home/deploy/.ssh chmod 700 /home/deploy/.ssh chmod 600 /home/deploy/.ssh/authorized_keys ---- === Test de la connexion CI Avant d'enregistrer la clĂ© privĂ©e dans Gitea, testez depuis votre poste que la connexion fonctionne avec la clĂ© privĂ©e gĂ©nĂ©rĂ©e : [source,bash] ---- # Sur votre poste local ssh -i annu-kute-ced-deploy deploy@ "whoami && hostname" # Attendu : "deploy" et le nom du serveur, sans mot de passe demandĂ© ---- La clĂ© privĂ©e (`annu-kute-ced-deploy`, sans passphrase) ira dans les secrets Gitea Ă  l'Ă©tape suivante. *Conservez-la en lieu sĂ»r et ne la commitez jamais.* == 9ïžâƒŁ Secrets Gitea Dans LaBola : *Settings du dĂ©pĂŽt → Actions → Secrets*, crĂ©er : [cols="1,3",options="header"] |=== | Secret | Valeur | `SSH_HOST` | Adresse du serveur (IP ou FQDN), port 22 par dĂ©faut (sinon `host:port`) | `SSH_USER` | `deploy` | `SSH_KEY` | Contenu *intĂ©gral* de la clĂ© privĂ©e gĂ©nĂ©rĂ©e Ă  l'Ă©tape 8 | `PROD_DEPLOY_PATH` | `/var/www/annu-kute-ced` |=== WARNING: `SSH_KEY` donne un accĂšs shell au serveur avec les droits de `deploy`. Ne jamais la coller ailleurs que dans les secrets Gitea, et rĂ©voquer la clĂ© publique (`authorized_keys`) en cas de doute. == 🔟 Test du pipeline . Pousser un commit sur `main` (par exemple une correction de coquille dans le README). . Dans LaBola, onglet *Actions* : le workflow *DĂ©ploiement PROD* doit exĂ©cuter `check` puis `deploy` au vert. . CĂŽtĂ© serveur : + [source,bash] ---- cd /var/www/annu-kute-ced git log -1 --oneline # doit correspondre au commit poussĂ© grep STATIC_CACHE_NAME sw.js # le suffixe de version a Ă©tĂ© bumpĂ© Ă  l'heure du dĂ©ploiement ---- . CĂŽtĂ© visiteur : au prochain chargement de page, le modal « Nouvelle version disponible » apparaĂźt (Service Worker dĂ©jĂ  installĂ© lors d'une visite prĂ©cĂ©dente). == 🔄 Fonctionnement courant [cols="1,3",options="header"] |=== | ÉvĂ©nement | RĂ©sultat | Pull request vers `main` | Workflow *VĂ©rification PR* : lint PHP/JS et tests unitaires PHP/JS bloquants en cas d'erreur | Push sur `main` | Workflow *DĂ©ploiement PROD* : lint PHP/JS et tests unitaires PHP/JS, puis SSH → `git pull --ff-only` → bump de la version des caches `sw.js` → modal de mise Ă  jour chez les visiteurs | En local, avant de pousser | `scripts/check.sh` exĂ©cute l'ensemble des vĂ©rifications qualitĂ© (PHP, JS, AsciiDoc, JSON, XML, shellcheck) |=== Le bump de version dans `sw.js` est fait *sur le serveur uniquement* : le dĂ©pĂŽt garde sa valeur de rĂ©fĂ©rence, le working tree du serveur est nettoyĂ© (`git checkout -- sw.js`) avant chaque pull pour garantir le fast-forward. == đŸ”„ PrĂ©chauffage du cache Pour Ă©viter que le premier visiteur ne paye le coĂ»t des appels API externes (PeerTube, Castopod) sur cache froid, prĂ©chauffez le cache par cron : [source,bash] ---- # Toutes les 5 minutes, avec l'utilisateur deploy sudo -u deploy crontab -e # Ajouter : */5 * * * * php /var/www/annu-kute-ced/scripts/warm-cache.php >/dev/null 2>&1 ---- Le script `scripts/warm-cache.php` appelle les fonctions de rĂ©cupĂ©ration des vidĂ©os, catĂ©gories, podcasts et live, ce qui remplit `cache/api/` avant l'arrivĂ©e des visiteurs. == 🧰 DĂ©pannage [cols="1,3",options="header"] |=== | SymptĂŽme | Piste | `git pull --ff-only` Ă©choue sur le serveur | Une modification locale existe : `git status` dans le docroot, puis `git checkout -- ` (le workflow le fait dĂ©jĂ  pour `sw.js`) | `Permission denied` sur `.git/FETCH_HEAD` (ou un autre fichier `.git`) | Des fichiers du clone appartiennent Ă  `root` (une commande `git` a Ă©tĂ© lancĂ©e en root, sans `sudo -u deploy`) : `chown -R deploy:www-data /var/www/annu-kute-ced` | L'action ne se dĂ©clenche pas | VĂ©rifier que le runner act est en ligne (LaBola → *Site Administration → Actions → Runners*) et que Actions est activĂ© pour le dĂ©pĂŽt (*Settings → Units*) | `Permission denied (publickey)` dans le job deploy | ClĂ© publique absente de `/home/deploy/.ssh/authorized_keys`, ou mauvais `SSH_USER`/`SSH_HOST` | Les visiteurs ne reçoivent pas la mise Ă  jour | VĂ©rifier `curl -I https://example.com/sw.js` → `Cache-Control: no-cache` requis (rĂšgles fournies dans `conf/`) | Erreurs 500 sur les vidĂ©os/podcasts | `cache/` non accessible en Ă©criture : `chown -R www-data:www-data cache/` | `502 Bad Gateway` (Nginx) | PHP-FPM injoignable : vĂ©rifier le socket rĂ©el avec `ls /var/run/php/` et adapter `fastcgi_pass` dans le vhost (ex. `php8.1-fpm.sock` sous Ubuntu 22.04, `php8.3-fpm.sock` sous 24.04) ; vĂ©rifier que le service tourne : `systemctl status php*-fpm` | `php-intl` manquant | `apt install php-intl` puis `systemctl restart php*-fpm` (Nginx) ou `systemctl restart apache2` (Apache) |=== == 🔒 Notes de sĂ©curitĂ© - La clĂ© privĂ©e du CI est *dĂ©diĂ©e* Ă  ce dĂ©pĂŽt : une clĂ© compromise ne donne accĂšs qu'au compte `deploy`, sans sudo. - La deploy key Gitea (Ă©tape 3) est en *lecture seule*. - Pour restreindre davantage la clĂ© du CI, on peut limiter les commandes dans `authorized_keys` (`command="..."`, `no-pty`) — facultatif, hors scope de ce guide. - Les fichiers sensibles (`includes/`, `.htaccess`, `config.local.php`) sont bloquĂ©s en accĂšs web par les configurations fournies dans `conf/`. == 📞 Support En cas de blocage : mailto:kontak@o-k-i.net[kontak@o-k-i.net]