- template docker/pull-check.sh.tpl (pull sans up, inspect before/after) - dockerPull: parseDockerPullCheck + buildDockerPullResult (TDD) — compare image id/digest/label OCI → services up_to_date|updates_available|error, changes operation=pulled ; erreurs registry nettoyées (URL/token/password) - dockerDedupKey (digests prioritaires, fallback image ids) + DockerImageChange.dedupKey - pullCheckStack: SSH + upsert docker_stack_services, refuse stack non enabled, refresh Docker séparé (hors refreshMachine, pas de pull auto) - execute: runAction(opts.stackId), branche docker_pull_check, injection stackDir (corrige docker_inspect_current) ; route: allowlist Docker passifs + pull_check, destructives toujours hors API jusqu'à action_requests (SJ-6) Pas de migration (schéma SJ-4 suffisant). tsc 0 erreur · 85 tests · build OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3.4 KiB
Tâche 2 — SJ-5 : Docker pull-check + comparaison déterministe
Statut : implémenté (2026-06-05). tsc 0 erreur · 85 tests · build OK. Réf. design :
docs/design/tache2/20-docker.md §4.3,40-contrats-json.md §3/§6,80-sous-jalons.mdSJ-5.
Périmètre livré
Télécharger les images candidates d'un stack Compose sans démarrer de conteneur
(docker compose pull), comparer avant/après par image ID + repo digest + label OCI,
et persister l'état des services — sans toucher au flux jalon 1 et sans déclencher
de pull automatique (action manuelle par stack, non incluse dans refreshMachine).
Composants
- Template
templates/docker/pull-check.sh.tpl— délimiteurs Mustache<% %>(<%stackDir%>), Go-templates{{.Id}}/{{join .RepoDigests ","}}préservés. Sections===SU:DOCKER_INSPECT_BEFORE/PULL/INSPECT_AFTER===+===SU:EXIT=N===. server/services/dockerPull.ts:parseDockerPullCheck(raw)— lit BEFORE/AFTER (id, digest, version), code de sortie, et extrait les erreurs de pull nettoyées de tout secret (URLs, token/bearer/password).buildDockerPullResult(stackName, raw)— comparaison déterministe →services(up_to_date | updates_available | errorpar image) +changes(operation:"pulled"uniquement pour les images modifiées) +statusglobal +errors.dockerDedupKey(image, fromDigest, toDigest, fromId?, toId?)— empreinte fonctionnelle (digests prioritaires, fallback image IDs), conforme40 §6.pullCheckStack(machineId, stackId, onData?)— orchestration SSH + upsert des services dansdocker_stack_services(parstackId + serviceName), majlastUpdateAtdu stack etlastPullCheckAtdes settings. Refuse un stack nonenabled.
server/services/dockerPull.test.ts— 7 cas (parse, nettoyage secret registry, classement up_to_date/updates_available, change unique, status global, dédup).- Wiring :
runAction(machineId, action, opts?: { stackId })— branche dédiéedocker_pull_check(archivage report/log,ExecutionResult.docker.pull.changes+dedupKey, event).- Chemin générique : injection
stackDirquandstackIdfourni → corrige aussidocker_inspect_current(SJ-4 le déclarait sans orchestration par stack). POST /:id/actions— allowlist élargie aux actions Docker passives/non-applicatives (docker_scan,docker_inspect_current,docker_pull_check) ;stackIdrequis pour les actions par-stack. Destructives (apply/down/prune agressif) toujours hors API jusqu'au socleaction_requests(SJ-6).
shared/types.ts:DockerImageChange.dedupKey?(additif, pour mutualisation Hermes).
Pas de migration
Le schéma SJ-4 (docker_stack_services avec current/candidate_image_id|digest,
version_label, status ; docker_settings.last_pull_check_at) couvrait déjà SJ-5.
Sécurité
docker compose pullsansup→ aucun conteneur recréé (pré-check pur applicatif).- Erreurs registry (
registry_auth_failed/pull_failed) nettoyées : ni URL, ni token, ni mot de passe ne remontent vers UI/MCP (test dédié). - Credentials registry (
~/.docker/config.json) jamais lus ni renvoyés.
Reste pour SJ-6
docker_compose_apply (up -d --remove-orphans), docker_prune_images, docker_compose_down,
table docker_image_events, et validation UI explicite via action_requests.