__init__.py
187 lignes · 6580 octets
""" Module Dagger pour la documentation gitrust (girust-doc). Conventions gitrust : - `.dagger/` à la racine du dépôt = mode Power (pipeline CI custom). - Gitrust détecte la présence du dossier et exécute `dagger call -m .dagger/ ci`. Fonctions exposées (appelables via `dagger call`) : - build : construit un site mdBook pour UNE langue (par défaut fr). - build-all : construit les 6 langues avec landing page Accept-Language. - lint : markdownlint-cli2 sur tous les .md. - validate-mermaid : vérifie chaque .mmd via mmdc. - ci : pipeline complet (lint + validate + build-all). Exemples : dagger call -m .dagger/ build --source=. --lang=en export --path=./dist dagger call -m .dagger/ build-all --source=. export --path=./dist dagger call -m .dagger/ ci --source=. export --path=./dist """ from typing import Annotated import dagger from dagger import DefaultPath, Doc, dag, function, object_type LANGS = ["fr", "en", "de", "es", "pt", "it"] @object_type class GirustDoc: """Pipeline de build de la documentation gitrust (mdBook + i18n + Mermaid).""" @function def base(self) -> dagger.Container: """ Image outillée réutilisée par toutes les autres fonctions (Rust + Node + gettext + mdBook + preprocessors + linters). """ return ( dag.container() .from_("rust:1.82-slim-bookworm") .with_exec([ "bash", "-c", "apt-get update && " "apt-get install -y --no-install-recommends " "curl ca-certificates git gettext " "nodejs npm build-essential pkg-config libssl-dev && " "rm -rf /var/lib/apt/lists/*", ]) .with_env_variable("CARGO_HOME", "/usr/local/cargo") .with_mounted_cache( "/usr/local/cargo/registry", dag.cache_volume("cargo-registry"), ) .with_exec([ "cargo", "install", "--locked", "mdbook", "mdbook-mermaid", "mdbook-i18n-helpers", ]) .with_exec([ "npm", "install", "-g", "markdownlint-cli2", "@mermaid-js/mermaid-cli", ]) ) def _sources(self, source: dagger.Directory) -> dagger.Container: """Prépare un conteneur avec les sources montées et mermaid installé.""" return ( self.base() .with_directory( "/src", source, exclude=["book/", "node_modules/", ".git/", ".dagger/"], ) .with_workdir("/src") .with_exec(["mdbook-mermaid", "install", "."]) ) @function async def build( self, source: Annotated[dagger.Directory, DefaultPath("/")], lang: Annotated[str, Doc("Code langue : fr, en, de, es, pt, it")] = "fr", ) -> dagger.Directory: """Construit le site mdBook pour UNE langue. Retourne le dossier book/<lang>/.""" return await ( self._sources(source) .with_env_variable("MDBOOK_BOOK__LANGUAGE", lang) .with_exec(["mdbook", "build", "-d", f"book/{lang}"]) .directory(f"/src/book/{lang}") .sync() ) @function async def build_all( self, source: Annotated[dagger.Directory, DefaultPath("/")], ) -> dagger.Directory: """ Construit les 6 langues et produit un dossier book/ avec une landing page racine qui redirige selon Accept-Language. """ ctr = self._sources(source) for lang in LANGS: ctr = ( ctr .with_env_variable("MDBOOK_BOOK__LANGUAGE", lang) .with_exec(["mdbook", "build", "-d", f"book/{lang}"]) ) ctr = ctr.with_new_file( "/src/book/index.html", """<!DOCTYPE html> <html lang="fr"><head><meta charset="utf-8"><title>Documentation gitrust</title> <script> const langs = ["fr","en","de","es","pt","it"]; const pref = (navigator.language || "fr").slice(0,2).toLowerCase(); location.replace((langs.includes(pref) ? pref : "fr") + "/index.html"); </script> <meta http-equiv="refresh" content="0;url=fr/index.html"></head> <body><p>Redirection vers <a href="fr/index.html">la documentation</a>…</p></body></html>""", ) return await ctr.directory("/src/book").sync() @function async def lint( self, source: Annotated[dagger.Directory, DefaultPath("/")], ) -> str: """ Exécute markdownlint-cli2 sur tous les .md du projet. Échoue avec code ≠ 0 si le lint trouve des violations. """ return await ( self.base() .with_directory( "/src", source, exclude=["book/", "node_modules/", ".git/", ".dagger/"], ) .with_workdir("/src") .with_exec([ "markdownlint-cli2", "**/*.md", "#!book/**", "#!node_modules/**", "#!.dagger/**", ]) .stdout() ) @function async def validate_mermaid( self, source: Annotated[dagger.Directory, DefaultPath("/")], ) -> str: """Vérifie que chaque fichier .mmd de diagrams/ est parsable par mmdc.""" return await ( self.base() .with_directory( "/src", source, include=["diagrams/**/*.mmd"], ) .with_workdir("/src") .with_exec([ "bash", "-c", "set -euo pipefail; " "for f in diagrams/*.mmd; do " " echo \"→ $f\"; " " mmdc -i \"$f\" -o \"/tmp/$(basename \"$f\" .mmd).svg\" --quiet; " "done; " "echo '✓ Tous les diagrammes Mermaid sont valides'", ]) .stdout() ) @function async def ci( self, source: Annotated[dagger.Directory, DefaultPath("/")], ) -> dagger.Directory: """ Pipeline complet : lint + validate-mermaid (en parallèle) + build-all. Fonction appelée par gitrust-ci en mode Power. """ # Dagger évalue paresseusement : les appels ci-dessous démarrent # en parallèle, et un échec propage l'erreur. await self.lint(source) await self.validate_mermaid(source) return await self.build_all(source)
GitRust