dagger-modules
container images, and for verifying Kubernetes deployment repos.Installation
dagger install git.xarif.de/base/dagger-modules@c885e9cc8a03e3f00b0bc9a4f17b90caa4fe5489Entrypoint
Return Type
DaggerModules Example
dagger -m git.xarif.de/base/dagger-modules@c885e9cc8a03e3f00b0bc9a4f17b90caa4fe5489 call \
func (m *MyModule) Example() *dagger.DaggerModules {
return dag.
Daggermodules()
}@function
def example() -> dagger.DaggerModules:
return (
dag.dagger_modules()
)@func()
example(): DaggerModules {
return dag
.daggerModules()
}Types
DaggerModules 🔗
DaggerModules is the main entry point for the Dagger module.
buildImage() 🔗
BuildImage builds a container from a Dockerfile in the given source directory and, optionally, runs a YAML-defined test suite against the freshly-built container before returning it.
When --tests is omitted, the build is pure (no execution) and returns the
container lazily. When --tests is supplied, tests are executed eagerly and
any failure short-circuits the call with an error.
PublishImage delegates to this function; there is no separate TestImage.
Return Type
Container !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | Source directory containing the Dockerfile |
| dockerfile | String | "Dockerfile" | Path to the Dockerfile relative to source root |
| buildArg | [String ! ] | - | Repeatable build arguments in KEY=VALUE form (e.g. |
| buildSecret | [Secret ! ] | - | Repeatable build secrets, each provided as a Dagger secret. Inside the
Dockerfile, mount as |
| platform | String | - | Target platform (e.g. |
| target | String | - | Target build stage in a multi-stage Dockerfile. |
| tests | File | - | YAML test specification file. When provided, tests are executed against the built container and any failure aborts the call. |
| root | Directory | - | Root directory of the repository. Used to resolve mount paths in test specs independently of the build-context (–source). Defaults to the caller’s working directory. |
Example
dagger -m git.xarif.de/base/dagger-modules@c885e9cc8a03e3f00b0bc9a4f17b90caa4fe5489 call \
build-image --source DIR_PATHfunc (m *MyModule) Example(source *dagger.Directory) *dagger.Container {
return dag.
Daggermodules().
Buildimage(source)
}@function
def example(source: dagger.Directory) -> dagger.Container:
return (
dag.dagger_modules()
.buildimage(source)
)@func()
example(source: Directory): Container {
return dag
.daggerModules()
.buildImage(source)
}ci() 🔗
Ci runs the pipeline declared in the repository’s dagger-ci.yaml (docs/ci.md).
Sections run in parallel: rust (VerifyRust), kubernetes (VerifyKustomize),
python (VerifyPython), ansible (VerifyAnsible), one check <name> per
script, and image (build, tests, and on a publishing ref the push to the
Forgejo registry plus the gitlab.com mirror). A branch push is skipped when
every target already serves an image built from identical inputs
(ciInputLabel). Returns one summary line per section.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | Repository root: locally |
| ref | String | - | Full git ref of the run (refs/heads/main, refs/tags/v1.2.3). Empty = local run: build and test only, never publish. |
| sha | String | - | Commit SHA of the run (names the mirror’s |
| server | String | - | Forgejo server URL; its host is the primary registry. |
| repository | String | - | Repository path owner/repo; the image path on both registries. |
| actor | String | - | User the registry token belongs to. |
| registryPassword | Secret | - | Token with package write access on the primary registry. |
| mirrorUsername | String | - | gitlab.com deploy-token user for the mirror push. |
| mirrorPassword | Secret | - | gitlab.com deploy token (write_registry) for the mirror push. |
| dryRun | Boolean | - | Decide and report what would be published, without pushing. |
Example
dagger -m git.xarif.de/base/dagger-modules@c885e9cc8a03e3f00b0bc9a4f17b90caa4fe5489 call \
ci --source DIR_PATHfunc (m *MyModule) Example(ctx context.Context, source *dagger.Directory) string {
return dag.
Daggermodules().
Ci(ctx, source)
}@function
async def example(source: dagger.Directory) -> str:
return await (
dag.dagger_modules()
.ci(source)
)@func()
async example(source: Directory): Promise<string> {
return dag
.daggerModules()
.ci(source)
}publishImage() 🔗
PublishImage builds and pushes a container image to an OCI registry.
Always pushes two tags: latest and the sanitized --ref-name.
When --ref-name is a SemVer-style tag (e.g. v1.2.3), additional floating
tags v1.2 and v1 are also pushed unless --no-semver-tags is set. This
is the conventional pattern for shared-action and GitHub Action repositories.
Additional ad-hoc tags can be supplied via repeatable --extra-tag flags.
If --tests is provided, tests must pass before any push happens. The
build+test phase is delegated to BuildImage so the two functions stay in
lockstep.
Mirror registry: --mirror-registry (with --mirror-username,
--mirror-password, --mirror-repository) pushes the SAME built container
to a second registry after the primary one, with the same tags plus any
--mirror-extra-tag. The image is built once; every mirror push must yield
the primary digest or the call fails, so both registries provably serve one
artifact. xarif uses this to publish every image to git.xarif.de AND
registry.gitlab.com from one Forgejo Actions run.
Returns the digest of the ref-tagged push.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | Source directory containing the Dockerfile |
| registry | String ! | - | Registry address (hostname or URL — protocol prefix is stripped automatically) |
| username | String ! | - | Registry username |
| password | Secret ! | - | Registry password or token |
| repository | String ! | - | Repository path, e.g. “myuser/myrepo” or “mygroup/myrepo” |
| refName | String ! | - | Git ref name used as image tag alongside “latest” |
| dockerfile | String | "Dockerfile" | Path to the Dockerfile relative to source root |
| tests | File | - | YAML test specification file — if provided, tests must pass before pushing |
| buildArg | [String ! ] | - | Repeatable build arguments in KEY=VALUE form |
| buildSecret | [Secret ! ] | - | Repeatable build secrets |
| platform | String | - | Target platform (e.g. |
| target | String | - | Target build stage in a multi-stage Dockerfile |
| extraTag | [String ! ] | - | Repeatable extra tags to push in addition to |
| noSemverTags | Boolean | - | Disable automatic SemVer floating-tag derivation (vMAJOR, vMAJOR.MINOR) |
| root | Directory | - | Root directory of the repository. Used to resolve mount paths in test specs independently of the build-context (–source). Defaults to the caller’s working directory. |
| mirrorRegistry | String | - | Second registry that receives the same image (hostname or URL). Empty = no mirror push. Requires mirror-username, mirror-password and mirror-repository. |
| mirrorUsername | String | - | Mirror registry username |
| mirrorPassword | Secret | - | Mirror registry password or token |
| mirrorRepository | String | - | Repository path on the mirror registry, e.g. “mygroup/myrepo” |
| mirrorExtraTag | [String ! ] | - | Repeatable tags pushed ONLY to the mirror, in addition to the primary
tag set (e.g. an immutable |
Example
dagger -m git.xarif.de/base/dagger-modules@c885e9cc8a03e3f00b0bc9a4f17b90caa4fe5489 call \
publish-image --source DIR_PATH --registry string --username string --password env:MYSECRET --repository string --ref-name stringfunc (m *MyModule) Example(ctx context.Context, source *dagger.Directory, registry string, username string, password *dagger.Secret, repository string, refName string) string {
return dag.
Daggermodules().
Publishimage(ctx, source, registry, username, password, repository, refName)
}@function
async def example(source: dagger.Directory, registry: str, username: str, password: dagger.Secret, repository: str, refname: str) -> str:
return await (
dag.dagger_modules()
.publishimage(source, registry, username, password, repository, refname)
)@func()
async example(source: Directory, registry: string, username: string, password: Secret, repository: string, refName: string): Promise<string> {
return dag
.daggerModules()
.publishImage(source, registry, username, password, repository, refName)
}verifyAnsible() 🔗
VerifyAnsible checks an Ansible repository without touching a host:
- lint:
yamllint --strict .andansible-lint --profile production(after installing –galaxy-requirements, if given); - syntax: in the repo’s own toolchain image (the Dockerfile in
–toolchain, i.e. the image the playbook really runs in):
ansible-playbook --syntax-check <playbook>, that the configured stdout_callback plugin resolves, then every –extra-check command.
Both stages always run; every failure is reported.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | Repository root (ansible.cfg, the playbook, roles/). |
| playbook | String | "site.yml" | Playbook to syntax-check, relative to source. |
| toolchain | String | "docker-ansible" | Directory (relative to source) with the toolchain Dockerfile. |
| toolchainBuildArg | [String ! ] | - | Repeatable KEY=VALUE build args for the toolchain image. |
| galaxyRequirements | String | - | Galaxy collection requirements file (relative to source), installed before ansible-lint. |
| extraCheck | [String ! ] | - | Repeatable shell commands run in the toolchain image after the syntax check. |
| lint | Boolean | true | Run the lint stage. |
| syntaxCheck | Boolean | true | Run the syntax stage. |
Example
dagger -m git.xarif.de/base/dagger-modules@c885e9cc8a03e3f00b0bc9a4f17b90caa4fe5489 call \
verify-ansible --source DIR_PATHfunc (m *MyModule) Example(ctx context.Context, source *dagger.Directory) string {
return dag.
Daggermodules().
Verifyansible(ctx, source)
}@function
async def example(source: dagger.Directory) -> str:
return await (
dag.dagger_modules()
.verifyansible(source)
)@func()
async example(source: Directory): Promise<string> {
return dag
.daggerModules()
.verifyAnsible(source)
}verifyImages() 🔗
VerifyImages checks that container images exist in their registry and provide a manifest for the target platform(s), without a full image pull. It is the standalone counterpart to the images: block in VerifyKustomize: use it to verify explicit refs, or refs harvested from source files (Renovate annotations / extra_sources), independently of a kustomize build.
At least one ref must be resolvable (via –image, –tests extra_sources, or harvested annotations in –source), otherwise the call errors.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| image | [String ! ] | - | Explicit image references to verify (repeatable). |
| platform | [String ! ] | - | Target platform(s) each image must provide a manifest for (os/arch[/variant]). Defaults to linux/amd64 when unset. |
| strict | Boolean | - | Strict mode: additionally assert the resolved image’s architecture/os match the platform (catches a single-arch image pinned for the wrong platform). |
| ignore | [String ! ] | - | Refs to skip: exact ref or glob; prefix “re:” for a regex (repeatable). |
| registryConfig | Secret | - | Docker config.json (as a Dagger secret) for private registry auth. Optional — public images are checked anonymously. |
| source | Directory | - | Source directory scanned for extra_sources / Renovate annotations. |
| tests | File | - | Test spec file; reuses its images: block (platforms, ignore, extra_sources, strict, harvest). CLI flags override where set. |
Example
dagger -m git.xarif.de/base/dagger-modules@c885e9cc8a03e3f00b0bc9a4f17b90caa4fe5489 call \
verify-imagesfunc (m *MyModule) Example(ctx context.Context) string {
return dag.
Daggermodules().
Verifyimages(ctx)
}@function
async def example() -> str:
return await (
dag.dagger_modules()
.verifyimages()
)@func()
async example(): Promise<string> {
return dag
.daggerModules()
.verifyImages()
}verifyKustomize() 🔗
VerifyKustomize validates a Kubernetes deployment repo by checking that
vendored Helm chart caches match the versions declared in kustomization.yaml
and that kustomize build produces valid output.
When helmCharts entries are found, the function:
- verifies charts/-/ exists for each entry
- detects stale chart directories not matching any current entry
- runs kustomize build --enable-helm .
When no helmCharts entries exist, it runs plain kustomize build ..
The build runs inside the provided toolImage container, which should match the production ArgoCD CMP image for version parity.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | Kubernetes deployment repo root containing kustomization.yaml |
| toolImage | String | "registry.gitlab.com/xarif/docker/argocd-sops-cmp:latest" | Container image with kustomize + helm CLI tools. Must match the production ArgoCD CMP image for version parity. |
| tests | File | - | YAML test specification file for manifest validation. When provided, runs linters and custom assertions against the rendered kustomize build output. See docs/kubernetes-testing.md. |
| verifyImages | Boolean | true | Verify that referenced container images exist in their registry and provide a manifest for the target platform(s). Default-on (opt-out): set to false to skip entirely. Configuration (platforms, ignore, extra_sources, …) lives in the tests spec under the images: block. |
| registryConfig | Secret | - | Docker config.json (as a Dagger secret) providing registry credentials
for the image-existence check against private registries. Optional —
public images are checked anonymously. Example:
|
Example
dagger -m git.xarif.de/base/dagger-modules@c885e9cc8a03e3f00b0bc9a4f17b90caa4fe5489 call \
verify-kustomize --source DIR_PATHfunc (m *MyModule) Example(ctx context.Context, source *dagger.Directory) string {
return dag.
Daggermodules().
Verifykustomize(ctx, source)
}@function
async def example(source: dagger.Directory) -> str:
return await (
dag.dagger_modules()
.verifykustomize(source)
)@func()
async example(source: Directory): Promise<string> {
return dag
.daggerModules()
.verifyKustomize(source)
}verifyPython() 🔗
VerifyPython runs a uv-managed Python project through its quality gates across an interpreter matrix, and — unless disabled — builds the distribution and verifies the built wheel actually installs and imports.
The pipeline is defined entirely by the YAML spec passed as --tests, so a
consumer repo needs no CI-system-specific job matrix. See
docs/python-testing.md for the spec format.
Stage placement, and why it differs per stage:
checksrun ONCE, on the primary version (the last entry of python.versions). Linters, formatters, type checkers and dependency audits answer a question about the source tree, not about an interpreter; running them per version would multiply cost and produce N copies of the same finding.matrixstages run on EVERY version. This is where the test suite goes: it is the only part whose result can legitimately differ per interpreter.build+smokerun on the primary version.uv syncinstalls from the source tree, so a broken package manifest passes the whole matrix and still ships a broken artifact — the smoke step installs the wheel into a bare interpreter, with no source tree and no dev dependencies, and is the only stage that can catch that.
Every stage runs even after an earlier one fails, and the summary lists all failures. Aborting on the first would mean fixing one lint finding, waiting for a full re-run, and discovering the type error — turning one review cycle into several. This matches how the Docker and Kubernetes test runners behave.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | Project root: the directory containing pyproject.toml and uv.lock. |
| tests | File ! | - | YAML spec defining the interpreter matrix and the stages to run. |
| pythonVersion | [String ! ] | - | Override python.versions from the spec. Repeatable, oldest first — the
last entry becomes the primary version. Intended for narrowing the matrix
in a local run ( |
| skipBuild | Boolean | - | Skip the build + smoke stages entirely. |
Example
dagger -m git.xarif.de/base/dagger-modules@c885e9cc8a03e3f00b0bc9a4f17b90caa4fe5489 call \
verify-python --source DIR_PATH --tests file:pathfunc (m *MyModule) Example(ctx context.Context, source *dagger.Directory, tests *dagger.File) string {
return dag.
Daggermodules().
Verifypython(ctx, source, tests)
}@function
async def example(source: dagger.Directory, tests: dagger.File) -> str:
return await (
dag.dagger_modules()
.verifypython(source, tests)
)@func()
async example(source: Directory, tests: File): Promise<string> {
return dag
.daggerModules()
.verifyPython(source, tests)
}verifyRust() 🔗
VerifyRust runs cargo-deny over a Cargo workspace: RustSec advisories, the banned-version list and the source allowlist from the project’s deny.toml.
It runs fresh on every call — the advisory database is fetched anew and the result is never served from the engine cache — because the question it answers (“is anything in Cargo.lock known-bad TODAY?”) changes without any change to the repository.
The project’s rust-toolchain.toml is overridden (RUSTUP_TOOLCHAIN = the image’s default) so no toolchain or WASM target is downloaded just to read the manifests.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | Project root: the directory containing Cargo.toml, Cargo.lock and deny.toml. |
| check | [String ! ] | - | cargo-deny checks to run. Repeatable; default advisories, bans, sources. |
Example
dagger -m git.xarif.de/base/dagger-modules@c885e9cc8a03e3f00b0bc9a4f17b90caa4fe5489 call \
verify-rust --source DIR_PATHfunc (m *MyModule) Example(ctx context.Context, source *dagger.Directory) string {
return dag.
Daggermodules().
Verifyrust(ctx, source)
}@function
async def example(source: dagger.Directory) -> str:
return await (
dag.dagger_modules()
.verifyrust(source)
)@func()
async example(source: Directory): Promise<string> {
return dag
.daggerModules()
.verifyRust(source)
}