From 97d376b83c21b781590f10b5419e3c5c264ca1f1 Mon Sep 17 00:00:00 2001 From: le king fu Date: Sat, 15 Aug 2026 11:51:21 -0400 Subject: [PATCH] docs(qa): fix the Linux state sequence the checklist had backwards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `downloadAndInstall` is one call that downloads AND installs (updater.rs:723-729), and the `Finished` event is a no-op in useUpdater.ts:119-121. `READY_TO_INSTALL` is therefore dispatched only after `dpkg -i` returns, so the pkexec prompt opens while the card still reads "Téléchargement en cours…". The checklist told the tester to tick `readyToInstall` before the prompt. That ordering neutralized the polkit precondition the page exists to enforce: with no agent the app hangs in `downloading`, not in `readyToInstall`, so a tester following the checklist files "the download stalls" instead of "no polkit agent" — the exact false trace this page is written to prevent. Also states plainly that at `readyToInstall` the .deb is already on disk and the button only calls `relaunch()`; the label misleads on this target. Three smaller corrections from the same review: - The update triggers are four manual sites, not two: UpdateCard idle / upToDate refresh / error retry, plus ErrorPage.tsx:82 — which only detects and offers no download path. The load-bearing claim, "no automatic check in the app", was already right. - "Si ça casse" now spells out that DELETE and PUT hit different API prefixes (/api/v1/packages/ vs /api/packages/, release.yml:217-219, which carries a comment about exactly this). Replaying both against one URL 404s at the worst possible moment. - The SKILL.md changelog entry moves back into date order. Co-Authored-By: Claude Opus 5 --- .claude/skills/release/SKILL.md | 2 +- docs/qa-update-cycle.md | 26 ++++++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index a8c676f..96479b0 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -61,5 +61,5 @@ updated: 2026-07-27 ## Changelog - 2026-04-19 — Added Cargo.lock + package-lock.json to bump list, `npm install --package-lock-only` fallback when lockfile stale, explicit `[Unreleased]` migration pattern, annotated tags (#102/#112 release cycle) - 2026-07-01 — Documenter que le header FR est `## [Non publié]` (≠ `[Unreleased]`), pour éviter le faux diagnostic « changelog FR vide » lors de la migration. Source : session 5466da98. -- 2026-07-27 — Étape 10 (post-CI) : dérouler un vrai cycle de mise à jour via `docs/qa-update-cycle.md` quand la release touche l'updater. Inspecter `latest.json` prouve qu'il est bien formé, pas qu'il installe : le téléchargement TLS, la vérification de signature, l'exécution de l'installeur et le redémarrage ne sont exercés par aucun test. Deux pièges qui font cocher sans vérifier — tester depuis la machine déjà à jour (`check()` répond « à jour ») et tester sans clé Base+ (auto-update verrouillé par édition, `notEntitled` avant toute requête). Source : session 50ac88d9 (#315, découvert en traitant les advisories `tar`/`rustls-webpki` de #310). - 2026-07-13 — Étape 0 (pré-vol) : revalider le tip localement avant de tagger — `check.yml` ne tourne pas sur `main`, le tip mergé n'a jamais été vu par le CI ; vérifier `.claude/worktrees/` vide (vitest récurse sinon). Étape 9 (post-CI) : vérifier la release publiée — 7 artefacts attendus + contenu de `latest.json` (pilote l'auto-update) ; `status=success` ne suffit pas. Règle : tagger publie vers l'extérieur (updater automatique) → confirmer avec Max avant de tagger. Source : session fdda84cb (release v0.13.0). +- 2026-07-27 — Étape 10 (post-CI) : dérouler un vrai cycle de mise à jour via `docs/qa-update-cycle.md` quand la release touche l'updater. Inspecter `latest.json` prouve qu'il est bien formé, pas qu'il installe : le téléchargement TLS, la vérification de signature, l'exécution de l'installeur et le redémarrage ne sont exercés par aucun test. Deux pièges qui font cocher sans vérifier — tester depuis la machine déjà à jour (`check()` répond « à jour ») et tester sans clé Base+ (auto-update verrouillé par édition, `notEntitled` avant toute requête). Source : session 50ac88d9 (#315, découvert en traitant les advisories `tar`/`rustls-webpki` de #310). diff --git a/docs/qa-update-cycle.md b/docs/qa-update-cycle.md index 23ab99a..d0e79bf 100644 --- a/docs/qa-update-cycle.md +++ b/docs/qa-update-cycle.md @@ -13,7 +13,7 @@ Elle existe parce que la CI ne peut rien prouver ici : `cargo check` et `cargo t Ce sont les conditions sans lesquelles le test ne teste rien. Chacune a un mode d'échec où la checklist se coche « OK » sans avoir rien vérifié. - [ ] **Une machine Windows** (hôte ou VM) et **une machine Linux de bureau**. Les deux flux sont réellement différents, ils ne se substituent pas l'un à l'autre. -- [ ] Sur la machine Linux, **un agent polkit qui tourne**. L'installation du `.deb` passe par `pkexec` ; sans agent, l'app retombe en cascade sur `zenity`/`kdialog` puis sur un `sudo` dans un terminal inexistant, et **paraît figée au lieu d'échouer**. Noter plus bas quelle invite est réellement apparue : si ce n'est pas polkit, c'est un chemin de repli qui a été validé, pas le chemin nominal. +- [ ] Sur la machine Linux, **un agent polkit qui tourne**. L'installation du `.deb` passe par `pkexec` ; sans agent, l'app retombe en cascade sur `zenity`/`kdialog` puis sur un `sudo` dans un terminal inexistant, et **paraît figée au lieu d'échouer — sur l'écran de téléchargement, pas sur `readyToInstall`** (voir §2, c'est le piège qui produit le mauvais rapport de bug). Noter plus bas quelle invite est réellement apparue : si ce n'est pas polkit, c'est un chemin de repli qui a été validé, pas le chemin nominal. - [ ] **La version précédente installée sur chaque machine**, depuis les artefacts de la release précédente (`.deb` et `-setup.exe` attachés à la release Forgejo). Ne pas tester sur la machine qui vient de builder la nouvelle version. > Sans ça, `check()` compare `release.version > version_courante`, trouve faux, et l'UI affiche « à jour ». La checklist se coche alors comme « rien à mettre à jour » — soit exactement le saut silencieux qu'elle est censée éliminer, avec en prime une trace écrite affirmant qu'elle est passée. @@ -35,7 +35,7 @@ Ce sont les conditions sans lesquelles le test ne teste rien. Chacune a un mode ## 1. Windows (NSIS) -Point d'entrée : **Paramètres → Systèmes → carte Mises à jour → « Vérifier les mises à jour »**. Il n'y a aucune vérification automatique dans l'app — ce bouton et celui de la page d'erreur sont les deux seuls déclencheurs. +Point d'entrée : **Paramètres → Systèmes → carte Mises à jour → « Vérifier les mises à jour »**. Il n'y a **aucune vérification automatique dans l'app** : les quatre déclencheurs sont manuels — ce bouton, l'icône rafraîchir de l'état `upToDate`, le « réessayer » de l'état `error`, et celui de la page d'erreur, qui **détecte seulement** et n'offre aucun chemin de téléchargement. - [ ] La carte passe en `checking`, puis affiche `available` avec le **numéro de la nouvelle version** et les notes extraites du CHANGELOG. - [ ] Cliquer sur télécharger → état `downloading`, **la progression avance** (elle vient de `contentLength`, donc un serveur qui ne le renvoie pas se voit ici). @@ -49,13 +49,18 @@ Point d'entrée : **Paramètres → Systèmes → carte Mises à jour → « Vé ## 2. Linux (.deb) - [ ] Même point d'entrée, mêmes états jusqu'à `downloading`. -- [ ] Après le téléchargement, l'app **reste vivante** et passe en `readyToInstall`. -- [ ] Une invite d'authentification apparaît. **Noter laquelle** : polkit (nominal), zenity/kdialog, ou rien du tout (repli `sudo` → l'app paraît figée). +- [ ] **Pendant que la carte affiche encore « Téléchargement en cours… »**, une invite d'authentification apparaît. **Noter laquelle** : polkit (nominal), zenity/kdialog, ou rien du tout (repli `sudo` → l'app reste figée **sur l'écran de téléchargement**). Invite observée : `________________` -- [ ] Authentifier → état `installing`, puis le contrôle de redémarrage apparaît. -- [ ] Redémarrer, vérifier la version, re-vérifier → `upToDate`. + > C'est bien le moment nominal, et c'est contre-intuitif : `downloadAndInstall` télécharge **et** installe dans le même appel (`updater.rs:723-729`), et l'événement `Finished` est un no-op côté UI (`useUpdater.ts:119-121`). L'écran reste donc sur `downloading` pendant tout le `dpkg -i`. Un testeur qui guette l'invite **après** `readyToInstall` ne la verra jamais arriver au bon moment : il rapportera « le téléchargement bloque » là où le vrai diagnostic est « pas d'agent polkit ». + +- [ ] Authentifier → l'app **reste vivante** et passe en `readyToInstall` (« Mise à jour prête à installer » + bouton « Installer et redémarrer »). + + > À ce stade le `.deb` est **déjà installé sur le disque** : `installAndRestart` ne fait que `relaunch()`. Le libellé du bouton ment sur cette cible — qui s'arrête là croit que rien n'a été installé, et qui voit la version changer après le clic attribue l'installation au clic. + +- [ ] Cliquer → état `installing`, l'app redémarre. +- [ ] Vérifier la version, re-vérifier → `upToDate`. ## 3. RPM — cassé, connu @@ -71,6 +76,15 @@ L'ordre compte, et la première action ne répare pas ce qu'on croit. 1. **Republier le `latest.json` précédent** sur `generic/simpl-resultat/latest` (`DELETE` puis `PUT`, avec le `PACKAGE_TOKEN` qu'utilise `release.yml`). Le corps est récupérable depuis les assets de la release précédente — sa copie dans le registre a été détruite par le `DELETE` que `release.yml` fait avant chaque envoi. + **Les deux verbes ne visent pas le même préfixe d'API**, et `release.yml:217-219` porte un commentaire explicite là-dessus — le piège est déjà tombé une fois : + + ``` + DELETE {serveur}/api/v1/packages/{owner}/generic/simpl-resultat/latest + PUT {serveur}/api/packages/{owner}/generic/simpl-resultat/latest + ``` + + Rejouer les deux sur la même URL donne un 404 sur l'un des deux, au moment où on peut le moins se permettre de le débugger. + 2. **Comprendre ce que ça arrête, et ce que ça n'arrête pas.** `check()` compare strictement `release.version > version_courante`. Republier vN-1 **stoppe la propagation** vers ceux qui n'ont pas encore cliqué. Les utilisateurs déjà passés à la vN cassée ne se verront **jamais** proposer vN-1 : ils sont bloqués dessus. 3. **Le vrai correctif est donc une vN+1**, pas un retour en arrière. Le republication de vN-1 n'achète que du temps — et si elle reste en place, elle prive aussi les utilisateurs restés en vN-1 de tout chemin de mise à jour.