The run builder
s.run() as a with block gives you a run builder. Each call inside the block adds a command, and on exit they join into one &&-chained RUN.
Compose a multi-command RUN
This recipe clones a repo, builds it in a subdirectory, and records the result:
from __future__ import annotations
from docker_dsl import Stage
from docker_dsl import context as ctx
ctx.register("ref", str)
with Stage("ubuntu:24.04") as s:
s.workdir("/src")
with s.run() as r:
r.git("clone", "https://github.com/example/widget.git", ".")
r.git("checkout", ctx.ref or "main")
with r.cd("build"):
r.cmake("..", build_type="Release")
r.make("-j$(nproc)")
r.make("install")
r.echo("widget built") >> "/var/log/build.txt" # pyright: ignore[reportUnusedExpression]
r.echo("build complete") > "/src/STATUS" # pyright: ignore[reportUnusedExpression]
r("rm -rf /src/build")Render it with --ref=v2.0.0:
# syntax=docker/dockerfile:1
FROM ubuntu:24.04 AS base
WORKDIR /src
RUN git clone https://github.com/example/widget.git . \
&& git checkout v2.0.0 \
&& cd build \
&& cmake .. --build-type Release \
&& make -j$(nproc) \
&& make install \
&& cd - \
&& echo "widget built" >> /var/log/build.txt \
&& echo "build complete" > /src/STATUS \
&& rm -rf /src/buildDynamic commands
Any attribute you access becomes a shell binary, so r.git(...) emits git and r.make(...) emits make. Keyword arguments become flags. build_type="Release" renders --build-type Release, a True value renders a bare --flag, and underscores become hyphens. The shipped builder.pyi stub gives editors completions for common binaries.
For a command the dispatch can’t express, call the builder with a raw string:
r("rm -rf /src/build")Change directory, then return
r.cd(path) as a with block runs its body in path and restores the previous directory with cd - on exit:
with r.cd("build"):
r.make("install")Called as a bare statement, r.cd(path) changes directory for the rest of the RUN without restoring it.
Redirect echo to a file
r.echo(text) builds an echo you redirect with >> to append or > to truncate, with the path on the right:
r.echo("widget built") >> "/var/log/build.txt" # append
r.echo("build complete") > "/src/STATUS" # truncateFetch helpers
r.curl_bash(url, args=(...))pipes a remote install script intobashover a pinned-TLS curl.r.install(url, target=..., strip=...)downloads a tarball and extracts it.r.fetch_file(url, dest)downloads one file, creating its parent directory.