Simpl-Resultat/.claude/skills/release/SKILL.md
le king fu 230ecd85ad fix(release): drop the generic linux-x86_64 key from latest.json
Max's decision on PR #416, after its review measured who still read the
key. tauri-plugin-updater looks up "{os}-{arch}-{installer}" first, and
every version that ships the updater (v0.2.1 and later, plugin 2.10.0,
tauri-utils 2.8.2 in each tag's Cargo.lock) carries its bundle-type
marker, so .deb and .rpm installs never reach the generic key. Its last
readers were the AppImage installs v0.4.3 to v0.4.5 (v0.4.2 targets a
route that answers 404, earlier versions read GitHub) and binaries run
outside a package; none of them can install the .deb it served, and the
updater wrote that .deb over the running file. They now get
TargetsNotFound when checking: an error on the Updates card, nothing
installed.

The key leaves the script, scripts/check-latest-json.jq and
verify-release.sh together; the test compares the three lists. Docs fix
the readers named by #416 (v0.4.2 is not one) and the CHANGELOG entry
says what changes for those AppImage installs.

Refs #320

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:30:34 -04:00

12 KiB

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

/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. Accès des testeurs. Si [Unreleased] touche FEATURE_TIERS, le gating par édition ou auto-update : vérifier que chaque testeur (Max, Michel) a une clé Base+ saisie avant le tag, émise via /issue-license au besoin — la clé s'entre dans la version installée et survit à la mise à jour ; une installation sans clé reçoit cette version, puis plus rien. Cas réel v0.15.0 : deux clés Premium émises le jour même, juste avant le tag.

  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 :

    bash .claude/skills/release/scripts/verify-release.sh vX.Y.Z
    

    Le script (token ~/.forgejo-token) lit la release par tag, compte les 7 artefacts par suffixe (.exe NSIS, .deb, .rpm, leurs 3 signatures .sig, latest.json — les noms exacts varient selon la version de Tauri), puis télécharge latest.json (il pilote l'auto-update des installations existantes) et vérifie version (il affiche pub_date et la longueur des notes), puis exige exactement trois clés (#320) : linux-x86_64-deb, linux-x86_64-rpm et windows-x86_64, sans clé générique linux-x86_64 depuis le 2026-10-11 (docs/qa-update-cycle.md). Pour chacune : l'extension de l'URL (.deb, .rpm, .exe), une signature non vide dont le file: (commentaire de confiance minisign, signature décodée du base64) est le nom de fichier de l'URL, décodé, et la version dans l'URL. Il sort en 1 sur tout écart (MANQUANT, VIDE, MAUVAISE VERSION, MAUVAISE EXTENSION, DIFFÉRENT, INATTENDUE) et finit par VERDICT : OK ou VERDICT : ÉCHEC : pas d'annonce de la release ni d'étape 10 avant un verdict OK. Un second argument (verify-release.sh vX.Y.Z fichier.json) contrôle un latest.json local au lieu de l'asset, pour éprouver les contrôles sur un fichier fabriqué. Lire aussi l'étape « Generate latest.json » du journal du run (même route web que ci-dessous) : scripts/build-latest-json.sh y nomme le paquet retenu pour chaque type (build-latest-json: .rpm <- …), puis le latest.json généré est affiché. Le script et le filtre scripts/check-latest-json.jq passent par les tests de check-frontend à chaque PR ; leur appel par release.yml et le délimiteur aléatoire des notes ne s'exercent qu'au tag. Un manifeste refusé par le filtre arrête le job sur ERROR: release-assets/latest.json failed scripts/check-latest-json.jq, avant le DELETE : registre intact, et la release Forgejo reste créée sans mise à jour servie. Durée observée du run : 22 à 30 min sur les 12 dernières releases — poser le Monitor puis passer à autre chose. Lire aussi le Node.js du run. release.yml n'est exercé qu'au tag : une montée de Node (Node 20 → 24 par #376) ne se prouve que dans ce journal. Il passe par la route web (l'API v1 des journaux répond 404). Le run_number vient de /actions/tasks, qui rend toute la liste, la plus récente en tête (le paramètre limit y est ignoré) :

    TAG=vX.Y.Z
    RUN=$(curl -s -H "Authorization: token $(cat ~/.forgejo-token)" \
      "https://git.lacompagniemaximus.com/api/v1/repos/maximus/simpl-resultat/actions/tasks" \
      | python3 -c "import json,sys; print(next(t['run_number'] for t in json.load(sys.stdin)['workflow_runs'] if t['workflow_id']=='release.yml' and t['head_branch']==sys.argv[1]))" "$TAG")
    curl -sL -H "Authorization: token $(cat ~/.forgejo-token)" \
      "https://git.lacompagniemaximus.com/maximus/Simpl-Resultat/actions/runs/$RUN/jobs/0/logs" \
      | grep -m1 -E 'Z v[0-9]+\.[0-9]+\.[0-9]+\s*$'
    

    Attendu : v24.x. C'est la sortie de node --version dans l'étape « Install base tools and runtimes », seule ligne de ce format dans le journal (la v0.17.1, run 508, affichait v20.20.2). Toute autre majeure est à expliquer avant l'étape 10.

  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-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).
  • 2026-09-13 — Étape 0 (pré-vol) : cross-référence vers /issue-license quand la release modifie FEATURE_TIERS ou verrouille une fonctionnalité Free — émettre et distribuer les clés testeurs avant de tagger, sinon ils perdent l'auto-update. Source : session 690a6129 via /skills-review-inbox (draft 2026-09-06).
  • 2026-09-13 — Étape 0 : prérequis « clés testeurs saisies avant le tag » précisé (déclencheurs FEATURE_TIERS, gating, auto-update ; cas réel v0.15.0, deux clés Premium émises le jour même). Étape 9 : script scripts/verify-release.sh (7 artefacts par suffixe + contenu de latest.json) au lieu d'un script réécrit à chaque release ; durée de run observée 22-30 min. Source : session 690a6129 (release v0.15.0, run 402) via /skills-review-inbox (draft 2026-09-12).
  • 2026-10-10 — Étape 9 (post-CI) : lire node --version dans le journal du run de release (route web, seule ligne vX.Y.Z du journal). release.yml n'est exercé qu'au tag, donc la montée Node 20 → 24 de #376 ne se prouve qu'à la release suivante. Source : caveat de planification de #376 (milestone planned-2026-10-10-release-deps).
  • 2026-10-10 — Étape 9 (post-CI) : verify-release.sh exige les quatre clés de latest.json (linux-x86_64, linux-x86_64-deb, linux-x86_64-rpm, windows-x86_64), l'extension de chaque URL et le file: minisign de chaque signature, et sort en 1 sur tout écart (verdict en dernière ligne) ; lire l'étape « Generate latest.json » du journal. Source : #320 (milestone planned-2026-10-10-release-deps).
  • 2026-10-11 — Étape 9 (post-CI) : latest.json à trois clés, la clé générique linux-x86_64 étant retirée (décision de Max sur #320 : ses derniers lecteurs, AppImage v0.4.3 à v0.4.5 et binaires hors paquet, ne savaient pas installer le .deb qu'elle servait). Le filtre d'avant le DELETE vit dans scripts/check-latest-json.jq, que les tests de check-frontend exercent à chaque PR, avec la comparaison des trois listes de clés. Source : revue de la PR #416.