PWA Yin Shi – Mise à jour fiabilisée & site de test DreamHost
🎯 Objectifs
- Fiabiliser la mise à jour de la structure de la PWA (shell Flutter web, JS/CSS/HTML), pour éviter les situations où certains utilisateurs restent sur une ancienne version.
- Mettre en place un site de test/staging à côté du site de production sur DreamHost, avec un flux de déploiement clair.
Ce document décrit une démarche progressive pour atteindre ces deux objectifs en s’appuyant sur l’existant :
- Build Flutter web (
flutter build web --release --base-href /web/). - Déploiement via
rsyncsur DreamHost (/home/farid/yinshi.app/web). - Fichier
version.txtécrit à chaque déploiement (script local + GitHub Actions). - Service worker généré automatiquement par Flutter pour la PWA.
0. Statut (décembre 2025)
- Étape 1 – Mise à jour PWA fiable (Web) est implémentée pour l’environnement de test :
- serveur de test :
https://yinshi-test.yinshi.app/(déploiement via./scripts/deploy_web.sh --env test), version.txtcontient désormais la versionpubspec.yaml(ex.1.0.24+40),VersionServicecôté Flutter Web litversion.txt(avec un paramètre?ts=pour éviter le cache) et compare la version distante avec la version locale (AppVersionInfo).- UX Web :
- au démarrage (et lorsqu’un nouveau service worker est installé), un contrôle de version est effectué ;
- si une version distante plus récente est détectée :
- un dialog de mise à jour s’affiche (boutons
Mettre à jour/Plus tard), - un bandeau discret en haut de l’écran rappelle la disponibilité de la mise à jour en cas de clic sur
Plus tard.
- un dialog de mise à jour s’affiche (boutons
- Android / iOS :
- le nouveau code reste isolé au Web (imports conditionnels /
kIsWeb), - le comportement de mise à jour mobile existant n’est pas modifié pour cette étape.
Les sections suivantes décrivent le contexte général et les étapes prévues (y compris les évolutions futures qui ne sont pas encore toutes implémentées).
1. État actuel (rappel)
1.1 Build & déploiement
- Script local
scripts/deploy_web.sh: - Build :
flutter build web --release --base-href /web/. - Déploiement :
rsync --delete build/web/ farid@iad1-shared-b7-36.dreamhost.com:/home/farid/yinshi.app/web. -
Écriture d’un
version.txtdistant via SSH avecgit describe --tags --always(ou timestamp). -
Workflow GitHub Actions
.github/workflows/deploy-web.yml: - Utilise
subosito/flutter-action@v2(Flutter 3.35.5). - Build :
flutter build web --release --base-href ${{ secrets.DEPLOY_BASE_HREF || '/' }}. - Déploiement :
rsync --delete build/web/ $REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/. - Écrit également un
version.txtavec la même logique.
➡️ Côté serveur : à chaque déploiement, l’arborescence build/web est proprement remplacée, et version.txt contient l’identifiant de version déployé.
1.2 Côté PWA / navigateur
web/index.htmlest minimal (base href, manifest, scriptflutter_bootstrap.js).web/manifest.jsondécrit les icônes/couleurs mais ne porte pas de logique de version.- Le service worker Flutter par défaut est généré dans
build/web/(pas de SW custom dans le repo).
Comportement du service worker Flutter :
- Cache
index.html,main.dart.js, assets… en mode offline‑first. - Lors d’un nouveau déploiement :
- Une nouvelle version du SW est téléchargée.
- Elle passe en état
waiting. - Elle n’est activée que lorsque tous les onglets utilisant l’ancienne version sont fermés, sauf si on force
skipWaiting.
➡️ Effet pour les utilisateurs :
- Certains voient immédiatement la nouvelle version (nouvel onglet, hard reload, etc.).
- D’autres restent sur l’ancienne version tant qu’ils gardent l’onglet ouvert.
- Aucune logique actuelle ne :
- lit
version.txtcôté client, - surveille le service worker,
- propose un dialog de mise à jour ou un reload automatique.
2. Objectif A – Fiabiliser la mise à jour de la PWA
Vue d’ensemble
On veut un comportement du type :
- L’utilisateur ouvre
https://yinshi.app/web/. - L’app démarre normalement (version N).
- En tâche de fond, l’app compare sa version locale avec la version déployée sur le serveur (
version.txt). - Si une nouvelle version (N+1) est détectée :
- soit l’app affiche un dialog "Nouvelle version disponible – Recharger ?";
- soit elle recharge automatiquement (mode testeurs).
- Le reload force alors la prise en compte du nouveau service worker et des nouveaux assets.
Pour y arriver, on s’appuie sur deux leviers :
version.txtcomme source de vérité côté serveur.- API Service Worker du navigateur pour déclencher/accélérer la mise à jour.
2.1 Étape 1 – Formaliser la version locale côté Flutter
But : exposer dans le code Flutter une information de version locale, comparable à version.txt.
Pistes :
- Lire la version depuis
pubspec.yamlau build (par ex. via un script de build ou un fichier Dart généré) et exposer : - soit
versionNametype1.2.3; - soit un identifiant plus technique (tag Git, hash court, etc.).
- Option simple :
- réutiliser le même identifiant que ce qui est écrit dans
version.txt(git describe --tags --always).
Décision à prendre :
- Forme de la version à afficher à l’utilisateur (ex :
v1.2.3). - Forme de la version technique de comparaison (tag/hash exact de
version.txt).
2.2 Étape 2 – Créer un VersionService dans Flutter
But : avoir un service unique qui sait :
- charger la version locale (exposée via constante ou fichier généré),
- faire un HTTP GET sur
https://yinshi.app/web/version.txt, - normaliser/trim la valeur retournée,
- comparer et exposer un résultat du type :
sameVersion,newerRemoteVersion,error(network, parse, etc.).
Comportement recommandé :
- Appeler ce service :
- au démarrage de l’app (après l’écran de splash ou sur le premier écran stable),
- éventuellement à intervalle régulier pour les sessions longues (facultatif).
- Gérer les erreurs réseau de manière silencieuse (pas de blocage de l’UI si
version.txtinaccessible).
2.3 Étape 3 – UX de mise à jour (dialog/snackbar)
But : informer l’utilisateur de manière claire lorsqu’une nouvelle version est disponible.
Options :
- Snackbar discrète en bas :
- Texte : "Nouvelle version disponible".
- Bouton :
Recharger. - Dialog bloquant (recommandé pour des changements critiques) :
- Titre : "Nouvelle version de Yin Shi disponible".
- Corps : résumé court (ou lien vers les notes de version plus tard).
- Boutons :
Mettre à jour maintenant→ déclenche un reload.- Optionnel :
Plus tard→ ferme le dialog (mais garde un indicateur discret).
Action technique lorsque l’utilisateur clique sur "Mettre à jour" :
- Côté web (Flutter), déclencher un
window.location.reload()pour recharger l’app et laisser le service worker prendre la nouvelle version.
2.4 Étape 4 – Intégrer l’API Service Worker
But : ne pas dépendre uniquement de la "magie" du cache, mais piloter le cycle de vie du service worker.
Actions possibles :
- Forcer une vérification de mise à jour :
navigator.serviceWorker.getRegistration()puisregistration.update().-
À appeler typiquement :
- au démarrage de l’app,
- juste avant de vérifier
version.txt, - ou quand l’utilisateur demande explicitement une mise à jour.
-
Détecter l’arrivée d’un nouveau service worker :
- écouter
registration.onupdatefound+installing.onstatechange; -
quand l’état devient
installedalors qu’un contrôleur existe déjà, ça signifie qu’une nouvelle version de la PWA est prête. -
Coupler cela avec l’UX :
- lorsqu’un nouveau SW est
installed, afficher le dialog "Nouvelle version disponible" (cf. 2.3) ; - si l’utilisateur accepte → reload.
Cette logique peut être :
- soit mise en place en Dart (via
dart:htmldans Flutter web), - soit ajoutée en JavaScript dans
web/index.html(puis exposer un hook minimal côté Flutter si nécessaire).
2.5 Étape 5 – Auto‑reload sur controllerchange (optionnel mais puissant)
But : éviter toute incohérence lorsque le service worker actif change.
Dans web/index.html, on peut ajouter un petit script JS générique qui :
- écoute
navigator.serviceWorker.addEventListener('controllerchange', ...), - appelle
window.location.reload()lorsqu’un nouveau contrôleur SW prend la main.
Effet :
- Dès qu’un nouveau service worker devient actif, la page se recharge automatiquement.
- Couplé au système de détection de version, ça donne un comportement très réactif.
Recommandation :
- Activer cette logique d’abord en environnement de test/staging.
- Si l’expérience est bonne, la déployer en production.
2.6 Étape 6 – Mode testeurs sans service worker
Pour des phases où l’on veut absolument que la dernière build soit servie à chaque chargement (ex. validation Google, tests intensifs) :
- Builder la web app avec une stratégie PWA désactivée (ex.
--pwa-strategy=nonesi supportée par la version de Flutter utilisée) pour un environnement de test. - Garder le service worker par défaut en production pour profiter du offline.
Des variantes possibles :
- Utiliser un environnement de test (voir partie B) avec une build sans SW.
- Garder la build PWA complète sur
/web/pour la production.
3. Objectif B – Site de test/staging sur DreamHost
On souhaite avoir deux environnements web distincts :
- Production :
https://yinshi.app/web/ - Test/Staging :
- par exemple
https://yinshi.app/web-test/, - ou un sous‑domaine dédié (ex.
https://test.yinshi.app/).
L’objectif est de :
- pouvoir déployer rapidement sur l’environnement de test,
- valider les nouvelles versions (PWA, mise à jour, UX),
- puis déployer sereinement en production.
3.1 Étape 1 – Choisir l’URL du site de test
Deux options principales :
- Sous‑chemin :
https://yinshi.app/web-test/- Dossier distant :
/home/farid/yinshi.app/web-test. -
--base-href /web-test/côté Flutter. -
Sous‑domaine :
https://test.yinshi.app/- Dossier distant dédié (selon config DreamHost).
--base-href /ou un chemin adapté.
Recommandation initiale (simple) :
- Commencer par un sous‑chemin
/web-test/, car : - configuration DreamHost plus simple (un nouveau dossier sous le même domaine),
- réutilisation directe de la logique actuelle (
rsync, SSH, etc.).
3.2 Étape 2 – Préparer la structure sur DreamHost
Pour un sous‑chemin /web-test/ :
- Créer un dossier sur le serveur :
/home/farid/yinshi.app/web-test.- Vérifier que la config Apache/Nginx (gérée par DreamHost) sert bien ce dossier via l’URL
https://yinshi.app/web-test/. - Éventuellement adapter
.htaccesssi nécessaire pour gérer les routes Flutter web (spa) dans ce sous‑chemin.
3.3 Étape 3 – Adapter le script de déploiement local
Objectif : permettre un déploiement vers prod ou test avec un seul script.
Approche suggérée :
- Modifier
scripts/deploy_web.shpour accepter un paramètre d’environnement, par exemple : --env prod(par défaut),--env test.
Pour chaque environnement :
- prod :
REMOTE_PATH="/home/farid/yinshi.app/web"FLUTTER_OPTS="--release --base-href /web/"- URL :
https://yinshi.app/web/ -
version.txtécrit dans/web/version.txt. -
test :
REMOTE_PATH="/home/farid/yinshi.app/web-test"FLUTTER_OPTS="--release --base-href /web-test/"- URL :
https://yinshi.app/web-test/ version.txtécrit dans/web-test/version.txt.
Checklist :
- S’assurer que le script :
- construit avec les bonnes options
FLUTTER_OPTSselon--env, - synchronise sur le bon dossier avec
rsync, - écrit
version.txtdans le bon chemin distant.
3.4 Étape 4 – Adapter le workflow GitHub Actions
Objectif : avoir au moins deux workflows ou un workflow paramétrable :
- Déploiement test/staging (ex. déclenché sur
pushsur une branchedevelopoufeature/*+workflow_dispatch). - Déploiement production (ex. déclenché manuellement ou sur
pushavec tag/release).
Éléments à prévoir :
- Secrets séparés ou au minimum :
DEPLOY_REMOTE_PATH_TESTpour/home/farid/yinshi.app/web-test.DEPLOY_REMOTE_PATHpour la prod (/home/farid/yinshi.app/web).DEPLOY_BASE_HREF_TEST=/web-test/.DEPLOY_BASE_HREF=/web/.- Job qui :
- installe Flutter,
- exécute
flutter pub get+flutter test, - build web avec le bon
--base-href, - déploie avec
rsyncsur le bon chemin, - met à jour
version.txtsur l’environnement visé.
Recommandation :
- Commencer par un workflow distinct pour le test (plus simple à raisonner),
- Puis éventuellement fusionner dans un workflow paramétrable par input (
env: test|prod).
3.5 Étape 5 – Gestion des URLs côté Flutter
Certaines URLs sont actuellement en dur dans le code, par exemple dans lib/services/payment_plan_service.dart :
https://yinshi.app/webhttps://yinshi.app/web/#/premium-plans
Avec deux environnements web, il faudra décider :
- soit d’utiliser les mêmes URLs backend (prod) mais des URLs de front différentes selon env,
- soit d’introduire une configuration par environnement (prod vs test) pour ces URLs (utile notamment pour Stripe ou d’autres intégrations).
Piste :
- Utiliser un fichier de config ou des constantes séparées par environnement (ex :
EnvironmentConfig) et un flag de build (--dart-define) pour choisir entreprodettest.
4. Ordre recommandé des travaux
Phase 1 – Mise à jour fiable en production (sans site de test)
- Formaliser la version locale exposée dans Flutter.
- Créer le
VersionServicequi litversion.txtet compare. - Ajouter l’UX de mise à jour (snackbar/dialog) + action
Recharger. - Intégrer une première interaction avec le service worker :
- au moins
registration.update()au démarrage, - éventuellement détection d’un SW
installed.
Objectif :
- Même sur l’URL actuelle
https://yinshi.app/web/, la mise à jour devient prévisible (l’app sait lorsqu’une nouvelle version est déployée et propose un reload).
Phase 2 – Site de test/staging
- Choisir l’URL de test (ex.
https://yinshi.app/web-test/). - Créer le dossier distant
/home/farid/yinshi.app/web-testsur DreamHost. - Adapter le script
deploy_web.shpour gérer--env test. - Créer un workflow GitHub Actions staging qui déploie sur
/web-test/. - Tester la PWA de staging :
- vérifier
version.txtsur/web-test/version.txt, - vérifier les routes Flutter,
- expérimenter les changements de service worker (auto‑reload, etc.).
Phase 3 – Ajustements avancés PWA
- Ajouter le listener
controllerchangedansindex.htmlsi l’expérience est satisfaisante. - Éventuellement introduire un mode sans service worker côté test pour certaines campagnes de validation.
- Mettre en place une gestion de config d’URLs par environnement (Stripe, redirections, etc.).
5. Checklist rapide
Pour la mise à jour fiable de la PWA
- [ ] Exposer une version locale côté Flutter (alignée sur
version.txt). - [ ] Implémenter un
VersionServicequi litversion.txtdepuis le serveur. - [ ] Comparer local vs distant au démarrage et au besoin.
- [ ] Afficher une snackbar ou un dialog "Nouvelle version disponible".
- [ ] Sur action utilisateur, recharger la page (
window.location.reload()). - [ ] Intégrer au minimum
serviceWorker.update()au démarrage. - [ ] (Optionnel) Ajouter un listener
controllerchangedansindex.htmlpour auto‑reload.
Pour le site de test sur DreamHost
- [ ] Choisir l’URL de test (sous‑chemin ou sous‑domaine).
- [ ] Créer le dossier distant (ex.
/home/farid/yinshi.app/web-test). - [ ] Adapter
deploy_web.shpour gérer--env test(REMOTE_PATH + base href). - [ ] Créer/adapter le workflow GitHub Actions pour staging.
- [ ] Vérifier que
https://yinshi.app/web-test/sert bien la PWA de test. - [ ] Vérifier que
/web-test/version.txtreflète correctement les déploiements de test.
Ce document sert de plan directeur. Chaque étape peut être implémentée progressivement.
Pour la suite, il est recommandé de :
- Commencer par la Phase 1 (détection de version + dialog de mise à jour) directement sur l’environnement actuel.
- Ajouter ensuite le site de test pour pouvoir expérimenter des comportements PWA plus agressifs (auto‑reload, désactivation du SW, etc.) sans impacter les utilisateurs finaux.