diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index a9d5414..6589d44 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -2,7 +2,7 @@ name: release description: Release a new version of Simpl-Resultat (bump, changelog, tag, push) user-invocable: true -updated: 2026-09-13 +updated: 2026-10-10 --- # /release — Release Simpl-Resultat @@ -45,6 +45,17 @@ updated: 2026-09-13 ``` 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`, `pub_date`, signature non vide et URL contenant la version pour `linux-x86_64` et `windows-x86_64`, notes non vides. Toute ligne `MANQUANT`, `VIDE` ou `MAUVAISE VERSION` bloque l'étape 10. 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é) : + ```bash + 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. 10. **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`](../../../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. @@ -69,3 +80,4 @@ updated: 2026-09-13 - 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`). diff --git a/.forgejo/workflows/audit.yml b/.forgejo/workflows/audit.yml index 34c794e..cbf7f30 100644 --- a/.forgejo/workflows/audit.yml +++ b/.forgejo/workflows/audit.yml @@ -27,6 +27,8 @@ on: - cron: '0 6 * * *' workflow_dispatch: +# What this key does on Forgejo 8.0.3: see the comment on `concurrency` in +# check-rust.yml (#376). concurrency: group: ci-audit-${{ github.ref }} cancel-in-progress: true @@ -39,7 +41,7 @@ jobs: runs-on: ubuntu container: ubuntu:22.04 steps: - - name: Install Node.js 20 + - name: Install Node.js 24 run: | apt-get update apt-get install -y --no-install-recommends \ @@ -47,7 +49,7 @@ jobs: # Node.js is required by actions/checkout, a JavaScript action that # needs `node` in the container PATH (taiki-e/install-action is a # composite action). - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + curl -fsSL https://deb.nodesource.com/setup_24.x | bash - apt-get install -y nodejs node --version diff --git a/.forgejo/workflows/check-frontend.yml b/.forgejo/workflows/check-frontend.yml index 24ae6c5..35758c6 100644 --- a/.forgejo/workflows/check-frontend.yml +++ b/.forgejo/workflows/check-frontend.yml @@ -25,7 +25,9 @@ on: - '*.md' - 'LICENSE' -# Distinct group from the rust workflow — see check-rust.yml. +# Same concurrency setup as check-rust.yml (own group, cancel-in-progress), +# whose comment says what it does on Forgejo 8.0.3 (#376) and why each +# workflow has its own group. concurrency: group: ci-frontend-${{ github.ref }} cancel-in-progress: true @@ -38,11 +40,11 @@ jobs: runs-on: ubuntu container: ubuntu:22.04 steps: - - name: Install Node.js 20 + - name: Install Node.js 24 run: | apt-get update apt-get install -y --no-install-recommends curl ca-certificates git - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + curl -fsSL https://deb.nodesource.com/setup_24.x | bash - apt-get install -y nodejs node --version npm --version diff --git a/.forgejo/workflows/check-rust.yml b/.forgejo/workflows/check-rust.yml index bba8cd9..ecb5240 100644 --- a/.forgejo/workflows/check-rust.yml +++ b/.forgejo/workflows/check-rust.yml @@ -52,9 +52,21 @@ on: - cron: '30 6 * * 1' workflow_dispatch: -# Cancel obsolete runs (e.g. on force-push) so only the latest commit runs. -# Distinct from the frontend group: a shared group would make the two -# workflows cancel each other on a PR that touches both. +# What this key does on Forgejo 8.0.3 / runner v12.7.0, as measured on +# 2026-10-02 (la-compagnie-maximus docs/forgejo-ops.md, sections "Le schéma du +# workflow est validé par le runner" and "Un push annule le run du push +# précédent"): +# - it passes the runner's schema validation, but what it adds has not been +# isolated; +# - without it, Forgejo cancels a `push` run when a newer one of the same +# workflow is created on the same ref, whether the older run's job was still +# queued or already running; +# - no pull_request (or pull_request_sync) job has been cancelled by a later +# run: on a PR, a force-push does not cancel the obsolete run. +# Kept anyway (#376): harmless here, and it keeps its GitHub Actions meaning +# should these workflows run where it is honoured (GitHub, a later Forgejo). +# There, a group shared with the frontend workflow would make the two cancel +# each other on a PR that touches both, hence the distinct group. concurrency: group: ci-rust-${{ github.ref }} cancel-in-progress: true diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index 7e76207..3b64138 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -16,8 +16,14 @@ jobs: run: | apt-get update apt-get install -y curl wget git sudo ca-certificates gnupg - # Install Node.js 20 - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + # Install Node.js 24 (#376). Trust note, kept on purpose without a + # pinned tarball: NodeSource's script runs as root in this + # container, and the Node.js it installs then runs `npx tauri build` + # with TAURI_SIGNING_PRIVATE_KEY in its environment. A compromised + # script or package would reach the updater signing key, and the + # publishing tokens of the later steps. See docs/architecture.md + # (CI/CD, release.yml). + curl -fsSL https://deb.nodesource.com/setup_24.x | bash - apt-get install -y nodejs # Install Rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y diff --git a/STATE.md b/STATE.md index d593fa5..f58e26a 100644 --- a/STATE.md +++ b/STATE.md @@ -73,6 +73,8 @@ Audit critique de la page Bilan livré (`docs/audit-bilan-2026-05.md`, revue CPA ## Blockers actifs - Aucun blocker externe dur. `spec-monetisation` **fermée 12/12** (2026-07-08) — #50/#52/#53/#135/#136 livrées, ne sont plus des blockers. +- **Vérifications dues à la prochaine release** (après le tag, étape 9 de `/release`) : + - `release.yml` sous Node 24 (#376) : `node --version` à `v24.x` dans le journal du run de release, et `verify-release.sh` vert. Le diff touchant `release.yml`, l'étape 10 de `/release` (cycle réel de mise à jour, `docs/qa-update-cycle.md`) s'applique aussi, selon la règle du skill. - **Le paiement est live : « aucun tag avant le go-live » n'a plus de raison d'être, et la release desktop qu'attendait le go-live est publiée (v0.16.0, 2026-10-03).** Bascule live le 2026-09-29 (Product et Price live à 19 $ CAD, webhook, variables Stripe de production, redeploy `3673d09` de maximus-api, URL `cs_live_` vérifiée), puis achat réel de contrôle le 2026-09-30 : clé sur `/merci` et par courriel, activation dans l'app, remboursement complet et licence révoquée par `charge.refunded`. **Volet serveur clos le 2026-10-03** (derniers commentaires relus le 2026-10-04) : maximus-api#62 fermée, tous critères cochés (maximus-api 0.4.2 en prod, secrets en runtime seul), avec la milestone `spec-paiements` de maximus-api (12/12) ; la-compagnie-maximus#150 (Link dans les textes légaux) livrée et fermée le 2026-10-01. **Go-live clos : la-compagnie-maximus#141** fermée le 2026-10-10 sur décision de Max, l'updater vérifié (Free refuse et invite à acheter, Base accepte ; confirmé le même jour). Restent pour mémoire, sans issue : le Dashboard Stripe (logo, paramètres Checkout, reçus automatiques, alertes d'échec de webhook) et les relevés du commentaire de fermeture. la-compagnie-maximus#184 (politique de confidentialité : données d'activation, contrôle au démarrage) est en `status:review` ; sa PR reste en brouillon jusqu'au déploiement de maximus-api#85 (commentaire du 2026-10-03). Les deux textes des suites de licence (contrôle à la saisie, retrait de la clé) y sont postés depuis le 2026-10-05 (commentaire 19724). **Vérifications manuelles sur la release publiée, toutes faites** : #365 le 2026-10-03, #346 et #350 le 2026-10-04 (voir Décisions). L'étape 10 sous Windows est faite (Michel, confirmée le 2026-10-10). - Backlog ouvert : **vague `planned-2026-09-24-paiements` close 3/3** (milestone fermée le 2026-09-30) : #270 (PR #359) et #346 (PR #362) mergées le 2026-09-27, #347 (PR #364, ferme aussi #360) mergée le 2026-09-30 ; volet site **clos** (maximus/la-compagnie-maximus#153, redirection de `/simpl-resultat` en prod). **#365 fermée le 2026-10-03** après sa vérification manuelle. **Milestone `planned-2026-10-03-suites-licence` fermée 3/3 le 2026-10-05** (#373, #370, #367 mergées, voir Décisions ; suivi #389 en `status:triage`). Hors milestone, en `status:ready` : **#369** (réserver le fractionnement de transactions à l'édition Base) et **#372** (la liste complète des versions repousse le Journal en bas de Paramètres → Systèmes). **#350 fermée le 2026-10-04** (étape 3 vérifiée en production, voir Décisions) ; #363 fermée le 2026-10-10 sur décision de Max (ses tests, absorbés par #347, sont livrés dans la v0.16.0) ; réglés côté serveur et site : maximus-api#63 (clés de 100 ans) et maximus-api#70 (réactivation d'une machine désactivée), fermées et contenues dans `3673d09`, le commit déployé à la bascule du 2026-09-29, et la-compagnie-maximus#157, fermée, dont les CGV sont publiées en tronc commun plus annexe Simpl-Résultat (annexe relue en production le 2026-09-30, plus aucune durée de 10 ans) ; côté serveur, `/v1/prices` ne vérifie pas qu'une machine est encore active (une machine désactivée garde les cours jusqu'à l'expiration de son jeton), issue maximus-api à ouvrir ; suites de #350 ouvertes le 2026-10-01 : maximus-api#82 (`status:triage` : identifiants machine bruts déjà stockés, lignes désactivées comprises ; à rehacher avant l'expiration des premiers jetons bruts, sinon une réactivation crée une seconde ligne) et la-compagnie-maximus#184 (`status:review`, voir ci-dessus) ; go-live clos (la-compagnie-maximus#141 fermée le 2026-10-10, maximus-api#62 le 2026-10-03) ; `spec-paiements` fermée ici 5/5 ; onboarding phase 2 (parcours guidé) **mergée** : milestone `planned-2026-10-06-onboarding-parcours-guide` fermée 6/6 le 2026-10-08 (#396 à #401, `ee17f31`), suivis **#408** et **#409** (`status:triage`) ; onboarding phase 1 **livrée** : milestone `planned-2026-10-03-onboarding-phase1` fermée 7/7 le 2026-10-04 (#351 à #357, mergées en avance rapide, `667e058`) ; suivis ouverts : **#384** (`status:ready`, suivis non bloquants des revues) et **#371** (`status:blocked`, hors `/autopilot` : en-têtes CSV BMO à fournir par Max depuis un compte réel, un par type de compte, chèques/épargne et Mastercard, avec des valeurs fictives) ; analyse d'origine dans `docs/analyse-onboarding-2026-09.md`, plan détaillé dans les `spec-*-onboarding-phase1.md` locaux, ignorés par git ; #271 absorbé par #301, livré ; `spec-ci-build-optimization` **fermée 4/4** le 2026-10-05 (#232 livrée le 2026-07-27 ; #234 close le 2026-10-05 avec ses quatre preuves : dispatch run 464, PR #386 run 480, run planifié 488, `actcache` à 999 Mo) ; **#376** (`status:triage` : Node 20, en fin de vie depuis le 2026-04-30, encore installé par `check-frontend.yml` et `audit.yml`, et commentaire `concurrency` de `check-rust.yml` démenti par `forgejo-ops.md` pour les runs de PR). Le suivi « glob `src-tauri/**` non prouvé » ouvert avec #232 est **levé** : la PR #344 est le cas test isolé attendu (`Cargo.lock` + 2 `.md` seulement) — `check-rust` a bien tourné, `check-frontend` a bien été sauté par sa denylist. - Suivis des advisories : **#312, #313 et #315 fermées le 2026-08-15** (PRs #321/#322 mergées). **#314 fermée le 2026-08-15, prémisse infirmée** — elle affirmait « zéro run `schedule` », or `audit.yml` tire tous les jours à 06:00 UTC (**19 runs `schedule` verts** du 07-28 au 08-15, sans trou) ; elle a été écrite le 07-27 vers 23:56Z, ~6 h avant le premier tir programmé possible, et décrivait donc une fenêtre trop courte, pas un scheduler en panne. Ces 19 runs confirment au passage que `.cargo/audit.toml` est bien lu en conteneur **sur `main`** — l'ADR 0018 ne l'avait vérifié que par un `workflow_dispatch` sur une branche. **#343 fermée le 2026-09-04** (PR #344) — RUSTSEC-2026-0258 sur `h2`, la seule vulnérabilité qui rougissait l'audit depuis le 08-19 ; l'audit est de nouveau vert sur `main`, prouvé par `workflow_dispatch` (run 393) et non déduit du run de PR. **#348 fermée le 2026-09-21** (PR #349) — RUSTSEC-2026-0285 sur `rustls`, seule vulnérabilité rougissant l'audit depuis le 09-15 (7 runs, 405→411) ; vert reprouvé par `workflow_dispatch` sur `main` (run 414 : 666 paquets, zéro vulnérabilité, 12 warnings autorisés). Deux advisories en cinq semaines sur le même chemin, celui de la mise à jour automatique. Restent **#317** (prémisse caduque depuis le 2026-09-26 : `react-router-dom` 7.18.2 et suivantes corrigent l'advisory par un bump de patch dans `^7.18.1`, sans migration v8 ; absorbée par #358, dont la PR la fermera), **#319** (déclencheur de retrait pour `rsa`, désormais **seule** entrée de `.cargo/audit.toml` — le garde-fou est passé à 2 vérifications) et **#345** (domicilier la contrainte `windows-sys 0.61.2` — analysée le 09-04 : le corps d'origine, écrit de mémoire en fin de revue, se trompait sur trois points. Le déclencheur n'est pas « n'importe quel `cargo update` » mais tout bump atteignant le sous-arbre réseau/TLS — mesuré : `-p anyhow` ne bouge rien, `-p hyper` fait descendre les 7 — soit précisément là où tombent les advisories. **Prémisse restreinte le 2026-09-21 par #348** : `-p rustls`, au cœur de ce même sous-arbre, n'en déplace **aucun** — le déclencheur tient à `hyper` et à ce qu'il tire, pas à la couche TLS en général, donc « sous-arbre réseau/TLS » prédit une dérive là où il n'y en a pas et ferait écrire une strophe à la main sans nécessité. D'où la suggestion portée en commentaire sur l'issue : formuler le garde-fou sur la **version résolue de `windows-sys`** plutôt que sur une liste de crates déclencheurs — la liste est ce qui vient d'être trouvé faux, la version est ce qui est protégé. #310 avait **bien** vu la dérive et l'avait jugée inoffensive *sur Linux*, où elle l'est ; son coût tombe sur le build Windows que personne ne mesurait : **3 versions de `windows-sys` compilées contre 5**. Aucun risque de correction — les 7 crates déclarent des bornes larges couvrant les deux états. Et cargo annonce « Locking 1 package » en écrivant 8 changements, donc le silence est le sien. Décision : garde-fou CI sur la version + ADR portant la condition de sortie. Le check croisé Windows depuis Linux a été **testé et écarté** — `cc-rs` s'arrête sur `lib.exe`, il faudrait `cargo-xwin`). Côté npm, **#358** ouverte le 2026-09-26 par `/analyse-vulnerabilite` : `npm audit` remonte 7 findings (4 high, 3 moderate) sur 4 dépendances racines, contre les 2 acceptés au 07-27, et chaque correctif tient dans la borne déjà déclarée ; le lock se régénère avec `npx npm@11` (npm 10 plante sur un bump de vitest 4.1.x). diff --git a/docs/architecture.md b/docs/architecture.md index 49a6f10..ca83372 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -612,15 +612,17 @@ Les deux workflows `check-*` doivent être verts avant tout merge. Ils évitent ### `release.yml` — Build et publication -Déclenché par les tags `v*`. Deux jobs : -1. **build-windows** (windows-latest) → Installeur `.exe` (NSIS) -2. **build-linux** (ubuntu-22.04) → `.deb` + `.rpm` +Déclenché par les tags `v*`. Un seul job, `build-and-release`, dans un conteneur `ubuntu:22.04` : +1. Linux → `.deb` + `.rpm` +2. Windows, compilé depuis Linux par `cargo-xwin` → installeur `.exe` (NSIS) Fonctionnalités : - Signature des binaires (clés TAURI_SIGNING_PRIVATE_KEY) - JSON d'updater publié sur `https://git.lacompagniemaximus.com/api/packages/maximus/generic/simpl-resultat/latest/latest.json` - Release Forgejo automatique avec assets et release notes extraites du CHANGELOG.md +**Node.js 24** ([#376](https://git.lacompagniemaximus.com/maximus/simpl-resultat/issues/376)), installé comme dans les trois autres workflows par le script NodeSource (`curl … | bash`). Ce script tourne en root dans le conteneur du job, et le Node.js qu'il installe lance ensuite `npx tauri build` avec `TAURI_SIGNING_PRIVATE_KEY` dans son environnement : le job confie à NodeSource (script et dépôt apt) la clé de signature de l'updater, et les jetons de publication des étapes suivantes. Risque accepté par Max le 2026-10-10, alternative écartée : un tarball Node.js épinglé. La chaîne Rust, installée de la même façon par `rustup`, est dans le même cas. Ce workflow ne tourne qu'au tag : la première release après #376 est la première sous Node 24, à vérifier par `node --version` dans le journal du run (étape 9 de `/release`). + ## Architecture Decision Records (ADRs) Les ADRs documentent les décisions techniques structurantes. Ils vivent dans `docs/adr/`.