feat(docker): pre-bundle WASM extensions in staging image (#2210)

* feat(docker): pre-bundle WASM extensions in staging image

Add a wasm-builder Docker stage that builds all registry tool/channel
extensions from source and copies the .wasm + .capabilities.json files
into the staging runtime image. Production images are unaffected — Docker
only builds the wasm-builder stage when --target runtime-staging is used.

The docker.yml workflow passes --target runtime-staging for scheduled
(staging) builds and workflow_dispatch with tag=staging, while all other
builds use --target runtime (no extensions).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(docker): address PR review feedback

- Use COPY --chown instead of separate RUN chown layer (fewer layers)
- Reorder stages so runtime (production) is last — bare docker build
  defaults to production, not staging
- Use --locked when Cargo.lock is present for reproducible WASM builds

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henry Park
2026-04-09 13:39:29 -07:00
committed by GitHub
parent aaeb904b9d
commit 26e5e4cf02
3 changed files with 64 additions and 4 deletions

View File

@@ -32,6 +32,7 @@ COPY tests/ tests/
COPY migrations/ migrations/
COPY registry/ registry/
COPY channels-src/ channels-src/
COPY tools-src/ tools-src/
COPY wit/ wit/
COPY providers.json providers.json
@@ -59,13 +60,56 @@ COPY tests/ tests/
COPY migrations/ migrations/
COPY registry/ registry/
COPY channels-src/ channels-src/
COPY tools-src/ tools-src/
COPY wit/ wit/
COPY providers.json providers.json
RUN cargo build --profile dist --bin ironclaw
# Stage 5: Minimal runtime
FROM debian:bookworm-slim
# Stage 4b: Build all WASM extensions from source (only used by runtime-staging)
FROM builder AS wasm-builder
RUN apt-get update && apt-get install -y --no-install-recommends jq && rm -rf /var/lib/apt/lists/*
RUN set -eux; \
mkdir -p /app/wasm-bundles/tools /app/wasm-bundles/channels; \
for manifest in registry/tools/*.json registry/channels/*.json; do \
[ -f "$manifest" ] || continue; \
kind=$(jq -r '.kind' "$manifest"); \
ext_name=$(jq -r '.name' "$manifest"); \
source_dir=$(jq -r '.source.dir' "$manifest"); \
caps_file=$(jq -r '.source.capabilities' "$manifest"); \
crate_name=$(jq -r '.source.crate_name' "$manifest"); \
[ -d "$source_dir" ] || continue; \
# Telegram is embedded in the binary at build time; skip it
[ "$ext_name" = "telegram" ] && continue; \
echo "=== Building $ext_name from $source_dir ==="; \
if [ -f "$source_dir/Cargo.lock" ]; then \
CARGO_TARGET_DIR=/app/target cargo build --locked --release --target wasm32-wasip2 \
--manifest-path "$source_dir/Cargo.toml" || { echo "WARN: build failed for $ext_name"; continue; }; \
else \
CARGO_TARGET_DIR=/app/target cargo build --release --target wasm32-wasip2 \
--manifest-path "$source_dir/Cargo.toml" || { echo "WARN: build failed for $ext_name"; continue; }; \
fi; \
wasm_artifact=$(echo "${crate_name}" | tr '-' '_'); \
raw_wasm="/app/target/wasm32-wasip2/release/${wasm_artifact}.wasm"; \
[ -f "$raw_wasm" ] || continue; \
dest_dir="/app/wasm-bundles/tools"; \
[ "$kind" = "channel" ] && dest_dir="/app/wasm-bundles/channels"; \
wasm-tools component new "$raw_wasm" -o "$dest_dir/${ext_name}.wasm" 2>/dev/null \
|| cp "$raw_wasm" "$dest_dir/${ext_name}.wasm"; \
wasm-tools strip "$dest_dir/${ext_name}.wasm" -o "$dest_dir/${ext_name}.wasm.tmp" 2>/dev/null \
&& mv "$dest_dir/${ext_name}.wasm.tmp" "$dest_dir/${ext_name}.wasm" \
|| true; \
[ -f "$source_dir/$caps_file" ] && cp "$source_dir/$caps_file" "$dest_dir/${ext_name}.capabilities.json"; \
echo " -> $dest_dir/${ext_name}.wasm"; \
done; \
count=$(find /app/wasm-bundles -name '*.wasm' | wc -l); \
echo "Built $count WASM extensions"; \
[ "$count" -gt 0 ] || { echo "ERROR: No WASM extensions were built"; exit 1; }
# Stage 5a: Shared runtime base
FROM debian:bookworm-slim AS runtime-base
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
@@ -80,10 +124,19 @@ RUN useradd -m -d /home/ironclaw -u 1000 ironclaw \
&& mkdir -p /home/ironclaw/.ironclaw \
&& chown -R ironclaw:ironclaw /home/ironclaw
WORKDIR /home/ironclaw
USER ironclaw
EXPOSE 3000
ENV RUST_LOG=ironclaw=info
ENTRYPOINT ["ironclaw"]
# Stage 5b: Staging runtime (with pre-built WASM extensions)
FROM runtime-base AS runtime-staging
COPY --from=wasm-builder --chown=ironclaw:ironclaw /app/wasm-bundles/tools/ /home/ironclaw/.ironclaw/tools/
COPY --from=wasm-builder --chown=ironclaw:ironclaw /app/wasm-bundles/channels/ /home/ironclaw/.ironclaw/channels/
USER ironclaw
# Stage 5c: Production runtime (default — no pre-bundled extensions)
FROM runtime-base AS runtime
USER ironclaw