# Diagrams as code
A series of 8 article(s), in reading order.
# Docker - Des diagrammes en tant que code
> Générez de superbes diagrammes d'infrastructure et d'application directement depuis du code Python, avec la librairie diagrams et une simple commande Docker. Visualisez vos systèmes complexes sans effort.
---

**TL;DR**
Cet article montre comment générer des diagrammes-as-code avec la librairie Python `diagrams` via une image Docker prête à l'emploi (`gtramontina/diagrams`) : on envoie un fichier `.py` dans `docker run` pour produire des diagrammes d'architecture (icônes AWS/Azure/GCP, par exemple) sans rien installer localement. Il se termine par un large tour d'horizon des autres outils de diagram-as-code : Mermaid, PlantUML, Graphviz, Kroki, DBML-renderer, Structurizr, et d'autres encore.
Quel plaisir de pouvoir dessiner des diagrammes en écrivant simplement du texte. Certains outils sont plus connus que d'autres, par exemple [Mermaid](https://mermaid-js.github.io/mermaid/) — que j'ai depuis automatisé dans [Documentation as Code - Transform Your Infrastructure into Beautiful Diagrams with Python and Mermaid](/blog/docker-python-mermaid), et dont le cousin Graphviz est traité dans [Python - Generate flows using pydot](/blog/python-pydot).
Connaissiez-vous ? Explorons-le avec, bien entendu, une image Docker prête à l'emploi.
## Le résultat {#what-comes-out}
Voici un diagramme produit par cet article, et il n'y avait que du texte en entrée :

La commande qui l'a produit tient sur une ligne ; le fichier `.py` est envoyé dans un container et le `.png` atterrit dans votre dossier courant :
```bash
$ cat team.py | docker run -i --rm -v $(pwd):/out -u 1000:1000 gtramontina/diagrams:0.23.3
```
## Pourquoi ça fonctionne {#why-it-works}
* Le fichier `.py` n'est pas exécuté sur votre machine : il est envoyé dans le container, qui contient Python, la librairie `diagrams`, Graphviz et l'ensemble des jeux d'icônes AWS/Azure/GCP/K8S.
* `-v $(pwd):/out` est ce qui permet à l'image générée de revenir dans votre dossier au lieu de mourir avec le container.
* `-u 1000:1000` exécute le container avec votre identité, de sorte que le fichier produit appartienne à votre utilisateur et pas à `root`.
> **Notation Windows:**
>
> Si vous travaillez sous Windows, remplacez `$(pwd)` par `%CD%`. Et remplacez `cat` par `type`.
## Le code source {#the-source}
Comme toujours pour la démo, ouvrez un shell Linux et lancez `mkdir -p /tmp/docker-diagrams && cd $_` pour créer un dossier `docker-diagrams` dans votre dossier temporaire Linux et vous y rendre.
Créez ensuite un nouveau fichier `team.py` avec le contenu qui a produit le diagramme ci-dessus :
**team.py**
```python
from diagrams import Diagram
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS
from diagrams.aws.network import ELB
with Diagram("My Team", show=False, direction="TB"):
(
ELB("Olivier")
>> [EC2("Christophe"), EC2("Jason"), EC2("Niki"), EC2("Rudy"), EC2("Stijn")]
>> RDS("Work together")
)
```
Puis lancez la commande de conversion montrée plus haut.
*0.23.3 est la dernière version disponible au moment d'écrire ce document. Voir pour récupérer la plus récente.*
Facile, non ?
## Une vraie architecture {#a-real-architecture}
Vingt lignes de Python pour un organigramme d'équipe, c'est sympa ; voici le même exercice sur une infrastructure réelle :
**stateful.py**
```python
from diagrams import Cluster, Diagram
from diagrams.k8s.compute import Pod, StatefulSet
from diagrams.k8s.network import Service
from diagrams.k8s.storage import PV, PVC, StorageClass
with Diagram("Stateful Architecture", show=False):
with Cluster("Apps"):
svc = Service("svc")
sts = StatefulSet("sts")
apps = []
for _ in range(3):
pod = Pod("pod")
pvc = PVC("pvc")
pod - sts - pvc
apps.append(svc >> pod >> pvc)
apps << PV("pv") << StorageClass("sc")
```
Et l'image obtenue :

Dingue, non ? Et tout ça sans rien installer !
> **Plus d'exemples:**
>
> Retrouvez d'autres exemples sur
Le code de l'image Docker est ici : , et vous trouverez plein d'autres exemples sur .
## Icônes et autres outils (référence, à garder sous le coude) {#icons-and-other-tools-reference-skip-this-for-now}
Une énorme liste d'icônes/nœuds est disponible sur plusieurs pages à partir de . Voir [OnPrem](https://diagrams.mingrammer.com/docs/nodes/onprem), [AWS](https://diagrams.mingrammer.com/docs/nodes/aws), [Azure](https://diagrams.mingrammer.com/docs/nodes/azure), [GCP](https://diagrams.mingrammer.com/docs/nodes/gcp), [IBM](https://diagrams.mingrammer.com/docs/nodes/ibm), [K8S](https://diagrams.mingrammer.com/docs/nodes/k8s) et aussi comment créer les vôtres (à partir d'images `.png` locales) : [Custom](https://diagrams.mingrammer.com/docs/nodes/custom).
Et comme `diagrams` est loin d'être le seul outil texte-vers-image, voici la liste sur laquelle je reviens sans cesse :
* [DB Diagram](https://dbdiagram.io/home) *(voir aussi [Drawdb-app - Render your database model as png, markdown, mermaid, ...](/blog/drawdb-app))*
* [DBML-renderer](https://github.com/softwaretechnik-berlin/dbml-renderer), dbml-renderer convertit des fichiers DBML en images SVG
* [Graphviz](https://www.graphviz.org/), un logiciel open source de visualisation de graphes
* [JSON Crack](https://jsoncrack.com/), visualisez instantanément vos données JSON sous forme de graphes *(je lui ai consacré un article : [Rendering a JSON file as a mind map](/blog/json-crack))*
* [Kroki](https://kroki.io/), crée des diagrammes à partir de descriptions textuelles
* [Mermaid](https://mermaid-js.github.io/mermaid/), son [éditeur en ligne](https://mermaid.live/), l'[extension de prévisualisation pour vscode](https://marketplace.visualstudio.com/items?itemName=vstirbu.vscode-mermaid-preview) et l'[outil de conversion en ligne de commande](https://github.com/mermaid-js/mermaid-cli)
* [Nomnoml](https://www.nomnoml.com/), un outil pour dessiner des diagrammes UML avec une syntaxe simple
* [Pikchr](https://pikchr.org/), Pikchr (prononcez « picture ») est un langage de balisage à la PIC pour les diagrammes dans la documentation technique
* [Plantuml](https://github.com/plantuml/plantuml), génère des diagrammes à partir d'une description textuelle
* [Sequence diagram](https://sequencediagram.org/) *(semble basé sur Mermaid)*
* [Structurizr](https://github.com/structurizr/dsl), une façon de créer des modèles d'architecture logicielle Structurizr basés sur le modèle C4, via un langage dédié textuel
* [svgbob](https://github.com/ivanceras/svgbob), transforme vos gribouillis de diagrammes ASCII en jolis petits SVG
* [Vega](https://vega.github.io/vega/), une grammaire de visualisation
* [yEd Graph Editor](https://www.yworks.com/products/yed), une interface graphique : il faut glisser-déposer des objets et les redimensionner. Il ne gère pas les fichiers texte comme les autres outils cités ici.
## Conclusion {#conclusion}
Des diagrammes d'architecture qui vivent dans votre repository, que vous pouvez differ et relire comme n'importe quel autre fichier, et qui n'exigent jamais Python ni Graphviz sur votre machine. La prochaine fois que l'infrastructure change, vous modifiez une ligne de texte au lieu de déplacer une boîte à la souris.
Si vous préférez rester sur une syntaxe que vous connaissez déjà, [Documentation as Code - Transform Your Infrastructure into Beautiful Diagrams with Python and Mermaid](/blog/docker-python-mermaid) fait la même chose avec Mermaid.
---
# Créer une mind map avec Docker et Markdown
> Apprenez à créer facilement une mind map dynamique avec Markmap, Docker et Markdown. Convertissez du texte brut en une superbe mind map HTML interactive.
---

**TL;DR**
Cet article montre comment transformer un simple plan Markdown (titres et listes à puces) en une mind map SVG interactive et zoomable grâce à Markmap, soit via son éditeur en ligne, soit sans rien installer avec l'image Docker `leopoul/markmap` (`docker run ... mindmap.md --output mindmap.html`).
Fan de markdown, j'adore dénicher un petit outil qui me permet d'écrire du texte et de le convertir dans un autre format.
Pour cet article, nous allons écrire une mind map en texte brut, autrement dit : notre texte sera converti en une image de mind map.
## Le résultat {#what-comes-out}
Voici la mind map que vous obtiendrez à la fin de cet article :

C'est un SVG dans une page HTML : les branches peuvent être repliées et dépliées, et vous pouvez zoomer ou dézoomer. Et voici l'unique commande qui l'a produite, à partir d'un simple fichier Markdown :
```bash
docker run -it --rm -v ${PWD}:/project -w /project -u $(id -u):$(id -g) \
leopoul/markmap:1.0.0 mindmap.md --output mindmap.html
```
Rien d'installé sur la machine ; l'outil que nous utilisons s'appelle `Markmap` et il reste dans son container.
## Et voici le texte qui l'a produite {#and-here-is-the-text-that-produced-it}
**mindmap.md**
```markdown
# Social Media Uses
## Blogging
- Blogger
- Medium
- Joomla
## Social Network
### Common
- Facebook
### For developers
- Dev.to
- Daily.dev
- Github
## Photo Sharing
- Flicker
- Pinterest
```
Les titres deviennent des branches, les listes à puces deviennent des feuilles. Voilà toute la syntaxe ; vous la connaissez déjà.
## Le faire sur votre machine {#doing-it-on-your-machine}
Pour la démo, lancez un shell Linux et exécutez `mkdir -p /tmp/markmap && cd $_` pour créer un dossier `markmap` dans votre dossier temporaire Linux et vous y placer.
Créez un nouveau fichier appelé `mindmap.md` avec le contenu markdown à propos de *Social Media Uses* fourni juste au-dessus. Vous devriez avoir ceci :
```bash
$ ls -alh
total 920K
drwxr-xr-x 2 christophe christophe 4.0K Dec 16 14:05 .
drwxrwxrwt 19 christophe christophe 908K Dec 16 14:05 ..
-rw-r--r-- 1 christophe christophe 198 Dec 16 14:05 mindmap.md
```
Exécutez maintenant la commande `docker run` affichée en début d'article pour convertir le document markdown en page HTML. L'image est automatiquement créée sous forme de contenu SVG dans le fichier `.html` :
```bash
$ ls -alh
total 920K
drwxr-xr-x 2 christophe christophe 4.0K Dec 16 14:05 .
drwxrwxrwt 19 christophe christophe 908K Dec 16 14:05 ..
-rw-r--r-- 1 christophe christophe 2.2K Dec 16 14:10 mindmap.html
-rw-r--r-- 1 christophe christophe 198 Dec 16 14:05 mindmap.md
```
Ouvrez `mindmap.html` dans votre navigateur et la carte s'affichera sur toute la largeur de l'écran.
> **Utilisateur WSL:**
>
> Si vous êtes sous Windows et WSL2, une façon d'ouvrir le fichier `mindmap.html` est de lancer `explorer.exe .` dans votre console Linux (voir [cet article](/blog/wsl-windows-explorer) pour en savoir plus). L'explorateur Windows démarrera ; il suffit alors de double-cliquer sur le fichier `mindmap.html`.
## Sans Docker, dans votre navigateur {#without-docker-in-your-browser}
`Markmap` peut aussi être utilisé en ligne : voyez la démo et l'éditeur sur . Copiez/collez le markdown ci-dessus dans l'[éditeur](https://markmap.js.org/repl) pour le voir en action, sans créer le moindre fichier.
## Aller plus loin {#go-further}
Vous pouvez ajouter des éléments de configuration dans votre document markdown, voyez pour obtenir la liste de toutes les options JSON supportées.
## Conclusion {#conclusion}
Une mind map que vous pouvez versionner, comparer et éditer dans n'importe quel éditeur de texte, produite par un seul `docker run` — et rien à désinstaller ensuite.
Deux suites : [Getting a more attractive mindmap with Markmap and Quarto](/blog/vscode-docker-markmap), qui soigne le rendu, et [Rendering a JSON file as a mind map](/blog/json-crack) quand la source est un fichier JSON plutôt que du Markdown.
---
# Afficher un fichier JSON sous forme de mind map
> Transformez des données JSON complexes en mind maps claires et interactives avec JSON Crack. Découvrez comment cet outil puissant simplifie la visualisation et la compréhension des données.
---

**TL;DR**
Cet article présente JSON Crack, un outil en ligne gratuit qui affiche du contenu JSON sous forme de mind map / diagramme interactif au lieu d'un texte brut imbriqué, ce qui rend les structures complexes beaucoup plus lisibles d'un coup d'œil. Il montre deux exemples de fichiers JSON rendus sous forme d'arbres visuels et mentionne la limitation du nombre de lignes de la version gratuite, ainsi qu'une extension VSCode comme alternative.
>
J'aime beaucoup l'idée de ne pas avoir à dessiner (en fait, c'est juste parce que je suis vraiment mauvais pour ça) un diagramme, un organigramme, une représentation graphique de quelque chose qui... peut s'écrire.
[JSON Crack](https://jsoncrack.com/editor) est l'un des outils de ma boîte à outils quand je veux dessiner quelque chose qui peut s'écrire en JSON. *Il côtoie [Markmap](/blog/docker-mindmap) (même idée, mais depuis du Markdown) et [les outils de diagram-as-code](/blog/docker-diagram-as-code) en général.*
*Quand vous avez juste besoin de lire le JSON plutôt que de le visualiser, [JSON - Online linter](/blog/json-lint) et [`jq`](/blog/linux-jq) font le travail.*
Prenons un exemple trouvé au hasard sur le net : [superheroes](https://medium.com/@Goldzila/superheroes-of-data-exploring-xml-json-and-binary-formats-through-the-lens-of-marvel-characters-3754f2691cdc). Comment représenter Spider-Man sous forme d'objet JSON ? Voici une tentative :
**superheroes.json**
```json
{
"superhero": "Spider-Man",
"realName": "Peter Parker",
"abilities": [
"Wall-Crawling",
"Spider-Sense",
"Super Strength"
],
"equipment": {
"webShooters": "True",
"spiderTracers": "True"
}
}
```
Ce n'est pas plus sympa en version visuelle ?

Un autre exemple (trouvé [ici](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON)) :
**superheroes.json**
```json
{
"squadName": "Super hero squad",
"homeTown": "Metro City",
"formed": 2016,
"secretBase": "Super tower",
"active": true,
"members": [
{
"name": "Molecule Man",
"age": 29,
"secretIdentity": "Dan Jukes",
"powers": ["Radiation resistance", "Turning tiny", "Radiation blast"]
},
{
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
]
},
{
"name": "Eternal Flame",
"age": 1000000,
"secretIdentity": "Unknown",
"powers": [
"Immortality",
"Heat Immunity",
"Inferno",
"Teleportation",
"Interdimensional travel"
]
}
]
}
```

L'avantage de l'image, c'est qu'elle est beaucoup plus claire : on voit tout de suite que l'équipe compte trois membres et que le plus fort (et le plus vieux) est *Eternal Flame*, qui possède cinq pouvoirs.
> **Note:**
>
> JSON Crack a quelques limitations dans sa version gratuite, comme le nombre de lignes de votre contenu JSON. Vous pouvez aussi installer un [add-on pour vscode](https://marketplace.visualstudio.com/items?itemName=AykutSarac.jsoncrack-vscode).
D'autres outils JSON à connaître : [générer de fausses données JSON](/blog/json-faker) et [linter et valider vos fichiers JSON](/blog/json-lint).
---
# Comment générer un graphe depuis compose.yaml
> Visualisez instantanément l'architecture de votre Docker Compose. Utilisez `compose-viz` pour générer un graphe clair des services, dépendances et ports exposés à partir de votre fichier `compose.yaml`.
---

**TL;DR**
Cet article montre comment visualiser un fichier `compose.yaml` sous forme de graphe de dépendances avec l'image Docker `compose-viz`. Le résultat est une image (PNG, SVG et d'autres formats via `--format`) qui affiche d'un coup d'œil les services, leurs dépendances et les ports exposés — pratique pour comprendre de grosses architectures Docker multi-services. On y parle aussi de `docker compose config` pour résoudre et fusionner plusieurs fichiers `.yml` et variables avant la visualisation.
Imaginez que vous ayez un très gros fichier `compose.yaml` avec beaucoup de services et de dépendances.
Vous avez aussi plusieurs ports exposés.
Vous aimeriez avoir une vue d'ensemble des services, des dépendances et des ports ouverts. C'est possible ? Oui ! fait la magie pour nous.
Voici ce que `compose-viz` génère à partir d'un `compose.yaml` Joomla :

> **Comment interpréter l'image ?:**
>
> On voit que le port `8080` est exposé sur la machine. Ce port est, en fait, le port `80` du container `joomla`. Et on voit aussi que `joomla` a une dépendance avec le container `joomladb`. Facile, non ?
## Reproduisez-le vous-même {#reproduce-it-yourself}
Pour la démo, ouvrez un shell Linux et lancez `mkdir -p /tmp/compose-viz && cd $_` pour créer un dossier appelé `compose-viz` dans votre dossier temporaire Linux et y entrer.
Créez ensuite un nouveau fichier appelé `compose.yaml` avec ce contenu **simplifié** :
**compose.yaml**
```yaml
services:
joomla:
image: joomla
ports:
- 8080:80
depends_on:
- joomladb
joomladb:
image: postgres:16.0-alpine
```
Laissez la magie opérer et lancez `docker run --rm -it -u $(id -u):$(id -g) -v $(pwd):/in wst24365888/compose-viz compose.yaml` — vous obtiendrez l'image montrée plus haut.
> **Le flag --format:**
>
> En ajoutant `--format svg`, vous obtiendrez un SVG au lieu d'un PNG. La liste des formats de sortie supportés est énorme : `png|dot|jpeg|json|svg|bmp|canon|cmap|cmapx|cmapx_np|dot_json|emf|emfplus|eps|fig|gif|gv|imap|imap_np|ismap|jpe|jpg|json0|metafile|mp|pdf|pic|plain|plain-ext|pov|ps|ps2|tif|tiff|tk|vml|xdot|xdot1.2|xdot1.4|xdot_json`.
## Un exemple plus complexe {#a-more-complex-example}
Remplacez le contenu du `compose.yaml` par celui-ci :
**compose.yaml**
```yaml
services:
redis:
image: redis:alpine
ports:
- "6379"
networks:
- frontend
deploy:
replicas: 2
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
db:
image: postgres:9.4
volumes:
- db-data:/var/lib/postgresql/data
networks:
- backend
deploy:
placement:
constraints: [ node.role == manager ]
vote:
image: dockersamples/examplevotingapp_vote:before
ports:
- 5000:80
networks:
- frontend
depends_on:
- redis
deploy:
replicas: 2
update_config:
parallelism: 2
restart_policy:
condition: on-failure
result:
image: dockersamples/examplevotingapp_result:before
ports:
- 5001:80
networks:
- backend
depends_on:
- db
deploy:
replicas: 1
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
worker:
image: dockersamples/examplevotingapp_worker
networks:
- frontend
- backend
deploy:
mode: replicated
replicas: 1
labels: [ APP=VOTING ]
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 3
window: 120s
placement:
constraints: [ node.role == manager ]
visualizer:
image: dockersamples/visualizer
ports:
- "8080:8080"
stop_grace_period: 1m30s
volumes:
- /var/run/docker.sock:/var/run/docker.sock
deploy:
placement:
constraints: [ node.role == manager ]
networks:
frontend:
backend:
volumes:
db-data:
```
Et l'image ci-dessous sera générée par l'outil :

Ici, on peut voir :
* Nous avons quatre ports exposés : `5001`, `5000`, `6379` et `8080`.
* Le port `8080` est lié à un service web `visualizer` ; ce service utilise un volume pointant vers `/var/run/docker.sock`
* Les ports `5000` et `5001` renvoient à des services appelés `vote` et `result`. Comme le port interne est le port `80`, on sait que ces ports sont des interfaces web.
* Le service `vote` (port `5000`) utilise un service `redis`, accessible via le port `6379`.
* Le service `result` (port `5001`) est lié à un service Postgres appelé `db` et dispose de données persistantes (puisqu'on a un volume Docker appelé `db-data`).
* Et ainsi de suite.
Un tel outil de visualisation simplifie énormément la compréhension d'une architecture Docker.
*`compose-viz` vous donne un seul rendu, figé. Si vous voulez contrôler exactement ce que le diagramme affiche, [Documentation as Code - Transform Your Infrastructure into Beautiful Diagrams with Python and Mermaid](/blog/docker-python-mermaid) analyse le même `compose.yaml` avec un script Python qui vous appartient, et [Docker - Diagrams as code](/blog/docker-diagram-as-code) liste une dizaine d'autres outils de diagram-as-code.*
## Docker config {#docker-config}
Si, comme moi, vous utilisez beaucoup de fichiers `.yml` quand vous lancez Docker et/ou que vous y utilisez des variables d'environnement, lancez simplement `docker compose config` pour demander à Docker de *rendre* (à l'écran uniquement) ce qu'il appelle *Parse, resolve and render compose file in canonical format*, c'est-à-dire qu'il va fusionner tous vos `.yml` en une seule chaîne et résoudre les variables.
Copiez/collez la sortie à l'écran dans un fichier temporaire et utilisez cet outil sur ce fichier.
---
# Drawdb-app - Rendez votre modèle de base de données en png, markdown, mermaid, ...
> Visualisez et documentez facilement votre modèle de base de données avec DrawDB-app. Importez des fichiers SQL, générez des diagrammes et exportez en PNG, Markdown, Mermaid et plus encore.
---

**TL;DR**
Cet article présente drawdb-app, un outil gratuit qui génère un diagramme ER visuel à partir d'un fichier `.sql` importé (ou construit de zéro) et l'exporte en PNG, JSON, Mermaid ou en documentation Markdown générée automatiquement (tables, colonnes, relations) — également auto-hébergeable via Docker (`xinsodev/drawdb`).
Dans mon flux RSS récemment, je suis tombé sur une autre excellente application qui permet de créer un modèle de base de données de zéro, c'est-à-dire de créer les tables une par une et d'établir les liens entre elles.
Mais il y a une autre option que je trouve vraiment sympa : vous pouvez obtenir en quelques secondes une vue des tables et des relations de n'importe quelle application existante à partir d'un fichier .sql que vous importez dans drawdb-app. *Récupérer ce dump `.sql` depuis un container qui tourne se fait en un clic avec [Adminer, pgadmin ou phpmyadmin](/blog/docker-adminer-pgadmin-phpmyadmin).*
Voyons rapidement comment cela fonctionne.
En ouvrant le site , la première chose qu'on vous demande est de choisir le format de base de données cible. Par exemple, choisissons PostgreSQL (Situation : *je développe une application Laravel qui utilise PostgreSQL et je veux voir les relations de ma DB dans une jolie image*).
Je pense que l'usage le plus courant que je pourrais personnellement en faire est de charger un fichier SQL. Pour cela, cliquez simplement sur le menu `File`, choisissez `Import from SQL` et, enfin, récupérez un fichier .sql créé précédemment sur votre disque dur.
## Créer et importer un fichier sql de test {#create-and-import-a-dummy-sql-file}
Si vous n'avez pas un tel fichier, je vous suggère d'en copier un depuis . Ci-dessous, vous trouverez le contenu PostgreSQL pris sur ce site ([lien direct](https://www.sqltutorial.org/wp-content/uploads/2020/04/postgresql.txt)) — ou vous pouvez prendre n'importe quel fichier `.sql` valide trouvé sur Internet.
**create\_db.sql**
```sql
CREATE TABLE regions (
region_id SERIAL PRIMARY KEY,
region_name CHARACTER VARYING (25)
);
CREATE TABLE countries (
country_id CHARACTER (2) PRIMARY KEY,
country_name CHARACTER VARYING (40),
region_id INTEGER NOT NULL,
FOREIGN KEY (region_id) REFERENCES regions (region_id) ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE locations (
location_id SERIAL PRIMARY KEY,
street_address CHARACTER VARYING (40),
postal_code CHARACTER VARYING (12),
city CHARACTER VARYING (30) NOT NULL,
state_province CHARACTER VARYING (25),
country_id CHARACTER (2) NOT NULL,
FOREIGN KEY (country_id) REFERENCES countries (country_id) ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE departments (
department_id SERIAL PRIMARY KEY,
department_name CHARACTER VARYING (30) NOT NULL,
location_id INTEGER,
FOREIGN KEY (location_id) REFERENCES locations (location_id) ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE jobs (
job_id SERIAL PRIMARY KEY,
job_title CHARACTER VARYING (35) NOT NULL,
min_salary NUMERIC (8, 2),
max_salary NUMERIC (8, 2)
);
CREATE TABLE employees (
employee_id SERIAL PRIMARY KEY,
first_name CHARACTER VARYING (20),
last_name CHARACTER VARYING (25) NOT NULL,
email CHARACTER VARYING (100) NOT NULL,
phone_number CHARACTER VARYING (20),
hire_date DATE NOT NULL,
job_id INTEGER NOT NULL,
salary NUMERIC (8, 2) NOT NULL,
manager_id INTEGER,
department_id INTEGER,
FOREIGN KEY (job_id) REFERENCES jobs (job_id) ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY (department_id) REFERENCES departments (department_id) ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY (manager_id) REFERENCES employees (employee_id) ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE dependents (
dependent_id SERIAL PRIMARY KEY,
first_name CHARACTER VARYING (50) NOT NULL,
last_name CHARACTER VARYING (50) NOT NULL,
relationship CHARACTER VARYING (25) NOT NULL,
employee_id INTEGER NOT NULL,
FOREIGN KEY (employee_id) REFERENCES employees (employee_id) ON DELETE CASCADE ON UPDATE CASCADE
);
```
Copiez/collez donc ce SQL dans Notepad, sauvegardez-le sur votre disque et retournez sur ; `File` -> `Import from SQL`.
Et laissez la magie se produire :

> **L'image ci-dessus a été réalisée avec la fonctionnalité `File` -> `Export as`.:**
### Vous pouvez auto-héberger l'application grâce à Docker si vous le souhaitez {#you-can-self-host-the-application-thanks-to-docker-if-you-want}
En visitant , vous trouverez quelques images Docker pour drawdb-app. Par exemple .
Pour la lancer, exécutez simplement `docker run --name some-drawdb -p 81:80 -d xinsodev/drawdb` et accédez à l'interface sur `http://localhost:81`.
## Jouons avec la fonctionnalité d'exportation {#playing-with-exportation-feature}
Maintenant, prenez le temps de regarder les fonctionnalités d'export ; cliquez à nouveau sur le menu `File` puis sur `Export as`.

C'est vraiment cool de pouvoir exporter aussi facilement un fichier SQL en JSON, mais c'est encore plus cool de l'exporter en markdown. *Cet export Mermaid s'intègre directement dans le pipeline décrit dans [Documentation as Code - Transform Your Infrastructure into Beautiful Diagrams with Python and Mermaid](/blog/docker-python-mermaid), et drawDB rejoint la liste des outils de [Docker - Diagrams as code](/blog/docker-diagram-as-code).*
## Sous le capot (passez cette section si vous voulez juste l'utiliser) {#under-the-hood-skip-this-if-you-just-want-to-use-it}
> **Contenu généré automatiquement ci-dessous:**
>
> Le reste de cet article a été généré par drawDB.app ; un outil vraiment utile pour aider les développeurs à documenter leur base de données ! Vous trouverez ci-dessous deux tables représentatives — `employees` (la plus chargée, avec trois clés étrangères) et `regions` (la plus simple) — parmi les sept produites par l'export complet, ainsi que les relations entre toutes.
### Structure des tables {#table-structure}
#### employees {#employees}
| Nom | Type | Paramètres | Références | Note |
| ------------------ | ------------ | --------------- | ----------------------------- | ---- |
| **employee\_id** | SERIAL | 🔑 PK, not null | | |
| **first\_name** | BLOB | not null | | |
| **last\_name** | BLOB | not null | | |
| **email** | BLOB | not null | | |
| **phone\_number** | BLOB | not null | | |
| **hire\_date** | DATE | not null | | |
| **job\_id** | INTEGER | not null | employees\_job\_id\_fk | |
| **salary** | NUMERIC(8,2) | not null | | |
| **manager\_id** | INTEGER | not null | | |
| **department\_id** | INTEGER | not null | employees\_department\_id\_fk | |
#### regions {#regions}
| Nom | Type | Paramètres | Références | Note |
| ---------------- | ------ | --------------- | ---------- | ---- |
| **region\_id** | SERIAL | 🔑 PK, not null | | |
| **region\_name** | BLOB | not null | | |
### Relations {#relationships}
* **countries vers regions** : many\_to\_one
* **locations vers countries** : many\_to\_one
* **departments vers locations** : many\_to\_one
* **employees vers jobs** : many\_to\_one
* **employees vers departments** : many\_to\_one
* **dependents vers employees** : many\_to\_one
## Conclusion {#conclusion}
Importez un fichier `.sql`, récupérez un diagramme ER rendu en quelques secondes, et exportez-le en PNG, JSON, Mermaid ou en documentation Markdown prête à l'emploi — le tout sans rien installer, ou en auto-hébergé via l'image Docker `xinsodev/drawdb` si vous préférez garder ça en interne. L'export Mermaid s'insère directement dans [Documentation as Code - Transform Your Infrastructure into Beautiful Diagrams with Python and Mermaid](/blog/docker-python-mermaid), et drawDB est lui-même une entrée de plus dans la boîte à outils de [Docker - Diagrams as code](/blog/docker-diagram-as-code).
---
# Python - Générer des flux avec pydot
> Utilisez Python et la bibliothèque pydot pour générer facilement des diagrams-as-code comme des flowcharts ETL, des diagrammes de classes et des arbres de décision. Avec installation Docker et exemples de code.
---

**TL;DR**
Cet article présente Pydot, une bibliothèque Python de diagram-as-code qui transforme du code Python en images (flux ETL, diagrammes de classes, flux de données, arbres de décision). À l'aide d'une petite image Docker, un script `.py` décrivant le diagramme est exécuté avec `docker run ... python etl.py` pour produire un PNG. D'autres exemples proviennent de la documentation Graphviz.
[Pydot](https://github.com/pydot/pydot) est un générateur **diagram-as-code** : vous écrivez des lignes de code et, grâce à un processus magique, vous pouvez restituer ce code sous forme d'image.
*Ce blog contient deux autres articles de la même famille : [Docker - Diagrams as code](/blog/docker-diagram-as-code), qui utilise la bibliothèque `diagrams` et ses icônes de fournisseurs cloud, et [Documentation as Code - Transform Your Infrastructure into Beautiful Diagrams with Python and Mermaid](/blog/docker-python-mermaid), où le diagramme est généré **depuis** votre `compose.yaml` au lieu d'être écrit à la main.*
Pour les gens comme moi, catastrophiques dès qu'il s'agit de visuel, c'est de l'or en barre.
Imaginons que vous deviez décrire un processus informatique comme un ETL (les données sont chargées, telle ou telle règle de transformation est appliquée et le résultat est chargé dans une base de données, par exemple). Vous vous voyez déjà dessiner des rectangles avec un outil comme (anciennement draw\.io), ou vous pouvez opter pour une méthode plus intelligente.
Voyons comment dans cet article.
Pydot peut générer une image comme celle ci-dessous :

D'abord, il nous faut un container Docker Python. Créez un fichier appelé `Dockerfile` avec ce contenu :
**Dockerfile**
```docker
FROM python:3.13-slim
# Prevents Python from writing pyc files.
ENV PYTHONDONTWRITEBYTECODE=1
# hadolint ignore=DL3008
RUN set -e -x \
&& apt-get update -yqq \
&& apt-get install -y --no-install-recommends graphviz \
&& apt-get clean \
&& rm -rf /tmp/* /var/lib/apt/lists/*
# hadolint ignore=DL3013
RUN pip install --no-cache-dir pydot
```
Et créez l'image en lançant `docker build --tag pydot .`.
Rien de plus n'est nécessaire, sauf... notre flux.
Pour construire une image ETL comme celle ci-dessus, créez un nouveau fichier appelé par exemple `etl.py` avec ce contenu :
**etl.py**
```python
graph = pydot.Dot(graph_type='digraph', rankdir='TB')
node_csv = pydot.Node('CSV', shape='box', style='filled', fillcolor='lightblue')
node_json = pydot.Node('JSON', shape='box', style='filled', fillcolor='lightblue')
node_extract = pydot.Node('Extract', shape='box')
node_clean = pydot.Node('Clean', shape='box')
node_transform = pydot.Node('Transform', shape='box')
node_load = pydot.Node('Load', shape='box', style='filled', fillcolor='lightgreen')
graph.add_edge(pydot.Edge(node_csv, node_extract))
graph.add_edge(pydot.Edge(node_json, node_extract))
graph.add_edge(pydot.Edge(node_extract, node_clean))
graph.add_edge(pydot.Edge(node_clean, node_transform))
graph.add_edge(pydot.Edge(node_transform, node_load))
node_enrich = pydot.Node('Get more data', shape='box')
graph.add_edge(pydot.Edge(node_transform, node_enrich))
graph.add_edge(pydot.Edge(node_enrich, node_load))
graph.write_png('etl.png')
```
Et maintenant, la partie facile : transformez le fichier Python `etl.py` en image en lançant `docker run --rm -it -v "${PWD}":/diagram -w /diagram pydot python etl.py`.
Et bingo, vous avez maintenant une image appelée `etl.png` dans votre répertoire.
## Quelques autres exemples {#some-other-examples}
Cherchez des exemples sur Internet et vous tomberez par exemple sur ce site : .
Vous y trouverez des exemples Python et, en plus, l'image rendue.
### Classes POO {#oop-classes}
**class\_diagram.py**
```python
graph = pydot.Dot(graph_type='digraph')
# Add nodes
node_person = pydot.Node('Person', shape='box')
node_student = pydot.Node('Student', shape='box')
node_teacher = pydot.Node('Teacher', shape='box')
# Add nodes to the graph
graph.add_node(node_person)
graph.add_node(node_student)
graph.add_node(node_teacher)
# Add edges (inheritance)
graph.add_edge(pydot.Edge(node_person, node_student))
graph.add_edge(pydot.Edge(node_person, node_teacher))
# Render the graph
graph.write_png('class_diagram.png')
```

### Flux de données {#data-flow}
**data\_flow\.py**
```python
graph = pydot.Dot(graph_type='digraph')
# Data sources
source1 = pydot.Node('Source 1', shape='box')
source2 = pydot.Node('Source 2', shape='box')
graph.add_node(source1)
graph.add_node(source2)
# Process nodes
process1 = pydot.Node('Process 1', shape='oval')
process2 = pydot.Node('Process 2', shape='oval')
graph.add_node(process1)
graph.add_node(process2)
# Data store
data_store = pydot.Node('Data Store', shape='cylinder')
graph.add_node(data_store)
# Edges representing data flow
graph.add_edge(pydot.Edge(source1, process1))
graph.add_edge(pydot.Edge(source2, process1))
graph.add_edge(pydot.Edge(process1, process2))
graph.add_edge(pydot.Edge(process1, data_store))
graph.add_edge(pydot.Edge(data_store, process2))
# Render the graph
graph.write_png('data_flow_diagram.png')
```

### Flux de décision {#decision-flow}
**decision\_flow\.py**
```python
graph = pydot.Dot(graph_type='digraph')
# Add nodes
node_start = pydot.Node('Start', shape='oval')
node_process1 = pydot.Node('Process 1', shape='box')
node_decision = pydot.Node('Decision', shape='diamond')
node_process2 = pydot.Node('Process 2', shape='box')
node_end = pydot.Node('End', shape='oval')
# Add edges
graph.add_edge(pydot.Edge(node_start, node_process1))
graph.add_edge(pydot.Edge(node_process1, node_decision))
graph.add_edge(pydot.Edge(node_decision, node_process2, label='Yes'))
graph.add_edge(pydot.Edge(node_decision, node_end, label='No'))
graph.add_edge(pydot.Edge(node_process2, node_end))
# Write the graph to a file
graph.write_png('flowchart.png')
```

### Arbre de décision {#decision-tree}
**decision\_tree.py**
```python
graph = pydot.Dot(graph_type='digraph')
# Add nodes
node_root = pydot.Node('Root', shape='box')
node_left = pydot.Node('Left Child', shape='box')
node_right = pydot.Node('Right Child', shape='box')
node_left_left = pydot.Node('Left-Left Grandchild', shape='ellipse')
node_left_right = pydot.Node('Left-Right Grandchild', shape='ellipse')
node_right_left = pydot.Node('Right-Left Grandchild', shape='ellipse')
node_right_right = pydot.Node('Right-Right Grandchild', shape='ellipse')
# Add nodes to the graph
graph.add_node(node_root)
graph.add_node(node_left)
graph.add_node(node_right)
graph.add_node(node_left_left)
graph.add_node(node_left_right)
graph.add_node(node_right_left)
graph.add_node(node_right_right)
# Add edges
graph.add_edge(pydot.Edge(node_root, node_left, label='Feature 1 < 5'))
graph.add_edge(pydot.Edge(node_root, node_right, label='Feature 1 >= 5'))
graph.add_edge(pydot.Edge(node_left, node_left_left, label='Feature 2 < 3'))
graph.add_edge(pydot.Edge(node_left, node_left_right, label='Feature 2 >= 3'))
graph.add_edge(pydot.Edge(node_right, node_right_left, label='Feature 3 < 10'))
graph.add_edge(pydot.Edge(node_right, node_right_right, label='Feature 3 >= 10'))
# Render the graph
graph.write_png('decision_tree.png')
```

---
# Obtenir une mindmap plus attrayante avec Markmap et Quarto
> Générez des mindmaps attrayantes à partir de Markdown avec Markmap et personnalisez-les dans VSCode. Affichez le résultat HTML final avec Quarto et Docker.
---

**TL;DR**
Cet article propose un guide pas à pas pour créer des mindmaps interactives et attrayantes à partir de simples fichiers Markdown. Vous apprendrez à utiliser l'extension VS Code Markmap pour visualiser instantanément vos listes Markdown sous forme de mindmap, directement dans votre éditeur. L'article explique aussi comment personnaliser l'apparence de votre mindmap via les paramètres de VS Code. Enfin, il montre comment utiliser Quarto, via un simple script shell basé sur Docker, pour générer votre mindmap sous forme de page HTML autonome et soignée, sans rien installer localement.
En 2023, j'ai écrit cet article [Build a mind map using Docker and Markdown](/blog/docker-mindmap) sur la façon de rendre un document Markdown sous forme de mindmap.
Cette fois, j'aimerais aller un cran plus loin et rendre le tout plus attrayant.
*Deux autres façons de transformer du texte en diagramme sur ce blog : [générer du Mermaid depuis un script Python](/blog/docker-python-mermaid) et [afficher un fichier JSON sous forme de mindmap](/blog/json-crack).*
Voici où nous allons — la même liste Markdown toute simple, rendue en mindmap HTML autonome et soignée avec Quarto :

Commençons par le commencement : lancez VSCode et créez un nouveau fichier. Comme nous allons faire un peu plus que ça, créons un nouveau dossier : `mkdir -p /tmp/markmap && cd $_`.
Créez-y un fichier Markdown, par exemple `overview.qmd`. L'extension `.qmd` sert juste à dire *ceci est un fichier Markdown et nous comptons l'utiliser avec Quarto* ; rien de spécial ici.
**overview\.qmd**
```qmd
# 📚 Project Overview
## 🚀 Goals
- ✅ **Launch MVP**
- 🌱 Collect ==user feedback==
- 📈 Iterate every 2 weeks
## 🧰 Tech Stack
- `Next.js` + `Tailwind`
- 🧠 AI via OpenAI
- 🐘 PostgreSQL DB
## 🔁 Feedback Loop
- 📞 Customer interviews
- 📝 Surveys
- 📊 Usage metrics
## 📅 Timeline
- Q1 → Prototyping
- Q2 → MVP 🚢
- Q3 → Growth 📈
```
> **Note:**
>
> Le contenu du fichier `overview.qmd` ci-dessus a été généré par IA.
Nous obtenons donc ceci :

## Afficher la markmap en ligne {#render-the-markmap-online}
Rendez-vous sur et collez le contenu Markdown pour obtenir immédiatement un premier rendu :

Ok, ça marche mais ce n'est pas vraiment impressionnant.
## Installer l'extension VSCode {#installing-the-vscode-extension}
Il existe une extension Markmap sur le marketplace ; rendez-vous sur pour l'installer.
Une fois installée dans VSCode, appuyez sur `CTRL`+`SHIFT`+`P` pour ouvrir la *Command Palette*, cherchez `Markmap`, sélectionnez `Markmap: open as markmap` et appuyez sur `Enter`.
Vous obtiendrez quasiment la même sortie que la version en ligne :

### Configurer le rendu {#configuring-the-rendering}
En créant un fichier `.vscode/settings.json`, vous pouvez définir du CSS et des options par défaut, c'est-à-dire configurer l'extension Markmap-vscode.
Créez le fichier `.vscode/settings.json` avec ce contenu :
**.vscode/settings.json**
```json
{
"markmap.customCSS": ".markmap-link,\nline {\n\tstroke: #505050;\n\tstroke-width: 1.5px;\n}\n.markmap-node > circle {\n\tstroke: #202020;\n\tstroke-width: 1.5px;\n\tr: 6px;\n\tfill: #242424;\n}\n\n.markmap-foreign a {\n\ttext-decoration: none;\n\tcolor: #bbbbbb !important;\n}\n\n.markmap-highlight rect {\n\t/* fill: #303030 !important; */\n\tfill: transparent !important;\n\ttransform: scale(0.84, 0.7) !important;\n\ttransform-origin: center !important;\n\ttransform-box: fill-box;\n\ttransform-origin: center;\n}",
"markmap.defaultOptions": "{\n \"color\": \"function(d, i) { return d3.interpolateRainbow(i / 10); }\",\n \"nodeFont\": \"20px 'Segoe UI', 'Helvetica Neue', sans-serif\",\n \"spacingVertical\": 40,\n \"spacingHorizontal\": 140,\n \"initialExpandLevel\": -1,\n \"duration\": 1000,\n \"paddingX\": 10,\n \"paddingY\": 10\n}"
}
```
Sauvez le fichier, retournez dans `overview.qmd` et, si nécessaire, relancez `Markmap: open as markmap`.
L'aperçu sera bien meilleur :

> **Lisez la doc:**
>
> La liste des options existantes est documentée sur le site officiel :
## Générer une page HTML avec Quarto {#rendering-as-an-html-page-using-quarto}
Dernière étape de cet article : rendons notre contenu Markdown sous forme d'une jolie page HTML, grâce à Quarto.
Créons un nouveau fichier `render.sh` avec le contenu suivant :
**render.sh**
```bash
#!/usr/bin/env bash
set -euo pipefail
mkdir -p .build/.quarto_deno_cache_data/deno
chmod -R 777 .build/.quarto_deno_cache_data
# shellcheck disable=SC2046 # id -u/-g never contain whitespace, safe unquoted
docker run -it --rm \
-u $(id -u):$(id -g) \
-v ".:/public" \
-w /public \
-v "./.build/.quarto_deno_cache_data:/container_cache" \
-e DENO_DIR="/container_cache/deno" \
-e QUARTO_CACHE_DIR="/container_cache/quarto" \
-e HOME="/public" \
ghcr.io/quarto-dev/quarto \
quarto render overview.qmd --no-cache --output-dir .build --quiet
( cd .build && explorer.exe overview.html )
```
Rendez ce fichier exécutable en lançant `chmod +x render.sh`.
Il est temps de l'appeler : dans votre console, lancez `./render.sh` pour exécuter le script.
La toute première fois, ce sera plus long car Docker doit télécharger l'image Docker de Quarto (`ghcr.io/quarto-dev/quarto`).
Le script va appeler Quarto, faire quelques initialisations et convertir le fichier `overview.qmd` en une page HTML qui sera stockée dans un nouveau dossier `.build`.
Après avoir lancé le script, vous aurez donc cette structure dans votre projet :

Le plus chouette : Quarto a rendu votre contenu Markdown sous forme d'une belle page — le même résultat déjà montré en haut de cet article, ce qui était l'objectif de ce billet.
## Conclusion {#conclusion}
Il est très simple d'écrire une structure de type MindMap et de générer soit une image, soit une page web. On pourrait aller encore plus loin avec Quarto, car une page web peut être personnalisée avec du CSS.
Et, comme c'est une page web, vous pouvez facilement copier-coller le rendu dans un e-mail, un document Word ou n'importe quoi d'autre et obtenir un visuel plutôt sympa pour ce qui n'était au départ qu'une simple liste.
---
# Documentation as Code - Transformez votre infrastructure en diagrammes soignés avec Python et Mermaid
> Automatisez votre Documentation as Code en construisant une stack dockerisée Python et Mermaid CLI pour générer des diagrammes d'architecture dynamiques directement depuis votre code.
---

**TL;DR**Une documentation écrite à la main devient obsolète dès que le code change. Pour éviter ça, adoptez la Documentation as Code : construisez une image Docker sur mesure équipée de Python et du CLI Mermaid pour générer automatiquement vos visuels. Avec de simples scripts Python qui analysent vos fichiers compose.yaml ou parcourent vos répertoires de projet, vous produisez dynamiquement du pseudocode Mermaid et le rendez directement en diagrammes PNG propres et à jour (mindmaps, camemberts, etc.) avec une seule commande.
J'aime la documentation, et quoi de plus inefficace que d'écrire de la doc pour des choses qui peuvent s'auto-documenter ?
Comme vous le savez peut-être, j'adore Docker et je fais presque tout de manière dockerisée. Donc, dans chacun de mes projets, il y a un fichier `compose.yaml`. Si je veux le documenter pour mes collègues, je pourrais écrire : *Il y a trois services, un pour PHP, un pour Nginx, un pour PostgreSQL. Celui-là utilise un volume pour rendre les données persistantes sur le disque et blah blah blah. Le service Nginx publie sur le port xxx et la base de données est accessible via le port yyy.* Je peux faire ça, bien sûr, mais n'y a-t-il pas une meilleure façon de procéder ? Et si j'ajoute un nouveau service ou que je change un numéro de port ? Ma documentation serait déjà obsolète, sauf si...
Voyons comment automatiser la documentation avec Docker, Python et Mermaid.
**Quick Jump:** [Voir le résultat](#the-result) | [Tous les fichiers nécessaires](#all-needed-files)
## Le résultat {#the-result}
Pointez le pipeline construit dans cet article vers un vrai `compose.yaml`, et une seule commande produit ceci :

Cette image a été entièrement générée depuis le fichier `compose.yaml` — aucun dessin manuel, aucun outil de diagramme. Modifiez le YAML, relancez la commande, et le diagramme se met à jour tout seul.
## Pourquoi Mermaid {#why-mermaid}
> **Du pseudocode en entrée, une image en sortie:**
>
> [Mermaid](https://mermaid.js.org/) transforme un petit bloc de texte — son propre *pseudocode* — en diagramme rendu. Regardez cet [exemple](https://mermaid.live/edit#pako:eNpFjctqwzAQRX9FzNoNtmzJtrYtfUALhWbT4o1ijR0RWRMUmTYN-fcqCU3uauZw7swBejIICqKd0FmPnWcp0UaH7NnuIoU9o4F9UG-1Y29orGbvTseBwnRxeZ5zptir9Rs0L_4KqwQfdY8ros0FnqPYE9Ho8OqJhD5pXs6rG5OJLb9tjBgggzFYAyqGGTOYMEz6tMLhZHcQ1zhhByqNBgc9u9hB54-pttX-i2j6bwaaxzWoQbtd2uat0REfrB6DvinoDYZ7mn0EVYi6Ph8BdYAfUHdFVS7aVuZlK9uiraumzGAPipcLIaXkKTWvmrqRxwx-z4-LBW8L0XDR5GVTiroSxz-rtW0b) : à gauche, le pseudocode — lisible d'un coup d'œil ; à droite, l'image rendue.

Rendez-vous sur le [Mermaid Live Editor](https://mermaid.live/edit) et jouez avec les *Sample Diagrams* en bas à gauche pour découvrir tout ce que l'outil propose. Un script Python peut générer ce même pseudocode depuis n'importe quelle source lisible — un `compose.yaml`, une arborescence de dossiers, n'importe quelles données que vous avez déjà — et le CLI Mermaid le rend en PNG transparent.
## Installation — créons l'image Docker {#installation--lets-create-the-docker-image}
Le Dockerfile que nous allons utiliser est celui-ci. Il est assez simple.
**Dockerfile**
```docker
FROM node:20-alpine
RUN apk add --no-cache \
chromium \
nss \
freetype \
harfbuzz \
ca-certificates \
ttf-freefont \
font-noto-emoji \
python3 \
py3-pip \
py3-yaml
RUN fc-cache -fv
RUN npm install -g @mermaid-js/mermaid-cli
RUN mkdir -p /home/node/.cache/puppeteer && \
chmod -R 777 /home/node
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
ENV HOME=/home/node
WORKDIR /app
```
Pour construire cette image, lancez `docker build -t yourself/docker-python-mermaid .` dans votre terminal :
```bash
$ docker build -t yourself/docker-python-mermaid .
```
La taille finale de l'image sera d'environ 2 Go.

## Préparer le rendu Mermaid {#prepare-mermaid-rendering}
Il nous faut deux fichiers externes pour le rendu.
Le premier va configurer Puppeteer :
**puppeteer-config.json**
```json
{
"args": [
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
"--disable-accelerated-2d-canvas",
"--no-first-run",
"--no-zygote",
"--disable-gpu"
]
}
```
Et le second va nous permettre d'utiliser notre propre mise en forme (police, couleurs, ...) lors du rendu du pseudocode en image :
**mermaid-config.json**
```json
{
"theme": "base",
"themeVariables": {
"fontFamily": "Inter, Segoe UI, Arial",
"fontSize": "16px",
"lineColor": "#475569",
"mainBkg": "#ffffff",
"cScale0": "#0f172a",
"cScaleLabel0": "#ffffff",
"cScale1": "#2563eb",
"cScaleLabel1": "#ffffff",
"cScale2": "#059669",
"cScaleLabel2": "#ffffff",
"cScale3": "#d97706",
"cScaleLabel3": "#ffffff",
"cScale4": "#6366f1",
"cScaleLabel4": "#ffffff",
"nodeBorder": "#cbd5e1",
"clusterBkg": "#f8fafc"
}
}
```
## Enfin, il nous faut un script Python {#finally-we-need-a-python-script}
Nous avons notre image Docker, nous avons nos fichiers de configuration. Il ne reste plus qu'à écrire un script Python qui va faire « quelque chose », générer du pseudocode Mermaid et rendre ce pseudocode sous forme d'image.
Que peut-on faire ? Tout ce que vous voulez ! Utilisez simplement votre moteur d'IA préféré et demandez-lui de créer le script Python correspondant.
Essayons deux choses :
### Nous allons lire un compose.yaml Docker et créer une mindmap {#well-read-a-docker-composeyaml-and-create-a-mindmap}
Le script ci-dessous va lire un fichier `compose.yaml` et générer une mindmap visuelle. *Il existe aussi un outil prêt à l'emploi pour ça, voir [How to generate a graph from compose.yaml](/blog/docker-compose-viz), mais écrire le script nous-mêmes nous donne une liberté totale sur la sortie.*
**mindmap.py**
```python
import yaml
import argparse
import subprocess
import os
def get_emoji(name):
name = name.lower()
if any(x in name for x in ['db', 'postgres', 'sql', 'redis', 'cache']):
return "🗄️"
if any(x in name for x in ['nginx', 'proxy', 'gateway', 'api']):
return "🌐"
if 'auth' in name:
return "🔐"
return "📦"
def generate_mindmap(compose_path):
with open(compose_path, 'r') as f:
data = yaml.safe_load(f)
lines = ["mindmap", " root(( 🏗️ INFRASTRUCTURE ))"]
lines.append(" Services")
for name, config in data.get('services', {}).items():
emoji = get_emoji(name)
lines.append(f" ({emoji} {name})")
if 'depends_on' in config:
for dep in config['depends_on']:
lines.append(f" {{{{ 🔗 {dep} }}}}")
lines.append(" Networks")
for net in data.get('networks', {}).keys():
lines.append(f" ) {net} (")
lines.append(" Volumes")
for vol in data.get('volumes', {}).keys():
lines.append(f" [{vol}]")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("input", help="compose.yaml file")
parser.add_argument("-o", "--output", default="infra_map.png")
parser.add_argument("--style", default="/app/mermaid-config.json")
args = parser.parse_args()
mmd_code = generate_mindmap(args.input)
with open("temp.mmd", "w") as f:
f.write(mmd_code)
print("🚀 Generating Pro diagram...")
cmd = [
"mmdc",
"-i", "temp.mmd",
"-o", args.output,
"--width", "1600",
"-p", "/app/puppeteer-config.json",
"--backgroundColor", "transparent"
]
if os.path.exists(args.style):
cmd.extend(["-c", args.style])
subprocess.run(cmd, check=True)
os.remove("temp.mmd")
print(f"✨ Done! Image: {args.output}")
if __name__ == "__main__":
main()
```
Voici le `compose.yaml` bidon que je vais utiliser comme exemple :
**compose.yaml**
```yaml
services:
nginx-proxy:
image: nginx:alpine
ports: ["80:80", "443:443"]
networks: [frontend]
volumes: ["./nginx.conf:/etc/nginx/nginx.conf"]
api-gateway:
build: ./services/gateway
depends_on: [auth-service, user-service]
networks: [frontend, backend]
environment:
- DB_URL=postgres://db:5432/app
auth-service:
image: node:18
networks: [backend]
volumes: ["auth_data:/data"]
user-service:
image: python:3.11
networks: [backend, database]
environment:
- REDIS_URL=redis:6379
db-primary:
image: postgres:15
networks: [database]
volumes: ["pg_data:/var/lib/postgresql/data"]
environment:
- POSTGRES_PASSWORD=secret
redis-cache:
image: redis:alpine
networks: [database]
networks:
frontend:
backend:
database:
driver: bridge
volumes:
pg_data:
auth_data:
```
Maintenant, le meilleur moment : lancez cette commande pour produire l'image :
```bash
$ docker run --rm -u $(id -u):$(id -g) -e HOME=/tmp -v .:/app yourself/docker-python-mermaid python3 /app/scripts/mindmap.py /app/samples/compose.yaml -o /app/compose.png
Generating single mermaid chart
🚀 Generating Pro diagram...
✨ Done! Image: /app/compose.png
```
Une seule ligne de commande produit la mindmap montrée en haut de cet article. Cette image a été générée par le script Python `mindmap.py` après traitement de notre fichier `compose.yaml`. Nous pouvons maintenant modifier notre fichier YAML, relancer la commande `docker run`, et l'image sera mise à jour automatiquement.
> **Couleurs:**
>
> Souvenez-vous : il suffit de modifier le fichier `mermaid-config.json` si vous voulez une autre palette de couleurs.
## Plus d'exemples {#more-examples}
### Un autre exemple {#another-example}
Ce script va examiner notre structure de fichiers, identifier les fichiers présents et créer une mindmap également :
**project-dna.py**
```python
import os
import argparse
import subprocess
def get_category(filename):
ext = os.path.splitext(filename)[1].lower()
name = filename.lower()
if name == 'dockerfile' or ext == '.dockerfile': return '🐳 Docker'
if ext in ['.py', '.sh', '.js', '.go']: return '💻 Code'
if ext in ['.yaml', '.yml', '.json', '.conf', '.ini']: return '⚙️ Config'
if ext in ['.md', '.txt', '.pdf']: return '📝 Doc'
if ext in ['.png', '.jpg', '.svg', '.ico']: return '🎨 Assets'
return '📄 Other'
def generate_dna(path):
structure = {}
# Analyze files
for root, dirs, files in os.walk(path):
# Ignore hidden and heavy folders
dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__']
for file in files:
cat = get_category(file)
structure.setdefault(cat, set()).add(file)
if not structure:
return "mindmap\n root(( 🧬 PROJECT DNA ))\n (Empty or unreadable folder)"
lines = ["mindmap", " root(( 🧬 PROJECT DNA ))"]
for cat, files in structure.items():
lines.append(f" {cat}")
# Show max 6 files per category to avoid cluttering the image
for i, f in enumerate(sorted(list(files))):
if i >= 6:
lines.append(" (...)")
break
# Clean special characters for Mermaid
clean_name = f.replace('(', '').replace(')', '').replace('[', '').replace(']', '')
lines.append(f" ({clean_name})")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("path", help="Folder to scan")
parser.add_argument("-o", "--output", default="dna.png")
args = parser.parse_args()
# Ensure the path is absolute
target_path = os.path.abspath(args.path)
mmd_code = generate_dna(target_path)
with open("temp.mmd", "w", encoding='utf-8') as f:
f.write(mmd_code)
print(f"🔎 Analyzing {target_path}...")
subprocess.run([
"mmdc", "-i", "temp.mmd", "-o", args.output,
"-p", "/app/puppeteer-config.json",
"-c", "/app/mermaid-config.json",
"--width", "1200"
], check=True)
os.remove("temp.mmd")
print(f"✨ DNA successfully generated in {args.output}")
if __name__ == "__main__":
main()
```
```bash
$ docker run --rm -u $(id -u):$(id -g) -e HOME=/tmp -v .:/app yourself/docker-python-mermaid python3 /app/scripts/project-dna.py /app -o /app/project-dna.png
Generating single mermaid chart
🔎 Analyzing /app...
✨ DNA successfully generated in /app/project-dna.png
```

Cette mindmap ne se contente pas de montrer nos dossiers : elle regroupe les fichiers dans des catégories comme `Code`, `Configuration`, `Assets`, etc.
### Dernier exemple {#last-example}
Cette fois, un camembert :
**pie.py**
```python
import os
import argparse
import subprocess
def get_category(filename):
ext = os.path.splitext(filename)[1].lower()
name = filename.lower()
if name == 'dockerfile' or ext == '.dockerfile': return '🐳 Docker'
if ext in ['.py', '.sh', '.js', '.go']: return '💻 Code'
if ext in ['.yaml', '.yml', '.json', '.conf', '.ini']: return '⚙️ Config'
if ext in ['.md', '.txt', '.pdf']: return '📝 Doc'
if ext in ['.png', '.jpg', '.svg', '.ico']: return '🎨 Assets'
return '📄 Other'
def generate_pie_chart(path):
counts = {}
# Analyze files
for root, dirs, files in os.walk(path):
# Ignore hidden and heavy folders
dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__']
for file in files:
cat = get_category(file)
counts[cat] = counts.get(cat, 0) + 1
if not counts:
return "pie title Empty or unreadable folder\n \"None\" : 1"
lines = ["pie title Project File Type Distribution"]
for cat, count in counts.items():
# Mermaid pie charts use raw numbers and automatically calculate percentages
lines.append(f' "{cat}" : {count}')
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("path", help="Folder to scan")
parser.add_argument("-o", "--output", default="file_types_pie.png")
args = parser.parse_args()
# Ensure the path is absolute
target_path = os.path.abspath(args.path)
mmd_code = generate_pie_chart(target_path)
with open("temp.mmd", "w", encoding='utf-8') as f:
f.write(mmd_code)
print(f"🔎 Analyzing {target_path}...")
subprocess.run([
"mmdc", "-i", "temp.mmd", "-o", args.output,
"-p", "/app/puppeteer-config.json",
"-c", "/app/mermaid-config.json",
"--width", "1200",
"--backgroundColor", "transparent"
], check=True)
os.remove("temp.mmd")
print(f"✨ Pie chart successfully generated in {args.output}")
if __name__ == "__main__":
main()
```
```bash
$ docker run --rm -u $(id -u):$(id -g) -e HOME=/tmp -v .:/app yourself/docker-python-mermaid python3 /app/scripts/pie.py /app -o /app/pie.png
Generating single mermaid chart
🔎 Analyzing /app...
✨ Pie chart successfully generated in /app/pie.png
```

## Tous les fichiers nécessaires {#all-needed-files}
**Project: /tmp/mermaid**
> **Note:**
>
> Maintenant, lancez 'docker build -t yourself/docker-python-mermaid .' pour
> créer l'image Docker.
**Dockerfile**
```docker
FROM node:20-alpine
RUN apk add --no-cache \
chromium \
nss \
freetype \
harfbuzz \
ca-certificates \
ttf-freefont \
font-noto-emoji \
python3 \
py3-pip \
py3-yaml
RUN fc-cache -fv
RUN npm install -g @mermaid-js/mermaid-cli
RUN mkdir -p /home/node/.cache/puppeteer && \
chmod -R 777 /home/node
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
ENV HOME=/home/node
WORKDIR /app
```
**puppeteer-config.json**
```json
{
"args": [
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
"--disable-accelerated-2d-canvas",
"--no-first-run",
"--no-zygote",
"--disable-gpu"
]
}
```
**mermaid-config.json**
```json
{
"theme": "base",
"themeVariables": {
"fontFamily": "Inter, Segoe UI, Arial",
"fontSize": "16px",
"lineColor": "#475569",
"mainBkg": "#ffffff",
"cScale0": "#0f172a",
"cScaleLabel0": "#ffffff",
"cScale1": "#2563eb",
"cScaleLabel1": "#ffffff",
"cScale2": "#059669",
"cScaleLabel2": "#ffffff",
"cScale3": "#d97706",
"cScaleLabel3": "#ffffff",
"cScale4": "#6366f1",
"cScaleLabel4": "#ffffff",
"nodeBorder": "#cbd5e1",
"clusterBkg": "#f8fafc"
}
}
```
**scripts/mindmap.py**
```python
import yaml
import argparse
import subprocess
import os
def get_emoji(name):
name = name.lower()
if any(x in name for x in ['db', 'postgres', 'sql', 'redis', 'cache']):
return "🗄️"
if any(x in name for x in ['nginx', 'proxy', 'gateway', 'api']):
return "🌐"
if 'auth' in name:
return "🔐"
return "📦"
def generate_mindmap(compose_path):
with open(compose_path, 'r') as f:
data = yaml.safe_load(f)
lines = ["mindmap", " root(( 🏗️ INFRASTRUCTURE ))"]
lines.append(" Services")
for name, config in data.get('services', {}).items():
emoji = get_emoji(name)
lines.append(f" ({emoji} {name})")
if 'depends_on' in config:
for dep in config['depends_on']:
lines.append(f" {{{{ 🔗 {dep} }}}}")
lines.append(" Networks")
for net in data.get('networks', {}).keys():
lines.append(f" ) {net} (")
lines.append(" Volumes")
for vol in data.get('volumes', {}).keys():
lines.append(f" [{vol}]")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("input", help="compose.yaml file")
parser.add_argument("-o", "--output", default="infra_map.png")
parser.add_argument("--style", default="/app/mermaid-config.json")
args = parser.parse_args()
mmd_code = generate_mindmap(args.input)
with open("temp.mmd", "w") as f:
f.write(mmd_code)
print("🚀 Generating Pro diagram...")
cmd = [
"mmdc",
"-i", "temp.mmd",
"-o", args.output,
"--width", "1600",
"-p", "/app/puppeteer-config.json",
"--backgroundColor", "transparent"
]
if os.path.exists(args.style):
cmd.extend(["-c", args.style])
subprocess.run(cmd, check=True)
os.remove("temp.mmd")
print(f"✨ Done! Image: {args.output}")
if __name__ == "__main__":
main()
```
**scripts/pie.py**
```python
import os
import argparse
import subprocess
def get_category(filename):
ext = os.path.splitext(filename)[1].lower()
name = filename.lower()
if name == 'dockerfile' or ext == '.dockerfile': return '🐳 Docker'
if ext in ['.py', '.sh', '.js', '.go']: return '💻 Code'
if ext in ['.yaml', '.yml', '.json', '.conf', '.ini']: return '⚙️ Config'
if ext in ['.md', '.txt', '.pdf']: return '📝 Doc'
if ext in ['.png', '.jpg', '.svg', '.ico']: return '🎨 Assets'
return '📄 Other'
def generate_pie_chart(path):
counts = {}
# Analyze files
for root, dirs, files in os.walk(path):
# Ignore hidden and heavy folders
dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__']
for file in files:
cat = get_category(file)
counts[cat] = counts.get(cat, 0) + 1
if not counts:
return "pie title Empty or unreadable folder\n \"None\" : 1"
lines = ["pie title Project File Type Distribution"]
for cat, count in counts.items():
# Mermaid pie charts use raw numbers and automatically calculate percentages
lines.append(f' "{cat}" : {count}')
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("path", help="Folder to scan")
parser.add_argument("-o", "--output", default="file_types_pie.png")
args = parser.parse_args()
# Ensure the path is absolute
target_path = os.path.abspath(args.path)
mmd_code = generate_pie_chart(target_path)
with open("temp.mmd", "w", encoding='utf-8') as f:
f.write(mmd_code)
print(f"🔎 Analyzing {target_path}...")
subprocess.run([
"mmdc", "-i", "temp.mmd", "-o", args.output,
"-p", "/app/puppeteer-config.json",
"-c", "/app/mermaid-config.json",
"--width", "1200",
"--backgroundColor", "transparent"
], check=True)
os.remove("temp.mmd")
print(f"✨ Pie chart successfully generated in {args.output}")
if __name__ == "__main__":
main()
```
**samples/compose.yaml**
```yaml
services:
nginx-proxy:
image: nginx:alpine
ports: ["80:80", "443:443"]
networks: [frontend]
volumes: ["./nginx.conf:/etc/nginx/nginx.conf"]
api-gateway:
build: ./services/gateway
depends_on: [auth-service, user-service]
networks: [frontend, backend]
environment:
- DB_URL=postgres://db:5432/app
auth-service:
image: node:18
networks: [backend]
volumes: ["auth_data:/data"]
user-service:
image: python:3.11
networks: [backend, database]
environment:
- REDIS_URL=redis:6379
db-primary:
image: postgres:15
networks: [database]
volumes: ["pg_data:/var/lib/postgresql/data"]
environment:
- POSTGRES_PASSWORD=secret
redis-cache:
image: redis:alpine
networks: [database]
networks:
frontend:
backend:
database:
driver: bridge
volumes:
pg_data:
auth_data:
```
**scripts/project-dna.py**
```python
import os
import argparse
import subprocess
def get_category(filename):
ext = os.path.splitext(filename)[1].lower()
name = filename.lower()
if name == 'dockerfile' or ext == '.dockerfile': return '🐳 Docker'
if ext in ['.py', '.sh', '.js', '.go']: return '💻 Code'
if ext in ['.yaml', '.yml', '.json', '.conf', '.ini']: return '⚙️ Config'
if ext in ['.md', '.txt', '.pdf']: return '📝 Doc'
if ext in ['.png', '.jpg', '.svg', '.ico']: return '🎨 Assets'
return '📄 Other'
def generate_dna(path):
structure = {}
# Analyze files
for root, dirs, files in os.walk(path):
# Ignore hidden and heavy folders
dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__']
for file in files:
cat = get_category(file)
structure.setdefault(cat, set()).add(file)
if not structure:
return "mindmap\n root(( 🧬 PROJECT DNA ))\n (Empty or unreadable folder)"
lines = ["mindmap", " root(( 🧬 PROJECT DNA ))"]
for cat, files in structure.items():
lines.append(f" {cat}")
# Show max 6 files per category to avoid cluttering the image
for i, f in enumerate(sorted(list(files))):
if i >= 6:
lines.append(" (...)")
break
# Clean special characters for Mermaid
clean_name = f.replace('(', '').replace(')', '').replace('[', '').replace(']', '')
lines.append(f" ({clean_name})")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("path", help="Folder to scan")
parser.add_argument("-o", "--output", default="dna.png")
args = parser.parse_args()
# Ensure the path is absolute
target_path = os.path.abspath(args.path)
mmd_code = generate_dna(target_path)
with open("temp.mmd", "w", encoding='utf-8') as f:
f.write(mmd_code)
print(f"🔎 Analyzing {target_path}...")
subprocess.run([
"mmdc", "-i", "temp.mmd", "-o", args.output,
"-p", "/app/puppeteer-config.json",
"-c", "/app/mermaid-config.json",
"--width", "1200"
], check=True)
os.remove("temp.mmd")
print(f"✨ DNA successfully generated in {args.output}")
if __name__ == "__main__":
main()
```
## Conclusion {#conclusion}
En combinant la puissance de parsing de Python avec l'élégance de rendu de Mermaid, nous avons transformé une documentation statique en un actif dynamique.
Si l'approche *Documentation as Code* vous plaît, jetez un œil à deux autres de mes articles qui vont dans la même direction : [Docker - Diagrams as code](/blog/docker-diagram-as-code) et [Python - Generate flows using pydot](/blog/python-pydot). Et quand il n'y a aucun parser à écrire — une phrase qui décrit un processus, un format de config pour lequel je n'ai pas de script — [ai-diagram](/blog/ollama-ai-diagram) demande le Mermaid à un LLM local.
---