commit 719a533130e0c22bf1d241746b4c4d1bcdb00b79 Author: claude Date: Fri Oct 2 09:43:36 2026 +0200 Version initiale : script, README et schéma d'architecture Co-Authored-By: Claude Opus 5.5 diff --git a/README.md b/README.md new file mode 100644 index 0000000..86fc7a6 --- /dev/null +++ b/README.md @@ -0,0 +1,156 @@ +# maj-stack-compose + +Script shell qui parcourt toutes les stacks **docker compose** d'un répertoire racine, détecte celles dont au moins une image a une nouvelle version, et les relance proprement (`down` puis `up -d`). + +![Structure du script](docs/architecture.png) + +## Principe + +Chaque sous-répertoire de la racine (par défaut `/home`) contenant un fichier compose est une stack : + +``` +/home +├── freshrss/ compose.yaml → stack « freshrss » +├── gitea/ docker-compose.yml → stack « gitea » +├── immich/ compose.yml → stack « immich » +└── notes/ (pas de fichier compose → ignoré) +``` + +Pour chaque stack, le script : + +1. **vérifie le fichier compose** avec `docker compose config -q` : + - fichier vide → stack ignorée sans rien afficher ; + - fichier invalide → erreur affichée en rouge ; +2. **ignore les stacks arrêtées** (sauf avec `-a`), pour ne pas démarrer ce qui a été coupé volontairement ; +3. **télécharge les images** avec `docker compose pull`. Cette étape ne touche pas aux conteneurs qui tournent ; +4. **compare**, pour chaque conteneur, l'image qu'il utilise avec l'image qui vient d'être téléchargée pour le même tag ; +5. si au moins une image a changé (ou avec `-f`), **relance toute la stack** : + `docker compose down` puis `docker compose up -d`. + +Les fichiers compose reconnus sont, dans cet ordre : `compose.yaml`, `compose.yml`, `docker-compose.yaml`, `docker-compose.yml`. + +## Prérequis + +- Linux avec **Docker** et **Docker Compose v2** (commande `docker compose`) ; +- `flock` (paquet `util-linux`, présent sur toutes les distributions courantes) ; +- un utilisateur ayant accès à Docker (root ou membre du groupe `docker`). + +## Installation + +```bash +git clone https://gitea.vlab.bzh/vLab-BZH/maj-stack-compose.git +cd maj-stack-compose +chmod +x update-stacks.sh +``` + +## Utilisation + +``` +update-stacks.sh [-v | -q] [-n] [-f] [-p] [-a] [RACINE] +``` + +| Option | Effet | +|---|---| +| `-v` | **verbose** : détaille chaque conteneur (image actuelle → nouvelle), chaque commande docker et sa sortie | +| `-q` | **quiet** : une seule ligne par stack (nom, répertoire, état) | +| `-n` | **dry-run** : détecte les mises à jour mais ne relance rien | +| `-f` | **force** : `down` + `up -d` de chaque stack, même sans mise à jour | +| `-p` | **prune** : supprime les images orphelines à la fin (`docker image prune -f`) | +| `-a` | **all** : traite aussi les stacks arrêtées (sinon elles sont ignorées) | +| `-h` | affiche l'aide | +| `RACINE` | répertoire contenant les stacks (défaut : `/home`) | + +### Exemples + +Voir ce qui serait mis à jour, sans rien toucher : + +```bash +./update-stacks.sh -n /home +``` + +Mettre à jour, avec un affichage minimal : + +```bash +./update-stacks.sh -q /home +``` + +Mettre à jour, puis supprimer les anciennes images : + +```bash +./update-stacks.sh -p /home +``` + +Relancer toutes les stacks, y compris celles qui sont arrêtées : + +```bash +./update-stacks.sh -f -a /home +``` + +## Affichage + +### Couleurs + +| Couleur | Signification | +|---|---| +| 🟢 vert | stack à jour | +| 🟡 jaune | mise à jour détectée, appliquée (ou à faire en dry-run), ou redémarrage forcé | +| 🔴 rouge | erreur (fichier compose invalide, échec du `pull`, du `down` ou du `up -d`) | +| gris | stack arrêtée, ignorée | + +Les couleurs sont désactivées automatiquement quand la sortie n'est pas un terminal (cron, redirection vers un fichier), ou si la variable `NO_COLOR` est définie. + +### Mode quiet (`-q`) + +``` +arcane /home/arcane ✔ à jour +arcane-agent /home/arcane-agent arrêtée +freshrss /home/freshrss ✔ à jour +immich /home/immich ↑ mise à jour appliquée +broken /home/broken ✘ erreur (fichier compose invalide) + yaml: line 3: mapping values are not allowed +``` + +### Mode normal (par défaut) + +``` +[2026-10-02 04:00:01] Racine : /home +[2026-10-02 04:00:01] == Stack : immich (/home/immich/compose.yml) +[2026-10-02 04:00:09] ↑ immich-server (ghcr.io/immich-app/immich-server:release) : nouvelle image disponible +[2026-10-02 04:00:31] ✔ stack relancée (down + up -d) : mise à jour +[2026-10-02 04:00:31] Terminé. Stacks relancées : 1 immich | Échecs : 0 +``` + +### Mode verbose (`-v`) + +Ajoute l'état de chaque conteneur avec les identifiants courts des images, ainsi que chaque commande docker (`$ docker compose pull …`) suivie de sa sortie complète. + +En mode normal et quiet, la sortie de docker est masquée ; elle n'est affichée, sous la stack concernée, qu'en cas d'échec. + +## Automatisation (cron) + +Exemple : chaque nuit à 4 h, avec nettoyage des images et journal dans `/var/log` (crontab de root) : + +``` +0 4 * * * /opt/maj-stack-compose/update-stacks.sh -p /home >> /var/log/update-stacks.log 2>&1 +``` + +Un verrou (`/tmp/update-stacks.lock`) empêche deux exécutions simultanées. + +## Code de retour + +| Code | Signification | +|---|---| +| `0` | toutes les stacks ont été traitées sans erreur | +| `1` | au moins une stack en erreur, une autre exécution en cours, Docker Compose v2 absent ou racine introuvable | + +## Limites + +- **Tags figés** : seules les nouvelles images publiées **sous le même tag** sont détectées (`latest`, `release`, `16`…). Une image figée sur `nginx:1.25.3` ne passera jamais en `1.25.4` : changer de version reste une décision manuelle. +- **Images construites localement** : les services avec `build:` sont ignorés au `pull` (`--ignore-buildable`) ; ils ne déclenchent donc pas de mise à jour. +- **Coupure de service** : `down` puis `up -d` arrête toute la stack quelques secondes, y compris les conteneurs dont l'image n'a pas changé. +- **Profondeur** : seuls les sous-répertoires directs de la racine sont examinés. + +## Documentation + +- [`docs/architecture.png`](docs/architecture.png) : schéma de fonctionnement du script ; +- [`docs/architecture.excalidraw`](docs/architecture.excalidraw) : source du schéma, modifiable sur [excalidraw.com](https://excalidraw.com) (menu *Ouvrir*). diff --git a/docs/architecture.excalidraw b/docs/architecture.excalidraw new file mode 100644 index 0000000..8c4c941 --- /dev/null +++ b/docs/architecture.excalidraw @@ -0,0 +1,3598 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "null", + "elements": [ + { + "id": "title", + "type": "text", + "x": 330, + "y": 20, + "width": 556.119140625, + "height": 42.5, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": null, + "seed": 1174136026, + "version": 2, + "versionNonce": 48566298, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "maj-stack-compose — update-stacks.sh", + "fontSize": 34, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "maj-stack-compose — update-stacks.sh", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "sub", + "type": "text", + "x": 360, + "y": 66, + "width": 454.0341796875, + "height": 22.5, + "angle": 0, + "strokeColor": "#757575", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a1", + "roundness": null, + "seed": 331673030, + "version": 2, + "versionNonce": 1439110790, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "structure du script : une stack = un sous-répertoire de RACINE", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "structure du script : une stack = un sous-répertoire de RACINE", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b1", + "type": "rectangle", + "x": 355, + "y": 100, + "width": 330, + "height": 110, + "angle": 0, + "strokeColor": "#4a9eed", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": { + "type": 3 + }, + "seed": 698235290, + "version": 3, + "versionNonce": 1480616346, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "AXA411FYd8AV_8oUcxwV8" + }, + { + "id": "a12", + "type": "arrow" + } + ], + "updated": 1790926958223, + "link": null, + "locked": false + }, + { + "id": "b2", + "type": "rectangle", + "x": 355, + "y": 245, + "width": 330, + "height": 85, + "angle": 0, + "strokeColor": "#4a9eed", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a3", + "roundness": { + "type": 3 + }, + "seed": 1907311878, + "version": 5, + "versionNonce": 957208538, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "qrdaVYNV4dx7YHDw-zqmu" + }, + { + "id": "a12", + "type": "arrow" + }, + { + "id": "a23", + "type": "arrow" + } + ], + "updated": 1790926958223, + "link": null, + "locked": false + }, + { + "id": "a12", + "type": "arrow", + "x": 520, + "y": 210, + "width": 0, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a4", + "roundness": null, + "seed": 932705882, + "version": 4, + "versionNonce": 428348678, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958223, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.5 + ], + [ + 0, + 34.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "b1", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "b2", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "b3", + "type": "rectangle", + "x": 355, + "y": 365, + "width": 330, + "height": 70, + "angle": 0, + "strokeColor": "#8b5cf6", + "backgroundColor": "#d0bfff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a5", + "roundness": { + "type": 3 + }, + "seed": 650949702, + "version": 4, + "versionNonce": 965668378, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "OTvnmJ5rWBUd50qKLJijV" + }, + { + "id": "a23", + "type": "arrow" + }, + { + "id": "a3d1", + "type": "arrow" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false + }, + { + "id": "a23", + "type": "arrow", + "x": 520, + "y": 325, + "width": 0, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a6", + "roundness": null, + "seed": 12327706, + "version": 4, + "versionNonce": 155167430, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958223, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.5 + ], + [ + 0, + 39.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "b2", + "focus": 0, + "gap": 5 + }, + "endBinding": { + "elementId": "b3", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "d1", + "type": "diamond", + "x": 370, + "y": 470, + "width": 300, + "height": 120, + "angle": 0, + "strokeColor": "#f59e0b", + "backgroundColor": "#fff3bf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a7", + "roundness": null, + "seed": 217725830, + "version": 5, + "versionNonce": 1487318874, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "VTqOiswF-52-lj61fqDzM" + }, + { + "id": "a3d1", + "type": "arrow" + }, + { + "id": "r1", + "type": "arrow" + }, + { + "id": "ad12", + "type": "arrow" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false + }, + { + "id": "a3d1", + "type": "arrow", + "x": 520, + "y": 435, + "width": 0, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a8", + "roundness": null, + "seed": 58566618, + "version": 4, + "versionNonce": 498210950, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958224, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.5 + ], + [ + 0, + 34.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "b3", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "d1", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "s1", + "type": "rectangle", + "x": 800, + "y": 500, + "width": 300, + "height": 60, + "angle": 0, + "strokeColor": "#adb5bd", + "backgroundColor": "#f1f3f5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a9", + "roundness": { + "type": 3 + }, + "seed": 980736710, + "version": 3, + "versionNonce": 1235357978, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "b6omSaoCkdLIkKpuS9hyG" + }, + { + "id": "r1", + "type": "arrow" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false + }, + { + "id": "r1", + "type": "arrow", + "x": 670, + "y": 530, + "width": 130, + "height": 0, + "angle": 0, + "strokeColor": "#757575", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aA", + "roundness": null, + "seed": 2012191898, + "version": 4, + "versionNonce": 1974361670, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958224, + "link": null, + "locked": false, + "points": [ + [ + 0.5, + 0 + ], + [ + 129.5, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "d1", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "s1", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "r1t", + "type": "text", + "x": 721.5, + "y": 504, + "width": 25.5, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aB", + "roundness": null, + "seed": 1977192966, + "version": 2, + "versionNonce": 1571439302, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "non", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "non", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "d2", + "type": "diamond", + "x": 370, + "y": 630, + "width": 300, + "height": 120, + "angle": 0, + "strokeColor": "#f59e0b", + "backgroundColor": "#fff3bf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aC", + "roundness": null, + "seed": 465893722, + "version": 5, + "versionNonce": 494950554, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "G0OLT1qfK_fRgVTapDgh0" + }, + { + "id": "ad12", + "type": "arrow" + }, + { + "id": "r2", + "type": "arrow" + }, + { + "id": "ad23", + "type": "arrow" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false + }, + { + "id": "ad12", + "type": "arrow", + "x": 520, + "y": 590, + "width": 0, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aD", + "roundness": null, + "seed": 707092806, + "version": 4, + "versionNonce": 1843530566, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "ninoBIv4wnNm7xHTY77gT" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.5 + ], + [ + 0, + 39.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "d1", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "d2", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "s2", + "type": "rectangle", + "x": 800, + "y": 650, + "width": 300, + "height": 80, + "angle": 0, + "strokeColor": "#ef4444", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aE", + "roundness": { + "type": 3 + }, + "seed": 2119220762, + "version": 3, + "versionNonce": 1462710874, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "gr1eJrd_WKprB63Yjhz83" + }, + { + "id": "r2", + "type": "arrow" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false + }, + { + "id": "r2", + "type": "arrow", + "x": 670, + "y": 690, + "width": 130, + "height": 0, + "angle": 0, + "strokeColor": "#ef4444", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aF", + "roundness": null, + "seed": 1521830022, + "version": 4, + "versionNonce": 121260294, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958224, + "link": null, + "locked": false, + "points": [ + [ + 0.5, + 0 + ], + [ + 129.5, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "d2", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "s2", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "r2t", + "type": "text", + "x": 721.5, + "y": 664, + "width": 25.5, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aG", + "roundness": null, + "seed": 1283844826, + "version": 2, + "versionNonce": 958387738, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "non", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "non", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "d3", + "type": "diamond", + "x": 370, + "y": 790, + "width": 300, + "height": 120, + "angle": 0, + "strokeColor": "#f59e0b", + "backgroundColor": "#fff3bf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aH", + "roundness": null, + "seed": 390481862, + "version": 5, + "versionNonce": 1490086362, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "ux8hfUxjZALgWJKxIwOPw" + }, + { + "id": "ad23", + "type": "arrow" + }, + { + "id": "r3", + "type": "arrow" + }, + { + "id": "ad3b4", + "type": "arrow" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false + }, + { + "id": "ad23", + "type": "arrow", + "x": 520, + "y": 750, + "width": 0, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aI", + "roundness": null, + "seed": 485982106, + "version": 4, + "versionNonce": 1002608134, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "gRFXHeGE-TA_aoubsLAUQ" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.5 + ], + [ + 0, + 39.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "d2", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "d3", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "s3", + "type": "rectangle", + "x": 800, + "y": 820, + "width": 300, + "height": 60, + "angle": 0, + "strokeColor": "#adb5bd", + "backgroundColor": "#f1f3f5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aJ", + "roundness": { + "type": 3 + }, + "seed": 1878351622, + "version": 3, + "versionNonce": 1179270042, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "NAiDjUi0XXaTQNMw9zrCr" + }, + { + "id": "r3", + "type": "arrow" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false + }, + { + "id": "r3", + "type": "arrow", + "x": 670, + "y": 850, + "width": 130, + "height": 0, + "angle": 0, + "strokeColor": "#757575", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aK", + "roundness": null, + "seed": 1004043354, + "version": 4, + "versionNonce": 1813892038, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958224, + "link": null, + "locked": false, + "points": [ + [ + 0.5, + 0 + ], + [ + 129.5, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "d3", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "s3", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "r3t", + "type": "text", + "x": 721.5, + "y": 824, + "width": 25.5, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aL", + "roundness": null, + "seed": 1765772870, + "version": 2, + "versionNonce": 1556727558, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "non", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "non", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b4", + "type": "rectangle", + "x": 355, + "y": 950, + "width": 330, + "height": 80, + "angle": 0, + "strokeColor": "#06b6d4", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aM", + "roundness": { + "type": 3 + }, + "seed": 1996218650, + "version": 5, + "versionNonce": 508706394, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "QqkPAtdymfMmB5PEJQ2LA" + }, + { + "id": "ad3b4", + "type": "arrow" + }, + { + "id": "r4", + "type": "arrow" + }, + { + "id": "a45", + "type": "arrow" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false + }, + { + "id": "ad3b4", + "type": "arrow", + "x": 520, + "y": 910, + "width": 0, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aN", + "roundness": null, + "seed": 1879168390, + "version": 4, + "versionNonce": 141473990, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "nwZ387APoiGA3ghmJxm2O" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.5 + ], + [ + 0, + 39.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "d3", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "b4", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "s4", + "type": "rectangle", + "x": 800, + "y": 960, + "width": 300, + "height": 60, + "angle": 0, + "strokeColor": "#ef4444", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aO", + "roundness": { + "type": 3 + }, + "seed": 1775545818, + "version": 3, + "versionNonce": 1697131738, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "QmHthwKxDP2ymc3KTeSGS" + }, + { + "id": "r4", + "type": "arrow" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false + }, + { + "id": "r4", + "type": "arrow", + "x": 685, + "y": 990, + "width": 115, + "height": 0, + "angle": 0, + "strokeColor": "#ef4444", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aP", + "roundness": null, + "seed": 1299788998, + "version": 4, + "versionNonce": 1465116294, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958224, + "link": null, + "locked": false, + "points": [ + [ + 0.5, + 0 + ], + [ + 114.5, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "b4", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "s4", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "r4t", + "type": "text", + "x": 720, + "y": 964, + "width": 38.681640625, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aQ", + "roundness": null, + "seed": 1232462490, + "version": 2, + "versionNonce": 137676250, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "échec", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "échec", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b5", + "type": "rectangle", + "x": 355, + "y": 1070, + "width": 330, + "height": 80, + "angle": 0, + "strokeColor": "#06b6d4", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aR", + "roundness": { + "type": 3 + }, + "seed": 457354246, + "version": 4, + "versionNonce": 711910554, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "2vWROU8vrWuqrUb8i2OPA" + }, + { + "id": "a45", + "type": "arrow" + }, + { + "id": "a5d4", + "type": "arrow" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false + }, + { + "id": "a45", + "type": "arrow", + "x": 520, + "y": 1030, + "width": 0, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aS", + "roundness": null, + "seed": 814907226, + "version": 4, + "versionNonce": 1438313542, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958224, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.5 + ], + [ + 0, + 39.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "b4", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "b5", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "d4", + "type": "diamond", + "x": 370, + "y": 1190, + "width": 300, + "height": 130, + "angle": 0, + "strokeColor": "#f59e0b", + "backgroundColor": "#fff3bf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aT", + "roundness": null, + "seed": 928725830, + "version": 5, + "versionNonce": 164951514, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "eMCImByi4QBuYIbTZQ7x-" + }, + { + "id": "a5d4", + "type": "arrow" + }, + { + "id": "r5", + "type": "arrow" + }, + { + "id": "ad45", + "type": "arrow" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false + }, + { + "id": "a5d4", + "type": "arrow", + "x": 520, + "y": 1150, + "width": 0, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aU", + "roundness": null, + "seed": 1803387930, + "version": 4, + "versionNonce": 601016838, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958224, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.5 + ], + [ + 0, + 39.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "b5", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "d4", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "s5", + "type": "rectangle", + "x": 800, + "y": 1225, + "width": 300, + "height": 60, + "angle": 0, + "strokeColor": "#22c55e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aV", + "roundness": { + "type": 3 + }, + "seed": 941613702, + "version": 3, + "versionNonce": 294832026, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "d60mYqdkQFCTyP_kC8D1k" + }, + { + "id": "r5", + "type": "arrow" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false + }, + { + "id": "r5", + "type": "arrow", + "x": 670, + "y": 1255, + "width": 130, + "height": 0, + "angle": 0, + "strokeColor": "#22c55e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aW", + "roundness": null, + "seed": 1143599322, + "version": 4, + "versionNonce": 2083160006, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958224, + "link": null, + "locked": false, + "points": [ + [ + 0.5, + 0 + ], + [ + 129.5, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "d4", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "s5", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "r5t", + "type": "text", + "x": 721.5, + "y": 1229, + "width": 25.5, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aX", + "roundness": null, + "seed": 1615700422, + "version": 2, + "versionNonce": 612780678, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "non", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "non", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "d5", + "type": "diamond", + "x": 370, + "y": 1360, + "width": 300, + "height": 120, + "angle": 0, + "strokeColor": "#f59e0b", + "backgroundColor": "#fff3bf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aY", + "roundness": null, + "seed": 1311425946, + "version": 5, + "versionNonce": 1966534426, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "9Cn50kC598m51ROD2_mJc" + }, + { + "id": "ad45", + "type": "arrow" + }, + { + "id": "r6", + "type": "arrow" + }, + { + "id": "ad5b6", + "type": "arrow" + } + ], + "updated": 1790926958225, + "link": null, + "locked": false + }, + { + "id": "ad45", + "type": "arrow", + "x": 520, + "y": 1320, + "width": 0, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aZ", + "roundness": null, + "seed": 318863622, + "version": 4, + "versionNonce": 1652649158, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "uEiy1dln5S8zjmH7Bzbg0" + } + ], + "updated": 1790926958224, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.5 + ], + [ + 0, + 39.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "d4", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "d5", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "s6", + "type": "rectangle", + "x": 800, + "y": 1380, + "width": 300, + "height": 80, + "angle": 0, + "strokeColor": "#f59e0b", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aa", + "roundness": { + "type": 3 + }, + "seed": 850592346, + "version": 3, + "versionNonce": 1573271770, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "dj8Sb1P_xQSOyhXLsBQQJ" + }, + { + "id": "r6", + "type": "arrow" + } + ], + "updated": 1790926958225, + "link": null, + "locked": false + }, + { + "id": "r6", + "type": "arrow", + "x": 670, + "y": 1420, + "width": 130, + "height": 0, + "angle": 0, + "strokeColor": "#f59e0b", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ab", + "roundness": null, + "seed": 1203267654, + "version": 4, + "versionNonce": 175315590, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "points": [ + [ + 0.5, + 0 + ], + [ + 129.5, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "d5", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "s6", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "r6t", + "type": "text", + "x": 721.5, + "y": 1394, + "width": 21.72314453125, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ac", + "roundness": null, + "seed": 2090182426, + "version": 2, + "versionNonce": 2169434, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "oui", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "oui", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b6", + "type": "rectangle", + "x": 355, + "y": 1520, + "width": 330, + "height": 80, + "angle": 0, + "strokeColor": "#f59e0b", + "backgroundColor": "#ffd8a8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ad", + "roundness": { + "type": 3 + }, + "seed": 2119135110, + "version": 5, + "versionNonce": 1653198938, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "xSOIXxviRDZ8NNqpFTZuq" + }, + { + "id": "ad5b6", + "type": "arrow" + }, + { + "id": "r7", + "type": "arrow" + }, + { + "id": "a67", + "type": "arrow" + } + ], + "updated": 1790926958225, + "link": null, + "locked": false + }, + { + "id": "ad5b6", + "type": "arrow", + "x": 520, + "y": 1480, + "width": 0, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ae", + "roundness": null, + "seed": 1682289626, + "version": 4, + "versionNonce": 1363584902, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "yi2gXDqR-aToPd49wNBem" + } + ], + "updated": 1790926958225, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.5 + ], + [ + 0, + 39.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "d5", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "b6", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "s7", + "type": "rectangle", + "x": 800, + "y": 1520, + "width": 300, + "height": 80, + "angle": 0, + "strokeColor": "#f59e0b", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "af", + "roundness": { + "type": 3 + }, + "seed": 855871174, + "version": 3, + "versionNonce": 79794714, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "5ZfHkCJzCjqVP_DJkhuHI" + }, + { + "id": "r7", + "type": "arrow" + } + ], + "updated": 1790926958225, + "link": null, + "locked": false + }, + { + "id": "r7", + "type": "arrow", + "x": 685, + "y": 1560, + "width": 115, + "height": 0, + "angle": 0, + "strokeColor": "#f59e0b", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ag", + "roundness": null, + "seed": 467019930, + "version": 4, + "versionNonce": 809061702, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "points": [ + [ + 0.5, + 0 + ], + [ + 114.5, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "b6", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "s7", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "r7t", + "type": "text", + "x": 706.5, + "y": 1534, + "width": 50.037109375, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ah", + "roundness": null, + "seed": 1383187974, + "version": 2, + "versionNonce": 174651078, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "résultat", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "résultat", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "loop", + "type": "arrow", + "x": 355, + "y": 1560, + "width": 110, + "height": 1160, + "angle": 0, + "strokeColor": "#8b5cf6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ai", + "roundness": null, + "seed": 452592986, + "version": 2, + "versionNonce": 1708867738, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "points": [ + [ + 0.5, + 0 + ], + [ + -110, + 0 + ], + [ + -110, + -1160 + ], + [ + -0.5, + -1160 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "loopt", + "type": "text", + "x": 150, + "y": 950, + "width": 59.9853515625, + "height": 45, + "angle": 0, + "strokeColor": "#8b5cf6", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aj", + "roundness": null, + "seed": 764876102, + "version": 2, + "versionNonce": 1760177670, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "stack\nsuivante", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "stack\nsuivante", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b7", + "type": "rectangle", + "x": 355, + "y": 1680, + "width": 330, + "height": 100, + "angle": 0, + "strokeColor": "#4a9eed", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ak", + "roundness": { + "type": 3 + }, + "seed": 1795443226, + "version": 3, + "versionNonce": 1684190490, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "kDNsnNZG9mM91Djas74w7" + }, + { + "id": "a67", + "type": "arrow" + } + ], + "updated": 1790926958225, + "link": null, + "locked": false + }, + { + "id": "a67", + "type": "arrow", + "x": 520, + "y": 1600, + "width": 0, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "al", + "roundness": null, + "seed": 1842379910, + "version": 4, + "versionNonce": 1073576518, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "WfQk0BLMNa0XivgyiOZuG" + } + ], + "updated": 1790926958225, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.5 + ], + [ + 0, + 79.5 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "b6", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "b7", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "leg", + "type": "rectangle", + "x": 1180, + "y": 100, + "width": 330, + "height": 520, + "angle": 0, + "strokeColor": "#adb5bd", + "backgroundColor": "#f8f9fa", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "am", + "roundness": { + "type": 3 + }, + "seed": 1927804634, + "version": 2, + "versionNonce": 413360666, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false + }, + { + "id": "legt", + "type": "text", + "x": 1205, + "y": 115, + "width": 82.6171875, + "height": 30, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "an", + "roundness": null, + "seed": 148569030, + "version": 2, + "versionNonce": 1062134918, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "Légende", + "fontSize": 24, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Légende", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "lg1", + "type": "rectangle", + "x": 1205, + "y": 165, + "width": 34, + "height": 26, + "angle": 0, + "strokeColor": "#22c55e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ao", + "roundness": null, + "seed": 1127746458, + "version": 2, + "versionNonce": 1290014426, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false + }, + { + "id": "lg1t", + "type": "text", + "x": 1255, + "y": 165, + "width": 41.484375, + "height": 22.5, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ap", + "roundness": null, + "seed": 886280966, + "version": 2, + "versionNonce": 1850500038, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "à jour", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "à jour", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "lg2", + "type": "rectangle", + "x": 1205, + "y": 205, + "width": 34, + "height": 26, + "angle": 0, + "strokeColor": "#f59e0b", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aq", + "roundness": null, + "seed": 1646757978, + "version": 2, + "versionNonce": 904995738, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false + }, + { + "id": "lg2t", + "type": "text", + "x": 1255, + "y": 205, + "width": 138.9375, + "height": 22.5, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ar", + "roundness": null, + "seed": 1547125318, + "version": 2, + "versionNonce": 936901382, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "mise à jour / forcée", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "mise à jour / forcée", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "lg3", + "type": "rectangle", + "x": 1205, + "y": 245, + "width": 34, + "height": 26, + "angle": 0, + "strokeColor": "#ef4444", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "as", + "roundness": null, + "seed": 394882330, + "version": 2, + "versionNonce": 1308467290, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false + }, + { + "id": "lg3t", + "type": "text", + "x": 1255, + "y": 245, + "width": 42.9609375, + "height": 22.5, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "at", + "roundness": null, + "seed": 300091782, + "version": 2, + "versionNonce": 1382984262, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "erreur", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "erreur", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "lg4", + "type": "rectangle", + "x": 1205, + "y": 285, + "width": 34, + "height": 26, + "angle": 0, + "strokeColor": "#adb5bd", + "backgroundColor": "#f1f3f5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "au", + "roundness": null, + "seed": 953203162, + "version": 2, + "versionNonce": 1356629274, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false + }, + { + "id": "lg4t", + "type": "text", + "x": 1255, + "y": 285, + "width": 116.9208984375, + "height": 22.5, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "av", + "roundness": null, + "seed": 85190854, + "version": 2, + "versionNonce": 485323142, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "ignorée / arrêtée", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "ignorée / arrêtée", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "modt", + "type": "text", + "x": 1205, + "y": 340, + "width": 94.62890625, + "height": 30, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aw", + "roundness": null, + "seed": 1964011162, + "version": 2, + "versionNonce": 134271450, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "Affichage", + "fontSize": 24, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Affichage", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "mod", + "type": "text", + "x": 1205, + "y": 380, + "width": 207.087890625, + "height": 67.5, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ax", + "roundness": null, + "seed": 1969676294, + "version": 2, + "versionNonce": 311513286, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "-q une ligne par stack\n(défaut) détail par stack\n-v conteneurs + commandes", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "-q une ligne par stack\n(défaut) détail par stack\n-v conteneurs + commandes", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "optt", + "type": "text", + "x": 1205, + "y": 470, + "width": 156.97265625, + "height": 30, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ay", + "roundness": null, + "seed": 553356122, + "version": 2, + "versionNonce": 377325210, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "Options d'action", + "fontSize": 24, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Options d'action", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "opt", + "type": "text", + "x": 1205, + "y": 510, + "width": 214.9013671875, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "az", + "roundness": null, + "seed": 651751238, + "version": 2, + "versionNonce": 1056393222, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958222, + "link": null, + "locked": false, + "text": "-n dry-run -f forcer\n-p prune -a stacks arrêtées", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "-n dry-run -f forcer\n-p prune -a stacks arrêtées", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "AXA411FYd8AV_8oUcxwV8", + "type": "text", + "x": 415.859375, + "y": 117.5, + "width": 208.28125, + "height": 75, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b00", + "roundness": null, + "seed": 1157997402, + "version": 3, + "versionNonce": 270680454, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "Lancement\noptions -v -q -n -f -p -a\nRACINE (défaut : /home)", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "b1", + "originalText": "Lancement\noptions -v -q -n -f -p -a\nRACINE (défaut : /home)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "qrdaVYNV4dx7YHDw-zqmu", + "type": "text", + "x": 386.1474609375, + "y": 250, + "width": 267.705078125, + "height": 75, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b01", + "roundness": null, + "seed": 1307322394, + "version": 3, + "versionNonce": 725680602, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "Contrôles\nverrou flock · docker compose v2\nracine existante", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "b2", + "originalText": "Contrôles\nverrou flock · docker compose v2\nracine existante", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "OTvnmJ5rWBUd50qKLJijV", + "type": "text", + "x": 407.8173828125, + "y": 387.5, + "width": 224.365234375, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b02", + "roundness": null, + "seed": 2064352326, + "version": 3, + "versionNonce": 1722556614, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "Pour chaque sous-répertoire", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "b3", + "originalText": "Pour chaque sous-répertoire", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "VTqOiswF-52-lj61fqDzM", + "type": "text", + "x": 477.5146484375, + "y": 505, + "width": 84.970703125, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b03", + "roundness": null, + "seed": 1120559622, + "version": 3, + "versionNonce": 1110947482, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "fichier\ncompose ?", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "d1", + "originalText": "fichier\ncompose ?", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b6omSaoCkdLIkKpuS9hyG", + "type": "text", + "x": 863.41015625, + "y": 518.75, + "width": 173.1796875, + "height": 22.5, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b04", + "roundness": null, + "seed": 36247494, + "version": 3, + "versionNonce": 1731729414, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "ignoré, rien n'est affiché", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "s1", + "originalText": "ignoré, rien n'est affiché", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "G0OLT1qfK_fRgVTapDgh0", + "type": "text", + "x": 484.169921875, + "y": 665, + "width": 71.66015625, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b05", + "roundness": null, + "seed": 639018374, + "version": 3, + "versionNonce": 1509544794, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "config -q\nvalide ?", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "d2", + "originalText": "config -q\nvalide ?", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "ninoBIv4wnNm7xHTY77gT", + "type": "text", + "x": 509.138427734375, + "y": 599.375, + "width": 21.72314453125, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b06", + "roundness": null, + "seed": 141821126, + "version": 3, + "versionNonce": 969090886, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "oui", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ad12", + "originalText": "oui", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "gr1eJrd_WKprB63Yjhz83", + "type": "text", + "x": 863.79248046875, + "y": 667.5, + "width": 172.4150390625, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b07", + "roundness": null, + "seed": 357347974, + "version": 3, + "versionNonce": 349853722, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "vide : ignoré\ninvalide : erreur (rouge)", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "s2", + "originalText": "vide : ignoré\ninvalide : erreur (rouge)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "ux8hfUxjZALgWJKxIwOPw", + "type": "text", + "x": 464.482421875, + "y": 825, + "width": 111.03515625, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b08", + "roundness": null, + "seed": 1549219910, + "version": 3, + "versionNonce": 2125090438, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "stack lancée ?\n(ou option -a)", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "d3", + "originalText": "stack lancée ?\n(ou option -a)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "gRFXHeGE-TA_aoubsLAUQ", + "type": "text", + "x": 509.138427734375, + "y": 759.375, + "width": 21.72314453125, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b09", + "roundness": null, + "seed": 915685254, + "version": 3, + "versionNonce": 1389118682, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "oui", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ad23", + "originalText": "oui", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "NAiDjUi0XXaTQNMw9zrCr", + "type": "text", + "x": 890.28271484375, + "y": 838.75, + "width": 119.4345703125, + "height": 22.5, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0A", + "roundness": null, + "seed": 1414391110, + "version": 3, + "versionNonce": 1118473670, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "« arrêtée » (gris)", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "s3", + "originalText": "« arrêtée » (gris)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "QqkPAtdymfMmB5PEJQ2LA", + "type": "text", + "x": 419.4775390625, + "y": 965, + "width": 201.044921875, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0B", + "roundness": null, + "seed": 1195139846, + "version": 3, + "versionNonce": 2103613850, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "docker compose pull\n(conteneurs non touchés)", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "b4", + "originalText": "docker compose pull\n(conteneurs non touchés)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "nwZ387APoiGA3ghmJxm2O", + "type": "text", + "x": 509.138427734375, + "y": 919.375, + "width": 21.72314453125, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0C", + "roundness": null, + "seed": 1225134662, + "version": 3, + "versionNonce": 1927341318, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "oui", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ad3b4", + "originalText": "oui", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "QmHthwKxDP2ymc3KTeSGS", + "type": "text", + "x": 872.3046875, + "y": 978.75, + "width": 155.390625, + "height": 22.5, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0D", + "roundness": null, + "seed": 1692905478, + "version": 3, + "versionNonce": 1406162522, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "échec : erreur (rouge)", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "s4", + "originalText": "échec : erreur (rouge)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "2vWROU8vrWuqrUb8i2OPA", + "type": "text", + "x": 396.30859375, + "y": 1085, + "width": 247.3828125, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0E", + "roundness": null, + "seed": 39921094, + "version": 3, + "versionNonce": 1355437126, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "comparer l'image du conteneur\navec l'image tirée", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "b5", + "originalText": "comparer l'image du conteneur\navec l'image tirée", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "eMCImByi4QBuYIbTZQ7x-", + "type": "text", + "x": 458.6328125, + "y": 1230, + "width": 122.734375, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0F", + "roundness": null, + "seed": 828470150, + "version": 3, + "versionNonce": 921205530, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "nouvelle image\nou option -f ?", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "d4", + "originalText": "nouvelle image\nou option -f ?", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "d60mYqdkQFCTyP_kC8D1k", + "type": "text", + "x": 907.021484375, + "y": 1243.75, + "width": 85.95703125, + "height": 22.5, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0G", + "roundness": null, + "seed": 876058950, + "version": 3, + "versionNonce": 1715723142, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "à jour (vert)", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "s5", + "originalText": "à jour (vert)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "9Cn50kC598m51ROD2_mJc", + "type": "text", + "x": 470.0146484375, + "y": 1395, + "width": 99.970703125, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0H", + "roundness": null, + "seed": 462591750, + "version": 3, + "versionNonce": 1964902362, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "dry-run\n(option -n) ?", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "d5", + "originalText": "dry-run\n(option -n) ?", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "uEiy1dln5S8zjmH7Bzbg0", + "type": "text", + "x": 509.138427734375, + "y": 1329.375, + "width": 21.72314453125, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0I", + "roundness": null, + "seed": 477480518, + "version": 3, + "versionNonce": 2039118534, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "oui", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ad45", + "originalText": "oui", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "dj8Sb1P_xQSOyhXLsBQQJ", + "type": "text", + "x": 857.29736328125, + "y": 1397.5, + "width": 185.4052734375, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0J", + "roundness": null, + "seed": 1384375302, + "version": 3, + "versionNonce": 377748634, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "mise à jour à faire (jaune)\nrien n'est relancé", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "s6", + "originalText": "mise à jour à faire (jaune)\nrien n'est relancé", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "xSOIXxviRDZ8NNqpFTZuq", + "type": "text", + "x": 412.24609375, + "y": 1535, + "width": 215.5078125, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0K", + "roundness": null, + "seed": 1937365446, + "version": 3, + "versionNonce": 20656646, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "docker compose down\npuis docker compose up -d", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "b6", + "originalText": "docker compose down\npuis docker compose up -d", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "yi2gXDqR-aToPd49wNBem", + "type": "text", + "x": 507.25, + "y": 1489.375, + "width": 25.5, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0L", + "roundness": null, + "seed": 1676062982, + "version": 3, + "versionNonce": 684546394, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "non", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ad5b6", + "originalText": "non", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "5ZfHkCJzCjqVP_DJkhuHI", + "type": "text", + "x": 856.7919921875, + "y": 1537.5, + "width": 186.416015625, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0M", + "roundness": null, + "seed": 307480262, + "version": 3, + "versionNonce": 408092998, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "succès : appliquée (jaune)\néchec : erreur (rouge)", + "fontSize": 18, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "s7", + "originalText": "succès : appliquée (jaune)\néchec : erreur (rouge)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "kDNsnNZG9mM91Djas74w7", + "type": "text", + "x": 391.455078125, + "y": 1692.5, + "width": 257.08984375, + "height": 75, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0N", + "roundness": null, + "seed": 1332458630, + "version": 3, + "versionNonce": 189603354, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "Fin\nnettoyage des images (-p)\nrécapitulatif coloré · code retour", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "b7", + "originalText": "Fin\nnettoyage des images (-p)\nrécapitulatif coloré · code retour", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "WfQk0BLMNa0XivgyiOZuG", + "type": "text", + "x": 476.093017578125, + "y": 1629.375, + "width": 87.81396484375, + "height": 21.25, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0O", + "roundness": null, + "seed": 38745030, + "version": 3, + "versionNonce": 1915676806, + "isDeleted": false, + "boundElements": null, + "updated": 1790926958225, + "link": null, + "locked": false, + "text": "plus de stack", + "fontSize": 17, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "a67", + "originalText": "plus de stack", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/architecture.png b/docs/architecture.png new file mode 100644 index 0000000..5b8ddfa Binary files /dev/null and b/docs/architecture.png differ diff --git a/update-stacks.sh b/update-stacks.sh new file mode 100644 index 0000000..a97d53b --- /dev/null +++ b/update-stacks.sh @@ -0,0 +1,216 @@ +#!/usr/bin/env bash +# +# update-stacks.sh — Met à jour les stacks docker compose d'une racine donnée. +# +# Pour chaque sous-répertoire de la racine contenant un fichier compose : +# 1. télécharge les images (pull) — sans toucher aux conteneurs en cours ; +# 2. compare l'image utilisée par chaque conteneur à l'image fraîchement tirée ; +# 3. si au moins une image a changé : down puis up -d de toute la stack. +# Les sous-répertoires sans fichier compose, ou avec un fichier compose vide, +# sont ignorés sans rien afficher. Un fichier compose invalide est signalé en erreur. +# +# Usage : update-stacks.sh [-v | -q] [-n] [-f] [-p] [-a] [RACINE] +# -v verbose : détaille chaque conteneur et chaque commande docker +# -q quiet : n'affiche qu'une ligne par stack (nom, répertoire, état) +# -n dry-run : détecte les mises à jour mais ne relance rien +# -f force : down + up -d de chaque stack, même sans mise à jour +# -p prune : supprime les images orphelines (dangling) à la fin +# -a all : traite aussi les stacks arrêtées (par défaut : ignorées) +# RACINE : répertoire contenant les stacks (défaut : /home) +# +# Couleurs : vert = à jour, jaune = mise(s) à jour détectée(s) ou forcée(s), rouge = erreur. +# Désactivées automatiquement hors terminal (cron, redirection) ou si NO_COLOR est défini. + +set -uo pipefail + +VERBOSITY=1 # 0 = quiet, 1 = normal, 2 = verbose +DRY_RUN=0 +FORCE=0 +PRUNE=0 +INCLUDE_STOPPED=0 + +while getopts "vqnfpah" opt; do + case "$opt" in + v) VERBOSITY=2 ;; + q) VERBOSITY=0 ;; + n) DRY_RUN=1 ;; + f) FORCE=1 ;; + p) PRUNE=1 ;; + a) INCLUDE_STOPPED=1 ;; + h|*) sed -n '2,23p' "$0"; exit 0 ;; + esac +done +shift $((OPTIND - 1)) + +ROOT="${1:-/home}" +ROOT="${ROOT%/}" +COMPOSE_FILES=(compose.yaml compose.yml docker-compose.yaml docker-compose.yml) +LOCK_FILE="/tmp/update-stacks.lock" + +if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then + GREEN=$'\e[32m' YELLOW=$'\e[33m' RED=$'\e[31m' BOLD=$'\e[1m' DIM=$'\e[2m' RESET=$'\e[0m' +else + GREEN="" YELLOW="" RED="" BOLD="" DIM="" RESET="" +fi + +ts() { date '+%F %T'; } +log() { (( VERBOSITY >= 1 )) && printf '[%s] %s\n' "$(ts)" "$*"; } +debug() { (( VERBOSITY >= 2 )) && printf '[%s] %s%s%s\n' "$(ts)" "$DIM" "$*" "$RESET"; } +ok() { log " ${GREEN}✔ $*${RESET}"; } +warn() { log " ${YELLOW}↑ $*${RESET}"; } +error() { printf '[%s] %s✘ %s%s\n' "$(ts)" "$RED" "$*" "$RESET" >&2; } + +# En mode quiet : une seule ligne par stack, l'état s'affiche au bout +quiet_status() { (( VERBOSITY == 0 )) && printf '%s%s%s\n' "$1" "$2" "$RESET"; } + +# Exécute une commande docker. En verbose, affiche la commande et sa sortie ; +# sinon la sortie est gardée dans RUN_OUTPUT pour être affichée par fail(). +RUN_OUTPUT="" +run() { + RUN_OUTPUT="" + if (( VERBOSITY >= 2 )); then + debug " \$ $*" + "$@" + else + RUN_OUTPUT=$("$@" 2>&1) + fi +} + +# Signale l'échec d'une stack, avec la sortie de la commande fautive +fail() { + quiet_status "$RED" "✘ erreur ($1)" + (( VERBOSITY >= 1 )) && error "$name : erreur ($1)" + [[ -n "$RUN_OUTPUT" ]] && printf '%s\n' "$RUN_OUTPUT" | sed 's/^/ /' >&2 + failed_stacks+=("$name") +} + +# Empêche deux exécutions simultanées (utile en cron) +exec 9>"$LOCK_FILE" +if ! flock -n 9; then + error "Une autre exécution est déjà en cours, abandon." + exit 1 +fi + +if ! docker compose version >/dev/null 2>&1; then + error "'docker compose' (v2) introuvable." + exit 1 +fi + +[[ -d "$ROOT" ]] || { error "$ROOT n'est pas un répertoire."; exit 1; } + +# Renvoie 0 si au moins un conteneur de la stack tourne sur une image obsolète +stack_has_updates() { + local cid running_id image_ref latest_id service updated=1 + for cid in $(docker compose ps -q 2>/dev/null); do + running_id=$(docker inspect -f '{{.Image}}' "$cid") + image_ref=$(docker inspect -f '{{.Config.Image}}' "$cid") + service=$(docker inspect -f '{{index .Config.Labels "com.docker.compose.service"}}' "$cid") + latest_id=$(docker image inspect -f '{{.Id}}' "$image_ref" 2>/dev/null) || { + debug " · $service ($image_ref) : image locale introuvable, ignoré" + continue + } + if [[ "$running_id" != "$latest_id" ]]; then + warn "$service ($image_ref) : nouvelle image disponible" + debug " actuelle : ${running_id:7:12} → nouvelle : ${latest_id:7:12}" + updated=0 + else + debug " · $service ($image_ref) : à jour (${running_id:7:12})" + fi + done + return $updated +} + +updated_stacks=() +failed_stacks=() + +log "${BOLD}Racine : $ROOT${RESET}$( (( DRY_RUN )) && echo ' [dry-run]')$( (( FORCE )) && echo ' [force]')" + +for dir in "$ROOT"/*/; do + dir="${dir%/}" + name=$(basename "$dir") + + found="" + for f in "${COMPOSE_FILES[@]}"; do + [[ -f "$dir/$f" ]] && { found="$f"; break; } + done + [[ -n "$found" ]] || continue + + pushd "$dir" >/dev/null || continue + + # Valide le fichier compose avant tout affichage : un fichier vide est ignoré + # comme un répertoire sans stack, un fichier invalide est signalé. + config_ok=1 + RUN_OUTPUT=$(docker compose config -q 2>&1) || config_ok=0 + if (( ! config_ok )) && [[ "$RUN_OUTPUT" == *"empty compose file"* ]]; then + popd >/dev/null; continue + fi + + log "${BOLD}== Stack : $name${RESET} ($dir/$found)" + (( VERBOSITY == 0 )) && printf '%-25s %-45s ' "$name" "$dir" + + if (( ! config_ok )); then + fail "fichier compose invalide" + popd >/dev/null; continue + fi + + if [[ -z "$(docker compose ps -q 2>/dev/null)" && $INCLUDE_STOPPED -eq 0 ]]; then + log " ${DIM}stack arrêtée, ignorée (utiliser -a pour l'inclure)${RESET}" + quiet_status "$DIM" "arrêtée" + popd >/dev/null; continue + fi + + # Le pull ne modifie pas les conteneurs en cours : il sert à la détection + debug " Téléchargement des images…" + if ! run docker compose pull --ignore-buildable; then + fail "pull" + popd >/dev/null; continue + fi + + # Stack arrêtée incluse via -a : aucun conteneur à comparer, on la lance. + # stack_has_updates est toujours appelé pour afficher le détail, même avec -f. + has_updates=0 + if [[ -z "$(docker compose ps -q 2>/dev/null)" ]] || stack_has_updates; then + has_updates=1 + fi + + if (( has_updates || FORCE )); then + if (( has_updates )); then + label="mise à jour" done_label="↑ mise à jour appliquée" + else + label="redémarrage forcé" done_label="↻ redémarrage forcé effectué" + fi + if [[ $DRY_RUN -eq 1 ]]; then + warn "[dry-run] $label nécessaire, rien n'est relancé" + quiet_status "$YELLOW" "↑ $label à faire" + updated_stacks+=("$name") + elif ! run docker compose down; then + fail "down" + elif ! run docker compose up -d; then + fail "up -d" + else + log " ${YELLOW}✔ stack relancée (down + up -d) : $label${RESET}" + quiet_status "$YELLOW" "$done_label" + updated_stacks+=("$name") + fi + else + ok "à jour" + quiet_status "$GREEN" "✔ à jour" + fi + + popd >/dev/null +done + +if [[ $PRUNE -eq 1 && $DRY_RUN -eq 0 ]]; then + log "Nettoyage des images orphelines…" + run docker image prune -f +fi + +if (( ${#failed_stacks[@]} )); then + color=$RED +elif (( ${#updated_stacks[@]} )); then + color=$YELLOW +else + color=$GREEN +fi +log "${color}Terminé. Stacks relancées : ${#updated_stacks[@]} ${updated_stacks[*]:-} | Échecs : ${#failed_stacks[@]} ${failed_stacks[*]:-}${RESET}" +[[ ${#failed_stacks[@]} -eq 0 ]]