# Dockerfile.e2e — Containerized E2E test environment for Midtown
#
# Provides a reproducible environment with tmux, git, and all dependencies
# needed to run E2E tests that exercise the daemon, coworker lifecycle, and
# channel communication.
#
# Usage:
# docker build -t midtown-e2e -f Dockerfile.e2e .
# docker run midtown-e2e # coordination mode (no auth)
# docker run -e ANTHROPIC_API_KEY midtown-e2e full # full mode (real Claude)
#
# Optimization: Uses cargo-chef for dependency caching. When only source code
# changes (not Cargo.toml/Cargo.lock), rebuilds are much faster.
# syntax=docker/dockerfile:1
# ─── Stage 1: Chef planner ───────────────────────────────────────────────────
# Analyzes the project and creates a "recipe" of dependencies
FROM rust:bookworm AS chef
RUN cargo install cargo-chef
WORKDIR /app
# ─── Stage 2: Recipe creation ────────────────────────────────────────────────
FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
# ─── Stage 3: Dependency builder ─────────────────────────────────────────────
# Builds only dependencies (cached until Cargo.toml/Cargo.lock change)
FROM chef AS builder
# System dependencies needed for building
RUN apt-get update && apt-get install -y --no-install-recommends \
pkg-config \
&& rm -rf /var/lib/apt/lists/*
# Build dependencies from recipe (cached layer)
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
# Now copy source and build the actual application
COPY . .
RUN cargo build --release \
&& cargo test --release --no-run 2>&1 | tail -5
# ─── Stage 4: Final runtime image ────────────────────────────────────────────
FROM rust:bookworm AS runtime
# System dependencies: tmux for process model, git for worktrees,
# procps for process inspection, jq for JSON parsing in scripts
RUN apt-get update && apt-get install -y --no-install-recommends \
tmux \
git \
curl \
procps \
jq \
&& rm -rf /var/lib/apt/lists/*
# gh CLI via GitHub's apt repo
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
| dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
> /etc/apt/sources.list.d/github-cli.list \
&& apt-get update && apt-get install -y gh \
&& rm -rf /var/lib/apt/lists/*
# Non-root user — tests use $HOME/.midtown/ paths and running as root
# would mask permission bugs that real users would hit
RUN useradd -m -s /bin/bash testuser
# Install CLI tools as testuser BEFORE copying build artifacts so these
# layers are cached even when source code changes.
USER testuser
# Claude Code CLI
RUN curl -fsSL https://claude.ai/install.sh | bash \
&& echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc \
|| echo "Claude CLI install failed (may not be available in all environments)"
ENV PATH="/home/testuser/.local/bin:${PATH}"
# Bun + Codex CLI
RUN curl -fsSL https://bun.sh/install | bash \
|| echo "Bun install failed (may not be available in all environments)"
ENV PATH="/home/testuser/.bun/bin:${PATH}"
RUN bun install -g @openai/codex \
|| echo "Codex CLI install failed (may not be available in all environments)"
# Switch back to root for COPY/mkdir, then back to testuser
USER root
# Copy built artifacts from builder stage
RUN mkdir -p /home/testuser/midtown && chown testuser:testuser /home/testuser/midtown
WORKDIR /home/testuser/midtown
COPY --from=builder --chown=testuser:testuser /app/target ./target
COPY --from=builder --chown=testuser:testuser /app/Cargo.toml /app/Cargo.lock ./
COPY --from=builder --chown=testuser:testuser /app/src ./src
COPY --from=builder --chown=testuser:testuser /app/agents ./agents
COPY --from=builder --chown=testuser:testuser /app/tests ./tests
COPY --from=builder --chown=testuser:testuser /app/scripts ./scripts
USER testuser
# Default environment: disable webhook server and chat monitor for tests
ENV MIDTOWN_WEBHOOK_PORT=0 \
MIDTOWN_CHAT_MONITOR=0
COPY --chmod=755 scripts/e2e-entrypoint.sh /usr/local/bin/e2e-entrypoint.sh
ENTRYPOINT ["e2e-entrypoint.sh"]
CMD ["coordination"]