magus v0.4.3 is out. See what's new
¶ View generated markdown
4 min read

docker

The docker spell forks the docker CLI (and hadolint) to build images and lint Dockerfiles. docker-build-check runs the builder's --check preflight without producing an image.

Runtime name: docker (source spells/docker/)

Version probe (docker): docker --version

Version probe (hadolint): hadolint --version

Version probe (trivy): trivy version

Passing arguments to ops

Every op is invoked as docker["<op>"](ctx, opts?). The first argument is the target's context, which is what carries the execution environment; the optional options map shapes the command itself:

Key Type Description Source
args [str] Extra arguments appended to the resolved command, replacing any trailing defaults the op declares (go-test's ./...), so passing args also states the scope. Omit it and a bare docker["<op>"]() keeps the defaults and forwards magus run <target> -- <extra> to the tool automatically; pass it to set the arguments explicitly, which replaces that passthrough. To keep the passthrough too, append the target's own args parameter: {"args": ["-race", "./..."] + args}. source
stdin str Data written to the command's standard input. source

Working directory and environment are NOT options: they ride the context, as docker["<op>"](ctx.withCwd("sub")) and docker["<op>"](ctx.withEnv({"CGO_ENABLED": "0"})). Only the context reaches the cache key, so an option-table cwd or env would change what the tool did while the key said otherwise; passing either as an option is an error.

Charms (the :charm suffix, e.g. magus run test:rw) are orthogonal: they patch the base argv, while these options add to it. See Charms.

docker-build

Command: docker build

Example

// docker-build's base command is just `docker build`, so pass the image tag and
// build context: `magus run image` forks `docker build -t app:latest .`.
import "magus";
import "magus/spell/docker";

magus\project({ "spells": [docker] });

export fun image(ctx: magus\Context, args: [str]) > void {
    docker["docker-build"](ctx, { "args": ["-t", "app:latest", "."] });
}

docker-build-check

Command: docker build --check

Example

// docker-build-check runs the builder `--check` preflight over a context without
// producing an image; pass the build context (`.`).
import "magus";
import "magus/spell/docker";

magus\project({ "spells": [docker] });

export fun image_check(ctx: magus\Context, args: [str]) > void {
    docker["docker-build-check"](ctx, { "args": ["."] });
}

docker-buildx

buildx is the op that PUSHES (--push), so it is the one that meets a registry and the one that fails on authentication. Docker's own message is a complete diagnosis to anyone who already knows docker and an exit code to everyone else, so the hints turn it into the command to run. Registries disagree about the wording - Docker Hub says "authentication required", GHCR and most OCI registries say "unauthorized" or "denied" - so each phrasing gets an entry rather than one guess. This is what a workspace should reach for before a separate <area>-login target: the failure teaches the fix, so nobody has to know a convention in advance. See docs/concepts/secrets.md.

Command: docker buildx build

Example

// docker-buildx builds with BuildKit; pass the tag and context. Add
// "--platform", "linux/amd64,linux/arm64" to the args for a multi-platform build.
import "magus";
import "magus/spell/docker";

magus\project({ "spells": [docker] });

export fun image_buildx(ctx: magus\Context, args: [str]) > void {
    docker["docker-buildx"](ctx, { "args": ["-t", "app:latest", "."] });
}

docker-run

--rm is baked in: an op that leaves containers behind turns a repeated target into a disk leak. The caller supplies mounts, workdir, image and command through args.

Command: docker run --rm

hadolint

Lints the Dockerfile, reporting in the GNU diagnostic format the tool declares in mgs_getTools, so magus reads each finding's file, line and rule rather than its prose.

Command: hadolint -f gnu Dockerfile

Example

// hadolint lints the Dockerfile for common mistakes.
import "magus";
import "magus/spell/docker";

magus\project({ "spells": [docker] });

export fun lint(ctx: magus\Context, args: [str]) > void {
    docker["hadolint"](ctx);
}

trivy-image

Scans an image OFFLINE by default: --skip-db-update is documented as "skip updating vulnerability database", so a plain run answers from the copy already on disk and downloads nothing (https://trivy.dev/latest/docs/references/configuration/cli/trivy_image/, read 2026-09-10). The caller appends the image reference and any output flags. The update charm drops the flag, which is the whole refresh: trivy pulls a stale database itself before scanning, so the arm needs no second command the way a separate --download-db-only pass would. Offline is the DEFAULT rather than the option because the charm is what says a run may move pinned upstream state forward, and a scan that silently refreshed its feed would be doing exactly that without being asked, and would change its verdict under an unchanged tree. What keeps that honest is the observe probe in mgs_getTools: the database on disk is in the cache key, so a scan replays only against the copy it was run against, and image-scan:update is what mints a new key. --offline-scan is NOT this flag and is deliberately not set: it stops trivy issuing API requests to identify dependencies (a Java-analysis concern), not database downloads.

Command: trivy image --skip-db-update

update

Drops --skip-db-update.

JSON Patch
[
  {
    "op": "remove",
    "path": "/1"
  }
]
generatedspells/docker/spell.buzzdockerspellcontainerimagehadolinttools
Last updated (63e73856)
Earlier changes on this page (7)

Full history ↗ · Blame source ↗

Glossary

Workspace

The magus root directory that owns a set of projects and shared config; the unit magus operates over. See workspace.

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. See workspace.

Target

A named operation (build, test, ...) you invoke with magus run <target>; it may compose a spell's tool-native operations and depend on other targets. See targets.

Op

A single tool-native command a target composes (long form: operation); the middle of the work hierarchy (Spell to Op to Target). See operations.

Spell

A language/runtime adapter (e.g. go, md) that maps generic targets onto a toolchain's real commands. See spells.

Charm

An execution modifier attached with : (lint:rw) that changes how a target runs, not which one; the built-in rw flips a check-only target to mutate in place, and ci always strips it. See charms.

Ward

A coded diagnostic that inspects a resolved op and nudges or blocks an anti-pattern before it runs. See wards.

Buzz

The language magusfiles are written in (the .buzz engine). See engines.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. See cache.

CI

An ordinary magusfile-defined target you compose yourself with magus\needs - magus does not hardcode its stages. Magus.RunCI treats it specially only in that it strips the rw charm, it is the anchor magus affected ci keys off, and a selected scope with no project declaring it is a load error rather than a silent no-op. See targets.

Run

One target executing under one magus invocation, such as magus run test web or magus affected ci. A run keeps its captured output behind an output reference. Every magus run is a run whether or not any job asked for it; see Job for how the two relate.

Conventions

Placeholders

Angle brackets mark a value you replace with your own - never type the brackets:

magus run <target>
magus completion <shell>    # e.g. bash, zsh, fish

<target>, <path>, <shell>, <name> and the like are stand-ins, not literal text.