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.
Synopsis
contree build [CONTEXT] [--dockerfile PATH] [--tag NAME[:TAG]]
[--build-arg K=V ...] [--no-cache] [--timeout SEC]
CONTEXT – build context directory (default .).
--dockerfile PATH – override the default <CONTEXT>/Dockerfile.
--tag NAME[:TAG] – tag the final image via PATCH /v1/images/{uuid}/tag.
--build-arg KEY=VALUE – supply a value for an ARG declared in the
Dockerfile (repeatable).
--no-cache – ignore existing layer:<hash> branches and rebuild.
--timeout SEC – per-RUN operation timeout in seconds (default 600).
Help output
usage: contree build [-h] [—dockerfile PATH] [—tag NAME[:TAG]] [—build-arg KEY=VALUE]
[—no-cache] [-t TIMEOUT]
[context]
Build an image from a Dockerfile.
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, RUN, COPY, ADD (local files/dirs
and http(s) URLs; no tar auto-extraction), 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).
positional arguments:
context Build context directory (default: .)
options:
-h, —help show this help message and exit
—dockerfile PATH Dockerfile path (default: <context>/Dockerfile) (default: )
—tag NAME[:TAG] Tag the final image (default: )
—build-arg KEY=VALUE
Build-time variable (repeatable)
—no-cache Ignore cached layers and rebuild
-t, —timeout TIMEOUT
Timeout in seconds for each RUN step (default: 600)
examples:
contree build .
contree build . —tag myimage:latest
contree build —dockerfile ./Dockerfile.test ./app
contree build —build-arg VERSION=1.2 .
contree build —no-cache .
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
agent note:
Before using this command in an automated workflow, read:
contree agent
Examples
# Simplest build; finds ./Dockerfile, tags the result
contree build . --tag myapp:dev
# Out-of-tree Dockerfile
contree build ./service --dockerfile ./service/Dockerfile.prod --tag svc:prod
# Override build-time variables
contree build . --build-arg VERSION=2.5 --build-arg DEBUG=1
# Force a rebuild ignoring cached layers
contree build . --no-cache --tag myapp:dev
Supported directives (MVP)
| Directive | Behaviour |
|---|
FROM ref[:tag] [AS name] | 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>. |
CMD, ENTRYPOINT, LABEL, EXPOSE, VOLUME, STOPSIGNAL, MAINTAINER, HEALTHCHECK, ONBUILD, SHELL | Parsed but skipped with a warning. |
COPY --from=stage is a Phase 2 feature; in MVP it warns and skips.
Sessions and layer cache
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:
sha256(parent_layer_hash || state(workdir/env/user/args) || directive || pending_files)
To inspect the resulting branches:
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:dev
contree -S agent_verify use tag:myapp:dev
contree -S agent_verify run -D -- myapp --version
.dockerignore
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.
# .dockerignore
**/*.log
.env*
node_modules
!logs/keep.log
Globs:
* matches a single path segment (does not cross /).
** matches zero or more path components.
? matches one character.
[abc] is a character class.
- Trailing
/ matches a directory and everything below it.
The default exclude list from run --file (.git, *.pyc,
__pycache__, .venv, node_modules, dist, build, etc.) is
always applied on top of .dockerignore.
Variable substitution
$VAR and ${VAR} are expanded in FROM, RUN, COPY/ADD
arguments, WORKDIR, ENV values, and USER. The value source is:
--build-arg KEY=VALUE (highest priority for declared ARG names).
ENV directives processed so far.
ARG defaults.
- Empty string for unknown names.
End-to-end demo
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.
% docs/examples/build-demo/Dockerfile
FROM python:3.12-alpine
ARG GREETING=hello
ENV APP_GREETING=${GREETING}
WORKDIR /app
COPY hello.py /app/hello.py
COPY src /app/src
ADD https://github.com/nebius/contree-cli/archive/refs/heads/master.zip /tmp/contree-cli.zip
RUN python -c "import sys; print('python', sys.version)"
RUN python -m zipfile -e /tmp/contree-cli.zip /opt/
RUN pip install --no-cache-dir /opt/contree-cli-master
RUN contree --help | head -20
RUN python /app/hello.py
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.
% docs/examples/build-demo/.dockerignore
**/*.log
**/__pycache__
.env*
Build and tag it:
contree build docs/examples/build-demo --tag contree-cli-build-demo:latest
Expected output (truncated):
[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:latest
IMAGE 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.
See also