forked from zeroclaw-labs/zeroclaw
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
299 lines (279 loc) Β· 16 KB
/
Copy pathDockerfile
File metadata and controls
299 lines (279 loc) Β· 16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# syntax=docker/dockerfile:1.7-labs
# >>> generated:base-arg-node from dev/ci/container-base-images.toml by `cargo generate installers` - do not edit <<<
ARG ZEROCLAW_BASE_NODE=node:24-bookworm-slim@sha256:6f7b03f7c2c8e2e784dcf9295400527b9b1270fd37b7e9a7285cf83b6951452d
# >>> end generated:base-arg-node <<<
# >>> generated:base-arg-rust-slim from dev/ci/container-base-images.toml by `cargo generate installers` - do not edit <<<
ARG ZEROCLAW_BASE_RUST_SLIM=rust:1.96-slim@sha256:31ee7fc65186be7e0e0ccb3f2ca305f14e4739e7642a1ae65753aa5d7b874523
# >>> end generated:base-arg-rust-slim <<<
# >>> generated:base-arg-debian from dev/ci/container-base-images.toml by `cargo generate installers` - do not edit <<<
ARG ZEROCLAW_BASE_DEBIAN=debian:trixie-slim@sha256:020c0d20b9880058cbe785a9db107156c3c75c2ac944a6aa7ab59f2add76a7bd
# >>> end generated:base-arg-debian <<<
# >>> generated:base-arg-distroless from dev/ci/container-base-images.toml by `cargo generate installers` - do not edit <<<
ARG ZEROCLAW_BASE_DISTROLESS=gcr.io/distroless/cc-debian13:nonroot@sha256:d97bc0a941b8d4be647dc0ee75b264ddbb772f1ac5ba690a4309c00723b23775
# >>> end generated:base-arg-distroless <<<
# ββ Stage 0: Frontend build βββββββββββββββββββββββββββββββββββββ
# The web dashboard bundle is architecture-independent (JS/WASM), so the
# frontend tooling stages are pinned to the native build platform. On a
# multi-platform (`--platform linux/amd64,linux/arm64`) build this keeps node
# and `cargo web build` running natively instead of under QEMU emulation.
FROM --platform=$BUILDPLATFORM ${ZEROCLAW_BASE_NODE} AS web-node
FROM --platform=$BUILDPLATFORM ${ZEROCLAW_BASE_RUST_SLIM} AS web-builder
WORKDIR /app
COPY --from=web-node /usr/local/bin/node /usr/local/bin/node
COPY --from=web-node /usr/local/lib/node_modules /usr/local/lib/node_modules
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y \
g++ \
pkg-config \
&& ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
&& ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \
&& rm -rf /var/lib/apt/lists/*
COPY web/package.json web/package-lock.json web/
RUN cd web && npm ci --ignore-scripts
COPY . .
RUN mkdir -p apps/tauri/src \
&& echo "fn main() {}" > apps/tauri/src/main.rs \
&& echo "fn main() {}" > apps/tauri/build.rs
RUN --mount=type=cache,id=zeroclaw-cargo-registry,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,id=zeroclaw-cargo-git,target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,id=zeroclaw-web-target,target=/app/target,sharing=locked \
cargo web build
# ββ Stage 1: Build ββββββββββββββββββββββββββββββββββββββββββββ
# Pinned to the native build platform; the Rust toolchain runs on the host arch
# (amd64 on the GitHub-hosted runners) and cross-compiles to $TARGETARCH so
# rustc never runs under QEMU. TARGETARCH is injected by BuildKit per target
# platform (`amd64`/`arm64`).
FROM --platform=$BUILDPLATFORM ${ZEROCLAW_BASE_RUST_SLIM} AS builder
WORKDIR /app
ARG TARGETARCH
# >>> generated:docker-features-arg by `cargo generate installers` - do not edit <<<
ARG ZEROCLAW_CARGO_FLAGS="--no-default-features --features acp-bridge,agent-runtime,channel-acp-server,channel-discord,channel-email,channel-filesystem,channel-lark,channel-matrix,channel-telegram,channel-webhook,gateway,observability-prometheus,schema-export,whatsapp-web"
# >>> end generated:docker-features-arg <<<
# Install build dependencies. g++ is required by inkjet (zerocode's syntax
# highlighter) to compile its tree-sitter grammars; the slim base ships cc but
# not a C++ compiler. For arm64 cross-builds, also install the aarch64 GNU
# cross toolchain (C and C++, for cc-crate-built C/C++ deps such as inkjet's
# grammars), the arm64 libc dev files, and the Rust target.
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y \
pkg-config \
g++ \
&& if [ "$TARGETARCH" = "arm64" ]; then \
dpkg --add-architecture arm64 && apt-get update && apt-get install -y \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu \
libc6-dev-arm64-cross \
&& rustup target add aarch64-unknown-linux-gnu; \
fi \
&& rm -rf /var/lib/apt/lists/*
# 1. Copy manifests to cache dependencies
COPY Cargo.toml Cargo.lock ./
# Copy every workspace-member manifest in one glob β adding or removing a crate
# no longer requires editing this file. --parents preserves the
# crates/<name>/Cargo.toml directory structure.
COPY --parents crates/*/Cargo.toml ./
# zeroclaw-macros is a proc-macro crate, compiled for the host even on a cross
# build. If only a stub lib.rs is present during the pre-fetch, its host-cached
# artifact is reused in the real build under the target-triple dir, leaving
# `zeroclaw_macros::Configurable` unresolved. Copy its real source now so the
# proc-macro is built from the genuine implementation during the pre-fetch.
COPY --parents crates/zeroclaw-macros/src/ ./
# apps/tauri: .dockerignore whitelists only Cargo.toml; src and build.rs are stubbed below.
COPY apps/tauri/Cargo.toml apps/tauri/Cargo.toml
# apps/zerocode: TUI app not shipped in the server image; copy only its manifest
# so Cargo can resolve the workspace, then stub its src/main.rs and build.rs
# below. Its real build.rs reads web/src/contexts/themes.json and would panic in
# this pre-fetch stage, so it is stubbed exactly like apps/tauri.
COPY apps/zerocode/Cargo.toml apps/zerocode/Cargo.toml
# tools/fill-translations and xtask are dev/build tools; copy manifests only so
# Cargo can resolve the workspace, then stub their entry points so the
# dependency pre-fetch step succeeds without building them into the image.
COPY tools/fill-translations/Cargo.toml tools/fill-translations/Cargo.toml
COPY xtask/Cargo.toml xtask/Cargo.toml
# Create dummy targets for all workspace members so manifest parsing succeeds.
# `src/bin/zeroclaw-acp-bridge.rs` is required because the `acp-bridge` feature
# is in the root crate's default set; cargo selects the bin target during the
# pre-fetch build even with only the workspace lib stubbed.
RUN mkdir -p src src/bin benches apps/tauri/src apps/zerocode/src tools/fill-translations/src xtask/src/bin \
&& echo "fn main() {}" > src/main.rs \
&& echo "" > src/lib.rs \
&& echo "fn main() {}" > src/bin/zeroclaw-acp-bridge.rs \
&& echo "fn main() {}" > benches/agent_benchmarks.rs \
&& echo "fn main() {}" > apps/tauri/src/main.rs \
&& echo "fn main() {}" > apps/tauri/build.rs \
&& echo "fn main() {}" > apps/zerocode/src/main.rs \
&& echo "fn main() {}" > apps/zerocode/build.rs \
&& echo "fn main() {}" > tools/fill-translations/src/main.rs \
&& echo "" > xtask/src/lib.rs \
&& echo "fn main() {}" > xtask/src/bin/mdbook.rs \
&& echo "fn main() {}" > xtask/src/bin/fluent.rs \
&& echo "fn main() {}" > xtask/src/bin/web.rs \
&& mkdir -p crates/zeroclaw-hardware/examples \
&& echo "fn main() {}" > crates/zeroclaw-hardware/examples/esp32_sim.rs \
&& for d in crates/*/; do [ "$d" = "crates/zeroclaw-macros/" ] && continue; mkdir -p "${d}src" && printf '' > "${d}src/lib.rs"; done
RUN --mount=type=cache,id=zeroclaw-cargo-registry,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,id=zeroclaw-cargo-git,target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,id=zeroclaw-target,target=/app/target,sharing=locked \
if [ "$TARGETARCH" = "arm64" ]; then \
export RUST_TARGET=aarch64-unknown-linux-gnu \
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc \
CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ \
PKG_CONFIG_ALLOW_CROSS=1 \
PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig; \
else \
export RUST_TARGET=x86_64-unknown-linux-gnu; \
fi && \
if [ -n "$ZEROCLAW_CARGO_FLAGS" ]; then \
cargo build --release --locked --target "$RUST_TARGET" -p zeroclawlabs -p zerocode $ZEROCLAW_CARGO_FLAGS; \
else \
cargo build --release --locked --target "$RUST_TARGET" -p zeroclawlabs -p zerocode; \
fi
RUN rm -rf src benches crates xtask tools/fill-translations
# 2. Copy only build-relevant source paths (avoid cache-busting on docs/tests/scripts)
COPY src/ src/
COPY benches/ benches/
COPY crates/ crates/
COPY xtask/ xtask/
COPY tools/fill-translations/ tools/fill-translations/
# apps/zerocode ships in the image; copy its real source. Its build.rs reads the
# dashboard theme registry under web/src/contexts, so that path must be present.
COPY apps/zerocode/ apps/zerocode/
COPY web/src/ web/src/
# locales.toml lives at repo root and is embedded by zeroclaw-runtime via
# include_str!("../../../locales.toml"); the real build needs it present.
COPY locales.toml .
COPY *.rs .
RUN touch src/main.rs apps/zerocode/src/main.rs
# Bust the stubbed workspace crates so the real sources rebuild. zerocode is
# purged from BOTH the target tree and the host tree (target/release/): with
# --target, build scripts compile in the host tree, so its real build.rs (which
# generates theme_presets.rs into OUT_DIR) must replace the no-op stub compiled
# during the dependency pre-fetch, otherwise the include! finds no file.
RUN --mount=type=cache,id=zeroclaw-cargo-registry,target=/usr/local/cargo/registry,sharing=locked \
--mount=type=cache,id=zeroclaw-cargo-git,target=/usr/local/cargo/git,sharing=locked \
--mount=type=cache,id=zeroclaw-target,target=/app/target,sharing=locked \
if [ "$TARGETARCH" = "arm64" ]; then \
export RUST_TARGET=aarch64-unknown-linux-gnu STRIP=aarch64-linux-gnu-strip \
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc \
CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ \
PKG_CONFIG_ALLOW_CROSS=1 \
PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig; \
else \
export RUST_TARGET=x86_64-unknown-linux-gnu STRIP=strip; \
fi && \
rm -rf target/"$RUST_TARGET"/release/.fingerprint/zeroclawlabs-* \
target/"$RUST_TARGET"/release/deps/zeroclawlabs-* \
target/"$RUST_TARGET"/release/incremental/zeroclawlabs-* \
target/"$RUST_TARGET"/release/.fingerprint/zeroclaw-* \
target/"$RUST_TARGET"/release/deps/zeroclaw_* \
target/"$RUST_TARGET"/release/incremental/zeroclaw_* \
target/"$RUST_TARGET"/release/.fingerprint/xtask-* \
target/"$RUST_TARGET"/release/deps/xtask-* \
target/"$RUST_TARGET"/release/.fingerprint/fill-translations-* \
target/"$RUST_TARGET"/release/deps/fill_translations-* \
target/"$RUST_TARGET"/release/.fingerprint/zerocode-* \
target/"$RUST_TARGET"/release/deps/zerocode-* \
target/"$RUST_TARGET"/release/incremental/zerocode-* \
target/"$RUST_TARGET"/release/build/zerocode-* \
target/release/.fingerprint/zerocode-* \
target/release/build/zerocode-* && \
if [ -n "$ZEROCLAW_CARGO_FLAGS" ]; then \
cargo build --release --locked --target "$RUST_TARGET" -p zeroclawlabs -p zerocode $ZEROCLAW_CARGO_FLAGS; \
else \
cargo build --release --locked --target "$RUST_TARGET" -p zeroclawlabs -p zerocode; \
fi && \
cp target/"$RUST_TARGET"/release/zeroclaw /app/zeroclaw && \
cp target/"$RUST_TARGET"/release/zerocode /app/zerocode && \
"$STRIP" /app/zeroclaw /app/zerocode
RUN for b in zeroclaw zerocode; do \
size=$(stat -c%s "/app/$b") && \
if [ "$size" -lt 1000000 ]; then echo "ERROR: $b too small (${size} bytes), likely dummy build artifact" && exit 1; fi; \
done
# Prepare runtime directory structure and default config inline (no extra stage).
# Dashboard assets live at /usr/share/zeroclawlabs/web/dist (outside the documented
# /zeroclaw-data mount point) so a bind mount on /zeroclaw-data cannot shadow them.
RUN mkdir -p /zeroclaw-data/.zeroclaw /zeroclaw-data/data && \
printf '%s\n' \
'api_key = ""' \
'default_provider = "openrouter"' \
'default_model = "anthropic/claude-sonnet-4-20250514"' \
'default_temperature = 0.7' \
'' \
'[gateway]' \
'port = 42617' \
'host = "[::]"' \
'allow_public_bind = true' \
'require_pairing = false' \
'web_dist_dir = "/usr/share/zeroclawlabs/web/dist"' \
'' \
'[risk_profiles.default]' \
'level = "supervised"' \
'auto_approve = ["file_read", "file_write", "file_edit", "memory_recall", "memory_store", "web_search_tool", "web_fetch", "calculator", "glob_search", "content_search", "image_info", "weather", "git_operations"]' \
> /zeroclaw-data/.zeroclaw/config.toml && \
chown -R 65534:65534 /zeroclaw-data
# ββ Stage 2: Development Runtime (Debian) ββββββββββββββββββββ
FROM ${ZEROCLAW_BASE_DEBIAN} AS dev
# Install essential runtime dependencies only (use docker-compose.override.yml for dev tools)
RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
vim-tiny \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /zeroclaw-data /zeroclaw-data
COPY --from=builder /app/zeroclaw /usr/local/bin/zeroclaw
COPY --from=builder /app/zerocode /usr/local/bin/zerocode
# Install the dashboard at /usr/share/zeroclawlabs/web/dist (outside the
# documented /zeroclaw-data mount) so user volumes do not shadow it (#6400).
COPY --from=web-builder /app/web/dist /usr/share/zeroclawlabs/web/dist
# Overwrite minimal config with DEV template (Ollama defaults)
COPY dev/config.template.toml /zeroclaw-data/.zeroclaw/config.toml
RUN chown 65534:65534 /zeroclaw-data/.zeroclaw/config.toml
# Environment setup
# Ensure UTF-8 locale so CJK / multibyte input is handled correctly
ENV LANG=C.UTF-8
# Bootstrap (uppercase tail) β pre-load: decides where the config file lives.
ENV ZEROCLAW_DATA_DIR=/zeroclaw-data/data
ENV HOME=/zeroclaw-data
# V0.8.0 env-var grammar: `ZEROCLAW_<dotted_path_with_double_underscores>=<value>`
# mirrors the TOML config 1:1; `__` is the path separator. Operators inject
# credentials and runtime knobs at `docker run -e ...` (or via docker-compose
# `environment:`). Legacy `PROVIDER`, `ZEROCLAW_MODEL`, `ANTHROPIC_API_KEY`,
# `API_KEY`, etc. fallbacks were eradicated. Example:
# docker run -e ZEROCLAW_providers__models__anthropic__default__api_key=sk-ant-... ...
ENV ZEROCLAW_gateway__port=42617
WORKDIR /zeroclaw-data
USER 65534:65534
EXPOSE 42617
HEALTHCHECK --interval=60s --timeout=10s --retries=3 --start-period=10s \
CMD ["zeroclaw", "status", "--format=exit-code"]
ENTRYPOINT ["zeroclaw"]
CMD ["daemon"]
# ββ Stage 3: Production Runtime (Distroless) βββββββββββββββββ
FROM ${ZEROCLAW_BASE_DISTROLESS} AS release
COPY --from=builder /app/zeroclaw /usr/local/bin/zeroclaw
COPY --from=builder /app/zerocode /usr/local/bin/zerocode
COPY --from=builder /zeroclaw-data /zeroclaw-data
# Install the dashboard at /usr/share/zeroclawlabs/web/dist (outside the
# documented /zeroclaw-data mount) so user volumes do not shadow it (#6400).
COPY --from=web-builder /app/web/dist /usr/share/zeroclawlabs/web/dist
# Environment setup
# Ensure UTF-8 locale so CJK / multibyte input is handled correctly
ENV LANG=C.UTF-8
ENV ZEROCLAW_DATA_DIR=/zeroclaw-data/data
ENV HOME=/zeroclaw-data
# Default provider and model are set in config.toml, not here,
# so config file edits are not silently overridden
#ENV PROVIDER=
ENV ZEROCLAW_GATEWAY_PORT=42617
# API_KEY must be provided at runtime!
WORKDIR /zeroclaw-data
USER 65534:65534
EXPOSE 42617
HEALTHCHECK --interval=60s --timeout=10s --retries=3 --start-period=10s \
CMD ["zeroclaw", "status", "--format=exit-code"]
ENTRYPOINT ["zeroclaw"]
CMD ["daemon"]