diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ca64e27 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,31 @@ +# VCS metadata +.git +.gitignore + +# Local dependencies and caches +node_modules +.pnpm-store + +# Build artifacts +dist +build +.turbo +.cache + +# Test fixtures and docs not needed in image +e2e +mock-global-claude-dir +docs +coverage + +# Editor and OS files +.DS_Store +.idea +.vscode + +# Logs and environment files +*.log +npm-debug.log* +pnpm-debug.log* +.env +.env.* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d9e5f1c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# syntax=docker/dockerfile:1.7 + +FROM node:22-slim AS base +ENV PNPM_HOME=/usr/local/share/pnpm +ENV PATH=${PNPM_HOME}:${PATH} +RUN corepack enable pnpm && apt-get update && apt-get install -y git openssh-client && rm -rf /var/lib/apt/lists/* + +FROM base AS builder +WORKDIR /app +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \ + pnpm install --frozen-lockfile + +COPY . . +RUN chmod +x scripts/docker-entrypoint.sh +RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \ + pnpm build && pnpm prune --prod + +FROM base AS runner +WORKDIR /app +ENV NODE_ENV=production \ + PORT=3400 \ + PATH="/app/node_modules/.bin:${PATH}" +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/scripts/docker-entrypoint.sh ./scripts/docker-entrypoint.sh +COPY package.json pnpm-lock.yaml ./ +EXPOSE 3400 +ENTRYPOINT ["./scripts/docker-entrypoint.sh"] +CMD ["node", "dist/main.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..20b2420 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +services: + app: + build: . + environment: + NODE_ENV: production + PORT: 3400 + ANTHROPIC_BASE_URL: + ANTHROPIC_API_KEY: + ANTHROPIC_AUTH_TOKEN: + ports: + - "3400:3400" + volumes: + # - claude_home:/root/.claude + - workspace:/root/workspace + restart: unless-stopped + init: true + +volumes: + claude_home: + workspace: diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh new file mode 100644 index 0000000..9ed1753 --- /dev/null +++ b/scripts/docker-entrypoint.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +CLAUDE_HOME="${HOME}/.claude" + +# Make sure `~/.claude/projects` folder exists. +mkdir -p "$CLAUDE_HOME/projects" + +# Only bootstrap when Claude home is backed by an external volume. +if ! mountpoint -q "$CLAUDE_HOME" && [ ! -f "$CLAUDE_HOME/settings.json" ]; then + cat < "$CLAUDE_HOME/settings.json" +{ + "env": { + "ANTHROPIC_BASE_URL": "${ANTHROPIC_BASE_URL:-}", + "ANTHROPIC_API_KEY": "${ANTHROPIC_API_KEY:-}", + "ANTHROPIC_AUTH_TOKEN": "${ANTHROPIC_AUTH_TOKEN:-}" + } +} +EOF +fi + +exec "$@"