Simpl-Resultat/.claude/skills/release/SKILL.md
le king fu 10d8a79fd8 docs(release): add a real update-cycle QA checklist to step 9
Step 9 inspected latest.json and stopped there. That proves the file is well
formed, not that it installs: the TLS download, signature verification,
installer execution and relaunch are exercised by no test at all — cargo check
and cargo test only prove that code compiles. A regression there surfaces at
update time, on a user's machine, and automatic updates are a Base+ feature.

Written as a manual checklist rather than automation because it needs two real
desktop environments. The value is in the preconditions, which are what make
the difference between running the test and only appearing to:

- Test from a machine still on the PREVIOUS version. check() compares
  release.version > current strictly, so testing on the machine that just
  built the release shows "up to date" and the checklist gets ticked as
  "nothing to update" — the silent skip it exists to prevent, now with a
  paper trail claiming it passed.
- A Base+ key on each machine, with any activation.token from another machine
  removed. Auto-update is entitlement-gated: useUpdater dispatches NOT_ENTITLED
  before check() ever runs, and a token bound to a different machine_id
  silently resolves the edition back to Free.
- A live polkit agent on Linux, and record which prompt actually appeared —
  install_deb cascades pkexec -> zenity/kdialog -> terminal sudo, so without an
  agent the app appears to hang instead of failing.

The two target flows are written separately because they genuinely differ: on
Windows downloadAndInstall never returns (the process exits and NSIS takes
over, so readyToInstall/installing never render), while on Linux the app stays
alive through a polkit prompt and a restart control.

The doc states what the test does NOT prove: `tar` stays unexercised. Its
vulnerable path is only reached by install_appimage and the macOS .app.tar.gz
branch; our .deb goes through `pkexec dpkg -i` and our NSIS path launches the
.exe. That corrects #315's own premise.

The failure path is documented as an incident playbook rather than a
"rollback", because it is not one. Republishing the previous latest.json stops
propagation, but check()'s strict comparison means users already on the broken
version are never offered the older one — the real fix is a vN+1.

rpm is recorded as known-broken, not unverified, and tracked in #320.

Resolves #315

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 21:34:11 -04:00

6.5 KiB

name description user-invocable updated
release Release a new version of Simpl-Resultat (bump, changelog, tag, push) true 2026-07-27

/release — Release Simpl-Resultat

Context injection

  1. Lire version dans src-tauri/Cargo.toml et package.json
  2. Lister les derniers tags : git tag --sort=-v:refname | head -10
  3. Lire CHANGELOG.md et CHANGELOG.fr.md (dernieres entrees)

Workflow

  1. Pré-vol — revalider le tip localement. Les workflows check-rust.yml / check-frontend.yml ne tournent pas sur main : le tip mergé n'a jamais été vu par le CI (le dernier run vert portait sur la branche d'issue avant merge), et le tag grave ce tip exact dans des binaires distribués. Lancer npm run build && npm test (vitest) + cd src-tauri && cargo check && cargo test. Vérifier aussi que .claude/worktrees/ est vide (worktrees leftover → vitest récurse et gonfle le compteur). Ne tagger que sur un tip vert.

  2. Determiner la nouvelle version (argument utilisateur ou demander)

  3. Bump version dans les 5 fichiers :

    • src-tauri/Cargo.toml (ligne version = "...")
    • src-tauri/Cargo.lock (bloc [[package]] name = "simpl-result" + sa ligne version = "..." ; ne PAS regenerer avec cargo)
    • src-tauri/tauri.conf.json (champ "version")
    • package.json (champ "version")
    • package-lock.json (deux champs "version" — root ~ligne 3 et le package racine "" ~ligne 9)
      • Si package-lock.json est stale (hygiene warning package-lock.json plus ancien que package.json) : npm install --package-lock-only --no-audit --no-fund pour resync. Note : peut ajouter des entrees bundled optionnelles (tailwindcss oxide wasm etc.) — cosmetique, pas d'install effective.
  4. Mettre a jour les 2 changelogs — format Keep a Changelog :

    • CHANGELOG.md (EN) : header ## [Unreleased]
    • CHANGELOG.fr.md (FR) : header ## [Non publié] (PAS [Unreleased] — ne pas le confondre avec un changelog vide)
    • Pattern de migration : transformer le header « non publié » de chaque fichier en ## [X.Y.Z] - YYYY-MM-DD, puis recreer la section « non publié » vide au-dessus pour accueillir les prochaines entrees. Le header differe selon la langue. Ne pas deplacer le contenu — les sections sont laissees en place.
  5. Si changement d'architecture : mettre a jour docs/architecture.md

  6. Commit : chore: release vX.Y.Z (ajouter les 7 fichiers : 5 bumps + 2 changelogs)

  7. Tag annote (permet une release notes par tag, lisible via git show vX.Y.Z) :

    git tag -a vX.Y.Z -m "Release X.Y.Z
    
    - <bullet highlights>"
    
  8. Push : git push origin main && git push origin vX.Y.Z

  9. Forgejo CI build automatique (Windows + Linux) via release.yml sur on: push: tags: v*

  10. Post-CI — vérifier la release publiée. Surveiller release.yml (outil Monitor sur le run), puis vérifier la release réellement attachée — status=success du workflow ne suffit pas :

    • Les 7 artefacts attendus : .exe NSIS, .deb, .rpm, leurs 3 signatures .sig, et latest.json.
    • Le contenu de latest.json (il pilote l'auto-update des installations existantes) : champ version correct, signatures non vides pour les deux plateformes, URLs pointant vers les bons binaires, notes extraites du CHANGELOG.
  11. Post-CI — dérouler un vrai cycle de mise à jour si la release touche tauri-plugin-updater, ses dépendances (reqwest/rustls-webpki, tar), release.yml, ou la config updater de tauri.conf.json : docs/qa-update-cycle.md.

    Inspecter latest.json prouve qu'il est bien formé, pas qu'il installe. Le reste de la chaîne — téléchargement TLS, vérification de signature, exécution de l'installeur, redémarrage — n'est exercé par aucun test : cargo check/cargo test prouvent que ce code compile, rien de plus. Une régression s'y voit chez l'utilisateur, au moment de la mise à jour.

    Deux pièges qui font cocher la checklist sans rien vérifier, détaillés dans la doc : tester depuis une machine déjà à la nouvelle version (check() répond « à jour ») et tester sans clé Base+ (l'auto-update est verrouillé par édition, l'app bascule en notEntitled avant même d'interroger le serveur).

    Si la checklist n'est pas déroulée, l'écrire avec la raison sur la release plutôt que de laisser le silence.

Regles

  • JAMAIS git push --tags — toujours push le tag individuellement
  • Toujours mettre a jour les 2 changelogs (EN + FR)
  • Format Keep a Changelog : ## [X.Y.Z] - YYYY-MM-DD
  • Les changelogs sont bundles dans public/ pour l'affichage in-app
  • Tag annote (-a), pas lightweight : les artefacts CI reference le tag pour les release notes
  • Tagger publie vers l'extérieur : release.yml pousse le JSON d'updater, donc les utilisateurs installés reçoivent la mise à jour automatiquement. Confirmer avec Max avant de tagger.

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).