- Python 56.2%
- Dockerfile 43.8%
## 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 |
||
|---|---|---|
| .forgejo/workflows | ||
| build | ||
| tests | ||
| .gitignore | ||
| .gitlab-ci.yml | ||
| mise.toml | ||
| README.md | ||
| renovate.json | ||
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/andtests/ - 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 getsCOPY'd into the image. This is what--source=buildpoints 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 pipelinemise.toml— local build/test tasksrenovate.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 | 15–20 s | docker run registry.dagger.io/engine:<version> into empty store |
| Build layer cache | cold | engine state lost after job |
~40–50% 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
commonpreset: general Renovate best practices