feat(deploy): paquet de livraison, unité durcie, contrôle de santé et retour arrière #6

Open
hermes-bot wants to merge 4 commits from hermes/paquet-service-deploiement into main
Collaborator

Empilée sur #5 (hermes/page-etat-svelte) : à fusionner après elle, le binaire embarque front/dist.

Ce que la carte demande, et ce qui est livré

  1. Binaire unique — build.rs embarque front/dist dans le binaire ; src/interface.rs sert la page sous /__status/ (redirection sans barre finale, refus des chemins sortant du dossier, pas de cache). Plus rien à copier côté machine.
  2. Unité durcie — deploy/tizentube-proxy.service : utilisateur tizentube, ProtectSystem=strict, ProtectHome, PrivateTmp, filtres d'appels système, CapabilityBoundingSet= vide, AC en lecture seule, dossier du script seul en écriture (la route de mise à jour y écrit). systemd-analyze verify ne relève qu'un défaut attendu ici (« Command /opt/... is not executable » : le binaire vit sur la cible, pas sur ce PC).
  3. deploy/deploy.sh — idempotent, --dry-run écrit le plan sans rien toucher, garde la version en place en bin/tizentube-proxy.precedent, bascule par mv, installe l'unité si elle change, redémarre, contrôle la santé, et ramène la version précédente si le contrôle échoue (codes 0 / 1 / 2 distincts). Rien n'est supprimé sur la cible.
  4. Contrôle de santé (deploy/controle-sante.sh) — /__sante = ok, /__status/ servie, et le proxy relaie vraiment : un CONNECT accepté puis une requête qui traverse le tunnel jusqu'à sa réponse. Un port ouvert ne prouve rien.
  5. Job CI de livraison — sur main (après un main vert), deploy/deploy.sh pousse vers la machine du service puis contrôle. Il refuse de se dire vert si la clé de livraison manque (TT_CLE_LIVRAISON, défaut ~/.ssh/id_livraison) : à poser une fois par l'opérateur sur l'exécutant.

Preuves

cargo test --release --locked :

running 58 tests
test result: ok. 58 passed; 0 failed; 0 ignored
running 7 tests
test result: ok. 7 passed; 0 failed; 0 ignored

cargo fmt --all --check : OK. cargo clippy --all-targets --release --locked -- -D warnings : OK. cargo build --release --locked : OK. shellcheck -S style deploy/*.sh : rien à redire.

deploy/essai-deploy.sh (sur cette machine, sans root, doublure de systemctl qui tue par PID) :

1. à blanc
  oui   le plan est écrit / rien n'est touché
2. livraison
  oui   la livraison rend 0 / binaire en place / unité posée / service sain / page servie
3. livraison rejouée
  oui   rien n'a été recopié / le service tourne toujours
4. livraison cassée
  oui   la livraison cassée rend 1 (livré puis ramené en arrière)
  oui   le journal dit le retour arrière / le binaire précédent est revenu / le service repart
essai de livraison : 16 mesures sur 16

État de la CI

Aucun exécutant n'est enregistré pour ce dépôt (t_299ce6aa) : le workflow ne tourne pas, la PR ne sera donc pas verte toute seule. Le code est mesuré à la main, ci-dessus. Si l'exécutant arrive, l'étape plan à blanc utilise une cible locale et ne demande pas de clé ; seul le job livraison (sur main) en a besoin.

Points pour le chef

  • Point chaud : .forgejo/workflows/ci.yml est modifié ici (jobs paquet et livraison) et l'était déjà par la carte front — à fusionner dans l'ordre #5 puis celle-ci.
  • La nouvelle unité remplace celle de mitmdump (tizentube-proxy.service à la racine, qui décrit encore l'installation en service). La carte d'installation (6/6) tranchera : je n'ai pas touché au fichier racine.
  • front/dist est bien versionné par #5 : sans lui, build.rs arrête la compilation en le disant.
Empilée sur #5 (`hermes/page-etat-svelte`) : à fusionner **après** elle, le binaire embarque `front/dist`. ## Ce que la carte demande, et ce qui est livré 1. **Binaire unique** — `build.rs` embarque `front/dist` dans le binaire ; `src/interface.rs` sert la page sous `/__status/` (redirection sans barre finale, refus des chemins sortant du dossier, pas de cache). Plus rien à copier côté machine. 2. **Unité durcie** — `deploy/tizentube-proxy.service` : utilisateur `tizentube`, `ProtectSystem=strict`, `ProtectHome`, `PrivateTmp`, filtres d'appels système, `CapabilityBoundingSet=` vide, **AC en lecture seule**, dossier du script seul en écriture (la route de mise à jour y écrit). `systemd-analyze verify` ne relève qu'un défaut attendu ici (« Command /opt/... is not executable » : le binaire vit sur la cible, pas sur ce PC). 3. **`deploy/deploy.sh`** — idempotent, `--dry-run` écrit le plan sans rien toucher, garde la version en place en `bin/tizentube-proxy.precedent`, bascule par `mv`, installe l'unité si elle change, redémarre, contrôle la santé, et **ramène la version précédente** si le contrôle échoue (codes 0 / 1 / 2 distincts). Rien n'est supprimé sur la cible. 4. **Contrôle de santé** (`deploy/controle-sante.sh`) — `/__sante` = ok, `/__status/` servie, et le proxy **relaie vraiment** : un `CONNECT` accepté puis une requête qui traverse le tunnel jusqu'à sa réponse. Un port ouvert ne prouve rien. 5. **Job CI de livraison** — sur `main` (après un `main` vert), `deploy/deploy.sh` pousse vers la machine du service puis contrôle. Il **refuse** de se dire vert si la clé de livraison manque (`TT_CLE_LIVRAISON`, défaut `~/.ssh/id_livraison`) : à poser une fois par l'opérateur sur l'exécutant. ## Preuves `cargo test --release --locked` : ``` running 58 tests test result: ok. 58 passed; 0 failed; 0 ignored running 7 tests test result: ok. 7 passed; 0 failed; 0 ignored ``` `cargo fmt --all --check` : OK. `cargo clippy --all-targets --release --locked -- -D warnings` : OK. `cargo build --release --locked` : OK. `shellcheck -S style deploy/*.sh` : rien à redire. `deploy/essai-deploy.sh` (sur cette machine, sans root, doublure de systemctl qui tue par PID) : ``` 1. à blanc oui le plan est écrit / rien n'est touché 2. livraison oui la livraison rend 0 / binaire en place / unité posée / service sain / page servie 3. livraison rejouée oui rien n'a été recopié / le service tourne toujours 4. livraison cassée oui la livraison cassée rend 1 (livré puis ramené en arrière) oui le journal dit le retour arrière / le binaire précédent est revenu / le service repart essai de livraison : 16 mesures sur 16 ``` ## État de la CI **Aucun exécutant n'est enregistré pour ce dépôt** (`t_299ce6aa`) : le workflow ne tourne pas, la PR ne sera donc pas verte toute seule. Le code est mesuré à la main, ci-dessus. Si l'exécutant arrive, l'étape `plan à blanc` utilise une cible locale et ne demande pas de clé ; seul le job `livraison` (sur `main`) en a besoin. ## Points pour le chef - Point chaud : `.forgejo/workflows/ci.yml` est modifié ici (jobs `paquet` et `livraison`) et l'était déjà par la carte front — à fusionner dans l'ordre #5 puis celle-ci. - La nouvelle unité **remplace** celle de mitmdump (`tizentube-proxy.service` à la racine, qui décrit encore l'installation en service). La carte d'installation (6/6) tranchera : je n'ai pas touché au fichier racine. - `front/dist` est bien versionné par #5 : sans lui, `build.rs` arrête la compilation en le disant.
feat(proxy): socle Rust du proxy TizenTube, mesuré par le banc de référence
Some checks failed
CI du proxy TizenTube (Rust) / Rust (proxy) (push) Failing after 16s
CI du proxy TizenTube (Rust) / Rust (proxy) (pull_request) Successful in 59s
2685d188c3
Le proxy est réécrit en Rust (édition 2024, tokio, rustls, rcgen) : écoute sur
la boucle locale ou le LAN, refus des sources hors réseau local, déchiffrement
limité aux hôtes autorisés (expressions régulières, même sémantique que
mitmproxy), tunnel opaque pour tout le reste, autorité de certification reprise
du dossier ou engendrée au premier lancement, injection de la sonde et du script
dans la page /tv, service d'état et métriques Prometheus.

Deux pannes mesurées par le banc, corrigées ici :
- l'amont forcé ne valait que pour le relais, pas pour les tunnels : un hôte
  déclaré opaque partait tout droit sur le réseau ;
- les certificats servis n'avaient pas d'extension authorityKeyIdentifier, que
  la vérification stricte de Python 3.13 exige (RFC 5280 §4.2.1.1).
La sonde se pose désormais juste après la balise ouvrante `<head …>` (sans
casse) et le script avant le dernier `</body>`, aux places du proxy Python.

Mesures de ce socle : banc --exigence cible, 14 verts, 0 rouge, 1 non mesuré ;
35 tests unitaires et 4 d'intégration verts ; couverture des lignes 82,0 %.
La CI est posée dans .forgejo/workflows/ci.yml ; aucun exécutant n'étant
enregistré pour ce dépôt, elle n'a jamais tourné.
fix(proxy): relire le script injecté quand son fichier change, et n'installer que la version épinglée
Some checks failed
CI du proxy TizenTube (Rust) / Rust (proxy) (push) Has been cancelled
CI du proxy TizenTube (Rust) / Rust (proxy) (pull_request) Has been cancelled
d01ab33ce0
Trois causes, mesurées dans le banc : le script TizenTube n'était lu qu'une fois, au démarrage,
donc le remplacer sur le disque ne changeait rien pour la télévision ; une page /tv revalidée
recevait un 304 sans corps, donc sans TizenTube ; et rien ne disait quelle version de l'amont
tournait.

- injection.rs : CacheScript garde le script sous RwLock, avec une Signature (taille + mtime de
  la modification) ; Rechargement (Rien / Recharge / Echec) dit ce qui s'est passé. Un
  rechargement raté garde l'ancien script et le dit, dans le journal et sur la page d'état.
- proxy.rs : quand on injecte, les en-têtes conditionnels (If-None-Match, If-Modified-Since…)
  et les en-têtes d'empreinte du corps ne sont plus relayés vers /tv : la télévision reçoit
  toujours le corps porteur de TizenTube. L'URL du script porte l'empreinte de son contenu et
  peut donc être gardée en cache longtemps.
- maj.rs + --maj-script : le script s'installe depuis la version épinglée de tizentube/version.json
  (empreinte de l'archive — sha256 et intégrité du registre — puis taille et empreinte de chaque
  fichier). Rien n'est écrit avant la dernière vérification, chaque fichier passe par un fichier
  voisin puis un renommage, et un script déjà à l'empreinte épinglée n'est pas retéléchargé.
  tizentube/version.json épingle 1.15.1 avec les empreintes réelles du paquet amont.

Tests : cargo test --release --locked, 59 tests verts (53 en bibliothèque, 6 d'intégration).
Couverture : 82,71 % des lignes (cargo llvm-cov, seuil 80).
test(banc): profil du banc de référence pour la branche d injection
All checks were successful
CI du proxy TizenTube (Rust) / Rust (proxy) (push) Successful in 58s
CI du proxy TizenTube (Rust) / Rust (proxy) (pull_request) Successful in 56s
9226f62fa5
Le banc vit dans tests/ ; ce profil lance le binaire de cette branche avec l amont du banc
declare en clair, comme le profil du socle, pour que la parite soit rejouable telle quelle.

Banc : 14 ok, 0 rouge, 1 non mesuré, code 0 (injection 01..10 ok).
feat(front): page d'état Svelte 5 et bouton de mise à jour du script
Some checks failed
CI du proxy TizenTube (Rust) / Rust (proxy) (push) Has been cancelled
CI du proxy TizenTube (Rust) / Page d'état (Svelte) (push) Has been cancelled
CI du proxy TizenTube (Rust) / Rust (proxy) (pull_request) Has been cancelled
CI du proxy TizenTube (Rust) / Page d'état (Svelte) (pull_request) Has been cancelled
0f887516ce
Le proxy ne montrait son état que par une page HTML écrite à la main dans le
Rust : ni essai, ni lisibilité sur téléphone. La page devient une application
Svelte 5 (dossier `front/`) que le proxy sert sur `/__status` — en-tête, cartes
script, trafic, appareils, santé et journal — avec un bouton « Mettre à jour le
script » qui appelle `POST /api/script/maj` et affiche le message rendu par le
proxy : `installed`, `up-to-date` ou `refused` (le champ `status` est testé, pas
`ok` seul).

Aucune donnée d'état n'est mise en cache : chaque actualisation relit
`/__status.json` et `/__metrics`. Le service worker ne garde que la coque, pour
que la page s'ouvre encore quand le proxy est muet. Un refus de lecture affiche
le code et l'explication donnée par le proxy, bornée à une ligne.

Vérifications : svelte-check 0 erreur, eslint 0 problème, 94 essais unitaires
(couverture 95,2 % des lignes), 32 essais de bout en bout Playwright en bureau
(1440×900) et téléphone (412×915), dont un balayage axe sans violation. Un
travail `front` ajoute ces contrôles à `.forgejo/workflows/ci.yml`.

L'import du composant de la carte script est renommé `SectionScript` : sous le
nom `Script`, eslint le signalait comme inutilisé alors qu'il est bien utilisé
dans le balisage — le nom entre en collision avec l'élément `script` du parseur.
docs(front): captures de la page d'état en clair et en sombre, bureau et téléphone
Some checks failed
CI du proxy TizenTube (Rust) / Rust (proxy) (push) Successful in 56s
CI du proxy TizenTube (Rust) / Page d'état (Svelte) (push) Failing after 58s
CI du proxy TizenTube (Rust) / Rust (proxy) (pull_request) Successful in 54s
CI du proxy TizenTube (Rust) / Page d'état (Svelte) (pull_request) Failing after 10s
2c358a71af
Les quatre captures viennent de l'essai qui bascule les thèmes : elles montrent
la page telle qu'un opérateur la voit, sans donnée réelle. Réduites à 1200 px de
large pour rester légères dans le dépôt.
Cause : la page d'état vivait dans une chaîne HTML écrite en dur dans etat.rs, en double
de la page Svelte construite dans front/dist ; la servir demandait un dossier à copier sur
le LXC ou un serveur de fichiers devant le binaire.

Remède : build.rs embarque front/dist dans le binaire (table générée dans OUT_DIR, types
MIME repris du simulateur, compilation arrêtée si le dossier manque) ; src/interface.rs
résout les adresses sous /__status (page à /__status/, refus des chemins sortant du
dossier, pas de cache) ; le routage d'état de proxy.rs devient requete_etat() : /__status/
sert la page, /__status.json et /__metrics rendent les données, /__sante répond ok ou 503
avec le motif ; page(), STYLE et echapper() partent de etat.rs, la page n'existe plus qu'en
Svelte. L'épreuve d'intégration sert chaque fichier embarqué, vérifie les octets des PNG,
la redirection /__status -> /__status/ et le refus de ../../etc/passwd.
feat(deploy): paquet de livraison, unité durcie, contrôle de santé et retour arrière
Some checks failed
CI du proxy TizenTube (Rust) / Rust (proxy) (push) Failing after 4s
CI du proxy TizenTube (Rust) / Livraison vers la machine du service (push) Has been skipped
CI du proxy TizenTube (Rust) / Page d'état (Svelte) (push) Failing after 26s
CI du proxy TizenTube (Rust) / Paquet de livraison (déploiement) (push) Failing after 0s
CI du proxy TizenTube (Rust) / Rust (proxy) (pull_request) Failing after 4s
CI du proxy TizenTube (Rust) / Livraison vers la machine du service (pull_request) Has been skipped
CI du proxy TizenTube (Rust) / Page d'état (Svelte) (pull_request) Failing after 10s
CI du proxy TizenTube (Rust) / Paquet de livraison (déploiement) (pull_request) Failing after 1s
60a3ac6fbf
Cause : la mise en service se faisait à la main — copie du binaire, unité systemd écrite sur
la machine, redémarrage, vérification à l'œil. Aucun geste ne mesurait que le proxy relayait
encore, et rien ne ramenait la version d'avant quand la nouvelle ne démarrait pas.

Remède : deploy/tizentube-proxy.service remplace l'unité mitmdump — binaire Rust, interface
embarquée, service durci (utilisateur dédié, ProtectSystem=strict, AC en lecture seule, dossier
du script seul en écriture, filtres d'appels système, aucune capacité). deploy/deploy.sh est
idempotent, écrit son plan avec --dry-run sans rien toucher, garde la version en place en
bin/tizentube-proxy.precedent, bascule le binaire par un mv, installe l'unité si elle change,
redémarre, puis contrôle : deploy/controle-sante.sh exige /__sante = ok, /__status/ servie, et
un CONNECT dont la requête traverse le tunnel jusqu'à sa réponse. Un contrôle en échec ramène la
version précédente, la redémarre et la recontrôle : la livraison rend 0 (saine), 1 (ramenée en
arrière) ou 2 (service à l'arrêt). deploy/essai-deploy.sh joue les quatre cas sur la machine
courante, sans root, avec une doublure de systemctl qui tue par PID : 16 mesures sur 16. La CI
relit les scripts (shellcheck), écrit le plan à blanc et joue l'essai à chaque branche poussée ;
sur main, le job livraison pousse vers la machine du service et refuse de se dire vert sans clé.
Collaborator

fusion-auto : pas de fusion, la CI est rouge (Rust). Le chef de l'atelier renvoie la carte à l'agent ; l'étiquette tombera d'elle-même une fois corrigé.

`fusion-auto` : pas de fusion, la CI est rouge (Rust). Le chef de l'atelier renvoie la carte à l'agent ; l'étiquette tombera d'elle-même une fois corrigé.
Collaborator

fusion-auto : pas de fusion, la PR est en conflit avec main : à rebaser. Le chef de l'atelier renvoie la carte à l'agent ; l'étiquette tombera d'elle-même une fois corrigé.

`fusion-auto` : pas de fusion, la PR est en conflit avec main : à rebaser. Le chef de l'atelier renvoie la carte à l'agent ; l'étiquette tombera d'elle-même une fois corrigé.
Some checks failed
CI du proxy TizenTube (Rust) / Rust (proxy) (push) Failing after 4s
CI du proxy TizenTube (Rust) / Livraison vers la machine du service (push) Has been skipped
CI du proxy TizenTube (Rust) / Page d'état (Svelte) (push) Failing after 26s
CI du proxy TizenTube (Rust) / Paquet de livraison (déploiement) (push) Failing after 0s
CI du proxy TizenTube (Rust) / Rust (proxy) (pull_request) Failing after 4s
CI du proxy TizenTube (Rust) / Livraison vers la machine du service (pull_request) Has been skipped
CI du proxy TizenTube (Rust) / Page d'état (Svelte) (pull_request) Failing after 10s
CI du proxy TizenTube (Rust) / Paquet de livraison (déploiement) (pull_request) Failing after 1s
This pull request has changes conflicting with the target branch.
  • .forgejo/workflows/ci.yml
  • README.md
  • src/proxy.rs
  • tests/integration.rs
View command line instructions

Manual merge helper

Use this merge commit message when completing the merge manually.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin "hermes/paquet-service-deploiement:hermes/paquet-service-deploiement"
git switch "hermes/paquet-service-deploiement"
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/tizentube-proxy!6
No description provided.