No description
  • Python 56.2%
  • Dockerfile 43.8%
Find a file
ThomasTSteinbach 472104b724
All checks were successful
publish / dagger (push) Successful in 14s
publish / publish (push) Successful in 0s
fix(ci): split build/publish into separate workflow files
## Intent

Forgejo v15 workflow expansion expands reusable workflow calls into their
inner jobs at parse time, before `if` conditions are evaluated. This caused
both build-image and publish-image to run on every push regardless of branch.

## Key changes

Build and publish are now gated by trigger filters (branch patterns) instead
of `if` conditions, ensuring only one workflow fires per event.

## Details

CI:
- ci.yml: build-only workflow, triggers on all branches except main/master
- publish.yml: new file, triggers only on main/master and v* tags
2026-05-19 23:35:48 +02:00
.forgejo/workflows fix(ci): split build/publish into separate workflow files 2026-05-19 23:35:48 +02:00
build feat!: adopt build/ + tests/ layout with test spec 2026-05-19 16:05:53 +02:00
tests feat!: adopt build/ + tests/ layout with test spec 2026-05-19 16:05:53 +02:00
.gitignore chore(repo): relocate dagger git config into dagger/ subdirectory 2026-05-12 12:45:03 +02:00
.gitlab-ci.yml ci: add blueprint/workflow references to CI files 2026-05-19 16:57:05 +02:00
mise.toml fix(mise): add --root=. for Dagger remote module mount resolution 2026-05-19 23:10:05 +02:00
README.md feat!: adopt build/ + tests/ layout with test spec 2026-05-19 16:05:53 +02:00
renovate.json refactor: rename job to publish-image, remove dagger.json 2026-05-18 21:49:27 +02:00

dagger-ci-basics

Reference consumer repo demonstrating how to use the shared base/dagger-modules for CI/CD on git.xarif.de (Forgejo) and gitlab.com.

Purpose

  • Shows how a project consumes the shared Dagger module and CI templates without any local Dagger code
  • Demonstrates the recommended build/ + tests/ directory layout
  • Demonstrates the test framework with fixtures mounted from both build/ and tests/
  • Provides a template for onboarding new repos onto the shared CI infrastructure

Repository Layout

.                                        repo root
├── .forgejo/workflows/ci.yml  - - - -   Forgejo CI (reusable workflow from dagger-modules)
├── .gitlab-ci.yml                       GitLab CI (include template from dagger-modules)
├── build/                               Docker build context (--source)
│   ├── Dockerfile - - - - - - - - - -   container build definition
│   ├── app.py                           demo application
│   └── config.json  - - - - - - - - -   default app config (also used as mount demo)
├── tests/                               test artifacts (outside build context)
│   ├── tests.yaml - - - - - - - - - -   test specification
│   └── fixtures/                        test-only files
│       └── config-override.json         config fixture for mount demo
├── mise.toml  - - - - - - - - - - - -   local build/test tasks via mise
└── renovate.json  - - - - - - - - - -   Renovate config (common + dagger presets)

Convention: build/ vs tests/

  • build/ — Docker build context. Contains the Dockerfile and everything that gets COPY'd into the image. This is what --source=build points to.
  • tests/ — Test artifacts. Contains the test spec (tests.yaml) and fixtures that are only needed during testing, not in the final image. Mounted into containers via root-relative paths.

Files that serve dual purposes (e.g. SSH keys used by both the Dockerfile and a test) stay in build/ and are referenced from tests via build/... mount paths.

How It Works

This repo has no local Dagger module — it calls functions from the remote shared module at git.xarif.de/base/dagger-modules.git@main. The CI templates handle cloning that module and installing the matching Dagger CLI.

Test Specification

The test spec at tests/tests.yaml demonstrates:

Test Feature What it shows
image-metadata inspect Zero-cost image metadata assertions (workdir, cmd, labels, max_size)
app-runs steps/run + output Run the app and verify output
build-artifacts files Assert files exist with correct type and content
config-override mounts from tests/ Mount a test fixture from outside the build context
mount-from-build-context mounts from build/ Mount a file from inside the build context
missing-module-fails expect_failure Verify that a command fails as expected

Mount paths are relative to the repository root (resolved via the --root parameter, which defaults to .).

Forgejo Pipeline

Defined in .forgejo/workflows/ci.yml:

Uses the reusable workflow from base/dagger-modules — a single uses: call with inputs and secrets. Requires a secret REGISTRY_TOKEN (PAT with package:write scope) at org or repo level.

GitLab Pipeline

Defined in .gitlab-ci.yml:

Includes the remote template from base/dagger-modules and extends .dagger-call. Registry credentials are automatic ($CI_REGISTRY_*) — no secrets setup needed.

Local Execution

Via mise tasks defined in mise.toml:

# Build and test the demo image
mise run pipeline

# Or directly with Dagger (build + test, no push)
dagger call -m git.xarif.de/base/dagger-modules.git@main \
  build-image --source=build --tests=tests/tests.yaml

Adopting This Pipeline

Copy these files to your repo:

  • .forgejo/workflows/ci.yml — Forgejo CI pipeline
  • .gitlab-ci.yml — GitLab CI pipeline
  • mise.toml — local build/test tasks
  • renovate.json — dependency update config

Changes per file

renovate.json — no changes needed.

.forgejo/workflows/ci.yml:

jobs:
  publish-image:
    with:
      dagger-args: --source=build --tests=tests/tests.yaml

Adjust --source if your Dockerfile is in a different directory. Requires a REGISTRY_TOKEN secret.

.gitlab-ci.yml:

publish-image:
  variables:
    DAGGER_ARGS: '--source=build --tests=tests/tests.yaml'

No secrets setup needed.

mise.toml — adjust --source to your Dockerfile directory.

Available functions

Function Behavior
build-image Build and optionally test (no push)
publish-image Build, optionally test, then push to the platform's container registry

Both accept --tests=<path> to gate on test success. See base/dagger-modules README for the full test spec format and all available parameters.

Pipeline Optimization

No Dagger Cloud. All pipelines use only the CI platform's own runners.

Shared-runner costs (gitlab.com)

Ephemeral VM per job — these fixed costs are not reducible via CI config:

Phase ~Duration Cause
Runner + DinD startup 20 s fresh VM, DinD must become healthy
Engine image pull 1520 s docker run registry.dagger.io/engine:<version> into empty store
Build layer cache cold engine state lost after job

~4050% of total job time. Mitigation requires persistent engine (self-hosted or Dagger Cloud).

Self-hosted runners (git.xarif.de)

Persistent Docker daemon: engine container survives between jobs. Eliminates engine pull, preserves layer cache. Same pipeline: ~20 s vs ~86 s on GitLab shared runners.

Renovate

  • common preset: general Renovate best practices