Archon/Dockerfile
DIY Smart Code 899e993d7a
fix(docker): fix Docker build failures and add CI guard (#1022)
* fix(docker): update Bun base image from 1.2 to 1.3

The lockfile was generated with Bun 1.3.x locally but the Docker image
used oven/bun:1.2-slim. Bun 1.3 changed the lockfile format, causing
--frozen-lockfile to fail during docker build.

* fix(docker): pin Bun to exact version 1.3.9 matching lockfile

Floating tag 1.3-slim resolved to 1.3.11 which has a different lockfile
format than 1.3.9 used to generate bun.lock. Pin to exact patch version
to prevent --frozen-lockfile failures.

* fix(docker): add missing docs-web workspace package.json

The docs-web package was added as a workspace member but its
package.json was never added to the Dockerfile COPY steps. This caused
bun install --frozen-lockfile to fail because the workspace layout
in Docker didn't match the lockfile.

* fix(docker): use hoisted linker for Vite/Rollup compatibility

Bun's default "isolated" linker stores packages in node_modules/.bun/
with symlinks that Vite's Rollup bundler cannot resolve during
production builds (e.g., remark-gfm → mdast-util-gfm chain).
Using --linker=hoisted gives the classic flat node_modules layout
that Rollup expects. Local dev is unaffected (Vite dev server handles
the isolated layout fine).

* ci: pin Bun version to 1.3.9 and add Docker build check

- Align CI Bun version (was 1.3.11) with Dockerfile and local dev
  (1.3.9) to prevent lockfile format mismatches between environments
- Add docker-build job to test.yml that builds the Docker image on
  every PR — catches Dockerfile regressions (missing workspace
  packages, linker issues, build failures) before they reach deploy

* fix(ci): add permissions for GHA cache and tighten Bun engine

- Add actions: write permission to docker-build job so GHA layer cache
  writes succeed on PRs from forks
- Tighten package.json engines.bun from >=1.0.0 to >=1.3.9 to document
  the minimum version that matches the lockfile format

* fix(ci): add smoke test, align Bun version across all workflows

Review fixes:
- Add load: true + health endpoint smoke test to docker-build CI job
  so we verify the image actually starts, not just compiles
- Align Bun 1.3.9 in deploy-docs.yml and release.yml (were still 1.3.11)
- Document why docs-web source is intentionally omitted from Docker

* chore: float Docker to bun:1.3 and align CI to 1.3.11

- Dockerfile: oven/bun:1.3-slim (auto-tracks latest 1.3.x patches)
- CI workflows: bun-version 1.3.11 (current latest, reproducible)
- engines.bun: >=1.3.9 (minimum for local devs)

Lockfile format is stable across 1.3.x patches, so this is safe.

* fix(docker,ci): pin Docker to 1.3.11, loosen engines, harden smoke test

- Dockerfile: pin oven/bun:1.3.11-slim (was floating 1.3-slim) so Docker
  builds are reproducible and match CI exactly.
- package.json: loosen engines to ^1.3.0 so end users on any 1.3.x can
  run the CLI; CI/Docker remain pinned to the canonical latest.
- CI smoke test: replace 'sleep 5' with curl --retry-connrefused, and
  move container cleanup to an 'if: always()' step so a failed health
  check no longer leaks the named container.

---------

Co-authored-by: Rasmus Widing <rasmus.widing@gmail.com>
2026-04-07 10:37:47 +03:00

179 lines
7.8 KiB
Docker

# =============================================================================
# Archon - Remote Agentic Coding Platform
# Multi-stage build: deps → web build → production image
# =============================================================================
# ---------------------------------------------------------------------------
# Stage 1: Install dependencies
# ---------------------------------------------------------------------------
FROM oven/bun:1.3.11-slim AS deps
WORKDIR /app
# Copy root package files and lockfile
COPY package.json bun.lock ./
# Copy ALL workspace package.json files (monorepo lockfile depends on all of them)
COPY packages/adapters/package.json ./packages/adapters/
COPY packages/cli/package.json ./packages/cli/
COPY packages/core/package.json ./packages/core/
# docs-web source is NOT copied — it's a static site deployed separately
# (see .github/workflows/deploy-docs.yml). package.json is included only
# so Bun's workspace lockfile resolves correctly.
COPY packages/docs-web/package.json ./packages/docs-web/
COPY packages/git/package.json ./packages/git/
COPY packages/isolation/package.json ./packages/isolation/
COPY packages/paths/package.json ./packages/paths/
COPY packages/server/package.json ./packages/server/
COPY packages/web/package.json ./packages/web/
COPY packages/workflows/package.json ./packages/workflows/
# Install ALL dependencies (including devDependencies needed for web build)
# --linker=hoisted: Bun's default "isolated" linker stores packages in
# node_modules/.bun/ with symlinks that Vite/Rollup cannot resolve during
# production builds. Hoisted layout gives classic flat node_modules.
RUN bun install --frozen-lockfile --linker=hoisted
# ---------------------------------------------------------------------------
# Stage 2: Build web UI (Vite + React)
# ---------------------------------------------------------------------------
FROM deps AS web-build
# Copy full source (needed for workspace resolution and web build)
COPY . .
# Build the web frontend — output goes to packages/web/dist/
RUN bun run build:web && \
test -f packages/web/dist/index.html || \
(echo "ERROR: Web build produced no index.html" >&2 && exit 1)
# ---------------------------------------------------------------------------
# Stage 3: Production image
# ---------------------------------------------------------------------------
FROM oven/bun:1.3.11-slim AS production
# OCI Labels for GHCR
LABEL org.opencontainers.image.source="https://github.com/coleam00/Archon"
LABEL org.opencontainers.image.description="Control AI coding assistants remotely from Telegram, Slack, Discord, and GitHub"
LABEL org.opencontainers.image.licenses="MIT"
# Prevent interactive prompts during installation
ENV DEBIAN_FRONTEND=noninteractive
WORKDIR /app
# Install system dependencies + gosu for privilege dropping in entrypoint
RUN apt-get update && apt-get install -y \
curl \
git \
bash \
ca-certificates \
gnupg \
gosu \
postgresql-client \
# Chromium for agent-browser E2E testing (drives browser via CDP)
chromium \
&& rm -rf /var/lib/apt/lists/*
# Install GitHub CLI
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" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& apt-get update \
&& apt-get install -y gh \
&& rm -rf /var/lib/apt/lists/*
# Install agent-browser CLI (Vercel Labs) for E2E testing workflows
# - Uses npm (not bun) because postinstall script downloads the native Rust binary
# - After install, symlink the Rust binary directly and purge nodejs/npm (~60MB saved)
# - The npm entry point is a Node.js wrapper; the native binary works standalone
# - agent-browser auto-detects Docker (via /.dockerenv) and adds --no-sandbox to Chromium
RUN apt-get update && apt-get install -y --no-install-recommends nodejs npm \
&& npm install -g agent-browser@0.22.1 \
&& NATIVE_BIN=$(find /usr/local/lib/node_modules/agent-browser -name 'agent-browser-*' -type f -executable 2>/dev/null | head -1) \
&& if [ -n "$NATIVE_BIN" ]; then \
cp "$NATIVE_BIN" /usr/local/bin/agent-browser-native \
&& chmod +x /usr/local/bin/agent-browser-native \
&& ln -sf /usr/local/bin/agent-browser-native /usr/local/bin/agent-browser; \
else \
echo "ERROR: agent-browser native binary not found after npm install" >&2 && exit 1; \
fi \
&& npm cache clean --force \
&& rm -rf /usr/local/lib/node_modules/agent-browser \
&& apt-get purge -y nodejs npm \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/*
# Point agent-browser to system Chromium (avoids ~400MB Chrome for Testing download)
ENV AGENT_BROWSER_EXECUTABLE_PATH=/usr/bin/chromium
# Create non-root user for running Claude Code
# Claude Code refuses to run with --dangerously-skip-permissions as root for security
RUN useradd -m -u 1001 -s /bin/bash appuser \
&& chown -R appuser:appuser /app
# Create Archon directories
RUN mkdir -p /.archon/workspaces /.archon/worktrees \
&& chown -R appuser:appuser /.archon
# Copy root package files and lockfile
COPY package.json bun.lock ./
# Copy ALL workspace package.json files
COPY packages/adapters/package.json ./packages/adapters/
COPY packages/cli/package.json ./packages/cli/
COPY packages/core/package.json ./packages/core/
# docs-web source is NOT copied — it's a static site deployed separately
# (see .github/workflows/deploy-docs.yml). package.json is included only
# so Bun's workspace lockfile resolves correctly.
COPY packages/docs-web/package.json ./packages/docs-web/
COPY packages/git/package.json ./packages/git/
COPY packages/isolation/package.json ./packages/isolation/
COPY packages/paths/package.json ./packages/paths/
COPY packages/server/package.json ./packages/server/
COPY packages/web/package.json ./packages/web/
COPY packages/workflows/package.json ./packages/workflows/
# Install production dependencies only (--ignore-scripts skips husky prepare hook)
RUN bun install --frozen-lockfile --production --ignore-scripts --linker=hoisted
# Copy application source (Bun runs TypeScript directly, no compile step needed)
COPY packages/adapters/ ./packages/adapters/
COPY packages/cli/ ./packages/cli/
COPY packages/core/ ./packages/core/
COPY packages/git/ ./packages/git/
COPY packages/isolation/ ./packages/isolation/
COPY packages/paths/ ./packages/paths/
COPY packages/server/ ./packages/server/
COPY packages/workflows/ ./packages/workflows/
# Copy pre-built web UI from build stage
COPY --from=web-build /app/packages/web/dist/ ./packages/web/dist/
# Copy config, migrations, and bundled defaults
COPY .archon/ ./.archon/
COPY migrations/ ./migrations/
COPY tsconfig*.json ./
# Fix permissions for appuser
RUN chown -R appuser:appuser /app
# Create .codex directory for Codex authentication
RUN mkdir -p /home/appuser/.codex && chown appuser:appuser /home/appuser/.codex
# Configure git to trust Archon directories (as appuser)
RUN gosu appuser git config --global --add safe.directory '/.archon/workspaces' && \
gosu appuser git config --global --add safe.directory '/.archon/workspaces/*' && \
gosu appuser git config --global --add safe.directory '/.archon/worktrees' && \
gosu appuser git config --global --add safe.directory '/.archon/worktrees/*'
# Copy entrypoint script (fixes volume permissions, drops to appuser)
# sed strips Windows CRLF in case .gitattributes eol=lf was bypassed
COPY docker-entrypoint.sh /usr/local/bin/
RUN sed -i 's/\r$//' /usr/local/bin/docker-entrypoint.sh \
&& chmod +x /usr/local/bin/docker-entrypoint.sh
# Default port (matches .env.example PORT=3000)
EXPOSE 3000
ENTRYPOINT ["docker-entrypoint.sh"]