fix: give rpm installs their own key in latest.json #416

Merged
maximus merged 2 commits from issue-320-latest-json-keys into main 2026-10-12 19:32:43 +00:00
Owner

Resolves #320

Generated autonomously by /autopilot run of 2026-10-10

PR empilée sur #415 (#345), elle-même sur #414 (#376) : elle ne montre que son propre diff.

Ce qui change pour l'utilisateur

  • Les installations .rpm (Fedora, openSUSE) reçoivent les mises à jour automatiques. Jusqu'ici latest.json n'avait qu'une clé linux-x86_64, construite depuis le .deb : une installation rpm cherchait linux-x86_64-rpm, retombait sur linux-x86_64, téléchargeait le .deb puis le refusait (InvalidUpdaterFormat).
  • Les installations .rpm existantes n'ont rien à réinstaller : le binaire du .rpm v0.17.1 porte le marqueur __TAURI_BUNDLE_TYPE_VAR_RPM (vérifié en analyse), il lira linux-x86_64-rpm dès la prochaine release.
  • Rien ne change pour les installations .deb et Windows : même fichier, même signature. Une installation .deb lit désormais linux-x86_64-deb, qui pointe sur le même .deb.
  • La page de release et le README nomment le .rpm au lieu d'une AppImage, qui n'est pas construite.

Technique

  • scripts/build-latest-json.sh (nouveau). Entrées : dossier d'assets, version, fichier de notes, URL de base. Sortie : le JSON sur stdout, le journal sur stderr. Échec fermé : exit 1, sans rien écrire sur stdout, s'il ne trouve pas exactement un .deb, un .rpm et un -setup.exe, chacun avec un "$asset.sig" non vide. Le JSON vient de jq seul (--arg, et --rawfile pour les notes), et les noms de fichiers sont encodés par @uri, comme à l'envoi des assets. Clés : linux-x86_64-deb, linux-x86_64-rpm, linux-x86_64 (le .deb, en repli : raison et condition de retrait dans l'en-tête du script et dans docs/qa-update-cycle.md) et windows-x86_64.
  • scripts/build-latest-json.test.mjs (nouveau, vitest). Il est en .mjs parce que tsconfig ne couvre que src. Il lance le script sous bash -e -o pipefail sur un dossier temporaire, avec les vrais noms de la v0.17.1 (espace comprise) et des valeurs attendues écrites en littéraux.
  • check-frontend.yml : installe jq et affiche jq --version.
  • release.yml :
    • « Extract changelog » : délimiteur GITHUB_OUTPUT aléatoire, CHANGELOG_EOF_ suivi de 32 caractères hexadécimaux tirés de /dev/urandom par od (coreutils, présent dans le conteneur) ;
    • « Generate latest.json » : écrit les notes dans /tmp/release-notes.md avec printf '%s', donc les mêmes octets qu'avant, sans saut de ligne final, et hors de release-assets/, dont chaque fichier est envoyé. Il appelle ensuite le script exactement comme le test. La sortie reste release-assets/latest.json ;
    • « Create release » : les notes d'installation disent « .deb (Debian, Ubuntu) ou .rpm (Fedora, openSUSE) » ;
    • « Publish latest.json » : une assertion jq -e en tête d'étape, avant le DELETE : version égale au tag, exactement les quatre clés, signatures de type chaîne non vides, extension de chaque URL. Un échec arrête le job et laisse le registre intact.
  • verify-release.sh : exige les quatre clés et aucune autre, l'extension de chaque URL (.deb, .deb, .rpm, .exe), et que le file: du commentaire de confiance minisign (signature décodée du base64) soit le dernier segment de l'URL, décodé. Il sort en 1 sur tout écart : les deux blocs python3 propagent leur code et la dernière ligne donne VERDICT : OK ou VERDICT : ÉCHEC. Un second argument optionnel remplace l'asset par un latest.json local, ce qui sert aux preuves ci-dessous.
  • Docs :
    • .claude/skills/release/SKILL.md (étape 9) ;
    • docs/qa-update-cycle.md : le §3 devient la procédure de test rpm, avec le tableau des clés, la raison et la condition de retrait du repli ;
    • docs/architecture.md : scripts/, jq dans check-frontend, les clés et l'assertion dans release.yml ;
    • README.md ;
    • CHANGELOG FR/EN (Fixed / Corrigé) ;
    • STATE.md : sous-puce sous « Vérifications dues à la prochaine release ».
  • Encodage des URL : avec @uri, l'URL de latest.json passe de …/v0.17.1/Simpl Resultat_0.17.1_amd64.deb (espace littérale, l'état publié aujourd'hui) à …/v0.17.1/Simpl%20Resultat_0.17.1_amd64.deb. La requête envoyée est la même : url 2.5.8, la version du Cargo.lock de l'app, parse les deux formes en un même Url (programme jetable, hors ligne). L'URL encodée est exactement le browser_download_url de Forgejo, vérifié sur les quatre clés (voir Preuves).

Preuves

Tests

  • npm run build vert ; npm test : 1915 tests / 110 fichiers, contre 1904 / 109 sur la base.
  • scripts/build-latest-json.test.mjs : 11 tests. Ils passent aussi sous jq 1.6, celui d'ubuntu:22.04 donc de la CI (binaire officiel jq-linux64 1.6, sha256 af986793…4c44). Cas couverts :
    • nominal : quatre clés, URL en %20, chaque clé avec la signature de son propre asset ;
    • .deb, .rpm ou -setup.exe absent ;
    • deux .deb ;
    • .sig vide, .sig fait de sauts de ligne, .sig absent ;
    • un .exe parasite que Windows ne reçoit pas ;
    • des notes avec ", \, une ligne CHANGELOG_EOF et un faux fragment JSON, gardées intactes ;
    • des arguments manquants.

Mutations du script

14 mutations, une à la fois, avec restauration et comparaison des octets d'origine :

Mutation Résultat
« exactement un » devient « au moins un » rouge
pas d'exit après un mauvais compte rouge
pas de contrôle du .sig vide rouge
pas de contrôle du .sig blanc rouge
pas de @uri rouge
la clé rpm sert le .deb rouge
la clé rpm porte la signature du .deb rouge
clé rpm retirée rouge
clé deb retirée rouge
le repli sert le .rpm rouge
notes passées par --arg "$(cat …)" rouge
pas de contrôle du nombre d'arguments rouge
*.exe au lieu de *-setup.exe rouge (survivait au premier passage, test ajouté)
set -euo pipefail retiré survit, par construction : le test et release.yml passent -e -o pipefail à l'invocation

Générateur sur la vraie v0.17.1

J'ai pris des bundles factices aux noms de la v0.17.1 et les vrais .sig de la release. Les quatre URL générées sont égales aux browser_download_url de Forgejo, et les notes comme les signatures sont identiques au latest.json publié.

release.yml : le texte exact des étapes, sous le shell du runner

PyYAML extrait le run:, qui tourne sous bash --noprofile --norc -e -o pipefail dans un espace de travail jetable. GITHUB_OUTPUT est relu avec la logique de parseEnvFile d'act. Résultats identiques sous jq 1.6 et 1.7 ; sortie sous jq 1.6 :

== A. Extract changelog (section holds a CHANGELOG_EOF line) ==
[origin/main] rc=0 first line='notes<<CHANGELOG_EOF' parse_error=None
[origin/main] notes intact: False | notes='- Text that the old delimiter turns into the notes.'
[this PR] rc=0 first line='notes<<CHANGELOG_EOF_72800362365cfa673a45f0f02710f498' parse_error=None
[this PR] notes intact: True | notes='### Fixed\n\n- A "quoted" word, a back\\slash and C:\\Users\\path (#320).\nCHANGELOG_EOF\nnotes<<CHANGELOG_EOF\n- Text that the old delimiter turns into the notes.'

== B. Generate latest.json ==
rc=0
build-latest-json: .deb <- Simpl Resultat_0.18.0_amd64.deb
build-latest-json: .rpm <- Simpl Resultat-0.18.0-1.x86_64.rpm
build-latest-json: -setup.exe <- Simpl Resultat_0.18.0_x64-setup.exe
keys: ['linux-x86_64', 'linux-x86_64-deb', 'linux-x86_64-rpm', 'windows-x86_64']
notes in latest.json == changelog section: True

== C. Publish latest.json to package registry (stub curl) ==
  nominal                              rc=0 curl calls=['DELETE', 'PUT']
  linux-x86_64-rpm removed             rc=1 curl calls=none | ERROR: release-assets/latest.json failed its checks, registry left as is:
  linux-x86_64 removed                 rc=1 curl calls=none | ERROR: …
  extra key darwin-aarch64             rc=1 curl calls=none | ERROR: …
  rpm key -> .deb URL                  rc=1 curl calls=none | ERROR: …
  windows key -> .exe.sig URL          rc=1 curl calls=none | ERROR: …
  deb key -> .rpm URL                  rc=1 curl calls=none | ERROR: …
  empty rpm signature                  rc=1 curl calls=none | ERROR: …
  signature not a string               rc=1 curl calls=none | ERROR: …
  version 0.17.1 (stale file)          rc=1 curl calls=none | ERROR: …
  url missing on a key                 rc=1 curl calls=none | ERROR: …
  platforms missing                    rc=1 curl calls=none | ERROR: …
  malformed JSON                       rc=1 curl calls=none | ERROR: …
  signatures of deb and rpm swapped    rc=0 curl calls=['DELETE', 'PUT']

En A, l'étape de main laisse une ligne CHANGELOG_EOF du CHANGELOG remplacer les notes par le texte qui la suit, sans erreur. En C, la dernière ligne est voulue : l'assertion ne contrôle pas l'appariement des signatures (voir « Pour relire en priorité »).

verify-release.sh, vert et rouge

Tag v0.17.1, dont la release porte bien les 7 assets. Le fichier vert est celui du générateur ci-dessus, fait avec les vraies signatures de la v0.17.1 :

=== latest.json (fichier local …, pas l'asset de la release) ===
version=0.17.1 OK | pub_date=2026-10-11T00:38:06Z
notes: 5337 caractères — début: '### Added\n\n- A getting-started banner …'
  linux-x86_64: url=Simpl Resultat_0.17.1_amd64.deb | extension .deb OK | signature OK (424 car.) | file: OK | url contient version=oui
  linux-x86_64-deb: url=Simpl Resultat_0.17.1_amd64.deb | extension .deb OK | signature OK (424 car.) | file: OK | url contient version=oui
  linux-x86_64-rpm: url=Simpl Resultat-0.17.1-1.x86_64.rpm | extension .rpm OK | signature OK (428 car.) | file: OK | url contient version=oui
  windows-x86_64: url=Simpl Resultat_0.17.1_x64-setup.exe | extension .exe OK | signature OK (428 car.) | file: OK | url contient version=oui

VERDICT : OK                        (exit 0)

Cas rouges : chacun échoue sur son propre contrôle, ligne en défaut seule.

v0.17.1 publiée, sans 2e argument :  linux-x86_64-deb: MANQUANT / linux-x86_64-rpm: MANQUANT        VERDICT : ÉCHEC (exit 1)
clé linux-x86_64-rpm retirée :       linux-x86_64-rpm: MANQUANT                                       VERDICT : ÉCHEC (exit 1)
clé linux-x86_64-deb retirée :       linux-x86_64-deb: MANQUANT                                       VERDICT : ÉCHEC (exit 1)
clé rpm = entrée du .deb :           linux-x86_64-rpm: url=Simpl Resultat_0.17.1_amd64.deb | extension .rpm MAUVAISE EXTENSION | signature OK (424 car.) | file: OK      ÉCHEC (exit 1)
signatures .deb/.rpm échangées :     linux-x86_64-deb: … | file: DIFFÉRENT ('Simpl Resultat-0.17.1-1.x86_64.rpm')
                                     linux-x86_64-rpm: … | file: DIFFÉRENT ('Simpl Resultat_0.17.1_amd64.deb')                                                       ÉCHEC (exit 1)
signature .rpm vide :                linux-x86_64-rpm: … | signature VIDE | file: DIFFÉRENT (None)    VERDICT : ÉCHEC (exit 1)
signature non base64 :               windows-x86_64: … | signature OK (15 car.) | file: DIFFÉRENT (None)                                                             ÉCHEC (exit 1)
URL doublement encodée (%2520) :     linux-x86_64-rpm: url=Simpl%20Resultat-0.17.1-1.x86_64.rpm | … | file: DIFFÉRENT ('Simpl Resultat-0.17.1-1.x86_64.rpm')          ÉCHEC (exit 1)
version 0.17.0 :                     version=0.17.0 MAUVAISE VERSION                                  VERDICT : ÉCHEC (exit 1)
clé inattendue darwin-aarch64 :      darwin-aarch64: clé INATTENDUE                                   VERDICT : ÉCHEC (exit 1)
release absente (v0.18.0), latest.json factice entièrement vert :  PAS DE RELEASE …           VERDICT : ÉCHEC (exit 1)

Une variante verte supplémentaire, où toutes les URL portent l'espace littérale (la forme publiée jusqu'ici), donne VERDICT : OK : le contrôle file: compare le nom décodé.

Mutations de verify-release.sh (copie hors du dépôt), contre ces 13 cas :

Mutation Résultat
bloc latest.json sans || STATUS=1 détectée (10 cas)
exit 0 en fin de script détectée (10 cas)
pas de contrôle d'extension détectée (clé rpm = entrée du .deb)
pas de contrôle file: détectée (3 cas)
file: comparé au segment brut, non décodé détectée (cas vert en %20)
clé rpm retirée des attendues détectée (3 cas)
clé inattendue tolérée détectée
version différente tolérée détectée
bloc release sans || STATUS=1 détectée (release absente, VERDICT : OK sinon)
contrôle « signature non vide » retiré survit, mutation équivalente : une signature vide fait déjà échouer file:

Ce qui ne se prouve qu'au prochain tag

  • release.yml ne tourne qu'au tag. Dans le journal du run, l'étape « Generate latest.json » doit afficher trois lignes build-latest-json: <type> <- <paquet>, puis le JSON à quatre clés, et aucune ligne ERROR: release-assets/latest.json failed its checks.
  • verify-release.sh vX.Y.Z doit finir en VERDICT : OK sur les quatre clés, et l'endpoint du registre (tauri.conf.json) doit servir le même fichier.
  • Un cycle réel depuis une installation .rpm (§3 de docs/qa-update-cycle.md), dès qu'une machine Fedora ou openSUSE est disponible ; d'ici là, « non déroulé » avec la raison dans la trace de la release. Le diff touche release.yml : l'étape 10 de /release s'applique donc aussi (deb et Windows).
  • Tout est consigné dans STATE.md, sous « Vérifications dues à la prochaine release ».

Pour relire en priorité

  1. Le repli linux-x86_64 n'est pas inoffensif, et il semble retirable. Gardé comme décidé, avec sa raison, ses lecteurs et sa condition de retrait écrits dans l'en-tête du script et dans qa-update-cycle.md (second commit, 5ce136f). Mesures :

    • Les 68 tags verrouillent @tauri-apps/cli 2.10.0 et tauri-utils 2.8.2, et l'updater existe depuis la v0.2.1 (plugin 2.10.0). Aucune version publiée n'est donc antérieure au marqueur : la condition de retrait semble remplie.
    • Ses lecteurs connus ne savent pas installer le .deb qu'il sert :
      • un binaire sans marqueur, lancé hors de son paquet ;
      • les AppImage v0.4.2 à v0.4.5. Elles sont empaquetées jusqu'à la v0.7.3 selon tauri.conf.json, publiées sur Forgejo pour les v0.4.2 et v0.4.3 et sur le miroir GitHub pour les v0.4.3 à v0.4.5, et leur updater lit le registre Forgejo depuis la v0.4.2. Elles cherchent linux-x86_64-appimage, absente, puis linux-x86_64.
    • Pour ces deux lecteurs, install_inner envoie le téléchargement vers install_appimage, avec executable_path = $APPIMAGE pour une AppImage. L'updater écrit alors les octets du .deb à la place du fichier lancé, et sa copie de secours vit dans un TempDir supprimé au retour : l'installation est détruite. C'est l'état depuis la v0.4.2, que cette PR ne change pas. Sans la clé, ces installations auraient une erreur à la vérification (TargetsNotFound, car check() appelle get_urls avant de comparer les versions) au lieu d'une mise à jour destructrice.

    Le retrait de la clé est à trancher par Max, hors de cette PR.

  2. L'assertion placée avant le DELETE ne contrôle pas l'appariement des signatures : des signatures échangées passent (cas C ci-dessus). C'est le périmètre du plan (version, clés, signature non vide, extension). L'échange est couvert par le test du script avant le merge (mutation « clé rpm avec la signature du .deb ») et par le contrôle file: de verify-release.sh après publication. Je n'ai pas mis de @base64d dans l'assertion : c'est hors plan, et ce code ne s'exercerait qu'au tag.

  3. verify-release.sh bloque aussi sur ses contrôles d'avant : assets manquants, release absente, version différente, version absente de l'URL. Le skill disait déjà que ces lignes bloquent l'étape 10. pub_date et la longueur des notes restent informatifs.

  4. Les notes passent par /tmp/release-notes.md (printf '%s') et non plus par --arg. Les octets sont les mêmes que dans le latest.json publié (comparaison faite sur la v0.17.1) ; /tmp suit le motif des /tmp/*_response.json du même workflow.

  5. jq est ajouté à l'étape « Install Node.js 24 » de check-frontend, dont le nom ne change pas. À lire dans le journal de la CI de cette PR : jq-1.6, et scripts/build-latest-json.test.mjs parmi les fichiers de test (11 tests, 110 fichiers au total).

🤖 Generated with Claude Code

Resolves #320 Generated autonomously by /autopilot run of 2026-10-10 > PR empilée sur #415 (#345), elle-même sur #414 (#376) : elle ne montre que son propre diff. ## Ce qui change pour l'utilisateur - Les installations `.rpm` (Fedora, openSUSE) reçoivent les mises à jour automatiques. Jusqu'ici `latest.json` n'avait qu'une clé `linux-x86_64`, construite depuis le `.deb` : une installation rpm cherchait `linux-x86_64-rpm`, retombait sur `linux-x86_64`, téléchargeait le `.deb` puis le refusait (`InvalidUpdaterFormat`). - Les installations `.rpm` existantes n'ont rien à réinstaller : le binaire du `.rpm` v0.17.1 porte le marqueur `__TAURI_BUNDLE_TYPE_VAR_RPM` (vérifié en analyse), il lira `linux-x86_64-rpm` dès la prochaine release. - Rien ne change pour les installations `.deb` et Windows : même fichier, même signature. Une installation `.deb` lit désormais `linux-x86_64-deb`, qui pointe sur le même `.deb`. - La page de release et le README nomment le `.rpm` au lieu d'une AppImage, qui n'est pas construite. ## Technique - **`scripts/build-latest-json.sh`** (nouveau). Entrées : dossier d'assets, version, fichier de notes, URL de base. Sortie : le JSON sur stdout, le journal sur stderr. Échec fermé : exit 1, sans rien écrire sur stdout, s'il ne trouve pas exactement un `.deb`, un `.rpm` et un `-setup.exe`, chacun avec un `"$asset.sig"` non vide. Le JSON vient de `jq` seul (`--arg`, et `--rawfile` pour les notes), et les noms de fichiers sont encodés par `@uri`, comme à l'envoi des assets. Clés : `linux-x86_64-deb`, `linux-x86_64-rpm`, `linux-x86_64` (le `.deb`, en repli : raison et condition de retrait dans l'en-tête du script et dans `docs/qa-update-cycle.md`) et `windows-x86_64`. - **`scripts/build-latest-json.test.mjs`** (nouveau, vitest). Il est en `.mjs` parce que `tsconfig` ne couvre que `src`. Il lance le script sous `bash -e -o pipefail` sur un dossier temporaire, avec les vrais noms de la v0.17.1 (espace comprise) et des valeurs attendues écrites en littéraux. - **`check-frontend.yml`** : installe `jq` et affiche `jq --version`. - **`release.yml`** : - « Extract changelog » : délimiteur `GITHUB_OUTPUT` aléatoire, `CHANGELOG_EOF_` suivi de 32 caractères hexadécimaux tirés de `/dev/urandom` par `od` (coreutils, présent dans le conteneur) ; - « Generate latest.json » : écrit les notes dans `/tmp/release-notes.md` avec `printf '%s'`, donc les mêmes octets qu'avant, sans saut de ligne final, et hors de `release-assets/`, dont chaque fichier est envoyé. Il appelle ensuite le script exactement comme le test. La sortie reste `release-assets/latest.json` ; - « Create release » : les notes d'installation disent « `.deb` (Debian, Ubuntu) ou `.rpm` (Fedora, openSUSE) » ; - « Publish latest.json » : une assertion `jq -e` en tête d'étape, **avant le `DELETE`** : version égale au tag, exactement les quatre clés, signatures de type chaîne non vides, extension de chaque URL. Un échec arrête le job et laisse le registre intact. - **`verify-release.sh`** : exige les quatre clés et aucune autre, l'extension de chaque URL (`.deb`, `.deb`, `.rpm`, `.exe`), et que le `file:` du commentaire de confiance minisign (signature décodée du base64) soit le dernier segment de l'URL, **décodé**. Il sort en 1 sur tout écart : les deux blocs `python3` propagent leur code et la dernière ligne donne `VERDICT : OK` ou `VERDICT : ÉCHEC`. Un second argument optionnel remplace l'asset par un `latest.json` local, ce qui sert aux preuves ci-dessous. - **Docs** : - `.claude/skills/release/SKILL.md` (étape 9) ; - `docs/qa-update-cycle.md` : le §3 devient la procédure de test rpm, avec le tableau des clés, la raison et la condition de retrait du repli ; - `docs/architecture.md` : `scripts/`, `jq` dans check-frontend, les clés et l'assertion dans `release.yml` ; - `README.md` ; - CHANGELOG FR/EN (Fixed / Corrigé) ; - `STATE.md` : sous-puce sous « Vérifications dues à la prochaine release ». - **Encodage des URL** : avec `@uri`, l'URL de `latest.json` passe de `…/v0.17.1/Simpl Resultat_0.17.1_amd64.deb` (espace littérale, l'état publié aujourd'hui) à `…/v0.17.1/Simpl%20Resultat_0.17.1_amd64.deb`. La requête envoyée est la même : `url` 2.5.8, la version du `Cargo.lock` de l'app, parse les deux formes en un même `Url` (programme jetable, hors ligne). L'URL encodée est exactement le `browser_download_url` de Forgejo, vérifié sur les quatre clés (voir Preuves). ## Preuves ### Tests - `npm run build` vert ; `npm test` : **1915 tests / 110 fichiers**, contre 1904 / 109 sur la base. - `scripts/build-latest-json.test.mjs` : 11 tests. Ils passent aussi sous **jq 1.6**, celui d'`ubuntu:22.04` donc de la CI (binaire officiel `jq-linux64` 1.6, sha256 `af986793…4c44`). Cas couverts : - nominal : quatre clés, URL en `%20`, chaque clé avec la signature de son propre asset ; - `.deb`, `.rpm` ou `-setup.exe` absent ; - deux `.deb` ; - `.sig` vide, `.sig` fait de sauts de ligne, `.sig` absent ; - un `.exe` parasite que Windows ne reçoit pas ; - des notes avec `"`, `\`, une ligne `CHANGELOG_EOF` et un faux fragment JSON, gardées intactes ; - des arguments manquants. ### Mutations du script 14 mutations, une à la fois, avec restauration et comparaison des octets d'origine : | Mutation | Résultat | |---|---| | « exactement un » devient « au moins un » | rouge | | pas d'`exit` après un mauvais compte | rouge | | pas de contrôle du `.sig` vide | rouge | | pas de contrôle du `.sig` blanc | rouge | | pas de `@uri` | rouge | | la clé rpm sert le `.deb` | rouge | | la clé rpm porte la signature du `.deb` | rouge | | clé rpm retirée | rouge | | clé deb retirée | rouge | | le repli sert le `.rpm` | rouge | | notes passées par `--arg "$(cat …)"` | rouge | | pas de contrôle du nombre d'arguments | rouge | | `*.exe` au lieu de `*-setup.exe` | rouge (survivait au premier passage, test ajouté) | | `set -euo pipefail` retiré | survit, par construction : le test et `release.yml` passent `-e -o pipefail` à l'invocation | ### Générateur sur la vraie v0.17.1 J'ai pris des bundles factices aux noms de la v0.17.1 et les **vrais** `.sig` de la release. Les quatre URL générées sont égales aux `browser_download_url` de Forgejo, et les notes comme les signatures sont identiques au `latest.json` publié. ### `release.yml` : le texte exact des étapes, sous le shell du runner PyYAML extrait le `run:`, qui tourne sous `bash --noprofile --norc -e -o pipefail` dans un espace de travail jetable. `GITHUB_OUTPUT` est relu avec la logique de `parseEnvFile` d'act. Résultats identiques sous jq 1.6 et 1.7 ; sortie sous jq 1.6 : ``` == A. Extract changelog (section holds a CHANGELOG_EOF line) == [origin/main] rc=0 first line='notes<<CHANGELOG_EOF' parse_error=None [origin/main] notes intact: False | notes='- Text that the old delimiter turns into the notes.' [this PR] rc=0 first line='notes<<CHANGELOG_EOF_72800362365cfa673a45f0f02710f498' parse_error=None [this PR] notes intact: True | notes='### Fixed\n\n- A "quoted" word, a back\\slash and C:\\Users\\path (#320).\nCHANGELOG_EOF\nnotes<<CHANGELOG_EOF\n- Text that the old delimiter turns into the notes.' == B. Generate latest.json == rc=0 build-latest-json: .deb <- Simpl Resultat_0.18.0_amd64.deb build-latest-json: .rpm <- Simpl Resultat-0.18.0-1.x86_64.rpm build-latest-json: -setup.exe <- Simpl Resultat_0.18.0_x64-setup.exe keys: ['linux-x86_64', 'linux-x86_64-deb', 'linux-x86_64-rpm', 'windows-x86_64'] notes in latest.json == changelog section: True == C. Publish latest.json to package registry (stub curl) == nominal rc=0 curl calls=['DELETE', 'PUT'] linux-x86_64-rpm removed rc=1 curl calls=none | ERROR: release-assets/latest.json failed its checks, registry left as is: linux-x86_64 removed rc=1 curl calls=none | ERROR: … extra key darwin-aarch64 rc=1 curl calls=none | ERROR: … rpm key -> .deb URL rc=1 curl calls=none | ERROR: … windows key -> .exe.sig URL rc=1 curl calls=none | ERROR: … deb key -> .rpm URL rc=1 curl calls=none | ERROR: … empty rpm signature rc=1 curl calls=none | ERROR: … signature not a string rc=1 curl calls=none | ERROR: … version 0.17.1 (stale file) rc=1 curl calls=none | ERROR: … url missing on a key rc=1 curl calls=none | ERROR: … platforms missing rc=1 curl calls=none | ERROR: … malformed JSON rc=1 curl calls=none | ERROR: … signatures of deb and rpm swapped rc=0 curl calls=['DELETE', 'PUT'] ``` En A, l'étape de `main` laisse une ligne `CHANGELOG_EOF` du CHANGELOG remplacer les notes par le texte qui la suit, sans erreur. En C, la dernière ligne est voulue : l'assertion ne contrôle pas l'appariement des signatures (voir « Pour relire en priorité »). ### `verify-release.sh`, vert et rouge Tag v0.17.1, dont la release porte bien les 7 assets. Le fichier vert est celui du générateur ci-dessus, fait avec les vraies signatures de la v0.17.1 : ``` === latest.json (fichier local …, pas l'asset de la release) === version=0.17.1 OK | pub_date=2026-10-11T00:38:06Z notes: 5337 caractères — début: '### Added\n\n- A getting-started banner …' linux-x86_64: url=Simpl Resultat_0.17.1_amd64.deb | extension .deb OK | signature OK (424 car.) | file: OK | url contient version=oui linux-x86_64-deb: url=Simpl Resultat_0.17.1_amd64.deb | extension .deb OK | signature OK (424 car.) | file: OK | url contient version=oui linux-x86_64-rpm: url=Simpl Resultat-0.17.1-1.x86_64.rpm | extension .rpm OK | signature OK (428 car.) | file: OK | url contient version=oui windows-x86_64: url=Simpl Resultat_0.17.1_x64-setup.exe | extension .exe OK | signature OK (428 car.) | file: OK | url contient version=oui VERDICT : OK (exit 0) ``` Cas rouges : chacun échoue sur son propre contrôle, ligne en défaut seule. ``` v0.17.1 publiée, sans 2e argument : linux-x86_64-deb: MANQUANT / linux-x86_64-rpm: MANQUANT VERDICT : ÉCHEC (exit 1) clé linux-x86_64-rpm retirée : linux-x86_64-rpm: MANQUANT VERDICT : ÉCHEC (exit 1) clé linux-x86_64-deb retirée : linux-x86_64-deb: MANQUANT VERDICT : ÉCHEC (exit 1) clé rpm = entrée du .deb : linux-x86_64-rpm: url=Simpl Resultat_0.17.1_amd64.deb | extension .rpm MAUVAISE EXTENSION | signature OK (424 car.) | file: OK ÉCHEC (exit 1) signatures .deb/.rpm échangées : linux-x86_64-deb: … | file: DIFFÉRENT ('Simpl Resultat-0.17.1-1.x86_64.rpm') linux-x86_64-rpm: … | file: DIFFÉRENT ('Simpl Resultat_0.17.1_amd64.deb') ÉCHEC (exit 1) signature .rpm vide : linux-x86_64-rpm: … | signature VIDE | file: DIFFÉRENT (None) VERDICT : ÉCHEC (exit 1) signature non base64 : windows-x86_64: … | signature OK (15 car.) | file: DIFFÉRENT (None) ÉCHEC (exit 1) URL doublement encodée (%2520) : linux-x86_64-rpm: url=Simpl%20Resultat-0.17.1-1.x86_64.rpm | … | file: DIFFÉRENT ('Simpl Resultat-0.17.1-1.x86_64.rpm') ÉCHEC (exit 1) version 0.17.0 : version=0.17.0 MAUVAISE VERSION VERDICT : ÉCHEC (exit 1) clé inattendue darwin-aarch64 : darwin-aarch64: clé INATTENDUE VERDICT : ÉCHEC (exit 1) release absente (v0.18.0), latest.json factice entièrement vert : PAS DE RELEASE … VERDICT : ÉCHEC (exit 1) ``` Une variante verte supplémentaire, où toutes les URL portent l'espace littérale (la forme publiée jusqu'ici), donne `VERDICT : OK` : le contrôle `file:` compare le nom décodé. Mutations de `verify-release.sh` (copie hors du dépôt), contre ces 13 cas : | Mutation | Résultat | |---|---| | bloc `latest.json` sans `\|\| STATUS=1` | détectée (10 cas) | | `exit 0` en fin de script | détectée (10 cas) | | pas de contrôle d'extension | détectée (clé rpm = entrée du `.deb`) | | pas de contrôle `file:` | détectée (3 cas) | | `file:` comparé au segment brut, non décodé | détectée (cas vert en `%20`) | | clé rpm retirée des attendues | détectée (3 cas) | | clé inattendue tolérée | détectée | | version différente tolérée | détectée | | bloc release sans `\|\| STATUS=1` | détectée (release absente, `VERDICT : OK` sinon) | | contrôle « signature non vide » retiré | survit, mutation équivalente : une signature vide fait déjà échouer `file:` | ## Ce qui ne se prouve qu'au prochain tag - `release.yml` ne tourne qu'au tag. Dans le journal du run, l'étape « Generate latest.json » doit afficher trois lignes `build-latest-json: <type> <- <paquet>`, puis le JSON à quatre clés, et aucune ligne `ERROR: release-assets/latest.json failed its checks`. - `verify-release.sh vX.Y.Z` doit finir en `VERDICT : OK` sur les quatre clés, et l'endpoint du registre (`tauri.conf.json`) doit servir le même fichier. - Un cycle réel depuis une installation `.rpm` (§3 de `docs/qa-update-cycle.md`), dès qu'une machine Fedora ou openSUSE est disponible ; d'ici là, « non déroulé » avec la raison dans la trace de la release. Le diff touche `release.yml` : l'étape 10 de `/release` s'applique donc aussi (deb et Windows). - Tout est consigné dans `STATE.md`, sous « Vérifications dues à la prochaine release ». ## Pour relire en priorité 1. **Le repli `linux-x86_64` n'est pas inoffensif, et il semble retirable.** Gardé comme décidé, avec sa raison, ses lecteurs et sa condition de retrait écrits dans l'en-tête du script et dans `qa-update-cycle.md` (second commit, `5ce136f`). Mesures : - Les 68 tags verrouillent `@tauri-apps/cli` 2.10.0 et `tauri-utils` 2.8.2, et l'updater existe depuis la v0.2.1 (plugin 2.10.0). Aucune version publiée n'est donc antérieure au marqueur : la condition de retrait semble remplie. - Ses lecteurs connus ne savent pas installer le `.deb` qu'il sert : - un binaire sans marqueur, lancé hors de son paquet ; - les **AppImage v0.4.2 à v0.4.5**. Elles sont empaquetées jusqu'à la v0.7.3 selon `tauri.conf.json`, publiées sur Forgejo pour les v0.4.2 et v0.4.3 et sur le miroir GitHub pour les v0.4.3 à v0.4.5, et leur updater lit le registre Forgejo depuis la v0.4.2. Elles cherchent `linux-x86_64-appimage`, absente, puis `linux-x86_64`. - Pour ces deux lecteurs, `install_inner` envoie le téléchargement vers `install_appimage`, avec `executable_path = $APPIMAGE` pour une AppImage. L'updater écrit alors les octets du `.deb` à la place du fichier lancé, et sa copie de secours vit dans un `TempDir` supprimé au retour : l'installation est détruite. C'est l'état depuis la v0.4.2, que cette PR ne change pas. Sans la clé, ces installations auraient une erreur à la vérification (`TargetsNotFound`, car `check()` appelle `get_urls` avant de comparer les versions) au lieu d'une mise à jour destructrice. Le retrait de la clé est à trancher par Max, hors de cette PR. 2. **L'assertion placée avant le `DELETE` ne contrôle pas l'appariement des signatures** : des signatures échangées passent (cas C ci-dessus). C'est le périmètre du plan (version, clés, signature non vide, extension). L'échange est couvert par le test du script avant le merge (mutation « clé rpm avec la signature du `.deb` ») et par le contrôle `file:` de `verify-release.sh` après publication. Je n'ai pas mis de `@base64d` dans l'assertion : c'est hors plan, et ce code ne s'exercerait qu'au tag. 3. **`verify-release.sh` bloque aussi sur ses contrôles d'avant** : assets manquants, release absente, version différente, version absente de l'URL. Le skill disait déjà que ces lignes bloquent l'étape 10. `pub_date` et la longueur des notes restent informatifs. 4. **Les notes passent par `/tmp/release-notes.md`** (`printf '%s'`) et non plus par `--arg`. Les octets sont les mêmes que dans le `latest.json` publié (comparaison faite sur la v0.17.1) ; `/tmp` suit le motif des `/tmp/*_response.json` du même workflow. 5. **`jq` est ajouté à l'étape « Install Node.js 24 » de check-frontend**, dont le nom ne change pas. À lire dans le journal de la CI de cette PR : `jq-1.6`, et `scripts/build-latest-json.test.mjs` parmi les fichiers de test (11 tests, 110 fichiers au total). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
maximus added 1 commit 2026-10-11 00:51:36 +00:00
fix: give rpm installs their own key in latest.json
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m54s
da7129d529
latest.json only had linux-x86_64, built from the .deb. An .rpm install
looks up linux-x86_64-rpm first, then fell back on linux-x86_64,
downloaded the .deb and refused it (InvalidUpdaterFormat).

- scripts/build-latest-json.sh writes linux-x86_64-deb,
  linux-x86_64-rpm, linux-x86_64 (the .deb, as a fallback) and
  windows-x86_64 from the release assets. It fails unless it finds
  exactly one .deb, one .rpm and one -setup.exe, each with a non-empty
  .sig. The JSON comes from jq alone, notes read with --rawfile, file
  names encoded with @uri like the upload step.
- scripts/build-latest-json.test.mjs (vitest) runs it under
  bash -e -o pipefail on a temporary assets dir; check-frontend
  installs jq.
- release.yml calls the script, writes the notes output under a random
  delimiter, checks the manifest with jq before the registry DELETE,
  and names the .rpm instead of an AppImage in the release notes.
- verify-release.sh requires the four keys, the extension of each URL
  and the minisign file: of each signature, and exits 1 on any failure.
- Docs: release skill, qa-update-cycle section 3 (rpm procedure, keys,
  reason and removal condition of the fallback), architecture, README,
  CHANGELOG, STATE.

Resolves #320

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
maximus added 1 commit 2026-10-11 00:55:07 +00:00
docs: name the readers of the linux-x86_64 fallback after self-review
All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m57s
5ce136f45e
The fallback key is also read by the AppImage installs of v0.4.2 to
v0.4.5 (bundled until v0.7.3, published on Forgejo for v0.4.2-v0.4.3
and on the GitHub mirror for v0.4.3-v0.4.5, all reading the registry),
which look up linux-x86_64-appimage, absent, then linux-x86_64. Like a
marker-less binary, they cannot install the .deb it serves: the updater
writes its bytes over the running AppImage. State since v0.4.2, not
changed by this PR; the key stays as decided.

Also quote the error the update card really shows for a refused
package (invalid updater binary format).

Refs #320

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Auto-revue du worker /autopilot : APPROVE

Ceci est une auto-revue de l'auteur, pas une revue indépendante : seul un /pr-review de la session principale peut poser status:approved.

Tentative 1 : REQUEST_CHANGES sur mon propre texte, deux points corrigés dans 5ce136f (Refs #320) :

  • le §3 de qa-update-cycle.md citait un message d'erreur inventé (« format de mise à jour invalide »). La carte Mises à jour affiche le message brut du plugin, invalid updater binary format (UpdateCard.tsx:221, error.rs de tauri-plugin-updater 2.10.0) ;
  • les lecteurs du repli linux-x86_64 étaient incomplets. Les AppImage v0.4.2 à v0.4.5 le lisent aussi : leur tauri.conf.json vise le registre Forgejo, des assets AppImage ont été publiés sur Forgejo pour les v0.4.2 et v0.4.3 et sur le miroir GitHub pour les v0.4.3 à v0.4.5, et linux-x86_64-appimage est absente. Écrit dans l'en-tête du script, dans qa-update-cycle.md et dans le point 1 de « Pour relire en priorité ».

Tentative 2 : APPROVE. Diff relu en entier (.diff de la PR, 12 fichiers), chaque script ligne à ligne sous -e -o pipefail.

Critères de planification

Critère Preuve
Le script échoue sans exactement un .deb, un .rpm et un -setup.exe, chacun avec un .sig non vide 7 tests d'échec (absent ×3, deux .deb, .sig vide, blanc ou absent), sortie standard vide vérifiée ; mutations M1 à M4 et M12 rouges
JSON par jq seul, --rawfile pour les notes, noms en @uri test des notes (", \, CHANGELOG_EOF, faux JSON), mutations M5 (sans @uri) et M11 (--arg "$(cat)") rouges ; URL égales aux browser_download_url de la v0.17.1
Quatre clés, bonnes extensions, signature de son propre asset test nominal en toStrictEqual à valeurs littérales ; mutations M6 à M10 et M13 rouges
Test vitest sous bash -e -o pipefail release.yml appelle le script exactement de la même façon ; 11 tests verts sous jq 1.7 et jq 1.6
check-frontend installe jq, le test passe dans la CI de la PR run 518 (da7129d) : Setting up jq (1.6-2.1ubuntu3.2), jq-1.6, ✓ scripts/build-latest-json.test.mjs (11 tests), 110 fichiers / 1915 tests, Job succeeded
Délimiteur aléatoire ; assertion jq avant le DELETE harnais sur le texte exact des étapes (PyYAML, shell du runner) : les notes restent intactes là où main les remplace ; 12 manifestes cassés arrêtent l'étape sans aucun appel curl, et le nominal fait DELETE puis PUT
verify-release.sh sort en 1 sur clé manquante, mauvaise extension ou file: différent 2 cas verts, 11 rouges (chacun sur son contrôle), sorties dans la description ; 10 mutations du script, 9 détectées, la dernière étant équivalente
Notes de release et README sans AppImage, rpm nommé ; CHANGELOG FR/EN diff release.yml (ligne « Installation ») et README.md ; entrées Fixed / Corrigé (#320)

Caveats de revue

Tous traités :

  • jq dans check-frontend ;
  • échec fermé ;
  • signature lue par asset ;
  • notes traitées comme des données ;
  • validation avant le DELETE ;
  • contrôle minisign dans verify-release.sh ;
  • raison et condition de retrait du repli ;
  • test en .mjs ;
  • étape 9 du skill : journal de génération et verify-release.sh bloquant.

Sécurité, justesse, conventions

  • Sécurité : aucun secret ajouté ; les lignes ajoutées ont été scannées (dépôt public), sans résultat.
  • Justesse : chaque garde a été prouvée par mutation.
  • Conventions : commits en anglais (Resolves #320, puis Refs #320) ; commentaires du code en anglais ; docs en français ; STATE.md limité à une sous-puce.

Ce que la CI et le prochain tag doivent encore montrer

  • Le run 519 (5ce136f : doc et commentaire du script seulement) était encore en attente à l'écriture de ce commentaire.
  • Au prochain tag :
    • dans l'étape « Generate latest.json », trois lignes build-latest-json: <type> <- <paquet> et le JSON à quatre clés, sans ERROR: release-assets/latest.json failed its checks ;
    • verify-release.sh vX.Y.Z en VERDICT : OK ;
    • le cycle rpm réel du §3 dès qu'une machine Fedora ou openSUSE est disponible ;
    • l'étape 10, puisque le diff touche release.yml.

À relire en priorité

  1. Le repli linux-x86_64. Ses seuls lecteurs connus (binaire sans marqueur, AppImage v0.4.2 à v0.4.5) ne savent pas installer le .deb, et l'updater écrirait ses octets à la place du fichier lancé. C'est l'état depuis la v0.4.2, que cette PR garde comme décidé. La condition de retrait semble déjà remplie : les 68 tags ont tous @tauri-apps/cli 2.10.0. À trancher par Max.
  2. L'assertion avant le DELETE ne contrôle pas l'appariement des signatures, comme le prévoit le plan. L'échange de signatures est couvert par le test du script et par verify-release.sh.
  3. verify-release.sh devient bloquant sur tous ses contrôles, y compris ceux d'avant #320.
## Auto-revue du worker /autopilot : APPROVE Ceci est une auto-revue de l'auteur, pas une revue indépendante : seul un `/pr-review` de la session principale peut poser `status:approved`. **Tentative 1 : REQUEST_CHANGES sur mon propre texte, deux points corrigés dans `5ce136f` (`Refs #320`) :** - le §3 de `qa-update-cycle.md` citait un message d'erreur inventé (« format de mise à jour invalide »). La carte Mises à jour affiche le message brut du plugin, `invalid updater binary format` (`UpdateCard.tsx:221`, `error.rs` de tauri-plugin-updater 2.10.0) ; - les lecteurs du repli `linux-x86_64` étaient incomplets. Les AppImage v0.4.2 à v0.4.5 le lisent aussi : leur `tauri.conf.json` vise le registre Forgejo, des assets AppImage ont été publiés sur Forgejo pour les v0.4.2 et v0.4.3 et sur le miroir GitHub pour les v0.4.3 à v0.4.5, et `linux-x86_64-appimage` est absente. Écrit dans l'en-tête du script, dans `qa-update-cycle.md` et dans le point 1 de « Pour relire en priorité ». **Tentative 2 : APPROVE.** Diff relu en entier (`.diff` de la PR, 12 fichiers), chaque script ligne à ligne sous `-e -o pipefail`. ### Critères de planification | Critère | Preuve | |---|---| | Le script échoue sans exactement un `.deb`, un `.rpm` et un `-setup.exe`, chacun avec un `.sig` non vide | 7 tests d'échec (absent ×3, deux `.deb`, `.sig` vide, blanc ou absent), sortie standard vide vérifiée ; mutations M1 à M4 et M12 rouges | | JSON par `jq` seul, `--rawfile` pour les notes, noms en `@uri` | test des notes (`"`, `\`, `CHANGELOG_EOF`, faux JSON), mutations M5 (sans `@uri`) et M11 (`--arg "$(cat)"`) rouges ; URL égales aux `browser_download_url` de la v0.17.1 | | Quatre clés, bonnes extensions, signature de son propre asset | test nominal en `toStrictEqual` à valeurs littérales ; mutations M6 à M10 et M13 rouges | | Test vitest sous `bash -e -o pipefail` | `release.yml` appelle le script exactement de la même façon ; 11 tests verts sous jq 1.7 et jq 1.6 | | `check-frontend` installe `jq`, le test passe dans la CI de la PR | run 518 (`da7129d`) : `Setting up jq (1.6-2.1ubuntu3.2)`, `jq-1.6`, `✓ scripts/build-latest-json.test.mjs (11 tests)`, 110 fichiers / 1915 tests, `Job succeeded` | | Délimiteur aléatoire ; assertion `jq` avant le `DELETE` | harnais sur le texte exact des étapes (PyYAML, shell du runner) : les notes restent intactes là où `main` les remplace ; 12 manifestes cassés arrêtent l'étape sans aucun appel `curl`, et le nominal fait `DELETE` puis `PUT` | | `verify-release.sh` sort en 1 sur clé manquante, mauvaise extension ou `file:` différent | 2 cas verts, 11 rouges (chacun sur son contrôle), sorties dans la description ; 10 mutations du script, 9 détectées, la dernière étant équivalente | | Notes de release et README sans AppImage, rpm nommé ; CHANGELOG FR/EN | diff `release.yml` (ligne « Installation ») et `README.md` ; entrées Fixed / Corrigé `(#320)` | ### Caveats de revue Tous traités : - `jq` dans check-frontend ; - échec fermé ; - signature lue par asset ; - notes traitées comme des données ; - validation avant le `DELETE` ; - contrôle minisign dans `verify-release.sh` ; - raison et condition de retrait du repli ; - test en `.mjs` ; - étape 9 du skill : journal de génération et `verify-release.sh` bloquant. ### Sécurité, justesse, conventions - **Sécurité** : aucun secret ajouté ; les lignes ajoutées ont été scannées (dépôt public), sans résultat. - **Justesse** : chaque garde a été prouvée par mutation. - **Conventions** : commits en anglais (`Resolves #320`, puis `Refs #320`) ; commentaires du code en anglais ; docs en français ; `STATE.md` limité à une sous-puce. ### Ce que la CI et le prochain tag doivent encore montrer - Le run 519 (`5ce136f` : doc et commentaire du script seulement) était encore en attente à l'écriture de ce commentaire. - Au prochain tag : - dans l'étape « Generate latest.json », trois lignes `build-latest-json: <type> <- <paquet>` et le JSON à quatre clés, sans `ERROR: release-assets/latest.json failed its checks` ; - `verify-release.sh vX.Y.Z` en `VERDICT : OK` ; - le cycle rpm réel du §3 dès qu'une machine Fedora ou openSUSE est disponible ; - l'étape 10, puisque le diff touche `release.yml`. ### À relire en priorité 1. **Le repli `linux-x86_64`.** Ses seuls lecteurs connus (binaire sans marqueur, AppImage v0.4.2 à v0.4.5) ne savent pas installer le `.deb`, et l'updater écrirait ses octets à la place du fichier lancé. C'est l'état depuis la v0.4.2, que cette PR garde comme décidé. La condition de retrait semble déjà remplie : les 68 tags ont tous `@tauri-apps/cli` 2.10.0. À trancher par Max. 2. **L'assertion avant le `DELETE` ne contrôle pas l'appariement des signatures**, comme le prévoit le plan. L'échange de signatures est couvert par le test du script et par `verify-release.sh`. 3. **`verify-release.sh` devient bloquant sur tous ses contrôles**, y compris ceux d'avant #320.
maximus added the
autopilot:pending-human
label 2026-10-11 00:56:33 +00:00
Author
Owner

CI lue par la session principale du run autopilot (pas une revue)

Run Commit Résultat Lu dans le journal
518 da7129d vert v24.21.0, jq-1.6, ✓ scripts/build-latest-json.test.mjs (11 tests), 1915 tests (110 fichiers)
519 5ce136f (tête) vert, 1 min 57 s idem

Pas de run check-rust (rien sous src-tauri/), conforme.

Vérifié au passage, à l'appui du point « repli linux-x86_64 » du corps : Forgejo a publié des AppImage pour v0.4.1, v0.4.2 et v0.4.3, avec la cible appimage dans tauri.conf.json. L'endpoint de la v0.4.2 lit le latest.json de la dernière release (/releases/latest/download/latest.json), celui de la v0.4.5 le registre : les deux servent le même contenu, donc la clé de repli. Défaut antérieur à cette PR, qui ne le change pas. À trancher par Max à la revue : garder le repli (décision du plan) ou le retirer (une AppImage ne verrait alors plus de mise à jour, au lieu d'en recevoir une qui la casse).

**CI lue par la session principale du run autopilot (pas une revue)** | Run | Commit | Résultat | Lu dans le journal | |---|---|---|---| | 518 | `da7129d` | vert | `v24.21.0`, `jq-1.6`, `✓ scripts/build-latest-json.test.mjs (11 tests)`, 1915 tests (110 fichiers) | | 519 | `5ce136f` (tête) | vert, 1 min 57 s | idem | Pas de run `check-rust` (rien sous `src-tauri/`), conforme. Vérifié au passage, à l'appui du point « repli `linux-x86_64` » du corps : Forgejo a publié des AppImage pour v0.4.1, v0.4.2 et v0.4.3, avec la cible `appimage` dans `tauri.conf.json`. L'endpoint de la v0.4.2 lit le `latest.json` de la dernière release (`/releases/latest/download/latest.json`), celui de la v0.4.5 le registre : les deux servent le même contenu, donc la clé de repli. Défaut antérieur à cette PR, qui ne le change pas. À trancher par Max à la revue : garder le repli (décision du plan) ou le retirer (une AppImage ne verrait alors plus de mise à jour, au lieu d'en recevoir une qui la casse).
Author
Owner

Revue /pr-review : APPROVE

Résumé. Le correctif est juste et vérifié de bout en bout : une installation .rpm cherche bien linux-x86_64-rpm, le script échoue fermé, l'assertion placée avant le DELETE tient sous jq 1.6 comme sous 1.7, et verify-release.sh rougit sur les écarts qu'il annonce. Aucun bloquant. Cinq suggestions, dont une épingle de test.

Ce que j'ai rejoué (copie hors dépôt de 5ce136f, supprimée depuis)

  1. CI de la tête lue dans le journal (run 519, HEAD is now at 5ce136f) : jq-1.6 installé, ✓ scripts/build-latest-json.test.mjs (11 tests), 110 fichiers et 1915 tests verts, Job succeeded.
  2. Mécanisme de l'updater, lu dans les sources de tauri-plugin-updater 2.10.0 et tauri-utils 2.8.2 (versions du Cargo.lock). get_urls essaie {os}-{arch}-{installer} puis {os}-{arch}, et check() l'appelle avant de comparer les versions. install_rpm refuse ce qui n'est pas un rpm (InvalidUpdaterFormat, message invalid updater binary format), puis passe par la même cascade pkexec → zenity/kdialog → sudo que le .deb. L'app ne fixe aucune cible (Builder::new().build() dans lib.rs, check() sans option dans useUpdater.ts et ErrorPage.tsx), donc la recherche par installeur s'applique.
  3. Marqueur dans les paquets publiés. J'ai extrait les deux paquets de la v0.17.1 : le .rpm avec un petit lecteur d'en-têtes rpm (charge gzip + cpio), le .deb avec dpkg-deb -x. /usr/bin/simpl-result porte __TAURI_BUNDLE_TYPE_VAR_RPM dans le .rpm et __TAURI_BUNDLE_TYPE_VAR_DEB dans le .deb. Les 68 tags verrouillent @tauri-apps/cli 2.10.0 et tauri-utils 2.8.2, avec tauri-plugin-updater 2.10.0 depuis la v0.2.1. L'entrée CHANGELOG « Existing .rpm installs need no reinstall » tient donc.
  4. Générateur sur la vraie v0.17.1 (bundles factices aux vrais noms, vrais .sig, notes tirées du latest.json publié) : les quatre URL sont égales aux browser_download_url de Forgejo, et les notes comme les signatures sont identiques au latest.json publié. La forme %20 et la forme à espace littérale donnent le même href sous un parseur WHATWG (new URL de Node, même norme que le crate url) : la requête envoyée ne change pas.
  5. Mutations du relecteur sur build-latest-json.sh, une à la fois, octets d'origine vérifiés par sha256 après la série :
Mutation Tests rouges
shopt -s nullglob retiré 3 / 11
la clé Windows porte la signature du .rpm 2 / 11
linux-x86_64-deb sert l'URL du .rpm 1 / 11
pas de / entre l'URL de base et le nom 2 / 11
pub_date sans Z 1 / 11
[ -s ] affaibli en [ -e ] sur le .sig 1 / 11
-ne 1 devient -gt 1 (zéro paquet admis) 3 / 11
journal du paquet retenu écrit sur stdout 7 / 11
message de die écrit sur stdout 4 / 11
version figée à "0.17.1" au lieu de l'argument 0 / 11, survit
garde [ -n "$version" ] retirée 0 / 11, survit
  1. Assertion de release.yml, extraite du fichier et lancée par jq -e -f sous jq 1.6 (binaire officiel, sha256 af986793…4c44) et sous jq 1.7, avec des résultats identiques. La sortie nominale passe. Sont refusés : le tag v0.18.0, le latest.json publié de la v0.17.1 (deux clés), une URL rpm en .rpm.sig et le repli retiré. Les signatures échangées et une signature faite d'espaces passent, ce qui correspond au périmètre annoncé (point 2 du corps).
  2. verify-release.sh (copie, TMPDIR isolé), contre la release v0.17.1 :
    • latest.json publié → linux-x86_64-deb: MANQUANT, linux-x86_64-rpm: MANQUANT, VERDICT : ÉCHEC, exit 1 ;
    • sortie du générateur → VERDICT : OK, exit 0 ;
    • signatures .deb/.rpm échangées → file: DIFFÉRENT sur les deux clés, exit 1 ;
    • cas absent de la PR : la clé -deb avec la signature du .deb de la v0.17.0 → file: DIFFÉRENT ('Simpl Resultat_0.17.0_amd64.deb'), exit 1. Le contrôle file: attrape donc aussi une signature périmée du bon type, que l'assertion d'avant le DELETE laisse passer.

Points examinés sans grief

  • Fermeture : la timeline de #320 porte un pull_ref closes venu de #416 (Resolves #320 dans le corps et dans da7129d). #414 et #417 la citent sans la fermer.
  • Commits conventionnels (fix:, docs:), commentaires en anglais, docs en français, CHANGELOG FR/EN sous Fixed / Corrigé.
  • Lignes ajoutées scannées (dépôt public) : aucun secret ni donnée personnelle, et les signatures du test sont factices.
  • Délimiteur aléatoire, notes passées par fichier (--rawfile, hors de release-assets/) et encodage @uri identique à celui de l'étape d'envoi : rien à redire.
  • scripts/** n'est pas dans le paths-ignore de check-frontend : toute modification du script relance son test.
  • La phrase du skill « la release Forgejo reste créée sans mise à jour servie » tient. Le seul client qui lirait l'asset de la release plutôt que le registre est la v0.4.2 (…/releases/latest/download/latest.json), et cette route répond 404 sur cette instance Forgejo (relevé ce jour, avec simpl-resultat comme avec Simpl-Resultat).

Suggestions (non bloquantes)

  1. Épingler la version. Toutes les fixtures passent 0.17.1, la valeur qui figure aussi dans les noms de fichiers : un script qui ignore son argument de version reste vert, comme un script sans garde de version vide (les deux mutations survivantes ci-dessus). L'assertion .version == $version de release.yml couvre la première faute, mais seulement au tag, après la création de la release. Un cas avec 0.18.0 en argument et un cas d'argument vide suffisent. Épingle sur un code correct : merge possible en l'état, avec une issue de suivi.
  2. Faire exercer l'assertion par la CI. Elle ne tourne qu'au tag et répète la liste des quatre clés, déjà écrite dans le script et dans verify-release.sh. Placée dans scripts/check-latest-json.jq (jq -e --arg version … -f, que jq 1.6 accepte, vérifié) et appliquée par le test vitest à la sortie du script, elle passerait par check-frontend à chaque PR, et une liste désalignée se verrait avant le tag. Le gain grandit si le repli est retiré, puisque trois listes devront alors changer ensemble.
  3. Le repli linux-x86_64 (point 1 du corps), à trancher par Max. Mes relevés vont dans le sens du retrait. Chaque binaire empaqueté avec l'updater porte son marqueur (CLI 2.10.0 sur les 68 tags, marqueurs lus dans les deux paquets v0.17.1). Les lecteurs restants (AppImage, binaire sans marqueur comme un build depuis les sources) ne savent pas installer le .deb servi : sans la clé, ils auraient TargetsNotFound à la vérification, au lieu d'une mise à jour qui écrase le fichier lancé. La PR garde l'état d'avant pour eux, ce qui ne bloque pas.
  4. Lecteurs du repli : la v0.4.2 n'en est pas un. Son endpoint (…/releases/latest/download/latest.json) répond 404, elle ne reçoit aucun manifeste. Les AppImage publiées qui lisent la clé sont donc les v0.4.3 à v0.4.5, sur le registre. À corriger dans docs/qa-update-cycle.md (tableau et liste), dans l'en-tête du script et dans le corps de la PR. Le message de 5ce136f (« all reading the registry ») et le commentaire de CI 22161 (« les deux servent le même contenu ») font la même erreur.
  5. STATE.md : la puce « Nouveau bug ouvert : #320 … (« ne pas tester : cassé, pas seulement non vérifié ») » contredit le nouveau §3. À retirer ou réécrire à la synchro post-merge.

Actions faites

  • Copie hors dépôt (git archive de 5ce136f), paquets et signatures téléchargés, binaire jq 1.6 et journal du run 519 : supprimés (/tmp/pr416-*).
  • Effet de bord signalé : par le lien symbolique vers node_modules, vitest a mis à jour son cache de résultats node_modules/.vite/vitest/…/results.json dans le dépôt principal. Ce fichier est ignoré par git et ne sert qu'à ordonner les tests. L'arbre de travail du dépôt est propre (git status vide).
  • Label de #320 : status:review → status:approved.
## Revue `/pr-review` : APPROVE **Résumé.** Le correctif est juste et vérifié de bout en bout : une installation `.rpm` cherche bien `linux-x86_64-rpm`, le script échoue fermé, l'assertion placée avant le `DELETE` tient sous jq 1.6 comme sous 1.7, et `verify-release.sh` rougit sur les écarts qu'il annonce. Aucun bloquant. Cinq suggestions, dont une épingle de test. ### Ce que j'ai rejoué (copie hors dépôt de `5ce136f`, supprimée depuis) 1. **CI de la tête lue dans le journal** (run 519, `HEAD is now at 5ce136f`) : `jq-1.6` installé, `✓ scripts/build-latest-json.test.mjs (11 tests)`, 110 fichiers et 1915 tests verts, `Job succeeded`. 2. **Mécanisme de l'updater, lu dans les sources** de `tauri-plugin-updater` 2.10.0 et `tauri-utils` 2.8.2 (versions du `Cargo.lock`). `get_urls` essaie `{os}-{arch}-{installer}` puis `{os}-{arch}`, et `check()` l'appelle avant de comparer les versions. `install_rpm` refuse ce qui n'est pas un rpm (`InvalidUpdaterFormat`, message `invalid updater binary format`), puis passe par la même cascade `pkexec` → zenity/kdialog → `sudo` que le `.deb`. L'app ne fixe aucune cible (`Builder::new().build()` dans `lib.rs`, `check()` sans option dans `useUpdater.ts` et `ErrorPage.tsx`), donc la recherche par installeur s'applique. 3. **Marqueur dans les paquets publiés.** J'ai extrait les deux paquets de la v0.17.1 : le `.rpm` avec un petit lecteur d'en-têtes rpm (charge gzip + cpio), le `.deb` avec `dpkg-deb -x`. `/usr/bin/simpl-result` porte `__TAURI_BUNDLE_TYPE_VAR_RPM` dans le `.rpm` et `__TAURI_BUNDLE_TYPE_VAR_DEB` dans le `.deb`. Les 68 tags verrouillent `@tauri-apps/cli` 2.10.0 et `tauri-utils` 2.8.2, avec `tauri-plugin-updater` 2.10.0 depuis la v0.2.1. L'entrée CHANGELOG « Existing `.rpm` installs need no reinstall » tient donc. 4. **Générateur sur la vraie v0.17.1** (bundles factices aux vrais noms, vrais `.sig`, notes tirées du `latest.json` publié) : les quatre URL sont égales aux `browser_download_url` de Forgejo, et les notes comme les signatures sont identiques au `latest.json` publié. La forme `%20` et la forme à espace littérale donnent le même `href` sous un parseur WHATWG (`new URL` de Node, même norme que le crate `url`) : la requête envoyée ne change pas. 5. **Mutations du relecteur sur `build-latest-json.sh`**, une à la fois, octets d'origine vérifiés par sha256 après la série : | Mutation | Tests rouges | |---|---| | `shopt -s nullglob` retiré | 3 / 11 | | la clé Windows porte la signature du `.rpm` | 2 / 11 | | `linux-x86_64-deb` sert l'URL du `.rpm` | 1 / 11 | | pas de `/` entre l'URL de base et le nom | 2 / 11 | | `pub_date` sans `Z` | 1 / 11 | | `[ -s ]` affaibli en `[ -e ]` sur le `.sig` | 1 / 11 | | `-ne 1` devient `-gt 1` (zéro paquet admis) | 3 / 11 | | journal du paquet retenu écrit sur stdout | 7 / 11 | | message de `die` écrit sur stdout | 4 / 11 | | **version figée à `"0.17.1"` au lieu de l'argument** | **0 / 11, survit** | | **garde `[ -n "$version" ]` retirée** | **0 / 11, survit** | 6. **Assertion de `release.yml`**, extraite du fichier et lancée par `jq -e -f` sous **jq 1.6** (binaire officiel, sha256 `af986793…4c44`) et sous jq 1.7, avec des résultats identiques. La sortie nominale passe. Sont refusés : le tag v0.18.0, le `latest.json` publié de la v0.17.1 (deux clés), une URL rpm en `.rpm.sig` et le repli retiré. Les signatures échangées et une signature faite d'espaces passent, ce qui correspond au périmètre annoncé (point 2 du corps). 7. **`verify-release.sh`** (copie, `TMPDIR` isolé), contre la release v0.17.1 : - `latest.json` publié → `linux-x86_64-deb: MANQUANT`, `linux-x86_64-rpm: MANQUANT`, `VERDICT : ÉCHEC`, exit 1 ; - sortie du générateur → `VERDICT : OK`, exit 0 ; - signatures `.deb`/`.rpm` échangées → `file: DIFFÉRENT` sur les deux clés, exit 1 ; - cas absent de la PR : la clé `-deb` avec la signature du `.deb` de la **v0.17.0** → `file: DIFFÉRENT ('Simpl Resultat_0.17.0_amd64.deb')`, exit 1. Le contrôle `file:` attrape donc aussi une signature périmée du bon type, que l'assertion d'avant le `DELETE` laisse passer. ### Points examinés sans grief - Fermeture : la timeline de #320 porte un `pull_ref` `closes` venu de #416 (`Resolves #320` dans le corps et dans `da7129d`). #414 et #417 la citent sans la fermer. - Commits conventionnels (`fix:`, `docs:`), commentaires en anglais, docs en français, CHANGELOG FR/EN sous Fixed / Corrigé. - Lignes ajoutées scannées (dépôt public) : aucun secret ni donnée personnelle, et les signatures du test sont factices. - Délimiteur aléatoire, notes passées par fichier (`--rawfile`, hors de `release-assets/`) et encodage `@uri` identique à celui de l'étape d'envoi : rien à redire. - `scripts/**` n'est pas dans le `paths-ignore` de `check-frontend` : toute modification du script relance son test. - La phrase du skill « la release Forgejo reste créée sans mise à jour servie » tient. Le seul client qui lirait l'asset de la release plutôt que le registre est la v0.4.2 (`…/releases/latest/download/latest.json`), et cette route répond 404 sur cette instance Forgejo (relevé ce jour, avec `simpl-resultat` comme avec `Simpl-Resultat`). ### Suggestions (non bloquantes) 1. **Épingler la version.** Toutes les fixtures passent `0.17.1`, la valeur qui figure aussi dans les noms de fichiers : un script qui ignore son argument de version reste vert, comme un script sans garde de version vide (les deux mutations survivantes ci-dessus). L'assertion `.version == $version` de `release.yml` couvre la première faute, mais seulement au tag, après la création de la release. Un cas avec `0.18.0` en argument et un cas d'argument vide suffisent. Épingle sur un code correct : merge possible en l'état, avec une issue de suivi. 2. **Faire exercer l'assertion par la CI.** Elle ne tourne qu'au tag et répète la liste des quatre clés, déjà écrite dans le script et dans `verify-release.sh`. Placée dans `scripts/check-latest-json.jq` (`jq -e --arg version … -f`, que jq 1.6 accepte, vérifié) et appliquée par le test vitest à la sortie du script, elle passerait par `check-frontend` à chaque PR, et une liste désalignée se verrait avant le tag. Le gain grandit si le repli est retiré, puisque trois listes devront alors changer ensemble. 3. **Le repli `linux-x86_64` (point 1 du corps), à trancher par Max.** Mes relevés vont dans le sens du retrait. Chaque binaire empaqueté avec l'updater porte son marqueur (CLI 2.10.0 sur les 68 tags, marqueurs lus dans les deux paquets v0.17.1). Les lecteurs restants (AppImage, binaire sans marqueur comme un build depuis les sources) ne savent pas installer le `.deb` servi : sans la clé, ils auraient `TargetsNotFound` à la vérification, au lieu d'une mise à jour qui écrase le fichier lancé. La PR garde l'état d'avant pour eux, ce qui ne bloque pas. 4. **Lecteurs du repli : la v0.4.2 n'en est pas un.** Son endpoint (`…/releases/latest/download/latest.json`) répond 404, elle ne reçoit aucun manifeste. Les AppImage publiées qui lisent la clé sont donc les v0.4.3 à v0.4.5, sur le registre. À corriger dans `docs/qa-update-cycle.md` (tableau et liste), dans l'en-tête du script et dans le corps de la PR. Le message de `5ce136f` (« all reading the registry ») et le commentaire de CI 22161 (« les deux servent le même contenu ») font la même erreur. 5. **`STATE.md`** : la puce « Nouveau bug ouvert : **#320** … (« ne pas tester : cassé, pas seulement non vérifié ») » contredit le nouveau §3. À retirer ou réécrire à la synchro post-merge. ### Actions faites - Copie hors dépôt (`git archive` de `5ce136f`), paquets et signatures téléchargés, binaire jq 1.6 et journal du run 519 : supprimés (`/tmp/pr416-*`). - Effet de bord signalé : par le lien symbolique vers `node_modules`, vitest a mis à jour son cache de résultats `node_modules/.vite/vitest/…/results.json` dans le dépôt principal. Ce fichier est ignoré par git et ne sert qu'à ordonner les tests. L'arbre de travail du dépôt est propre (`git status` vide). - Label de #320 : `status:review` → `status:approved`.
Author
Owner

Correction de mon commentaire 22161 (session principale du run autopilot)

J'y écrivais que l'endpoint de la v0.4.2 « lit le latest.json de la dernière release ». C'est faux, comme le relève la revue 22437 : https://git.lacompagniemaximus.com/maximus/simpl-resultat/releases/latest/download/latest.json répond 404 (vérifié le 2026-10-11, avec et sans la casse Simpl-Resultat), alors que l'endpoint du registre (/api/packages/maximus/generic/simpl-resultat/latest/latest.json, celui de la v0.4.5 et suivantes) répond 200.

Les lecteurs de la clé de repli linux-x86_64 sont donc les AppImage v0.4.3 à v0.4.5, pas v0.4.2 (ni v0.4.1, qui lisait GitHub). La conclusion ne change pas : ces installations recevraient le .deb à la place de leur AppImage, et le choix entre garder et retirer le repli reste à trancher.

**Correction de mon commentaire 22161 (session principale du run autopilot)** J'y écrivais que l'endpoint de la v0.4.2 « lit le `latest.json` de la dernière release ». C'est faux, comme le relève la revue 22437 : `https://git.lacompagniemaximus.com/maximus/simpl-resultat/releases/latest/download/latest.json` répond **404** (vérifié le 2026-10-11, avec et sans la casse `Simpl-Resultat`), alors que l'endpoint du registre (`/api/packages/maximus/generic/simpl-resultat/latest/latest.json`, celui de la v0.4.5 et suivantes) répond 200. Les lecteurs de la clé de repli `linux-x86_64` sont donc les AppImage **v0.4.3 à v0.4.5**, pas v0.4.2 (ni v0.4.1, qui lisait GitHub). La conclusion ne change pas : ces installations recevraient le `.deb` à la place de leur AppImage, et le choix entre garder et retirer le repli reste à trancher.
maximus changed target branch from issue-345-windows-sys-guard to main 2026-10-12 19:32:38 +00:00
maximus merged commit 5ce136f45e into main 2026-10-12 19:32:43 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/Simpl-Resultat#416
No description provided.