hermes update ne sabote plus votre travail : gateways vidées en douceur, backends survivants, kills Windows ciblés


Vous connaissez la scène ? Une longue tâche est à mi-chemin, vous tapez tranquillement hermes update, et la seconde suivante la gateway est tuée sur-le-champ — la conversation meurt en plein tour et tout ce que vous attendiez disparaît. Sur Windows, c’était pire : le programme de mise à jour a un jour tué tous les hermes.exe de la machine, emportant avec eux des installations et des projets sans rapport. Ces dernières semaines, l’équipe a reconstruit le flux de mise à jour de bout en bout (campagne #91277), et le dernier lot vient d’atterrir. Les mises à jour sont désormais douces : les tours en cours se vident avant la sortie, les connexions distantes restent actives, et les installations basées sur des conteneurs sont protégées d’office.

Mettre à jour était une loterie

L’ancien pipeline de mise à jour était essentiellement un chemin de force-kill : hermes update devait déverrouiller les fichiers du venv, il tuait donc en bloc toute gateway en cours d’exécution, qu’elle soit ou non bloquée en plein tour. Identifier « quels processus sont les miens » reposait sur un scan de motifs argv — imprécis et sujet aux dommages collatéraux.

Sur Windows, le compromis était encore plus brutal : la gateway devait mourir pour que la mise à jour puisse avancer, et l’ancien mécanisme de pause vous forçait à choisir entre « la gateway survit à la fermeture de l’application » et « la mise à jour peut se dérouler ». Une mise à jour arrivant en plein tour détruisait simplement ce tour.

Amélioration 1 : les gateways se vident au lieu d’être tuées (#95695)

hermes update demande désormais à une gateway en cours d’exécution de se vider et de sortir via son socket de contrôle, grâce à un nouveau verbe pause-for-update : refuser les nouveaux tours, terminer celui en cours, délivrer sa réponse finale, libérer tous les descripteurs de fichiers du venv, puis s’arrêter — le chemin de drainage exact déjà utilisé par SIGUSR1 et les redémarrages de service.

Si le socket est inaccessible (une gateway step-1 sans le verbe, ou pas de socket du tout), le client reçoit None et retombe sur le chemin legacy, octet pour octet. Un ACK réussi transporte pausing / already_stopping / pid / drain_timeout, et le programme de mise à jour étend son attente au budget de drainage déclaré par la gateway elle-même (plus 10 s de grâce de teardown) au lieu de tuer de force une gateway en plein tour à la fin d’un défaut local trop court.

Pour la plupart des utilisateurs, cela signifie : le tour en cours au moment de la mise à jour va pouvoir se terminer.

Amélioration 2 : les backends serve distants survivent à la mise à jour (#95576)

Si vous lancez hermes serve --host <ip> pour alimenter un Desktop distant, l’ancien pipeline ignorait l’existence du processus : absent de l’inventaire d’exécution, invisible pour --status, et une fois tué il n’était jamais relancé — laissant le client distant face à un endpoint mort.

Les backends serve/dashboard s’enregistrent désormais eux-mêmes dans le ledger de spawn avec leur host/port/profile réellement liés (une identité volontairement auto-déclarée, pas un autre scan de motifs argv). Après une mise à jour, ils continuent de tourner sur leur endpoint enregistré, si bien que les connexions distantes ne sont pas interrompues. Cela comble aussi une asymétrie de longue date : les backends serve pouvaient être tués via hermes dashboard --stop tout en restant cachés de --status — désormais --status les liste.

Amélioration 3 : le programme de mise à jour Windows ne tue que le hermes.exe de cette installation (#95086)

C’était le plus effrayant. L’ancien programme de mise à jour Windows exécutait taskkill /IM hermes.exe — un kill à l’échelle du nom d’image qui abattait tous les processus nommés hermes.exe de la machine, y compris des installations sans rapport et du travail en cours (l’issue #91964 était un cas réel où le shim du programme de mise à jour a tué des installations sans rapport en pleine exécution).

force_kill_other_hermes() ne termine désormais que les processus dont le chemin complet de l’exécutable correspond au répertoire Scripts du venv de cette installation (instantané Toolhelp32 + QueryFullProcessImageNameW, prédicat de chemin insensible à la casse). Plusieurs installations de Hermes sur une même machine ne se gênent plus mutuellement — une mise à jour ne touche jamais que la sienne.

Amélioration 4 : les backends SSH et les HOME étrangers ne sont plus des dommages collatéraux (#95641)

Le nettoyage des backends obsolètes a reçu la même discipline : le balayage ne tue plus les backends appartenant à SSH (la propriété SSH est préservée pendant les mises à jour), et il ne relance jamais de backends depuis un HERMES_HOME étranger (#94030). Auparavant, une mise à jour pouvait tuer vos sessions SSH distantes et des processus dans des répertoires sans rapport ; ces frontières sont désormais appliquées.

Amélioration 5 : les installations gérées par image refusent les mises à jour en place (#95722)

Les installations Docker, Nix et apt doivent être mises à jour en tirant une nouvelle image et en reconstruisant le conteneur, et non en exécutant hermes update dans un conteneur pour muter une image immuable. Avant, les trois surfaces de mise à jour (hermes update, hermes update --check et le bouton Update du desktop) avaient chacune leurs propres heuristiques Docker/Nix/apt — et un checkout monté en bind dans un conteneur ressemblait à une installation git, trompant les vérifications. Les refus ne laissaient d’ailleurs aucune trace.

La construction de l’image intègre désormais /etc/hermes/image-provenance.json dans l’image (hors du checkout montable en bind et du volume HERMES_HOME), et les trois points d’entrée passent par un portail d’admission partagé :

  • Marqueur présent (même malformé) → géré par image ; les mises à jour en place sont refusées, la CLI sort avec le code 2 (refused-by-contract) et affiche not updatable in place (<code>); use: <command> ;
  • Marqueur absent → les heuristiques existantes s’appliquent comme avant ;
  • hermes update --plan rapporte désormais honnêtement updatable_in_place=False, même lorsqu’un checkout monté en bind tromperait les anciennes heuristiques git.

À quoi votre flux de mise à jour devrait ressembler

Les installations standard (le script curl) fonctionnent comme avant :

hermes update --check    # examinez le plan d'abord — pas de surprises
hermes update            # les gateways se vident, les backends serve survivent

Utilisateurs de Docker, retenez-le : n’exécutez pas hermes update dans un conteneur. Tirez la nouvelle image et reconstruisez plutôt :

docker pull <your-hermes-image>:latest
docker compose up -d --build   # ou comme vous l'orchestrez habituellement

Plusieurs installations sur Windows ? Vous pouvez respirer — une mise à jour ne touche que la sienne. Avant de mettre à jour, hermes status affiche les backends serve/dashboard pour que vous sachiez ce qui tourne.

Pour conclure

Cette salve n’ajoute aucune nouvelle fonctionnalité, mais elle transforme « mise à jour », qui pouvait exploser à tout moment, en une opération que l’on peut lancer en toute sécurité au milieu d’une journée de travail : les tours se terminent, les connexions distantes tiennent, les autres installations sont laissées tranquilles, et les installations conteneurisées ne peuvent pas se casser elles-mêmes. Combiné au watchdog de boucle de gateway et aux conteneurs Docker partagés vus précédemment, le quotidien gagne nettement en fiabilité. Ces changements sont tous sur main pour l’instant, pas encore dans un tag de release — pour les essayer dès aujourd’hui, faites hermes update vers le dernier build de dev ; en attendant, les notes de version v0.20.5 résument la vague précédente d’améliorations opérationnelles.