docs: cartographier le comportement actuel (synchro et page d'état) #1

Merged
hermes-fusion merged 7 commits from hermes/cartographie-pc-mirror into main 2026-10-16 10:20:02 +02:00
Collaborator

Carte 1/6 du chantier PC Mirror : documenter ce que font réellement la synchro et le serveur d'état, avec des réponses de référence.

Ce que contient la branche

  • docs/COMPORTEMENT.md — comportement mesuré au 2026-10-15 :
    • pcmirror-sync.sh : sources (/mnt/Media + ~/Documents, ~/Images, ~/Téléchargements), exclusions dynamiques (/mirror/_exclusions.txt) et fixes, options rsync des trois passes (--delete, --backup --backup-dir, --ignore-existing), verrou flock non bloquant, garde-fous (disque non monté, hôte injoignable, dossier personnel vide), journaux (journal systemd utilisateur), codes de sortie (0/1), planification (minuteur --user horaire, RandomizedDelaySec=2min).
    • status-server/server.py : table des 15 routes avec leur code, leur effet et la ligne de code ; fichiers lus et écrits ; contrat de /json ; cache de scan ; seuils de santé.
    • les écarts relevés : sync/pcmirror-sync.sh du dépôt est la version du 4 juillet, la version déployée date du 12 octobre et en diffère sur dix points ; les seuils affichés en pied de page ne correspondent pas au code ; POST /exclude supprime aussi le dossier du miroir.
  • tests/fixtures/ — 19 sondages bruts (code, en-têtes, corps) contre http://192.168.1.210:9090, la copie de référence du script déployé (sha256 cfe3a4b0…), ses unités systemd, la ligne de cron périmée et les fichiers de télémétrie du miroir, plus capturer.sh et verifier_contrat.py.
  • tests/verifier_reference.py + .forgejo/workflows/ci.yml — contrôle hors ligne des références (le dépôt n'avait aucun workflow).

Preuve

$ PCMIRROR_BASE=http://192.168.1.210:9090 python3 tests/fixtures/verifier_contrat.py
… 20/20 contrôles passés (base http://192.168.1.210:9090)
rc=0
$ python3 tests/verifier_reference.py
19/19 sondages conformes (code, en-têtes HTTP/1.0, corps présents, couverture du vérificateur)
$ bash tests/fixtures/capturer.sh
_exclusions.txt : avant=6e9f1130… après=6e9f1130…
Miroir intact : _exclusions.txt inchangé.

Aucun sondage n'envoie de paramètre p : rien n'a été exclu ni supprimé du miroir, rien n'a été écrit sur le PC ni sur le 108.

CI : elle ne tourne pas, et ce n'est pas le workflow

Les deux vérifications de ce dépôt restent « Waiting to run » depuis la poussée :

$ curl -s …/repos/foXaCe/pc-mirror/commits/68a87b2a/status
{"state":"pending","total_count":2, "context":"Contrôles des références / Références du comportement (push)", "description":"Waiting to run"}

L'exécutant pc-studio du PC (id 12, étiquettes pc-lourd et studio) ne prend que les travaux de foXaCe/studio — mesuré sur 20 minutes : 0 tâche foXaCe/pc-mirror, 16 tâches foXaCe/studio, dont trois lancées à 12:00:23 (capacité libre à ce moment-là). L'étiquette demandée ne change rien (studio puis pc-lourd, même attente). La vérification ne pourra donc pas devenir verte ici sans qu'un exécutant soit enregistré pour foXaCe/pc-mirror (ou au niveau de l'instance).

Points à trancher par le chef de chantier

  1. sync/pcmirror-sync.sh doit-il être aligné sur la version déployée (copie dans tests/fixtures/synchro/pcmirror-sync.sh.deploye) ? Non fait ici : c'est une réécriture, pas une cartographie.
  2. Enregistrer l'exécutant pour ce dépôt, ou dire que la fusion se fait sans CI.

Non mesuré, et dit comme tel dans le document : /opt/mirror-status/server.py sur le LXC 108 (SSH refusé, version déduite d'un marqueur du HTML servi), les journaux de la synchro et la cause de l'échec du dernier passage (/home/stephane non lisible depuis le compte atelier).

Carte 1/6 du chantier PC Mirror : documenter ce que font réellement la synchro et le serveur d'état, avec des réponses de référence. ## Ce que contient la branche - `docs/COMPORTEMENT.md` — comportement mesuré au 2026-10-15 : - `pcmirror-sync.sh` : sources (`/mnt/Media` + `~/Documents`, `~/Images`, `~/Téléchargements`), exclusions dynamiques (`/mirror/_exclusions.txt`) et fixes, options rsync des trois passes (`--delete`, `--backup --backup-dir`, `--ignore-existing`), verrou `flock` non bloquant, garde-fous (disque non monté, hôte injoignable, dossier personnel vide), journaux (journal systemd utilisateur), codes de sortie (0/1), planification (minuteur `--user` horaire, `RandomizedDelaySec=2min`). - `status-server/server.py` : table des 15 routes avec leur code, leur effet et la ligne de code ; fichiers lus et écrits ; contrat de `/json` ; cache de scan ; seuils de santé. - les écarts relevés : `sync/pcmirror-sync.sh` du dépôt est la version du 4 juillet, la version déployée date du 12 octobre et en diffère sur dix points ; les seuils affichés en pied de page ne correspondent pas au code ; `POST /exclude` supprime aussi le dossier du miroir. - `tests/fixtures/` — 19 sondages bruts (code, en-têtes, corps) contre `http://192.168.1.210:9090`, la copie de référence du script déployé (`sha256 cfe3a4b0…`), ses unités systemd, la ligne de cron périmée et les fichiers de télémétrie du miroir, plus `capturer.sh` et `verifier_contrat.py`. - `tests/verifier_reference.py` + `.forgejo/workflows/ci.yml` — contrôle hors ligne des références (le dépôt n'avait aucun workflow). ## Preuve ``` $ PCMIRROR_BASE=http://192.168.1.210:9090 python3 tests/fixtures/verifier_contrat.py … 20/20 contrôles passés (base http://192.168.1.210:9090) rc=0 $ python3 tests/verifier_reference.py 19/19 sondages conformes (code, en-têtes HTTP/1.0, corps présents, couverture du vérificateur) $ bash tests/fixtures/capturer.sh _exclusions.txt : avant=6e9f1130… après=6e9f1130… Miroir intact : _exclusions.txt inchangé. ``` Aucun sondage n'envoie de paramètre `p` : rien n'a été exclu ni supprimé du miroir, rien n'a été écrit sur le PC ni sur le 108. ## CI : elle ne tourne pas, et ce n'est pas le workflow Les deux vérifications de ce dépôt restent « Waiting to run » depuis la poussée : ``` $ curl -s …/repos/foXaCe/pc-mirror/commits/68a87b2a/status {"state":"pending","total_count":2, "context":"Contrôles des références / Références du comportement (push)", "description":"Waiting to run"} ``` L'exécutant `pc-studio` du PC (id 12, étiquettes `pc-lourd` et `studio`) ne prend que les travaux de `foXaCe/studio` — mesuré sur 20 minutes : **0 tâche `foXaCe/pc-mirror`, 16 tâches `foXaCe/studio`**, dont trois lancées à 12:00:23 (capacité libre à ce moment-là). L'étiquette demandée ne change rien (`studio` puis `pc-lourd`, même attente). La vérification ne pourra donc pas devenir verte ici sans qu'un exécutant soit enregistré pour `foXaCe/pc-mirror` (ou au niveau de l'instance). ## Points à trancher par le chef de chantier 1. `sync/pcmirror-sync.sh` doit-il être aligné sur la version déployée (copie dans `tests/fixtures/synchro/pcmirror-sync.sh.deploye`) ? Non fait ici : c'est une réécriture, pas une cartographie. 2. Enregistrer l'exécutant pour ce dépôt, ou dire que la fusion se fait sans CI. Non mesuré, et dit comme tel dans le document : `/opt/mirror-status/server.py` sur le LXC 108 (SSH refusé, version déduite d'un marqueur du HTML servi), les journaux de la synchro et la cause de l'échec du dernier passage (`/home/stephane` non lisible depuis le compte `atelier`).
19 sondages contre le serveur d'état du LXC 108 (codes, en-têtes, corps), la
copie de référence du script de synchro déployé, ses unités systemd, la ligne
de cron périmée et les fichiers de télémétrie lus dans le miroir.

Ajoute capturer.sh (rejoue la capture, en lecture seule) et
verifier_contrat.py (20 contrôles du contrat du serveur vivant).
Documente pcmirror-sync.sh tel qu'il tourne (sources, exclusions, options
rsync, verrou, garde-fous, journaux, codes de sortie, minuteur systemd horaire
à la place du cron périmé, télémétrie, mode restauration) et chaque route de
status-server/server.py avec les fichiers qu'elle lit et écrit.

Relève les écarts : sync/pcmirror-sync.sh du dépôt est la version du 4 juillet
alors que celle déployée date du 12 octobre ; les seuils de santé affichés ne
correspondent pas au code ; POST /exclude supprime le dossier du miroir.
ci: contrôler les réponses de référence à chaque poussée
Some checks failed
Contrôles des références / Références du comportement (push) Has been cancelled
Contrôles des références / Références du comportement (pull_request) Has been cancelled
3db9522adc
Le dépôt n'avait aucun workflow : la carte demandait une CI verte et il n'y
avait rien à passer. Ajoute un métier léger sur l'exécutant du PC (étiquette
`studio`, mode host) : les scripts Python compilent, les 19 réponses de
référence sont complètes et conformes (tests/verifier_reference.py), les
fichiers de télémétrie se relisent. Aucun accès réseau, aucun secret.

Le socle Rust de la carte 2/6 viendra s'ajouter à ce même fichier.
ci: demander l'étiquette pc-lourd pour vérifier l'exécutant disponible
Some checks failed
Contrôles des références / Références du comportement (push) Has been cancelled
Contrôles des références / Références du comportement (pull_request) Has been cancelled
68a87b2acb
docs: notes de reference sur la synchro pcmirror (depot vs version en service)
Some checks failed
Contrôles des références / Références du comportement (push) Has been cancelled
Contrôles des références / Références du comportement (pull_request) Has been cancelled
4166906e46
Cause : le script du depot (sync/pcmirror-sync.sh, 79 lignes) et celui en service
(tests/fixtures/synchro/pcmirror-sync.sh.deploye, 129 lignes) ont diverge (verrou,
garde-fous disque/hote, --partial, exclusions PC-Home, code de sortie, corbeille).
Mesure : avec rsync 3.5.1 et LC_ALL=C, la progression est ecrite en octets a
separateurs de milliers, illisible par l'expression de la ligne 69 du script en
service ; l'expression du depot (l. 50) ne lit jamais le debit.

Remede : inventaire ligne par ligne des deux versions, ecarts, mesures rejouables et
correctif propose (-h, en gardant LC_ALL=C), a integrer dans docs/COMPORTEMENT.md.
tests: capturer les routes qui écrivent et vérifier leurs effets de bord
Some checks failed
Contrôles des références / Références du comportement (push) Has been cancelled
Contrôles des références / Références du comportement (pull_request) Has been cancelled
3da9d160f7
Les instantanés de référence ne couvraient que les routes en lecture : sonder
POST /exclude sur le serveur du LXC 108 supprime un dossier du miroir, donc la
capture en ligne ne pouvait pas montrer ces routes sans détruire des données.

capturer_local.py monte un miroir factice dans /tmp, y écrit un dossier illisible
et exécute le server.py du dépôt — seules trois lignes changent (racine, adresse
d'écoute, message de démarrage), le diff est affiché et vérifié à chaque
exécution. Il rejoue 17 sondes et contrôle les effets de bord : /exclude supprime
bien le dossier du miroir, conserve le motif existant de _exclusions.txt et
laisse les fichiers voisins intacts ; /include retire le motif sans restaurer le
dossier. --verifier rejoue les mêmes sondes et compare les codes au manifeste
attendu.json, sans rien réécrire : c'est ce que lance la CI.

Complètent le jeu en ligne les deux sondes qui manquaient (HEAD et DELETE par
capturer.sh), et verifier_reference.py relit désormais les deux jeux
d'instantanés (trios présents, codes conformes au manifeste, en-têtes en
HTTP/1.0, couverture du vérificateur en ligne).

Deux défauts mesurés sont documentés dans docs/COMPORTEMENT.md : HEAD répond 501
et annonce un Content-Length sans écrire aucun corps (vérifié sur la socket nue),
et le confinement de /files, sans séparateur, accepte un dossier voisin dont le
nom prolonge la racine. Le comportement du serveur n'est pas modifié.
Author
Collaborator

Nouveau commit sur cette PR : 3da9d16 — les fixtures des routes qui écrivent, et la vérification de leur contrat.

Ce que ça ajoute

  • tests/fixtures/capturer_local.py : monte un miroir factice dans /tmp, exécute le status-server/server.py du dépôt (trois lignes changées : racine, adresse d'écoute, message de démarrage — diff affiché et vérifié à chaque exécution) et rejoue 17 sondes, dont POST /exclude et POST /include avec un p réel, en contrôlant les effets de bord (dossier supprimé, fichiers voisins intacts, _exclusions.txt conservé puis nettoyé). --verifier rejoue les mêmes sondes et compare les codes au manifeste sans rien réécrire : c'est ce que lance la CI.
  • tests/fixtures/status-server-local/ : les 17 instantanés et leur manifeste attendu.json.
  • tests/fixtures/capturer.sh : les deux sondes qui manquaient au jeu en ligne (HEAD /files et DELETE /files → 501), plus PCMIRROR_SONDES=… pour ne rejouer que les sondes nommées.
  • tests/verifier_reference.py : relit les deux jeux d'instantanés (trios présents, codes conformes au manifeste, en-têtes en HTTP/1.0, couverture du vérificateur en ligne).
  • .forgejo/workflows/ci.yml : compile le nouveau script et rejoue le contrôle hors ligne (écoute sur 127.0.0.1 seulement, rien ne sort du PC).
  • docs/COMPORTEMENT.md : § 3.1 (HEAD), § 3.5 (effets mesurés de /exclude et /include), § 3.6 (deux défauts de plus), § 4 (les deux jeux de réponses).
  • tests/fixtures/README.md : mode d'emploi des deux jeux.

Ce qui est mesuré en plus

  • HEAD /files → 501 et aucun octet de corps malgré un Content-Length: 357 annoncé : vérifié sur la socket nue, pas seulement avec un client qui masque le problème.
  • Le confinement de /files (startswith(ROOT), sans séparateur) accepte un dossier voisin dont le nom prolonge celui de la racine : GET /files?p=../mirror-voisin → 200 et son listing, là où /dl le refuse. Aucune traversée plus large (../.. → 400).
  • POST /exclude p=Documents supprime bien le dossier du miroir et conserve le motif existant de _exclusions.txt ; POST /include p=Documents retire le motif sans restaurer le dossier.

Sorties, sur l'état poussé 3da9d16 (arbre propre)

$ python3 -m py_compile status-server/server.py tests/fixtures/capturer_local.py tests/fixtures/verifier_contrat.py tests/verifier_reference.py
exit=0 (aucune sortie : tout compile)

$ python3 tests/verifier_reference.py
21/21 sondages conformes en ligne (code, en-têtes HTTP/1.0, corps présents, couverture du vérificateur)
17/17 sondages conformes hors ligne (miroir factice, routes à effet de bord)
exit=0

$ python3 tests/fixtures/capturer_local.py --verifier | tail -4
17 sondes comparées au manifeste de référence

0 incident(s)

$ PCMIRROR_BASE=http://192.168.1.210:9090 python3 tests/fixtures/verifier_contrat.py | tail -3
ok    forme de /json                   12 clés attendues présentes

22/22 contrôles passés (base http://192.168.1.210:9090)

CI : en attente, pas verte. Aucun exécutant n'est enregistré pour foXaCe/pc-mirror. L'exécutant du PC (nom pc-studio, étiquettes pc-lourd:host et studio:host) fait tourner les workflows de foXaCe/studio, qui demandent la même étiquette pc-lourd — réussites à 17:13, 17:14, 17:16 et 17:18 — pendant que les deux contrôles de ce dépôt attendent : créés à 17:06:39, encore « Waiting to run » à 17:22, soit seize minutes sans qu'un exécutant les prenne. Je ne fusionne pas et je ne force rien.

Nouveau commit sur cette PR : `3da9d16` — les fixtures des routes qui écrivent, et la vérification de leur contrat. **Ce que ça ajoute** - `tests/fixtures/capturer_local.py` : monte un miroir factice dans `/tmp`, exécute le `status-server/server.py` **du dépôt** (trois lignes changées : racine, adresse d'écoute, message de démarrage — diff affiché et vérifié à chaque exécution) et rejoue 17 sondes, dont `POST /exclude` et `POST /include` avec un `p` réel, en contrôlant les effets de bord (dossier supprimé, fichiers voisins intacts, `_exclusions.txt` conservé puis nettoyé). `--verifier` rejoue les mêmes sondes et compare les codes au manifeste sans rien réécrire : c'est ce que lance la CI. - `tests/fixtures/status-server-local/` : les 17 instantanés et leur manifeste `attendu.json`. - `tests/fixtures/capturer.sh` : les deux sondes qui manquaient au jeu en ligne (`HEAD /files` et `DELETE /files` → 501), plus `PCMIRROR_SONDES=…` pour ne rejouer que les sondes nommées. - `tests/verifier_reference.py` : relit les deux jeux d'instantanés (trios présents, codes conformes au manifeste, en-têtes en HTTP/1.0, couverture du vérificateur en ligne). - `.forgejo/workflows/ci.yml` : compile le nouveau script et rejoue le contrôle hors ligne (écoute sur 127.0.0.1 seulement, rien ne sort du PC). - `docs/COMPORTEMENT.md` : § 3.1 (`HEAD`), § 3.5 (effets mesurés de `/exclude` et `/include`), § 3.6 (deux défauts de plus), § 4 (les deux jeux de réponses). - `tests/fixtures/README.md` : mode d'emploi des deux jeux. **Ce qui est mesuré en plus** - `HEAD /files` → 501 et **aucun octet de corps** malgré un `Content-Length: 357` annoncé : vérifié sur la socket nue, pas seulement avec un client qui masque le problème. - Le confinement de `/files` (`startswith(ROOT)`, sans séparateur) accepte un dossier voisin dont le nom prolonge celui de la racine : `GET /files?p=../mirror-voisin` → 200 et son listing, là où `/dl` le refuse. Aucune traversée plus large (`../..` → 400). - `POST /exclude` `p=Documents` supprime bien le dossier du miroir et conserve le motif existant de `_exclusions.txt` ; `POST /include` `p=Documents` retire le motif **sans restaurer** le dossier. **Sorties, sur l'état poussé `3da9d16` (arbre propre)** ``` $ python3 -m py_compile status-server/server.py tests/fixtures/capturer_local.py tests/fixtures/verifier_contrat.py tests/verifier_reference.py exit=0 (aucune sortie : tout compile) $ python3 tests/verifier_reference.py 21/21 sondages conformes en ligne (code, en-têtes HTTP/1.0, corps présents, couverture du vérificateur) 17/17 sondages conformes hors ligne (miroir factice, routes à effet de bord) exit=0 $ python3 tests/fixtures/capturer_local.py --verifier | tail -4 17 sondes comparées au manifeste de référence 0 incident(s) $ PCMIRROR_BASE=http://192.168.1.210:9090 python3 tests/fixtures/verifier_contrat.py | tail -3 ok forme de /json 12 clés attendues présentes 22/22 contrôles passés (base http://192.168.1.210:9090) ``` **CI : en attente, pas verte.** Aucun exécutant n'est enregistré pour `foXaCe/pc-mirror`. L'exécutant du PC (nom `pc-studio`, étiquettes `pc-lourd:host` et `studio:host`) fait tourner les workflows de `foXaCe/studio`, qui demandent la même étiquette `pc-lourd` — réussites à 17:13, 17:14, 17:16 et 17:18 — pendant que les deux contrôles de ce dépôt attendent : créés à 17:06:39, encore « Waiting to run » à 17:22, soit seize minutes sans qu'un exécutant les prenne. Je ne fusionne pas et je ne force rien.
docs: sourcer et corriger l'état mesuré de COMPORTEMENT.md
All checks were successful
Contrôles des références / Références du comportement (push) Successful in 3s
Contrôles des références / Références du comportement (pull_request) Successful in 3s
ea9c9dfafb
La section « écart dépôt / version en service » et la méthode s'appuyaient sur
des copies et des dates non nommées, et trois affirmations étaient fausses.

- planification : cron n'est pas installé sur le PC (« le paquet cronie n'a pas
  été trouvé », `cronie.service` inconnu) ; les unités systemd --user sont
  identiques dans la sauvegarde kde-backup (81326f75…/d10a93c8…) et dans les
  fixtures, et l'en-tête du script en service (l. 14) les déclare ; deux
  horodatages de `_last-sync.json` à ~:03 de l'heure pleine corroborent la
  cadence horaire.
- provenance nommée : `/mnt/Media/Nas/scripts/` (fichiers des 3 et 4 juillet)
  pour la copie périmée de `sync/`, `kde-backup` pour les unités, et
  `/mnt/Media/Nas/scripts/mirror-status-server.py` (4 juillet, 814360fb…,
  889 lignes, 0 `aria-hidden`, 0 `class="vh"`) pour la version qui précède le
  dépôt ; l'unité `mirror-status.service` (de36f6c0…) est lue dans le même
  dossier.
- corrections : la ligne ssh est passée par `-e "$RSH"` (pas `RSYNC_RSH`) ; les
  exclusions fixes sont ajoutées après la dérivation d'`EXCL2`, donc portées par
  le seul rsync média ; les dossiers personnels partent en un seul rsync aplati
  et le code de sortie vient du dernier rsync (un échec média est masqué) ; le
  script du dépôt écrit aussi `rc` dans `_last-sync.json`.
- mesure ajoutée : `/dl?p=_pve-configs/.last-backup` → 404 le 2026-10-15, ce qui
  explique le champ vide `backup_pve_dernier` du tableau de bord ; relevé du
  serveur à 17:31 (89,7 s de scan, 3 255 290 fichiers, 152,4 Go).

Tests : `tests/verifier_reference.py` 21/21 en ligne et 17/17 hors ligne,
`tests/fixtures/capturer_local.py --verifier` 0 incident,
`tests/fixtures/verifier_contrat.py` 22/22 sur 192.168.1.210:9090.
hermes-fusion deleted branch hermes/cartographie-pc-mirror 2026-10-16 10:20:02 +02:00
Collaborator

Fusionnée automatiquement par fusion-auto (forge) : CI verte, sans conflit.

Fusionnée automatiquement par `fusion-auto` (forge) : CI verte, sans conflit.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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
foXaCe/pc-mirror!1
No description provided.