DocsFrom ScratchConstruire un pipeline CI/CDConstruire un pipeline CI/CD Ce guide explique comment construire un pipeline CI/CD de zéro en utilisant Cadence.CI et le langage Mélodium. À la fin, vous aurez un pipeline fonctionnel qui clone un dépôt, compile un binaire et streame le résultat vers votre machine locale. Note Vous avez besoin de Mélodium version 0.10.0 ou supérieure installée. Pour une introduction plus approfondie au langage Mélodium, consultez le Livre du Langage Mélodium . Créer le projet Commencez par créer la structure d’un nouveau programme Mélodium : melodium new my_cicd Ouvrez ensuite my_cicd/Compo.toml et ajoutez les dépendances suivantes : [dependencies] std = "0.10.0" cicd = "0.10.0" fs = "0.10.0" process = "0.10.0" work = "0.10.0" Ces packages fournissent la bibliothèque standard, les primitives de runner CI/CD, l’accès au système de fichiers, la gestion des processus et les définitions de ressources. Définir le traitement Un traitement en Mélodium est une unité de travail nommée avec des entrées, des sorties et des dépendances typées. Ouvrez my_cicd/lib-root.mel et déclarez le traitement buildSoftware : use cicd/runners::CicdDispatchEngine use cicd/runners::CicdRunnerEngine treatment buildSoftware[cicd: CicdDispatchEngine]() input trigger: Block<void> output finished: Block<void> output binary: Stream<byte> model runner: CicdRunnerEngine() { } Décomposition de la signature : [cicd: CicdDispatchEngine], le modèle de dispatch qui gère le provisionnement des runners (docs ). input trigger: Block<void>, un signal qui déclenche le build. output finished: Block<void>, un signal émis quand le build se termine. output binary: Stream<byte>, un flux d’octets typé transportant le binaire compilé. model runner: CicdRunnerEngine(), le modèle de runner que ce traitement utilisera (docs ). Définir les étapes Dans le corps du traitement, déclarez trois étapes qui s’exécuteront séquentiellement sur le runner : l’installation des outils, le clonage du dépôt et la compilation du binaire. use cicd/steps::stepOn use process/command::|raw_commands /* … */ install: stepOn[runner=runner]( name="install", executor_name="ubuntu", commands=|raw_commands([ "apt-get update", "apt-get install -y gcc" ]) ) clone: stepOn[runner=runner]( name="clone", executor_name="ubuntu", commands=|raw_commands([ "git clone https://github.com/niwasawa/c-hello-world.git" ]) ) build: stepOn[runner=runner]( name="build", executor_name="ubuntu", commands=|raw_commands([ "gcc c-hello-world/hello.c", "mv a.out /mnt/data/executable" ]), out_filesystem="data", out_file="executable" ) Les trois étapes s’exécutent sur l’exécuteur nommé "ubuntu" du runner. L’étape build streame également le fichier compilé via le paramètre out_file. Consultez la documentation de stepOn pour la référence complète. Connecter les étapes Les étapes sont connectées en déclarant des flux de données entre leurs entrées et sorties. Les étapes install et clone peuvent s’exécuter en parallèle ; build attend que les deux soient terminées avant de démarrer. use std/flow::waitBlock /* … */ awaitPrepare: waitBlock<void>() install.success -> awaitPrepare.a clone.success ---> awaitPrepare.b awaitPrepare.awaited -> build.trigger,finished -> Self.finished build.data -------------> Self.binary waitBlock est une primitive de synchronisation qui émet awaited seulement après que a et b ont tous les deux reçu une valeur. Une fois build terminé, finished et binary sont transmis aux propres sorties du traitement. Provisionner le runner Les étapes ci-dessus s’exécutent sur un runner, mais le runner lui-même doit être démarré et arrêté dans le traitement. Ajoutez les appels setupRunner et stopRunner et connectez-les au cycle de vie des étapes : use cicd/runners::setupRunner use cicd/runners::stopRunner use work/resources/arch::|amd64 use work/resources::|container use work/resources::|mount use work/resources::|volume /* … */ setupRunner[dispatcher=cicd, runner=runner]( cpu=200, memory=100, storage=100, name="builder", volumes=[|volume("data", 30)], containers=[|container("ubuntu", 1000, 1000, 1000, |amd64(), [|mount("data", "/mnt/data")], "ubuntu:latest", _)] ) stopRunner[runner=runner]() Self.trigger -> setupRunner.trigger,ready -> install.trigger setupRunner.ready ---------> clone.trigger build.finished -> stopRunner.trigger Le runner est démarré quand le traitement est déclenché, install et clone démarrent dès qu’il est prêt, et stopRunner est appelé après que build se termine. Le runner n’est jamais inactif plus longtemps que nécessaire. Traitement complet En assemblant tout : use cicd/runners::CicdDispatchEngine use cicd/runners::CicdRunnerEngine use cicd/runners::setupRunner use cicd/runners::stopRunner use cicd/steps::stepOn use process/command::|raw_commands use std/flow::waitBlock use work/resources/arch::|amd64 use work/resources::|container use work/resources::|mount use work/resources::|volume treatment buildSoftware[cicd: CicdDispatchEngine]() input trigger: Block<void> output finished: Block<void> output binary: Stream<byte> model runner: CicdRunnerEngine() { setupRunner[dispatcher=cicd, runner=runner]( cpu=200, memory=100, storage=100, name="builder", volumes=[|volume("data", 30)], containers=[|container("ubuntu", 1000, 1000, 1000, |amd64(), [|mount("data", "/mnt/data")], "ubuntu:latest", _)] ) stopRunner[runner=runner]() Self.trigger -> setupRunner.trigger,ready -> install.trigger setupRunner.ready ---------> clone.trigger build.finished -> stopRunner.trigger install: stepOn[runner=runner]( name="install", executor_name="ubuntu", commands=|raw_commands([ "apt-get update", "apt-get install -y gcc" ]) ) clone: stepOn[runner=runner]( name="clone", executor_name="ubuntu", commands=|raw_commands([ "git clone https://github.com/niwasawa/c-hello-world.git" ]) ) build: stepOn[runner=runner]( name="build", executor_name="ubuntu", commands=|raw_commands([ "gcc c-hello-world/hello.c", "mv a.out /mnt/data/executable" ]), out_filesystem="data", out_file="executable" ) awaitPrepare: waitBlock<void>() install.success -> awaitPrepare.a clone.success ---> awaitPrepare.b awaitPrepare.awaited -> build.trigger,finished -> Self.finished build.data -------------> Self.binary } Créer le point d’entrée Un traitement ne peut pas être exécuté directement ; il nécessite un point d’entrée qui le connecte au monde extérieur. Ajoutez un traitement runBuild dans my_cicd/lib-root.mel : use fs/local::writeLocal use std/engine/util::startup treatment runBuild(var output_file: string = "./executable") model cicd: CicdDispatchEngine(location="compose", api_token="") { startup() buildSoftware[cicd=cicd]() writeLocal(path=output_file) startup.trigger -> buildSoftware.trigger,binary -> writeLocal.data } Déclarez-le ensuite comme point d’entrée du programme dans my_cicd/Compo.toml : [entrypoints] main = "my_cicd::runBuild" Exécuter melodium my_cicd/Compo.toml Un fichier nommé executable apparaîtra dans le répertoire courant une fois le pipeline terminé. Note Le CicdDispatchEngine est configuré avec location="compose" ici, ce qui utilise Podman Compose ou Docker Compose pour provisionner les runners localement. Pour une utilisation en production avec Cadence.CI, définissez location sur votre cluster configuré et fournissez le api_token approprié. Voir la documentation de CicdDispatchEngine. Programme complet Téléchargez le programme complet pour référence.Intégration avec les clusters KubernetesTemplate CI/CD