fix: give rpm installs their own key in latest.json #416
No reviewers
Labels
No labels
autopilot:pending-human
source:analyste
source:defenseur
source:human
source:medic
status:approved
status:blocked
status:in-progress
status:needs-clarification
status:needs-fix
status:ready
status:review
status:triage
type:bug
type:feature
type:infra
type:refactor
type:schema
type:security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: maximus/Simpl-Resultat#416
Loading…
Reference in a new issue
No description provided.
Delete branch "issue-320-latest-json-keys"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Resolves #320
Generated autonomously by /autopilot run of 2026-10-10
Ce qui change pour l'utilisateur
.rpm(Fedora, openSUSE) reçoivent les mises à jour automatiques. Jusqu'icilatest.jsonn'avait qu'une clélinux-x86_64, construite depuis le.deb: une installation rpm cherchaitlinux-x86_64-rpm, retombait surlinux-x86_64, téléchargeait le.debpuis le refusait (InvalidUpdaterFormat)..rpmexistantes n'ont rien à réinstaller : le binaire du.rpmv0.17.1 porte le marqueur__TAURI_BUNDLE_TYPE_VAR_RPM(vérifié en analyse), il liralinux-x86_64-rpmdès la prochaine release..debet Windows : même fichier, même signature. Une installation.deblit désormaislinux-x86_64-deb, qui pointe sur le même.deb..rpmau 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.rpmet un-setup.exe, chacun avec un"$asset.sig"non vide. Le JSON vient dejqseul (--arg, et--rawfilepour 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 dansdocs/qa-update-cycle.md) etwindows-x86_64.scripts/build-latest-json.test.mjs(nouveau, vitest). Il est en.mjsparce quetsconfigne couvre quesrc. Il lance le script sousbash -e -o pipefailsur 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: installejqet affichejq --version.release.yml:GITHUB_OUTPUTaléatoire,CHANGELOG_EOF_suivi de 32 caractères hexadécimaux tirés de/dev/urandomparod(coreutils, présent dans le conteneur) ;/tmp/release-notes.mdavecprintf '%s', donc les mêmes octets qu'avant, sans saut de ligne final, et hors derelease-assets/, dont chaque fichier est envoyé. Il appelle ensuite le script exactement comme le test. La sortie resterelease-assets/latest.json;.deb(Debian, Ubuntu) ou.rpm(Fedora, openSUSE) » ;jq -een tête d'étape, avant leDELETE: 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 lefile: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 blocspython3propagent leur code et la dernière ligne donneVERDICT : OKouVERDICT : ÉCHEC. Un second argument optionnel remplace l'asset par unlatest.jsonlocal, ce qui sert aux preuves ci-dessous..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/,jqdans check-frontend, les clés et l'assertion dansrelease.yml;README.md;STATE.md: sous-puce sous « Vérifications dues à la prochaine release ».@uri, l'URL delatest.jsonpasse 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 :url2.5.8, la version duCargo.lockde l'app, parse les deux formes en un mêmeUrl(programme jetable, hors ligne). L'URL encodée est exactement lebrowser_download_urlde Forgejo, vérifié sur les quatre clés (voir Preuves).Preuves
Tests
npm run buildvert ;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.04donc de la CI (binaire officieljq-linux641.6, sha256af986793…4c44). Cas couverts :%20, chaque clé avec la signature de son propre asset ;.deb,.rpmou-setup.exeabsent ;.deb;.sigvide,.sigfait de sauts de ligne,.sigabsent ;.exeparasite que Windows ne reçoit pas ;",\, une ligneCHANGELOG_EOFet un faux fragment JSON, gardées intactes ;Mutations du script
14 mutations, une à la fois, avec restauration et comparaison des octets d'origine :
exitaprès un mauvais compte.sigvide.sigblanc@uri.deb.deb.rpm--arg "$(cat …)"*.exeau lieu de*-setup.exeset -euo pipefailretirérelease.ymlpassent-e -o pipefailà l'invocationGénérateur sur la vraie v0.17.1
J'ai pris des bundles factices aux noms de la v0.17.1 et les vrais
.sigde la release. Les quatre URL générées sont égales auxbrowser_download_urlde Forgejo, et les notes comme les signatures sont identiques aulatest.jsonpublié.release.yml: le texte exact des étapes, sous le shell du runnerPyYAML extrait le
run:, qui tourne sousbash --noprofile --norc -e -o pipefaildans un espace de travail jetable.GITHUB_OUTPUTest relu avec la logique deparseEnvFiled'act. Résultats identiques sous jq 1.6 et 1.7 ; sortie sous jq 1.6 :En A, l'étape de
mainlaisse une ligneCHANGELOG_EOFdu 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 rougeTag 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 :
Cas rouges : chacun échoue sur son propre contrôle, ligne en défaut seule.
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ôlefile:compare le nom décodé.Mutations de
verify-release.sh(copie hors du dépôt), contre ces 13 cas :latest.jsonsans|| STATUS=1exit 0en fin de script.deb)file:file:comparé au segment brut, non décodé%20)|| STATUS=1VERDICT : OKsinon)file:Ce qui ne se prouve qu'au prochain tag
release.ymlne tourne qu'au tag. Dans le journal du run, l'étape « Generate latest.json » doit afficher trois lignesbuild-latest-json: <type> <- <paquet>, puis le JSON à quatre clés, et aucune ligneERROR: release-assets/latest.json failed its checks.verify-release.sh vX.Y.Zdoit finir enVERDICT : OKsur les quatre clés, et l'endpoint du registre (tauri.conf.json) doit servir le même fichier..rpm(§3 dedocs/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 toucherelease.yml: l'étape 10 de/releases'applique donc aussi (deb et Windows).STATE.md, sous « Vérifications dues à la prochaine release ».Pour relire en priorité
Le repli
linux-x86_64n'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 dansqa-update-cycle.md(second commit,5ce136f). Mesures :@tauri-apps/cli2.10.0 ettauri-utils2.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..debqu'il sert :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 cherchentlinux-x86_64-appimage, absente, puislinux-x86_64.install_innerenvoie le téléchargement versinstall_appimage, avecexecutable_path = $APPIMAGEpour une AppImage. L'updater écrit alors les octets du.debà la place du fichier lancé, et sa copie de secours vit dans unTempDirsupprimé 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, carcheck()appelleget_urlsavant de comparer les versions) au lieu d'une mise à jour destructrice.Le retrait de la clé est à trancher par Max, hors de cette PR.
L'assertion placée avant le
DELETEne 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ôlefile:deverify-release.shaprès publication. Je n'ai pas mis de@base64ddans l'assertion : c'est hors plan, et ce code ne s'exercerait qu'au tag.verify-release.shbloque 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_dateet la longueur des notes restent informatifs.Les notes passent par
/tmp/release-notes.md(printf '%s') et non plus par--arg. Les octets sont les mêmes que dans lelatest.jsonpublié (comparaison faite sur la v0.17.1) ;/tmpsuit le motif des/tmp/*_response.jsondu même workflow.jqest 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, etscripts/build-latest-json.test.mjsparmi les fichiers de test (11 tests, 110 fichiers au total).🤖 Generated with Claude Code
Auto-revue du worker /autopilot : APPROVE
Ceci est une auto-revue de l'auteur, pas une revue indépendante : seul un
/pr-reviewde la session principale peut poserstatus:approved.Tentative 1 : REQUEST_CHANGES sur mon propre texte, deux points corrigés dans
5ce136f(Refs #320) :qa-update-cycle.mdcitait 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.rsde tauri-plugin-updater 2.10.0) ;linux-x86_64étaient incomplets. Les AppImage v0.4.2 à v0.4.5 le lisent aussi : leurtauri.conf.jsonvise 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, etlinux-x86_64-appimageest absente. Écrit dans l'en-tête du script, dansqa-update-cycle.mdet dans le point 1 de « Pour relire en priorité ».Tentative 2 : APPROVE. Diff relu en entier (
.diffde la PR, 12 fichiers), chaque script ligne à ligne sous-e -o pipefail.Critères de planification
.deb, un.rpmet un-setup.exe, chacun avec un.signon vide.deb,.sigvide, blanc ou absent), sortie standard vide vérifiée ; mutations M1 à M4 et M12 rougesjqseul,--rawfilepour les notes, noms en@uri",\,CHANGELOG_EOF, faux JSON), mutations M5 (sans@uri) et M11 (--arg "$(cat)") rouges ; URL égales auxbrowser_download_urlde la v0.17.1toStrictEqualà valeurs littérales ; mutations M6 à M10 et M13 rougesbash -e -o pipefailrelease.ymlappelle le script exactement de la même façon ; 11 tests verts sous jq 1.7 et jq 1.6check-frontendinstallejq, le test passe dans la CI de la PRda7129d) :Setting up jq (1.6-2.1ubuntu3.2),jq-1.6,✓ scripts/build-latest-json.test.mjs (11 tests), 110 fichiers / 1915 tests,Job succeededjqavant leDELETEmainles remplace ; 12 manifestes cassés arrêtent l'étape sans aucun appelcurl, et le nominal faitDELETEpuisPUTverify-release.shsort en 1 sur clé manquante, mauvaise extension oufile:différentrelease.yml(ligne « Installation ») etREADME.md; entrées Fixed / Corrigé(#320)Caveats de revue
Tous traités :
jqdans check-frontend ;DELETE;verify-release.sh;.mjs;verify-release.shbloquant.Sécurité, justesse, conventions
Resolves #320, puisRefs #320) ; commentaires du code en anglais ; docs en français ;STATE.mdlimité à une sous-puce.Ce que la CI et le prochain tag doivent encore montrer
5ce136f: doc et commentaire du script seulement) était encore en attente à l'écriture de ce commentaire.build-latest-json: <type> <- <paquet>et le JSON à quatre clés, sansERROR: release-assets/latest.json failed its checks;verify-release.sh vX.Y.ZenVERDICT : OK;release.yml.À relire en priorité
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/cli2.10.0. À trancher par Max.DELETEne 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 parverify-release.sh.verify-release.shdevient bloquant sur tous ses contrôles, y compris ceux d'avant #320.CI lue par la session principale du run autopilot (pas une revue)
da7129dv24.21.0,jq-1.6,✓ scripts/build-latest-json.test.mjs (11 tests), 1915 tests (110 fichiers)5ce136f(tête)Pas de run
check-rust(rien soussrc-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 cibleappimagedanstauri.conf.json. L'endpoint de la v0.4.2 lit lelatest.jsonde 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).Revue
/pr-review: APPROVERésumé. Le correctif est juste et vérifié de bout en bout : une installation
.rpmcherche bienlinux-x86_64-rpm, le script échoue fermé, l'assertion placée avant leDELETEtient sous jq 1.6 comme sous 1.7, etverify-release.shrougit 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)HEAD is now at 5ce136f) :jq-1.6installé,✓ scripts/build-latest-json.test.mjs (11 tests), 110 fichiers et 1915 tests verts,Job succeeded.tauri-plugin-updater2.10.0 ettauri-utils2.8.2 (versions duCargo.lock).get_urlsessaie{os}-{arch}-{installer}puis{os}-{arch}, etcheck()l'appelle avant de comparer les versions.install_rpmrefuse ce qui n'est pas un rpm (InvalidUpdaterFormat, messageinvalid updater binary format), puis passe par la même cascadepkexec→ zenity/kdialog →sudoque le.deb. L'app ne fixe aucune cible (Builder::new().build()danslib.rs,check()sans option dansuseUpdater.tsetErrorPage.tsx), donc la recherche par installeur s'applique..rpmavec un petit lecteur d'en-têtes rpm (charge gzip + cpio), le.debavecdpkg-deb -x./usr/bin/simpl-resultporte__TAURI_BUNDLE_TYPE_VAR_RPMdans le.rpmet__TAURI_BUNDLE_TYPE_VAR_DEBdans le.deb. Les 68 tags verrouillent@tauri-apps/cli2.10.0 ettauri-utils2.8.2, avectauri-plugin-updater2.10.0 depuis la v0.2.1. L'entrée CHANGELOG « Existing.rpminstalls need no reinstall » tient donc..sig, notes tirées dulatest.jsonpublié) : les quatre URL sont égales auxbrowser_download_urlde Forgejo, et les notes comme les signatures sont identiques aulatest.jsonpublié. La forme%20et la forme à espace littérale donnent le mêmehrefsous un parseur WHATWG (new URLde Node, même norme que le crateurl) : la requête envoyée ne change pas.build-latest-json.sh, une à la fois, octets d'origine vérifiés par sha256 après la série :shopt -s nullglobretiré.rpmlinux-x86_64-debsert l'URL du.rpm/entre l'URL de base et le nompub_datesansZ[ -s ]affaibli en[ -e ]sur le.sig-ne 1devient-gt 1(zéro paquet admis)dieécrit sur stdout"0.17.1"au lieu de l'argument[ -n "$version" ]retiréerelease.yml, extraite du fichier et lancée parjq -e -fsous jq 1.6 (binaire officiel, sha256af986793…4c44) et sous jq 1.7, avec des résultats identiques. La sortie nominale passe. Sont refusés : le tag v0.18.0, lelatest.jsonpublié de la v0.17.1 (deux clés), une URL rpm en.rpm.siget 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).verify-release.sh(copie,TMPDIRisolé), contre la release v0.17.1 :latest.jsonpublié →linux-x86_64-deb: MANQUANT,linux-x86_64-rpm: MANQUANT,VERDICT : ÉCHEC, exit 1 ;VERDICT : OK, exit 0 ;.deb/.rpméchangées →file: DIFFÉRENTsur les deux clés, exit 1 ;-debavec la signature du.debde la v0.17.0 →file: DIFFÉRENT ('Simpl Resultat_0.17.0_amd64.deb'), exit 1. Le contrôlefile:attrape donc aussi une signature périmée du bon type, que l'assertion d'avant leDELETElaisse passer.Points examinés sans grief
pull_refclosesvenu de #416 (Resolves #320dans le corps et dansda7129d). #414 et #417 la citent sans la fermer.fix:,docs:), commentaires en anglais, docs en français, CHANGELOG FR/EN sous Fixed / Corrigé.--rawfile, hors derelease-assets/) et encodage@uriidentique à celui de l'étape d'envoi : rien à redire.scripts/**n'est pas dans lepaths-ignoredecheck-frontend: toute modification du script relance son test.…/releases/latest/download/latest.json), et cette route répond 404 sur cette instance Forgejo (relevé ce jour, avecsimpl-resultatcomme avecSimpl-Resultat).Suggestions (non bloquantes)
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 == $versionderelease.ymlcouvre la première faute, mais seulement au tag, après la création de la release. Un cas avec0.18.0en argument et un cas d'argument vide suffisent. Épingle sur un code correct : merge possible en l'état, avec une issue de suivi.verify-release.sh. Placée dansscripts/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 parcheck-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.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.debservi : sans la clé, ils auraientTargetsNotFoundà 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.…/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 dansdocs/qa-update-cycle.md(tableau et liste), dans l'en-tête du script et dans le corps de la PR. Le message de5ce136f(« all reading the registry ») et le commentaire de CI 22161 (« les deux servent le même contenu ») font la même erreur.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
git archivede5ce136f), paquets et signatures téléchargés, binaire jq 1.6 et journal du run 519 : supprimés (/tmp/pr416-*).node_modules, vitest a mis à jour son cache de résultatsnode_modules/.vite/vitest/…/results.jsondans 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 statusvide).status:review→status:approved.Correction de mon commentaire 22161 (session principale du run autopilot)
J'y écrivais que l'endpoint de la v0.4.2 « lit le
latest.jsonde 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.jsonrépond 404 (vérifié le 2026-10-11, avec et sans la casseSimpl-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_64sont 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.