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ête | Stratégie | Pourquoi |
|---|---|---|
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.html | La 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écache | Fichiers 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ée | Idem. |
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-Policyqui refuse caméra, micro, géolocalisation, paiement et USB. NiX-Frame-Optionsniframe-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 (URLdata:,cdn.basthon.fr, WebAssembly) et les sorties graphiques demandent une mise au point en conditions réelles, enContent-Security-Policy-Report-Onlyd'abord. - Cache :
no-cachepar défaut (le navigateur revalide chaque fois par l'ETag, ce qui vaut pourindex.html,sw.js, le manifeste et les icônes) ;public, max-age=31536000, immutablepour 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
e2eattendtest(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), pasnode_modules/:npm cil'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 buildvidedist/: 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.tsexclutdoc/(denylist), sinon une page de la documentation, hors ligne, serait remplacée par l'index.htmlde l'application. - Les en-têtes de
public/.htaccesss'y appliquent aussi : pages enno-cache, fichiers dedoc/assets/(nommés par leur empreinte) enimmutable. - Le chemin suit
BASE_PATH:BASE_PATH=/ npm run docs:buildpublie la documentation sous/doc/. En local,npm run docs:devla sert sous le même chemin (/p/capycode/doc/, sur le port qu'il affiche), etnpm run build && npm run docs:build && npm run previewsert l'application et sa documentation comme en production.
Vérifier une mise à jour en local
npm run build
npx vite previewOuvrir 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.