Files
huskies/docker/Dockerfile
T
Timmy ce688fc0bf fix: drop package-lock.json + node_modules before npm install in Dockerfile
Previous attempt (c1318964) used npm ci + npm install --include=optional
--no-save, which still missed rolldown's platform-specific native
binding (@rolldown/binding-linux-arm64-gnu) — the runtime build still
fails with `Cannot find native binding`.

Wipe both the lockfile and node_modules so npm install resolves the
dependency tree fresh for the build platform.  The lockfile mutation
stays inside the container image.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 23:47:43 +01:00

143 lines
6.7 KiB
Docker
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Huskies single-container runtime
# All components (server, agents, web UI) run inside this container.
# The target project repo is bind-mounted at /workspace.
#
# Build: docker build -t huskies -f docker/Dockerfile .
# Run: docker compose -f docker/docker-compose.yml up
#
# Tested with: OrbStack (recommended on macOS), Docker Desktop (slower bind mounts)
FROM rust:1.93-bookworm AS base
# Clippy and rustfmt are needed at runtime for acceptance gates
RUN rustup component add clippy rustfmt
# ── System deps ──────────────────────────────────────────────────────
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
ca-certificates \
build-essential \
pkg-config \
libssl-dev \
# cargo-nextest is a pre-built binary
&& rm -rf /var/lib/apt/lists/*
# ── Node.js 22.x (matches host) ─────────────────────────────────────
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*
# ── cargo-nextest (test runner) ──────────────────────────────────────
RUN curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C /usr/local/bin
# ── Claude Code CLI ──────────────────────────────────────────────────
# Claude Code is distributed as an npm global package.
# The CLI binary is `claude`.
RUN npm install -g @anthropic-ai/claude-code
# ── Working directory ────────────────────────────────────────────────
# /app holds the huskies source (copied in at build time for the binary).
# /workspace is where the target project repo gets bind-mounted at runtime.
WORKDIR /app
# ── Build the huskies server binary ─────────────────────────────────
# Copy the full project tree so `cargo build` and `npm run build` (via
# build.rs) can produce the release binary with embedded frontend assets.
COPY . .
# Build frontend deps first (better layer caching).
# Cannot use `npm ci` because of npm's optional-dependencies bug
# (npm/cli#4828): platform-specific bindings (e.g. rolldown's
# linux-arm64-gnu native binary, introduced by 1119's vite 5→8 upgrade)
# get listed in package-lock.json for the lockfile author's platform
# only, so `npm ci` skips them on every other platform — the build
# then fails at runtime with `Cannot find native binding`. Wipe the
# lockfile + node_modules and let `npm install` resolve fresh for the
# build platform. The lockfile mutation stays inside the container
# image and never reaches the host repo.
RUN cd frontend && rm -rf node_modules package-lock.json && npm install
# Build the release binary (build.rs runs npm run build for the frontend)
RUN cargo build --release \
&& cp target/release/huskies /usr/local/bin/huskies
# ── Runtime stage (smaller image) ───────────────────────────────────
FROM debian:bookworm-slim AS runtime
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
ca-certificates \
libssl3 \
# build-essential (gcc/cc) needed at runtime for:
# - rebuild_and_restart (cargo build --release)
# - agent-driven cargo commands (clippy, test, build)
build-essential \
pkg-config \
libssl-dev \
# procps provides ps, needed by tests and process management
procps \
&& rm -rf /var/lib/apt/lists/*
# Node.js in runtime
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*
# Claude Code CLI in runtime
RUN npm install -g @anthropic-ai/claude-code
# Cargo and Rust toolchain needed at runtime for:
# - rebuild_and_restart (cargo build inside the container)
# - Agent-driven cargo commands (cargo clippy, cargo test, etc.)
COPY --from=base /usr/local/cargo /usr/local/cargo
COPY --from=base /usr/local/rustup /usr/local/rustup
ENV PATH="/usr/local/cargo/bin:${PATH}"
ENV RUSTUP_HOME="/usr/local/rustup"
ENV CARGO_HOME="/usr/local/cargo"
# cargo-nextest
COPY --from=base /usr/local/bin/cargo-nextest /usr/local/bin/cargo-nextest
# The huskies binary
COPY --from=base /usr/local/bin/huskies /usr/local/bin/huskies
# Copy the full source tree so rebuild_and_restart can do `cargo build`
# from the workspace root (CARGO_MANIFEST_DIR is baked into the binary).
# Alternative: mount the source as a volume.
COPY --from=base /app /app
# ── Non-root user ────────────────────────────────────────────────────
# Claude Code refuses --dangerously-skip-permissions (bypassPermissions)
# when running as root. Create a dedicated user so agents can launch.
RUN groupadd -r huskies \
&& useradd -r -g huskies -m -d /home/huskies huskies \
&& mkdir -p /home/huskies/.claude \
&& chown -R huskies:huskies /home/huskies \
&& chown -R huskies:huskies /usr/local/cargo /usr/local/rustup \
&& chown -R huskies:huskies /app \
&& mkdir -p /workspace/target /app/target \
&& chown huskies:huskies /workspace/target /app/target \
&& git config --global init.defaultBranch master
# ── Entrypoint ───────────────────────────────────────────────────────
# Validates required env vars (GIT_USER_NAME, GIT_USER_EMAIL) and
# configures git identity before starting the server.
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
USER huskies
WORKDIR /workspace
# ── Ports ────────────────────────────────────────────────────────────
# Web UI + MCP server
EXPOSE 3001
# ── Volumes (defined in docker-compose.yml) ──────────────────────────
# /workspace bind mount: target project repo
# /home/huskies/.claude named volume: Claude Code sessions/state
# /usr/local/cargo/registry named volume: cargo dependency cache
ENTRYPOINT ["entrypoint.sh"]
CMD ["huskies", "/workspace"]