Salta ai contenuti

Dagger

Motore CI/CD programmabile che esegue pipeline in container utilizzando SDK Go, Python o TypeScript.

ComandoDescrizione
curl -fsSL https://dl.dagger.io/dagger/install.sh | shInstalla la CLI Dagger su Linux/macOS
brew install dagger/tap/daggerInstalla su macOS con Homebrew
choco install daggerInstalla su Windows con Chocolatey
scoop install daggerInstalla su Windows con Scoop
nix profile install nixpkgs#daggerInstalla su NixOS/con Nix
dagger versionMostra la versione di Dagger installata
ComandoDescrizione
dagger initInizializza un nuovo modulo Dagger
dagger init --sdk=pythonInizializza modulo con SDK Python
dagger init --sdk=typescriptInizializza modulo con SDK TypeScript
dagger init --sdk=goInizializza modulo con SDK Go
dagger init --name=my-pipelineInizializza modulo con nome personalizzato
dagger init --sdk=go --source=./ciInizializza in una sottodirectory
ComandoDescrizione
dagger callChiama una funzione dal modulo corrente
dagger call buildChiama la funzione build
dagger call test --source=.Chiama la funzione test con argomento source
dagger call build --source=. export --path=./distBuild ed esporta l’output localmente
dagger call lint --source=. stdoutEsegui lint e stampa stdout
dagger call publish --source=. --tag=v1.0Chiama la funzione publish con tag
dagger functionsElenca le funzioni disponibili nel modulo
dagger shellApri la shell interattiva Dagger
dagger queryEsegui query GraphQL raw contro il motore
dagger run cmdEsegui comando dentro una sessione Dagger
ComandoDescrizione
dagger loginAutenticati con Dagger Cloud
dagger logoutRimuovi l’autenticazione
dagger configMostra la configurazione del modulo
dagger cloud tracesVisualizza le tracce delle pipeline in Dagger Cloud
DAGGER_CLOUD_TOKEN=token dagger call buildEsegui con token Dagger Cloud
ComandoDescrizione
dagger install github.com/org/repoInstalla un modulo come dipendenza
dagger install github.com/org/repo@v1.0Installa una versione specifica
dagger install github.com/org/repo/subpathInstalla modulo da sottodirectory
dagger developGenera/aggiorna i binding SDK
dagger develop --sdk=pythonCambia SDK del modulo
dagger mod syncSincronizza le dipendenze del modulo
dagger mod updateAggiorna le dipendenze del modulo
dagger publish ttl.sh/my-modulePubblica modulo nel registro
dagger publish ghcr.io/org/module:v1.0Pubblica su GitHub Container Registry
ComandoDescrizione
func (m *MyModule) Build(ctx context.Context, src *dagger.Directory) *dagger.ContainerDefinisci funzione di build
dag.Container().From("golang:1.22")Crea container da immagine
container.WithDirectory("/src", src)Monta directory nel container
container.WithExec([]string{"go", "build", "."})Esegui comando nel container
container.WithEnvVariable("KEY", "value")Imposta variabile d’ambiente
container.File("/app/binary")Ottieni file dal container
container.Directory("/app/dist")Ottieni directory dal container
container.WithServiceBinding("db", dbSvc)Collega dipendenza di servizio
container.WithWorkdir("/src")Imposta directory di lavoro
container.WithUser("nonroot")Imposta utente del container
container.Publish(ctx, "ttl.sh/my-image")Pubblica container come immagine
container.Export(ctx, "./output.tar")Esporta container come 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)
}
ComandoDescrizione
@functionDecora funzione per esporla a Dagger
async def build(self, src: dagger.Directory) -> dagger.ContainerDefinisci funzione di build asincrona
dag.container().from_("python:3.12")Crea container da immagine
container.with_directory("/src", src)Monta directory
container.with_exec(["pip", "install", "-r", "requirements.txt"])Esegui pip install
await container.stdout()Ottieni output del comando
container.with_workdir("/app")Imposta directory di lavoro
container.with_user("nonroot")Imposta utente del container
await container.publish("ttl.sh/my-app")Pubblica container come immagine
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}`)
  }
}
ComandoDescrizione
container.WithMountedCache("/go/pkg", dag.CacheVolume("gomod"))Monta volume di cache persistente (Go)
container.with_mounted_cache("/root/.cache/pip", dag.cache_volume("pip"))Monta cache pip (Python)
dag.CacheVolume("node-modules")Crea volume di cache con nome
container.WithMountedCache("/root/.cache/go-build", dag.CacheVolume("gobuild"))Cache degli artefatti di build Go
container.WithMountedCache("/root/.npm", dag.CacheVolume("npm"))Cache dei pacchetti npm
container.WithMountedCache("/root/.cargo/registry", dag.CacheVolume("cargo"))Cache delle dipendenze Rust
ComandoDescrizione
container.WithEnvVariable("CACHE_BUSTER", time.Now().String())Invalida la cache quando necessario
dagger call --focus=false buildEsegui senza focus/caching automatico
container.WithoutEnvVariable("CACHE_BUSTER")Rimuovi variabile di invalidazione cache
ComandoDescrizione
dag.SetSecret("token", os.Getenv("API_TOKEN"))Crea segreto da variabile d’ambiente
container.WithSecretVariable("API_KEY", secret)Imposta variabile d’ambiente da segreto
container.WithMountedSecret("/run/secrets/key", secret)Monta segreto come file
dagger call deploy --token=env:API_TOKENPassa segreto da env tramite CLI
dagger call deploy --token=file:./token.txtPassa segreto da file tramite CLI
dagger call deploy --token=cmd:"vault read -field=token secret/app"Passa segreto da comando
ComandoDescrizione
container.WithServiceBinding("db", dbSvc)Collega un servizio a un container
container.WithExposedPort(8080)Esponi una porta da un container
container.AsService()Converti container in un servizio
dag.Container().From("postgres:16").WithExposedPort(5432).AsService()Crea un servizio database
service.Start(ctx)Avvia un servizio esplicitamente
service.Stop(ctx)Ferma un servizio in esecuzione
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 {
	// Fase 1: Build delle dipendenze
	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"})

	// Fase 2: Build del binario
	build := deps.
		WithDirectory("/src", src).
		WithExec([]string{"go", "build", "-ldflags=-s -w", "-o", "/app/server", "./cmd/server"})

	// Fase 3: Runtime minimale
	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=.
ComandoDescrizione
dagger call --debug buildEsegui con logging di debug
dagger call build --interactiveApri shell interattiva in caso di errore
container.WithExec(["sh", "-c", "cat /etc/os-release"])Debug del contenuto del container
container.Terminal()Apri terminale nel container (Go)
export DAGGER_LOG_LEVEL=debugImposta logging verboso
dagger query '{ container { from(address:"alpine") { exec(args:["ls"]) { stdout } } } }'Debug con GraphQL raw
dagger call build 2>&1 | tee dagger-output.logSalva output nel file di log
container.WithExec(["ls", "-la", "/app"])Elenca file per debug
container.WithExec(["env"])Stampa variabili d’ambiente
  1. Usa i volumi di cache generosamente — monta cache per i gestori di pacchetti (pip, npm, cargo, moduli Go) per ridurre drasticamente i tempi di build tra le esecuzioni.

  2. Mantieni i moduli piccoli e componibili — suddividi pipeline grandi in funzioni riutilizzabili che fanno una cosa sola bene, poi componile insieme.

  3. Usa i segreti invece delle variabili d’ambiente — non passare mai valori sensibili come stringhe in chiaro; usa sempre dag.SetSecret() o riferimenti a segreti dalla CLI.

  4. Fissa le versioni delle immagini base — usa tag specifici come golang:1.22 invece di latest per build riproducibili.

  5. Sfrutta i pattern multi-stage — separa le dipendenze di build dal runtime per produrre container finali minimali.

  6. Usa i binding dei servizi per i test di integrazione — avvia database, cache e altre dipendenze come servizi invece di simularli con mock.

  7. Esporta gli artefatti esplicitamente — usa .Export() o .Publish() per rendere gli output disponibili al di fuori del motore Dagger.

  8. Abilita Dagger Cloud — connettiti a Dagger Cloud per caching delle pipeline, tracciamento e debug collaborativo.

  9. Usa --interactive per il debug — quando un passaggio fallisce, --interactive ti porta nel container al punto di errore.

  10. Testa localmente prima della CI — le pipeline Dagger funzionano in modo identico sui laptop e nella CI, quindi verifica tutto localmente prima.