Build an image from a Dockerfile. Each directive runs against the
contree API and produces a new image layer; successful layers are
materialised as branches named layer:<chain-hash> so re-running the
same Dockerfile reuses prior work.
Reads the Dockerfile at the given path (default “<CONTEXT>/Dockerfile“) and applies each directive against an isolated build session keyed by the absolute path of the context directory. Successful layers are materialised as branches named “layer:<chain-hash>“ so that re-running the same Dockerfile reuses prior work.
Supported directives (MVP): FROM (multistage via “FROM … AS name“), RUN, COPY (including “—from=<alias|index|image>“), ADD (local files/dirs and http(s) URLs; no tar auto-extraction, no “—from“), WORKDIR, ENV, ARG, USER. Other Dockerfile directives parse cleanly but are skipped with a warning (CMD, ENTRYPOINT, LABEL, EXPOSE, VOLUME, STOPSIGNAL, MAINTAINER, HEALTHCHECK, ONBUILD, SHELL).
Multistage notes: “COPY —from“ exports the source path from the referenced stage image as a tar archive, uploads it once (content deduplicated) and unpacks it with an extraction RUN inside the target sandbox. For now this means the target image must provide “/bin/sh“, “tar“, “cp“ and “mv“ (busybox suffices; “FROM scratch“ targets cannot receive “COPY —from“). This is a temporary limitation of the client-side extraction and will be lifted in a future release once the backend unpacks archives itself. Unlike docker, ARG/ENV live in one global namespace across stages.
options: -h, —help show this help message and exit —dockerfilePATH Dockerfile path (default: <context>/Dockerfile) (default: ) —tagNAME[:TAG] Tag the final image (default: ) —build-argKEY=VALUE Build-time variable (repeatable) —no-cache Ignore cached layers and rebuild -t, —timeoutTIMEOUT Timeout in seconds for each RUN step (default: 600)
for coding agents: mutating command, may create operations against the API layer cache is per-context (session keyed by abspath(context)) use —no-cache to bypass cached layers and rebuild from scratch multistage supported: FROM … AS name + COPY —from=<name|index|image> COPY —from unpacks a tar inside the target: needs sh/tar/cp/mv there (temporary limitation, to be lifted in a future release)
agent note: Before using this command in an automated workflow, read: contree agent
Resolves the base image. If the tag is not found locally, the build auto-imports it via POST /v1/images/import. AS name is parsed but ignored (multi-stage is Phase 2).
RUN ...
Shell-form (RUN echo hi) or JSON exec-form (RUN ["echo","hi"]). Spawns POST /v1/instances, polls until terminal status, captures the resulting image.
COPY [--chown=...] [--chmod=...] SRC... DEST
Walks local sources relative to the build context, applies .dockerignore, uploads files (with SHA256 dedup), and stages them for the next RUN.
ADD ...
Local paths behave like COPY. https:///http:// URLs are streamed straight from the source socket into POST /v1/files (no temp file on disk); the URL plus its ETag/Last-Modified/Content-MD5 validators are cached so repeat builds skip the download via a conditional HEAD. Tar auto-extraction is not implemented.
WORKDIR /path
Sets the working directory for subsequent directives.
ENV KEY=VALUE ...
Accumulates environment variables passed to every RUN.
ARG NAME[=DEFAULT]
Declares a build-time variable. Overridden by --build-arg.
USER name
Subsequent RUN commands are wrapped in su -s /bin/sh -c '<cmd>' <name>.
Builds run in a dedicated session keyed by the absolute path of the
context directory: build:<sha16(abspath(context))>. Re-running the
same Dockerfile in the same context reuses cached layers across
invocations of contree build; switching to --no-cache rebuilds
everything.Layers are stored as branches whose names are the chain-hash of:
contree session list --filter build:contree session show
build is project-scoped from the user’s point of view: it does
not bind to the agent’s -S <key> session. Passing -S is harmless
but does not move that key’s image. After a successful build, attach
the result to your normal agent session by tag:
contree build . --tag myapp:devcontree -S agent_verify use tag:myapp:devcontree -S agent_verify run -D -- myapp --version
contree build reads <CONTEXT>/.dockerignore and filters every
COPY/ADD walk. Rules are matched in order against POSIX-style
paths relative to the context root; the last matching rule wins,
so ! re-includes a previously ignored path.
A small example lives in docs/examples/build-demo/. The Dockerfile
exercises FROM, ARG, ENV, WORKDIR, two COPY directives (file
and directory), and two RUN directives. A .dockerignore filters
log files and __pycache__ from the upload.
The ADD line streams the zip straight from GitHub into the contree
API (no local temp file). The subsequent RUN steps unpack it,
pip install the project, and prove the installed contree binary
works inside the built image.
[INFO] RUN spawned op=019e... RUN python -c "import sys; print('python', sys.version)"[INFO] stdout:python 3.12.13 ...[INFO] RUN spawned op=019e... RUN python /app/hello.py[INFO] stdout:+---------------+| hello || contree build |+---------------+[INFO] tagged <uuid> as contree-cli-build-demo:latestIMAGE TAG SESSION<uuid> contree-cli-build-demo:latest build:<sha16>
Re-running the same command without --no-cache produces layer cache
hits, and the ADD URL step short-circuits at the HEAD probe (look
for URL cache hit (HEAD validators match) in the log) – no body
download, no upload.