La demi-mise à jour Windows : hermes.exe disparaît, le tampon de version reste


Vous lancez hermes update sous Windows, vous regardez défiler un écran de lignes de log, et la dernière est :

⚠ Update follow-up 'launchers' did not finish: launchers: source launcher publication failed — retry the source update (the next launch or `hermes update` retries it)

Ça se lit comme « ça se réparera tout seul la prochaine fois », alors vous fermez la fenêtre. Le lendemain, hermes --version annonce toujours l’ancienne version, bin\hermes.exe a disparu et il ne reste que hermes.cmd. Relancer la mise à jour affiche le même avertissement. Deux PR fusionnées les 22 et 23 octobre 2026 démontent cette boucle.

Deux symptômes, une seule cause

Cette ligne de log, ce sont en réalité deux échecs distincts empilés :

  1. Un exe en cours d’exécution ne peut pas être supprimé. La mise à jour a réépinglé Python (pm/lock.json passe de 3.14.7 à 3.14.9), donc les launchers — bin\hermes.exe et compagnie — doivent être republiés. La publication supprime d’abord l’ancien hermes.exe pour qu’il ne masque pas la nouvelle commande, et Windows refuse de supprimer une image mappée. L’image en question est celle qui exécute hermes update en ce moment.
  2. L’écriture du tampon est sautée quand la publication échoue. Pas de launcher, pas de write_source_stamp — donc install-stamp.json et .hermes-bootstrap-complete disent toujours v0.22.4.

Puis la troisième couche, celle qui rend le tout collant : le conseil de l’avertissement — le prochain lancement ou un autre hermes update réessaiera — réessaie depuis ce même exe. Il ne peut jamais se débloquer. Vous attendez qu’il se répare seul ; lui attend un autre exe.

Pourquoi seulement sous Windows

Sur POSIX, un fichier en cours d’exécution peut être unlink et le processus continue d’utiliser l’inode. Windows interdit de supprimer une image mappée.

Le déclencheur est étroit lui aussi : Windows, et uniquement lors d’un changement de version de Python. En passant de v0.22.3 à v0.22.4, Python est resté en 3.14.7, donc l’exe existant correspondait et restait en place. C’est précisément ce qui rend le cas difficile à repérer — la plupart des mises à jour se passent bien.

Correctif un : si la suppression échoue, renommer et mettre de côté

#143291 ajoute _retire_exe dans hermes_cli/_launchers.py : on tente la suppression ; quand Windows refuse parce que le fichier est utilisé, on le renomme de côté en hermes.exe.<pid>.deleteme. Windows autorise bien le renommage d’une image mappée — distlib fait exactement le même geste. La copie mise de côté est ramassée lors d’une publication ultérieure, une fois son processus terminé.

La publication n’échouant plus, le tampon est de nouveau écrit. _collect_retired balaie en plus le hermes.exe.deleteme que distlib laisse lui-même quand c’est l’exe de la mise à jour qui est remplacé.

Correctif deux : arrêter de dégrader vers .cmd

Réparer la suppression seule ne suffisait pas. #143339 documente l’autre moitié : le processus de mise à jour n’active que l’environnement de dépendances, qui n’a pas distlib, et les site-packages du Python du store (là où vit pip) sont écartés. mint_launcher ne trouve alors aucun ScriptMaker et publie un .cmd — et comme le correctif précédent sait désormais renommer l’exe en cours d’exécution, cet exe a été mis à la retraite pour de bon. L’installation finit avec hermes.cmd et rien d’autre.

À l’origine, l’installateur forgeait son premier exe avec le pip embarqué de python-build-standalone, un chemin que la mise à jour n’interrogeait jamais. Le correctif le rebranche : quand le processus en cours ne peut pas importer distlib, mint_launcher charge pip._vendor.distlib depuis le voisin de l’interpréteur qu’il lie (<python>\Lib\site-packages\pip\_vendor\distlib, fourni par chaque build PBS), sous un nom de module privé pour que le pip du store ne réponde jamais à import pip dans le processus. Un repli .cmd subsiste pour un interpréteur totalement dépourvu de distlib — il est juste devenu rare.

Ce qu’il faut vérifier sur votre machine

Si vous êtes sous Windows et avez récemment franchi un changement de version de Python, trois vérifications rapides :

hermes --version                                    # version sémantique et Python lié
  • bin\hermes.exe est-il toujours là, dans votre home Hermes ? S’il ne reste que hermes.cmd, vous êtes tombé dessus.
  • Ouvrez install-stamp.json et .hermes-bootstrap-complete : le commit qu’ils nomment doit être le build que vous exécutez réellement, pas le précédent.
  • S’ils ne concordent pas, passez d’abord sur un build contenant les deux correctifs, puis relancez hermes update une fois — seul le nouveau code de publication peut écarter l’ancien exe pendant qu’il tourne.

Si l’installation semble vraiment cassée plutôt qu’à moitié mise à jour (un worktree git sale, des commandes qui existent parfois et parfois non), le guide de réparation d’un git endommagé a la liste de contrôles complète. La sémantique des canaux et du rollback est dans le guide du canal stable, et les flags dans la référence hermes update.

État de la publication

Les deux correctifs ont été fusionnés dans main les 22 et 23 octobre 2026 ; une troisième PR (#143261) corrige l’empreinte disque de l’E2E Windows, qui avait rempli le runner — chaque parcours de test supprime désormais son installation au démontage. Les échecs ont été détectés par la campagne E2E du release candidate v0.22.5 sur le chemin de mise à jour de v0.22.4 vers HEAD, donc la population touchée est celle des utilisateurs Windows qui mettent à jour depuis v0.22.4. La dernière version publiée reste v0.22.4 (21 octobre) : ces correctifs ne sont pas encore arrivés sur le canal stable.

Une remarque sur ce qui n’est pas qu’un entretien de CI : dès qu’un launcher Windows dégrade vers .cmd, les raccourcis, les tâches planifiées et tout script dont l’entrée PATH contient .exe échouent en silence — et le seul signal est cette unique ligne ⚠.