Claude
Skills
Sign in
Back

dagger-helper

Included with Lifetime
$97 forever

Dagger pipeline development, CI/CD workflow, and GitOps deployment flow. When user works with Dagger, mentions CI/CD pipelines, dagger commands, .dagger/ directory, deployment flow, how changes get deployed, or GitOps.

Cloud & DevOps

What this skill does


# Dagger Helper Agent

## Overview

This agent helps develop Dagger CI/CD pipelines using the **TypeScript SDK** with **Bun** runtime. This monorepo uses Dagger for portable, programmable pipelines that run locally and in CI.

**Key Files:**

- `dagger.json` - Module configuration (engine version, SDK source)
- `.dagger/src/index.ts` - Main pipeline functions

## CLI Commands

```bash
# List available functions
dagger functions

# Run pipeline functions (--source maps to a Directory param)
dagger call ci --source=.
dagger call birmel-ci --source=.

# Dagger Shell (v0.20) — interactive bash-syntax frontend
dagger                          # opens Dagger Shell by default

# Interactive development
dagger develop

# Check version
dagger version

# Debug on failure (opens terminal)
dagger call build --source=. -i

# Verbose output levels
dagger call ci --source=. -v    # basic
dagger call ci --source=. -vv   # detailed
dagger call ci --source=. -vvv  # maximum

# Update dependencies
dagger update

# Uninstall a dependency
dagger uninstall <module>

# Open trace in browser
dagger call ci --source=. -w
```

**Supported Runtimes (0.19+):** docker, podman, nerdctl, finch, Apple containers — no Docker required.

## Three-Level Caching

Dagger provides three caching mechanisms:

| Type                      | What It Caches                       | Benefit                      |
| ------------------------- | ------------------------------------ | ---------------------------- |
| **Layer Caching**         | Build instructions, API call results | Reuses unchanged build steps |
| **Volume Caching**        | Filesystem data (node_modules, etc.) | Persists across sessions     |
| **Function Call Caching** | Returned values from functions       | Skips entire re-execution    |

## Monorepo: Avoid --source .

For per-package builds, never use `--source .` (syncs entire monorepo). Instead, use `ignore` annotations on `Directory` parameters to filter before transfer:

```typescript
@func()
async lint(
  @argument({ defaultPath: "/", ignore: ["*", "!packages/foo/**", "!tsconfig.json", "!bun.lock"] })
  source: Directory,
): Promise<string> { ... }
```

For repo-wide operations (prettier, shellcheck), `--source .` may still be appropriate. See `references/monorepo-performance.md` for multi-module architecture, cache debugging, remote cache, and Dagger Shell/Checks.

## TypeScript Module Structure

Dagger modules use class-based structure with decorators:

```typescript
import {
  dag,
  Container,
  Directory,
  Secret,
  Service,
  object,
  func,
} from "@dagger.io/dagger";

@object()
class Monorepo {
  @func()
  async ci(source: Directory): Promise<string> {
    // Pipeline logic
  }

  @func()
  build(source: Directory): Container {
    return dag
      .container()
      .from("oven/bun:1.3.4-debian")
      .withDirectory("/app", source)
      .withExec(["bun", "run", "build"]);
  }
}

// Enum declaration (registered when used by module)
export enum Status {
  Active = "Active",
  Inactive = "Inactive",
}

// Type object declaration
export type Message = { content: string };
```

**Key Decorators:**

- `@object()` - Marks class as Dagger module
- `@func()` - Exposes method as callable function

**Typed Parameters:** `Directory`, `Container`, `Secret`, `Service`, `File`

## Key Patterns

### Layer Ordering for Caching

Order operations from least to most frequently changing:

```typescript
function getBaseContainer(): Container {
  return (
    dag
      .container()
      .from(`oven/bun:${BUN_VERSION}-debian`)
      // 1. System packages (rarely change)
      .withMountedCache(
        "/var/cache/apt",
        dag.cacheVolume(`apt-cache-${BUN_VERSION}`),
      )
      .withExec(["apt-get", "update"])
      .withExec(["apt-get", "install", "-y", "python3"])
      // 2. Tool caches (version-keyed)
      .withMountedCache(
        "/root/.bun/install/cache",
        dag.cacheVolume("bun-cache"),
      )
      .withMountedCache(
        "/root/.cache/ms-playwright",
        dag.cacheVolume(`playwright-${VERSION}`),
      )
      // 3. Build caches
      .withMountedCache(
        "/workspace/.eslintcache",
        dag.cacheVolume("eslint-cache"),
      )
      .withMountedCache(
        "/workspace/.tsbuildinfo",
        dag.cacheVolume("tsbuildinfo-cache"),
      )
  );
}
```

### 4-Phase Dependency Installation

Optimal pattern for Bun workspaces with layer caching:

```typescript
function installDeps(base: Container, source: Directory): Container {
  return (
    base
      // Phase 1: Mount only dependency files (cached if lockfile unchanged)
      .withMountedFile("/workspace/package.json", source.file("package.json"))
      .withMountedFile("/workspace/bun.lock", source.file("bun.lock"))
      .withMountedFile(
        "/workspace/packages/foo/package.json",
        source.file("packages/foo/package.json"),
      )
      .withWorkdir("/workspace")
      // Phase 2: Install dependencies (cached if deps unchanged)
      .withExec(["bun", "install", "--frozen-lockfile"])
      // Phase 3: Mount source code (changes frequently - added AFTER install)
      .withMountedDirectory(
        "/workspace/packages/foo/src",
        source.directory("packages/foo/src"),
      )
      .withMountedFile("/workspace/tsconfig.json", source.file("tsconfig.json"))
      // Phase 4: Re-run install to recreate workspace symlinks
      .withExec(["bun", "install", "--frozen-lockfile"])
  );
}
```

### Parallel Execution

Run independent operations concurrently:

```typescript
await Promise.all([
  container.withExec(["bun", "run", "typecheck"]).sync(),
  container.withExec(["bun", "run", "lint"]).sync(),
  container.withExec(["bun", "run", "test"]).sync(),
]);
```

### Mount vs Copy

| Operation                | Use Case          | In Final Image? | Content-Based Cache?                                          |
| ------------------------ | ----------------- | --------------- | ------------------------------------------------------------- |
| `withMountedDirectory()` | CI operations     | No              | No (BuildKit skips checksums unless read-only non-root mount) |
| `withDirectory()`        | Publishing images | Yes             | Yes (full content hash)                                       |

```typescript
// CI - mount for speed
const ciContainer = base.withMountedDirectory("/app", source);

// Publish - copy for inclusion
const publishContainer = base.withDirectory("/app", source);
await publishContainer.publish("ghcr.io/org/app:latest");
```

### Secrets Management

**5 Secret Sources:**

```bash
# Environment variable
dagger call deploy --token=env:API_TOKEN

# File
dagger call deploy --token=file:./secret.txt

# Command output
dagger call deploy --token=cmd:"gh auth token"

# 1Password
dagger call deploy --token=op://vault/item/field

# HashiCorp Vault
dagger call deploy --token=vault://path/to/secret
```

**Usage in Code:**

```typescript
@func()
async deploy(
  source: Directory,
  token: Secret,
): Promise<string> {
  return await dag.container()
    .from("alpine:latest")
    .withSecretVariable("API_TOKEN", token)
    .withExec(["sh", "-c", "deploy.sh"])
    .stdout();
}
```

**Security:** Secrets never leak to logs, filesystem, or cache.

### Multi-Stage Builds

```typescript
@func()
build(source: Directory): Container {
  const builder = dag.container()
    .from("golang:1.21")
    .withDirectory("/src", source)
    .withExec(["go", "build", "-o", "app"]);

  return dag.container()
    .from("alpine:latest")
    .withFile("/usr/local/bin/app", builder.file("/src/app"));
}
```

### Multi-Architecture Builds

```typescript
const platforms: Platform[] = ["linux/amd64", "linux/arm64"];
const variants = platforms.map((p) =>
  dag
    .container({ platform: p })
    .from("node:20")
    .withDirectory("/app", source)
    .withExec(["npm", "run", "build"]),
);
```

## CI Log Analysis

When reading Dagger CI logs (e.g. from `gh run view --log-failed`), these are **not true e

Related in Cloud & DevOps