Aller au contenu

Dagger

Moteur CI/CD programmable qui exécute des pipelines dans des conteneurs en utilisant les SDK Go, Python ou TypeScript.

CommandeDescription
curl -fsSL https://dl.dagger.io/dagger/install.sh | shInstaller le CLI Dagger sur Linux/macOS
brew install dagger/tap/daggerInstaller sur macOS avec Homebrew
choco install daggerInstaller sur Windows avec Chocolatey
scoop install daggerInstaller sur Windows avec Scoop
nix profile install nixpkgs#daggerInstaller sur NixOS/avec Nix
dagger versionAfficher la version installée de Dagger
CommandeDescription
dagger initInitialiser un nouveau module Dagger
dagger init --sdk=pythonInitialiser un module avec le SDK Python
dagger init --sdk=typescriptInitialiser un module avec le SDK TypeScript
dagger init --sdk=goInitialiser un module avec le SDK Go
dagger init --name=my-pipelineInitialiser un module avec un nom personnalisé
dagger init --sdk=go --source=./ciInitialiser dans un sous-répertoire
CommandeDescription
dagger callAppeler une fonction du module courant
dagger call buildAppeler la fonction build
dagger call test --source=.Appeler la fonction test avec l’argument source
dagger call build --source=. export --path=./distCompiler et exporter la sortie localement
dagger call lint --source=. stdoutExécuter le lint et afficher stdout
dagger call publish --source=. --tag=v1.0Appeler la fonction publish avec un tag
dagger functionsLister les fonctions disponibles dans le module
dagger shellOuvrir un shell Dagger interactif
dagger queryExécuter une requête GraphQL brute contre le moteur
dagger run cmdExécuter une commande dans une session Dagger
CommandeDescription
dagger loginS’authentifier auprès de Dagger Cloud
dagger logoutSupprimer l’authentification
dagger configAfficher la configuration du module
dagger cloud tracesVoir les traces de pipeline dans Dagger Cloud
DAGGER_CLOUD_TOKEN=token dagger call buildExécuter avec un token Dagger Cloud
CommandeDescription
dagger install github.com/org/repoInstaller un module de dépendance
dagger install github.com/org/repo@v1.0Installer une version spécifique
dagger install github.com/org/repo/subpathInstaller un module depuis un sous-répertoire
dagger developGénérer/mettre à jour les bindings SDK
dagger develop --sdk=pythonChanger le module vers un autre SDK
dagger mod syncSynchroniser les dépendances du module
dagger mod updateMettre à jour les dépendances du module
dagger publish ttl.sh/my-modulePublier le module dans un registre
dagger publish ghcr.io/org/module:v1.0Publier sur GitHub Container Registry
CommandeDescription
func (m *MyModule) Build(ctx context.Context, src *dagger.Directory) *dagger.ContainerDéfinir une fonction build
dag.Container().From("golang:1.22")Créer un conteneur à partir d’une image
container.WithDirectory("/src", src)Monter un répertoire dans le conteneur
container.WithExec([]string{"go", "build", "."})Exécuter une commande dans le conteneur
container.WithEnvVariable("KEY", "value")Définir une variable d’environnement
container.File("/app/binary")Obtenir un fichier du conteneur
container.Directory("/app/dist")Obtenir un répertoire du conteneur
container.WithServiceBinding("db", dbSvc)Lier une dépendance de service
container.WithWorkdir("/src")Définir le répertoire de travail
container.WithUser("nonroot")Définir l’utilisateur du conteneur
container.Publish(ctx, "ttl.sh/my-image")Publier le conteneur comme image
container.Export(ctx, "./output.tar")Exporter le conteneur comme tarball
package main

import (
	"context"
	"dagger/my-pipeline/internal/dagger"
)

type MyPipeline struct{}

func (m *MyPipeline) Build(ctx context.Context, src *dagger.Directory) *dagger.Container {
	goCache := dag.CacheVolume("go-mod")
	buildCache := dag.CacheVolume("go-build")

	return dag.Container().
		From("golang:1.22-alpine").
		WithMountedCache("/go/pkg/mod", goCache).
		WithMountedCache("/root/.cache/go-build", buildCache).
		WithDirectory("/src", src).
		WithWorkdir("/src").
		WithExec([]string{"go", "mod", "download"}).
		WithExec([]string{"go", "build", "-o", "/app/server", "./cmd/server"})
}

func (m *MyPipeline) Test(ctx context.Context, src *dagger.Directory) (string, error) {
	return m.Build(ctx, src).
		WithExec([]string{"go", "test", "-v", "./..."}).
		Stdout(ctx)
}

func (m *MyPipeline) Publish(ctx context.Context, src *dagger.Directory, tag string) (string, error) {
	build := m.Build(ctx, src)
	return dag.Container().
		From("cgr.dev/chainguard/static:latest").
		WithFile("/app/server", build.File("/app/server")).
		WithEntrypoint([]string{"/app/server"}).
		Publish(ctx, "ttl.sh/my-app:"+tag)
}
CommandeDescription
@functionDécorer une fonction pour l’exposer à Dagger
async def build(self, src: dagger.Directory) -> dagger.ContainerDéfinir une fonction build asynchrone
dag.container().from_("python:3.12")Créer un conteneur à partir d’une image
container.with_directory("/src", src)Monter un répertoire
container.with_exec(["pip", "install", "-r", "requirements.txt"])Exécuter pip install
await container.stdout()Obtenir la sortie de la commande
container.with_workdir("/app")Définir le répertoire de travail
container.with_user("nonroot")Définir l’utilisateur du conteneur
await container.publish("ttl.sh/my-app")Publier le conteneur comme image
import dagger
from dagger import dag, function, object_type

@object_type
class MyPipeline:
    @function
    async def build(self, src: dagger.Directory) -> dagger.Container:
        pip_cache = dag.cache_volume("pip")
        return (
            dag.container()
            .from_("python:3.12-slim")
            .with_mounted_cache("/root/.cache/pip", pip_cache)
            .with_directory("/app", src)
            .with_workdir("/app")
            .with_exec(["pip", "install", "--no-cache-dir", "-r", "requirements.txt"])
            .with_exec(["pip", "install", "."])
        )

    @function
    async def test(self, src: dagger.Directory) -> str:
        return await (
            self.build(src)
            .with_exec(["pytest", "-v", "--tb=short"])
            .stdout()
        )

    @function
    async def lint(self, src: dagger.Directory) -> str:
        return await (
            self.build(src)
            .with_exec(["ruff", "check", "."])
            .stdout()
        )

    @function
    async def publish(self, src: dagger.Directory, tag: str) -> str:
        build = self.build(src)
        return await (
            dag.container()
            .from_("cgr.dev/chainguard/python:latest")
            .with_directory("/app", build.directory("/app"))
            .with_entrypoint(["python", "-m", "myapp"])
            .publish(f"ttl.sh/my-python-app:{tag}")
        )
import { dag, Container, Directory, object, func } from "@dagger.io/dagger"

@object()
class MyPipeline {
  @func()
  build(src: Directory): Container {
    const nodeCache = dag.cacheVolume("node-modules")
    return dag
      .container()
      .from("node:20-slim")
      .withMountedCache("/app/node_modules", nodeCache)
      .withDirectory("/app", src)
      .withWorkdir("/app")
      .withExec(["npm", "ci"])
      .withExec(["npm", "run", "build"])
  }

  @func()
  async test(src: Directory): Promise<string> {
    return this.build(src)
      .withExec(["npm", "test"])
      .stdout()
  }

  @func()
  async lint(src: Directory): Promise<string> {
    return this.build(src)
      .withExec(["npm", "run", "lint"])
      .stdout()
  }

  @func()
  async publish(src: Directory, tag: string): Promise<string> {
    return dag
      .container()
      .from("cgr.dev/chainguard/node:latest")
      .withDirectory("/app", this.build(src).directory("/app/dist"))
      .withEntrypoint(["node", "/app/index.js"])
      .publish(`ttl.sh/my-node-app:${tag}`)
  }
}
CommandeDescription
container.WithMountedCache("/go/pkg", dag.CacheVolume("gomod"))Monter un volume de cache persistant (Go)
container.with_mounted_cache("/root/.cache/pip", dag.cache_volume("pip"))Monter le cache pip (Python)
dag.CacheVolume("node-modules")Créer un volume de cache nommé
container.WithMountedCache("/root/.cache/go-build", dag.CacheVolume("gobuild"))Mettre en cache les artefacts de compilation Go
container.WithMountedCache("/root/.npm", dag.CacheVolume("npm"))Mettre en cache les paquets npm
container.WithMountedCache("/root/.cargo/registry", dag.CacheVolume("cargo"))Mettre en cache les dépendances Rust
CommandeDescription
container.WithEnvVariable("CACHE_BUSTER", time.Now().String())Invalider le cache si nécessaire
dagger call --focus=false buildExécuter sans focus/mise en cache automatique
container.WithoutEnvVariable("CACHE_BUSTER")Supprimer la variable d’invalidation de cache
CommandeDescription
dag.SetSecret("token", os.Getenv("API_TOKEN"))Créer un secret depuis une variable d’environnement
container.WithSecretVariable("API_KEY", secret)Définir une variable d’env depuis un secret
container.WithMountedSecret("/run/secrets/key", secret)Monter un secret comme fichier
dagger call deploy --token=env:API_TOKENPasser un secret depuis l’env via le CLI
dagger call deploy --token=file:./token.txtPasser un secret depuis un fichier via le CLI
dagger call deploy --token=cmd:"vault read -field=token secret/app"Passer un secret depuis une commande
CommandeDescription
container.WithServiceBinding("db", dbSvc)Lier un service à un conteneur
container.WithExposedPort(8080)Exposer un port depuis un conteneur
container.AsService()Convertir un conteneur en service
dag.Container().From("postgres:16").WithExposedPort(5432).AsService()Créer un service de base de données
service.Start(ctx)Démarrer un service explicitement
service.Stop(ctx)Arrêter un service en cours d’exécution
func (m *MyPipeline) IntegrationTest(ctx context.Context, src *dagger.Directory) (string, error) {
	postgres := dag.Container().
		From("postgres:16-alpine").
		WithEnvVariable("POSTGRES_PASSWORD", "test").
		WithEnvVariable("POSTGRES_DB", "testdb").
		WithExposedPort(5432).
		AsService()

	redis := dag.Container().
		From("redis:7-alpine").
		WithExposedPort(6379).
		AsService()

	return m.Build(ctx, src).
		WithServiceBinding("db", postgres).
		WithServiceBinding("cache", redis).
		WithEnvVariable("DATABASE_URL", "postgres://postgres:test@db:5432/testdb").
		WithEnvVariable("REDIS_URL", "redis://cache:6379").
		WithExec([]string{"go", "test", "-v", "-tags=integration", "./..."}).
		Stdout(ctx)
}
func (m *MyPipeline) BuildProduction(ctx context.Context, src *dagger.Directory) *dagger.Container {
	// Étape 1 : Compiler les dépendances
	deps := dag.Container().
		From("golang:1.22-alpine").
		WithDirectory("/src", src, dagger.ContainerWithDirectoryOpts{
			Include: []string{"go.mod", "go.sum"},
		}).
		WithWorkdir("/src").
		WithMountedCache("/go/pkg/mod", dag.CacheVolume("gomod")).
		WithExec([]string{"go", "mod", "download"})

	// Étape 2 : Compiler le binaire
	build := deps.
		WithDirectory("/src", src).
		WithExec([]string{"go", "build", "-ldflags=-s -w", "-o", "/app/server", "./cmd/server"})

	// Étape 3 : Runtime minimal
	return dag.Container().
		From("cgr.dev/chainguard/static:latest").
		WithFile("/app/server", build.File("/app/server")).
		WithExposedPort(8080).
		WithEntrypoint([]string{"/app/server"})
}
name: CI with Dagger
on:
  push:
    branches: [main]
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Dagger
        run: |
          curl -fsSL https://dl.dagger.io/dagger/install.sh | sh
          sudo mv ./bin/dagger /usr/local/bin/
      - name: Run pipeline
        env:
          DAGGER_CLOUD_TOKEN: ${{ secrets.DAGGER_CLOUD_TOKEN }}
        run: |
          dagger call build --source=.
          dagger call test --source=.
          dagger call lint --source=.
      - name: Publish
        if: github.ref == 'refs/heads/main'
        run: |
          dagger call publish --source=. --tag=${{ github.sha }}
stages:
  - build
  - test

variables:
  DAGGER_CLOUD_TOKEN: $DAGGER_CLOUD_TOKEN

build:
  stage: build
  image: alpine:latest
  before_script:
    - apk add curl
    - curl -fsSL https://dl.dagger.io/dagger/install.sh | sh
    - export PATH=$PWD/bin:$PATH
  script:
    - dagger call build --source=.
    - dagger call test --source=.
CommandeDescription
dagger call --debug buildExécuter avec la journalisation de débogage
dagger call build --interactiveOuvrir un shell interactif en cas d’échec
container.WithExec(["sh", "-c", "cat /etc/os-release"])Déboguer le contenu du conteneur
container.Terminal()Ouvrir un terminal dans le conteneur (Go)
export DAGGER_LOG_LEVEL=debugDéfinir la journalisation détaillée
dagger query '{ container { from(address:"alpine") { exec(args:["ls"]) { stdout } } } }'Déboguer avec du GraphQL brut
dagger call build 2>&1 | tee dagger-output.logSauvegarder la sortie dans un fichier journal
container.WithExec(["ls", "-la", "/app"])Lister les fichiers pour le débogage
container.WithExec(["env"])Afficher les variables d’environnement
  1. Utiliser les volumes de cache généreusement — montez des caches pour les gestionnaires de paquets (pip, npm, cargo, modules Go) pour réduire considérablement les temps de compilation entre les exécutions.

  2. Garder les modules petits et composables — découpez les grands pipelines en fonctions réutilisables qui font chacune une seule chose bien, puis composez-les ensemble.

  3. Utiliser les secrets plutôt que les variables d’environnement — ne passez jamais de valeurs sensibles en texte brut ; utilisez toujours dag.SetSecret() ou les références de secrets du CLI.

  4. Fixer les versions des images de base — utilisez des tags spécifiques comme golang:1.22 plutôt que latest pour des builds reproductibles.

  5. Exploiter les patterns multi-étapes — séparez les dépendances de compilation du runtime pour produire des conteneurs finaux minimaux.

  6. Utiliser les liaisons de services pour les tests d’intégration — démarrez des bases de données, caches et autres dépendances comme services au lieu de les simuler.

  7. Exporter les artefacts explicitement — utilisez .Export() ou .Publish() pour rendre les sorties disponibles en dehors du moteur Dagger.

  8. Activer Dagger Cloud — connectez-vous à Dagger Cloud pour la mise en cache des pipelines, le traçage et le débogage collaboratif.

  9. Utiliser --interactive pour le débogage — quand une étape échoue, --interactive vous place dans le conteneur au point d’échec.

  10. Tester localement avant le CI — les pipelines Dagger s’exécutent de manière identique sur les portables et en CI, donc validez tout localement d’abord.