Skip to content
English

Déploiement et mises à jour ​

CapyCode est un site statique : npm run build produit dist/, que la CI GitLab (job deploy, déclenché à la main sur main) copie par rsync --delete sur le serveur de Capytale, sous /p/capycode/. Une mise à jour doit être visible des utilisateurs sans vider leur cache ni forcer le rechargement (Ctrl+Maj+R). Trois mécanismes y concourent : le service worker, des noms de fichiers qui changent avec leur contenu, et les en-têtes HTTP.

Le service worker : src/sw.ts ​

vite-plugin-pwa le construit avec la stratégie injectManifest : le fichier est écrit à la main, Workbox y injecte la liste des fichiers à précacher.

RequêteStratégiePourquoi
Navigation (index.html, avec ou sans paramètres)Réseau d'abord ; en cas d'échec ou après 4 s, copie précachée d'index.htmlLa page référence les fichiers de sa version : la charger depuis le réseau donne la dernière version dès le premier rechargement. Hors ligne, l'application démarre quand même.
Interface (assets/*, polices, icônes)PrécacheFichiers nommés par leur empreinte : jamais périmés.
Noyaux Basthon (basthon/<dossier>/…)Cache à la première utilisation (basthon-<dossier>)Plusieurs Mo, inutiles à qui n'exécute pas de code. Le dossier change avec le contenu (Intégration de Basthon) ; les caches des versions précédentes sont supprimés à l'activation.
Ruff (ruff_wasm_bg-<empreinte>.wasm)Cache à la première utilisation, une seule entréeIdem.

La route de navigation est déclarée avant le précache : sinon Workbox servirait index.html depuis le précache sans consulter le réseau. C'était le défaut de la configuration précédente (generateSW) : après un déploiement, le premier rechargement affichait encore l'ancienne version.

src/sw.ts a ses propres types (WebWorker), vérifiés par tsconfig.sw.json dans npm run build.

Recharger une page restée ouverte : useAppUpdate ​

Une nouvelle version du service worker s'active dès qu'elle est installée (skipWaiting, clientsClaim). src/main.ts l'enregistre (virtual:pwa-register) et en cherche une nouvelle toutes les heures et à chaque retour sur l'onglet. Quand elle prend la main, src/composables/useAppUpdate.ts regarde si la page est périmée (un de ses scripts ou feuilles de style a quitté le précache) ; si oui :

  • onglet masqué et aucun fichier à enregistrer : rechargement immédiat ;
  • sinon : notification invitant à recharger, puis rechargement automatique dès que l'onglet est masqué sans fichier à enregistrer.

Une page périmée qui charge un module à la demande déjà supprimé du serveur (vite:preloadError) est rechargée, sauf s'il y a des fichiers à enregistrer.

Le serveur : public/.htaccess ​

Le fichier est copié dans dist/ par Vite et déployé avec le site (le rsync --delete supprimerait sinon toute configuration placée à la main dans le dossier). Il suppose Apache avec AllowOverride FileInfo (au moins) sur le dossier : une directive interdite rend tout le dossier en erreur 500, d'où le choix de directives qui n'exigent que FileInfo (Header … "expr=…" plutôt qu'un bloc <If>).

  • HTTPS : redirection permanente de HTTP vers HTTPS (y compris derrière un mandataire, par X-Forwarded-Proto) et HSTS pour un an.
  • Sécurité : X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy qui refuse caméra, micro, géolocalisation, paiement et USB. Ni X-Frame-Options ni frame-ancestors, car Capytale intègre CapyCode dans un cadre ; ni COOP/COEP (Intégration de Basthon). Une Content-Security-Policy reste à écrire : les workers Basthon (URL data:, cdn.basthon.fr, WebAssembly) et les sorties graphiques demandent une mise au point en conditions réelles, en Content-Security-Policy-Report-Only d'abord.
  • Cache : no-cache par défaut (le navigateur revalide chaque fois par l'ETag, ce qui vaut pour index.html, sw.js, le manifeste et les icônes) ; public, max-age=31536000, immutable pour les fichiers dont le nom change avec le contenu (assets/, basthon/<version>-<empreinte>/).
  • Types MIME : .webmanifest, .wasm, .js.

Les fichiers absents sous basthon/ doivent rester de vrais 404 (le noyau Python sonde un Pyodide local) : aucune règle de réécriture vers index.html.

Le pipeline de la CI ​

  • e2e attend test (needs) : lancés ensemble, les deux jobs se partageaient le même nœud et les tests de bout en bout dépassaient leurs délais.
  • Le cache de la CI garde le cache de téléchargement d'npm (.npm/, clé : package-lock.json), pas node_modules/ : npm ci l'efface de toute façon, et renvoyer ses 40 000 fichiers coûtait plusieurs minutes par job.

La documentation des contributeurs : doc/ ​

Cette documentation est publiée avec l'application, sous <BASE_PATH>doc/ (/p/capycode/doc/ sur Capytale). Le job build la construit après l'application : npm run docs:build écrit dans dist/doc/ (outDir de docs/.vitepress/config.ts), et le rsync la déploie avec le reste.

  • L'ordre compte. vite build vide dist/ : la documentation se construit après. Comme elle n'existe pas encore quand vite-plugin-pwa dresse la liste du précache, elle n'y entre pas : un élève ne télécharge pas la documentation.
  • Le service worker la laisse au navigateur : la route de navigation de src/sw.ts exclut doc/ (denylist), sinon une page de la documentation, hors ligne, serait remplacée par l'index.html de l'application.
  • Les en-têtes de public/.htaccess s'y appliquent aussi : pages en no-cache, fichiers de doc/assets/ (nommés par leur empreinte) en immutable.
  • Le chemin suit BASE_PATH : BASE_PATH=/ npm run docs:build publie la documentation sous /doc/. En local, npm run docs:dev la sert sous le même chemin (/p/capycode/doc/, sur le port qu'il affiche), et npm run build && npm run docs:build && npm run preview sert l'application et sa documentation comme en production.

Vérifier une mise à jour en local ​

bash
npm run build
npx vite preview

Ouvrir l'application, modifier un texte, refaire npm run build sans arrêter vite preview : un seul rechargement normal doit afficher la modification, et une page laissée ouverte doit proposer de recharger. Arrêter vite preview et recharger : l'application démarre hors ligne.