build - Build an image from a Dockerfile¶
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 viaPATCH /v1/images/{uuid}/tag.--build-arg KEY=VALUE– supply a value for anARGdeclared in the Dockerfile (repeatable).--no-cache– ignore existinglayer:<hash>branches and rebuild.--timeout SEC– per-RUNoperation timeout in seconds (default 600).
Help output¶
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 |
|---|---|
|
Resolves the base image. If the tag is not found locally, the build auto-imports it via |
|
Shell-form ( |
|
Walks local sources relative to the build context, applies |
|
Local paths behave like |
|
Sets the working directory for subsequent directives. |
|
Accumulates environment variables passed to every |
|
Declares a build-time variable. Overridden by |
|
Subsequent |
|
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
Note
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 declaredARGnames).ENVdirectives processed so far.ARGdefaults.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¶
run - Execute a command in the sandbox – the single-shot version of what
RUNdoes.session - Manage sessions, branches, history – inspect or branch the layer history.
images - List and import images – list, import, and tag images directly.