blumeops/docs/reference/tools/dagger.md
Erich Blume c86b5d7772
All checks were successful
Build Container / detect (push) Successful in 3s
Build Container / build-dagger (navidrome) (push) Successful in 22m26s
Native Dagger container builds + Navidrome v0.61.1 (#330)
## Summary
- Move Dagger module from `.dagger/` to repo root (`src/blumeops/`), rename `blumeops-ci` → `blumeops`
- Replace opaque `docker_build()` with native Dagger pipelines that surface full build errors per step
- Migrate navidrome as the first container (`containers/navidrome/container.py`)
- Upgrade navidrome from v0.60.3 to v0.61.1 (major artwork overhaul, SQLite FTS5 search, server-managed transcoding)
- Add `dagger call container-version` for CI version extraction without Dockerfile parsing
- All mise tasks (`container-list`, `container-version-check`, `container-build-and-release`) updated for hybrid mode
- Legacy `docker_build()` fallback preserved for all other containers

## Motivation
When navidrome v0.61.0 added a new Go build tag (`sqlite_fts5`), `docker_build()` showed only "exit code: 1". We had to run `docker build --progress=plain` manually to find `undefined: buildtags.SQLITE_FTS5`. Native Dagger pipelines show the full error inline.

## Container build dispatch needed
After merge, dispatch container build for navidrome:
```
mise run container-build-and-release navidrome --ref 470b4bd
```

## Deploy steps
1. Wait for container build to complete
2. Back up navidrome-data PVC (non-reversible DB migrations)
3. `argocd app set navidrome --revision main && argocd app sync navidrome`
4. Verify at https://dj.ops.eblu.me

## Future
Remaining containers migrate incrementally in follow-up PRs using the same pattern.

Reviewed-on: #330
2026-04-11 17:11:56 -07:00

4 KiB

title modified tags
Dagger 2026-04-11
reference
ci-cd
dagger

Dagger

Build engine for BlumeOps CI/CD pipelines. Replaces shell-based build scripts with Python functions that run identically locally and in CI.

Quick Reference

Property Value
Module blumeops
Engine Version v0.20.1
SDK Python
Source src/blumeops/main.py
Config dagger.json (source: .)

Functions

Function Signature Description
build (src, container_name) → Container Build a container — uses native pipeline (container.py) if available, falls back to docker_build() for Dockerfile containers
container_version (container_name) → str Return the VERSION from a container's container.py (empty string if no container.py)
publish (src, container_name, version, registry?) → str Build and push to registry (default: registry.ops.eblu.me)
build_nix (src, container_name) → File Build a nix container from containers/<name>/default.nix, return docker-archive tarball
nix_version (package) → str Extract the version of a nixpkgs package
build_docs (src, version) → File Build Quartz docs site, return docs tarball
flake_lock (src, flake_path?) → File Resolve flake inputs, return updated flake.lock
flake_update (src, flake_path?) → File Update all flake inputs to latest, return flake.lock

Container Build Types

Containers can be built in three ways:

Build file How it works Error visibility
container.py Native Dagger pipeline (preferred) Full per-step output
Dockerfile docker_build() fallback (legacy) Opaque — errors swallowed
default.nix nix-build on ringtail runner Full nix output

New containers for indri (k8s runner) should use container.py. Ringtail containers should continue using default.nix. Existing Dockerfile containers are migrated incrementally during review-services. See containers/navidrome/container.py for the reference pattern.

CLI Examples

# Build a container
dagger call build --src=. --container-name=devpi

# Drop into container shell for inspection
dagger call build --src=. --container-name=devpi terminal

# Debug a failure interactively
dagger call --interactive build --src=. --container-name=devpi

# Publish a container to zot
dagger call publish --src=. --container-name=devpi --version=v1.1.0

# Build a nix container (no local nix required)
dagger call build-nix --src=. --container-name=ntfy export --path=./ntfy.tar.gz

# Check a nixpkgs package version
dagger call nix-version --package=authentik

# Build docs tarball locally
dagger call build-docs --src=. --version=dev export --path=./docs-dev.tar.gz

# Debug a docs build failure
dagger call --interactive build-docs --src=. --version=dev

# Update all ringtail flake inputs
dagger call flake-update --src=. --flake-path=nixos/ringtail \
    export --path=nixos/ringtail/flake.lock

Secrets

Dagger has a first-class Secret type — values are never logged or cached. Pass secrets from environment variables using the env:VAR syntax:

dagger call release-docs \
  --src=. --version=v1.6.0 \
  --forgejo-token=env:FORGEJO_TOKEN \
  --argocd-token=env:ARGOCD_TOKEN

In forgejo Actions, secrets are injected as env vars. Locally, mise tasks call op read to populate them.

Caveats

  • Pre-1.0 API — Current version is v0.20.x. Pin the CLI version and test upgrades on a branch before adopting. See upgrade-dagger for the upgrade procedure.
  • Privileged container — The Dagger engine requires privileged container access. The Forgejo runner's DinD sidecar provides this.
  • forgejo — CI/CD trigger layer
  • zot — Container registry (publish target)
  • docs — Documentation site (build target)
  • manage-lockfile — Ringtail flake lockfile management