Skip to content
Skillv1.0.0

dagger

Write CI/CD pipelines as code with Dagger — portable, cacheable, container-based pipelines that run locally and in any CI system. Use when someone asks to "write CI pipeline in TypeScript", "portable

by terminalskills(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from terminalskills/skills (skills/dagger/SKILL.md). Install upstream with npx skills add terminalskills/skills --skill dagger. Copyright stays with the author (Apache-2.0).

Dagger

Overview

Dagger lets you write CI/CD pipelines in TypeScript, Python, or Go instead of YAML. Pipelines run in containers, are fully cacheable, and work identically on your laptop and in GitHub Actions/GitLab CI/Jenkins. No more "works in CI but not locally" — test your entire pipeline before pushing.

When to Use

  • Tired of debugging YAML-based CI configs by pushing commits
  • Need to run the exact same pipeline locally and in CI
  • Complex build pipelines that benefit from TypeScript/Python logic (conditionals, loops)
  • Want container-level caching for build steps
  • Multi-language monorepo where different projects need different pipelines

Instructions

Setup

# Install Dagger CLI (macOS/Linux)
brew install dagger/tap/dagger

# Initialize in your project
dagger init --sdk=typescript

Basic Pipeline

// dagger/src/index.ts — CI pipeline for a Node.js project
/**
 * Dagger pipeline that builds, tests, and creates a Docker image.
 * Runs identically on your laptop and in any CI system.
 */
import { dag, Container, Directory, object, func } from "@dagger.io/dagger";

@object()
class Ci {
  /**
   * Run the full CI pipeline: install → lint → test → build.
   */
  @func()
  async ci(source: Directory): Promise<string> {
    const node = this.base(source);

    // Run steps in sequence (each cached independently)
    await this.lint(source);
    await this.test(source);
    const built = await this.build(source);

    return "✅ CI passed";
  }

  /**
   * Base container with dependencies installed.
   * Cached — only re-runs if package.json changes.
   */
  @func()
  base(source: Directory): Container {
    return dag
      .container()
      .from("node:20-slim")
      .withDirectory("/app", source)
      .withWorkdir("/app")
      // Cache node_modules across runs
      .withMountedCache("/app/node_modules", dag.cacheVolume("node-modules"))
      .withExec(["npm", "ci"]);
  }

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

  @func()
  async test(source: Directory): Promise<string> {
    return this.base(source)
      .withExec(["npm", "run", "test", "--", "--run"])
      .stdout();
  }

  @func()
  async build(source: Directory): Promise<Directory> {
    return this.base(source)
      .withExec(["npm", "run", "build"])
      .directory("/app/dist");
  }

  /**
   * Build and push a Docker image.
   */
  @func()
  async publish(source: Directory, registry: string, tag: string): Promise<string> {
    const built = await this.build(source);

    const image = dag
      .container()
      .from("node:20-slim")
      .withDirectory("/app", built)
      .withWorkdir("/app")
      .withEntrypoint(["node", "index.js"]);

    const ref = await image.publish(`${registry}:${tag}`);
    return `📦 Published: ${ref}`;
  }
}

Run Locally

# Run the full CI pipeline on your local code
dagger call ci --source=.

# Just run tests
dagger call test --source=.

# Build and publish Docker image
dagger call publish --source=. --registry=ghcr.io/myorg/myapp --tag=latest

Pipeline with Services (Database)

// dagger/src/index.ts — Pipeline with Postgres for integration tests
@object()
class Ci {
  @func()
  async integrationTest(source: Directory): Promise<string> {
    // Start a Postgres service container
    const db = dag
      .container()
      .from("postgres:16")
      .withEnvVariable("POSTGRES_PASSWORD", "test")
      .withEnvVariable("POSTGRES_DB", "testdb")
      .withExposedPort(5432)
      .asService();

    // Run tests with database available
    return this.base(source)
      .withServiceBinding("db", db)
      .withEnvVariable("DATABASE_URL", "postgresql://postgres:test@db:5432/testdb")
      .withExec(["npm", "run", "db:migrate"])
      .withExec(["npm", "run", "test:integration"])
      .stdout();
  }
}

Secrets

// Handle secrets safely — never baked into container layers
@func()
async deploy(source: Directory, apiKey: Secret): Promise<string> {
  return this.base(source)
    .withSecretVariable("API_KEY", apiKey)  // Mounted as env var, not in layer
    .withExec(["npm", "run", "deploy"])
    .stdout();
}

GitHub Actions Integration

# .github/workflows/ci.yml — Run Dagger pipeline in GitHub Actions
name: CI
on: [push, pull_request]

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dagger/dagger-for-github@v7
        with:
          verb: call
          args: ci --source=.

Examples

Example 1: Monorepo CI pipeline

User prompt: "I have a monorepo with a Next.js frontend and a Python backend. Set up a CI pipeline that tests both."

The agent will create a Dagger pipeline with separate functions for frontend (Node.js container, npm test) and backend (Python container, pytest), both running from the same pipeline with shared caching.

Example 2: Replace GitHub Actions with Dagger

User prompt: "My GitHub Actions workflow is 200 lines of YAML and I can't test it locally. Convert it to Dagger."

The agent will translate each YAML step into a Dagger function, add caching for dependencies, and show how to run the same pipeline locally with dagger call.

Guidelines

  • Run locally firstdagger call on your laptop before pushing to CI
  • Cache aggressivelywithMountedCache for node_modules, pip cache, build artifacts
  • Secrets via Secret type — never pass secrets as plain strings or env vars in Dockerfiles
  • Services for databasesasService() + withServiceBinding() for Postgres/Redis in tests
  • Each @func() is independently callable — design for composability
  • Container layers are cached — order operations so rarely-changing steps come first
  • Dagger Cloud for team caching — share build cache across CI runners
  • Use withExec for each command — separate steps for better cache granularity

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/terminalskills-skills-dagger/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

terminalskills-skills-dagger.ocm.jsonjson
{
  "ocm": "1",
  "id": "terminalskills-skills-dagger",
  "kind": "skill",
  "name": "dagger",
  "description": "Write CI/CD pipelines as code with Dagger — portable, cacheable, container-based pipelines that run locally and in any CI system. Use when someone asks to \"write CI pipeline in TypeScript\", \"portable CI/CD\", \"run GitHub Actions locally\", \"Dagger pipeline\", \"CI as code\", \"containerized build pipeline\", or \"test my CI locally before pushing\". Covers Dagger SDK (TypeScript/Python), pipeline composition, caching, secrets, and multi-stage builds.",
  "publisher": "terminalskills",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "coding"
    ],
    "tags": [
      "skill-md",
      "ci-cd",
      "containers",
      "pipelines",
      "dagger",
      "docker",
      "skills-sh"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Write CI/CD pipelines as code with Dagger — portable, cacheable, container-based pipelines that run locally and in any CI system. Use when someone asks to \"write CI pipeline in TypeScript\", \"portable CI/CD\", \"run GitHub Actions locally\", \"Dagger pipeline\", \"CI as code\", \"containerized build pipeline\", or \"test my CI locally before pushing\". Covers Dagger SDK (TypeScript/Python), pipeline composition, caching, secrets, and multi-stage builds."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "skills.sh",
      "repository": "https://github.com/terminalskills/skills",
      "path": "skills/dagger/SKILL.md",
      "ref": "HEAD",
      "url": "https://github.com/terminalskills/skills/blob/HEAD/skills/dagger/SKILL.md",
      "key": "terminalskills/skills/skills/dagger/SKILL.md"
    },
    "compatibility": "Docker required. Dagger CLI. TypeScript/Python/Go SDK.",
    "license": "Apache-2.0"
  },
  "instructions": "# Dagger\n\n## Overview\n\nDagger lets you write CI/CD pipelines in TypeScript, Python, or Go instead of YAML. Pipelines run in containers, are fully cacheable, and work identically on your laptop and in GitHub Actions/GitLab CI/Jenkins. No more \"works in CI but not locally\" — test your entire pipeline before pushing.\n\n## When to Use\n\n- Tired of debugging YAML-based CI configs by pushing commits\n- Need to run the exact same pipeline locally and in CI\n- Complex build pipelines that benefit from TypeScript/Python logic (conditionals, loops)\n- Want container-level caching for build steps\n- Multi-lang",
  "cost": {
    "context_tokens": 1510
  }
}

Fetch it by URL: GET /api/v1/registry/terminalskills-skills-dagger/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.