From 8f458e55e2b5287ce051c31e53c578b19e3d71be Mon Sep 17 00:00:00 2001 From: Kacper Date: Sun, 28 Dec 2025 18:33:59 +0100 Subject: [PATCH 1/7] refactor: update Dockerfiles for server and UI to streamline dependency installation and build process - Modified Dockerfiles to copy package files for all workspaces, enhancing modularity. - Changed dependency installation to skip scripts, preventing unnecessary execution during builds. - Updated build commands to first build packages in dependency order before building the server and UI, ensuring proper build sequence. --- apps/server/Dockerfile | 20 ++++++++++++++------ apps/ui/Dockerfile | 20 ++++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/apps/server/Dockerfile b/apps/server/Dockerfile index 67ecedf0..981bcd10 100644 --- a/apps/server/Dockerfile +++ b/apps/server/Dockerfile @@ -9,19 +9,27 @@ RUN apk add --no-cache python3 make g++ WORKDIR /app -# Copy package files and scripts needed for postinstall +# Copy package files for all workspaces COPY package*.json ./ COPY apps/server/package*.json ./apps/server/ +COPY libs/types/package*.json ./libs/types/ +COPY libs/utils/package*.json ./libs/utils/ +COPY libs/prompts/package*.json ./libs/prompts/ +COPY libs/platform/package*.json ./libs/platform/ +COPY libs/model-resolver/package*.json ./libs/model-resolver/ +COPY libs/dependency-resolver/package*.json ./libs/dependency-resolver/ +COPY libs/git-utils/package*.json ./libs/git-utils/ COPY scripts ./scripts -# Install dependencies -RUN npm ci --workspace=apps/server +# Install dependencies (--ignore-scripts to skip husky and build:packages in prepare script) +RUN npm ci --ignore-scripts -# Copy source +# Copy all source files +COPY libs ./libs COPY apps/server ./apps/server -# Build TypeScript -RUN npm run build --workspace=apps/server +# Build packages in dependency order, then build server +RUN npm run build:packages && npm run build --workspace=apps/server # Production stage FROM node:20-alpine diff --git a/apps/ui/Dockerfile b/apps/ui/Dockerfile index 3ccd09c7..dbfd16d9 100644 --- a/apps/ui/Dockerfile +++ b/apps/ui/Dockerfile @@ -9,25 +9,33 @@ RUN apk add --no-cache python3 make g++ WORKDIR /app -# Copy package files +# Copy package files for all workspaces COPY package*.json ./ COPY apps/ui/package*.json ./apps/ui/ +COPY libs/types/package*.json ./libs/types/ +COPY libs/utils/package*.json ./libs/utils/ +COPY libs/prompts/package*.json ./libs/prompts/ +COPY libs/platform/package*.json ./libs/platform/ +COPY libs/model-resolver/package*.json ./libs/model-resolver/ +COPY libs/dependency-resolver/package*.json ./libs/dependency-resolver/ +COPY libs/git-utils/package*.json ./libs/git-utils/ COPY scripts ./scripts -# Install dependencies (skip electron postinstall) -RUN npm ci --workspace=apps/ui --ignore-scripts +# Install dependencies (--ignore-scripts to skip husky and build:packages in prepare script) +RUN npm ci --ignore-scripts -# Copy source +# Copy all source files +COPY libs ./libs COPY apps/ui ./apps/ui -# Build for web (skip electron) +# Build packages in dependency order, then build UI # VITE_SERVER_URL tells the UI where to find the API server # Using localhost:3008 since both containers expose ports to the host # Use ARG to allow overriding at build time: --build-arg VITE_SERVER_URL=http://api.example.com ARG VITE_SERVER_URL=http://localhost:3008 ENV VITE_SKIP_ELECTRON=true ENV VITE_SERVER_URL=${VITE_SERVER_URL} -RUN npm run build --workspace=apps/ui +RUN npm run build:packages && npm run build --workspace=apps/ui # Production stage - serve with nginx FROM nginx:alpine From 6012e8312b7c8f4ff66ebe90058ad2e9a9333ef1 Mon Sep 17 00:00:00 2001 From: Kacper Date: Sun, 28 Dec 2025 19:57:22 +0100 Subject: [PATCH 2/7] refactor: consolidate Dockerfiles into single multi-stage build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create unified Dockerfile with multi-stage builds (base, server, ui targets) - Centralize lib package.json COPYs in shared base stage (DRY) - Add Claude CLI installation for Docker authentication support - Remove duplicate apps/server/Dockerfile and apps/ui/Dockerfile - Update docker-compose.yml to use target: parameter - Add docker-compose.override.yml to .gitignore Build commands: docker build --target server -t automaker-server . docker build --target ui -t automaker-ui . docker-compose build && docker-compose up -d 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .gitignore | 2 +- Dockerfile | 143 +++++++++++++++++++++++++++++++++++++++++ apps/server/Dockerfile | 75 --------------------- apps/ui/Dockerfile | 51 --------------- docker-compose.yml | 6 +- 5 files changed, 148 insertions(+), 129 deletions(-) create mode 100644 Dockerfile delete mode 100644 apps/server/Dockerfile delete mode 100644 apps/ui/Dockerfile diff --git a/.gitignore b/.gitignore index 5efd9e1f..d35ec2d1 100644 --- a/.gitignore +++ b/.gitignore @@ -80,4 +80,4 @@ blob-report/ *.pem docker-compose.override.yml -.claude/ \ No newline at end of file +.claude/docker-compose.override.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..1b6bb0a7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,143 @@ +# Automaker Multi-Stage Dockerfile +# Single Dockerfile for both server and UI builds +# Usage: +# docker build --target server -t automaker-server . +# docker build --target ui -t automaker-ui . +# Or use docker-compose which selects targets automatically + +# ============================================================================= +# BASE STAGE - Common setup for all builds (DRY: defined once, used by all) +# ============================================================================= +FROM node:20-alpine AS base + +# Install build dependencies for native modules (node-pty) +RUN apk add --no-cache python3 make g++ + +WORKDIR /app + +# Copy root package files +COPY package*.json ./ + +# Copy all libs package.json files (centralized - add new libs here) +COPY libs/types/package*.json ./libs/types/ +COPY libs/utils/package*.json ./libs/utils/ +COPY libs/prompts/package*.json ./libs/prompts/ +COPY libs/platform/package*.json ./libs/platform/ +COPY libs/model-resolver/package*.json ./libs/model-resolver/ +COPY libs/dependency-resolver/package*.json ./libs/dependency-resolver/ +COPY libs/git-utils/package*.json ./libs/git-utils/ + +# Copy scripts (needed by npm workspace) +COPY scripts ./scripts + +# ============================================================================= +# SERVER BUILD STAGE +# ============================================================================= +FROM base AS server-builder + +# Copy server-specific package.json +COPY apps/server/package*.json ./apps/server/ + +# Install dependencies (--ignore-scripts to skip husky/prepare, then rebuild native modules) +RUN npm ci --ignore-scripts && npm rebuild node-pty + +# Copy all source files +COPY libs ./libs +COPY apps/server ./apps/server + +# Build packages in dependency order, then build server +RUN npm run build:packages && npm run build --workspace=apps/server + +# ============================================================================= +# SERVER PRODUCTION STAGE +# ============================================================================= +FROM node:20-alpine AS server + +# Install git, curl, and GitHub CLI (pinned version for reproducible builds) +RUN apk add --no-cache git curl && \ + GH_VERSION="2.63.2" && \ + curl -L "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz" -o gh.tar.gz && \ + tar -xzf gh.tar.gz && \ + mv "gh_${GH_VERSION}_linux_amd64/bin/gh" /usr/local/bin/gh && \ + rm -rf gh.tar.gz "gh_${GH_VERSION}_linux_amd64" + +# Install Claude CLI globally +RUN npm install -g @anthropic-ai/claude-code + +WORKDIR /app + +# Create non-root user +RUN addgroup -g 1001 -S automaker && \ + adduser -S automaker -u 1001 + +# Copy root package.json (needed for workspace resolution) +COPY --from=server-builder /app/package*.json ./ + +# Copy built libs (workspace packages are symlinked in node_modules) +COPY --from=server-builder /app/libs ./libs + +# Copy built server +COPY --from=server-builder /app/apps/server/dist ./apps/server/dist +COPY --from=server-builder /app/apps/server/package*.json ./apps/server/ + +# Copy node_modules (includes symlinks to libs) +COPY --from=server-builder /app/node_modules ./node_modules + +# Create data and projects directories +RUN mkdir -p /data /projects && chown automaker:automaker /data /projects + +# Switch to non-root user +USER automaker + +# Environment variables +ENV NODE_ENV=production +ENV PORT=3008 +ENV DATA_DIR=/data + +# Expose port +EXPOSE 3008 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3008/api/health || exit 1 + +# Start server +CMD ["node", "apps/server/dist/index.js"] + +# ============================================================================= +# UI BUILD STAGE +# ============================================================================= +FROM base AS ui-builder + +# Copy UI-specific package.json +COPY apps/ui/package*.json ./apps/ui/ + +# Install dependencies (--ignore-scripts to skip husky and build:packages in prepare script) +RUN npm ci --ignore-scripts + +# Copy all source files +COPY libs ./libs +COPY apps/ui ./apps/ui + +# Build packages in dependency order, then build UI +# VITE_SERVER_URL tells the UI where to find the API server +# Use ARG to allow overriding at build time: --build-arg VITE_SERVER_URL=http://api.example.com +ARG VITE_SERVER_URL=http://localhost:3008 +ENV VITE_SKIP_ELECTRON=true +ENV VITE_SERVER_URL=${VITE_SERVER_URL} +RUN npm run build:packages && npm run build --workspace=apps/ui + +# ============================================================================= +# UI PRODUCTION STAGE +# ============================================================================= +FROM nginx:alpine AS ui + +# Copy built files +COPY --from=ui-builder /app/apps/ui/dist /usr/share/nginx/html + +# Copy nginx config for SPA routing +COPY apps/ui/nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/apps/server/Dockerfile b/apps/server/Dockerfile deleted file mode 100644 index 981bcd10..00000000 --- a/apps/server/Dockerfile +++ /dev/null @@ -1,75 +0,0 @@ -# Automaker Backend Server -# Multi-stage build for minimal production image - -# Build stage -FROM node:20-alpine AS builder - -# Install build dependencies for native modules (node-pty) -RUN apk add --no-cache python3 make g++ - -WORKDIR /app - -# Copy package files for all workspaces -COPY package*.json ./ -COPY apps/server/package*.json ./apps/server/ -COPY libs/types/package*.json ./libs/types/ -COPY libs/utils/package*.json ./libs/utils/ -COPY libs/prompts/package*.json ./libs/prompts/ -COPY libs/platform/package*.json ./libs/platform/ -COPY libs/model-resolver/package*.json ./libs/model-resolver/ -COPY libs/dependency-resolver/package*.json ./libs/dependency-resolver/ -COPY libs/git-utils/package*.json ./libs/git-utils/ -COPY scripts ./scripts - -# Install dependencies (--ignore-scripts to skip husky and build:packages in prepare script) -RUN npm ci --ignore-scripts - -# Copy all source files -COPY libs ./libs -COPY apps/server ./apps/server - -# Build packages in dependency order, then build server -RUN npm run build:packages && npm run build --workspace=apps/server - -# Production stage -FROM node:20-alpine - -# Install git, curl, and GitHub CLI (pinned version for reproducible builds) -RUN apk add --no-cache git curl && \ - GH_VERSION="2.63.2" && \ - curl -L "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz" -o gh.tar.gz && \ - tar -xzf gh.tar.gz && \ - mv "gh_${GH_VERSION}_linux_amd64/bin/gh" /usr/local/bin/gh && \ - rm -rf gh.tar.gz "gh_${GH_VERSION}_linux_amd64" - -WORKDIR /app - -# Create non-root user -RUN addgroup -g 1001 -S automaker && \ - adduser -S automaker -u 1001 - -# Copy built files and production dependencies -COPY --from=builder /app/apps/server/dist ./dist -COPY --from=builder /app/apps/server/package*.json ./ -COPY --from=builder /app/node_modules ./node_modules - -# Create data directory -RUN mkdir -p /data && chown automaker:automaker /data - -# Switch to non-root user -USER automaker - -# Environment variables -ENV NODE_ENV=production -ENV PORT=3008 -ENV DATA_DIR=/data - -# Expose port -EXPOSE 3008 - -# Health check -HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:3008/api/health || exit 1 - -# Start server -CMD ["node", "dist/index.js"] diff --git a/apps/ui/Dockerfile b/apps/ui/Dockerfile deleted file mode 100644 index dbfd16d9..00000000 --- a/apps/ui/Dockerfile +++ /dev/null @@ -1,51 +0,0 @@ -# Automaker UI -# Multi-stage build for minimal production image - -# Build stage -FROM node:20-alpine AS builder - -# Install build dependencies -RUN apk add --no-cache python3 make g++ - -WORKDIR /app - -# Copy package files for all workspaces -COPY package*.json ./ -COPY apps/ui/package*.json ./apps/ui/ -COPY libs/types/package*.json ./libs/types/ -COPY libs/utils/package*.json ./libs/utils/ -COPY libs/prompts/package*.json ./libs/prompts/ -COPY libs/platform/package*.json ./libs/platform/ -COPY libs/model-resolver/package*.json ./libs/model-resolver/ -COPY libs/dependency-resolver/package*.json ./libs/dependency-resolver/ -COPY libs/git-utils/package*.json ./libs/git-utils/ -COPY scripts ./scripts - -# Install dependencies (--ignore-scripts to skip husky and build:packages in prepare script) -RUN npm ci --ignore-scripts - -# Copy all source files -COPY libs ./libs -COPY apps/ui ./apps/ui - -# Build packages in dependency order, then build UI -# VITE_SERVER_URL tells the UI where to find the API server -# Using localhost:3008 since both containers expose ports to the host -# Use ARG to allow overriding at build time: --build-arg VITE_SERVER_URL=http://api.example.com -ARG VITE_SERVER_URL=http://localhost:3008 -ENV VITE_SKIP_ELECTRON=true -ENV VITE_SERVER_URL=${VITE_SERVER_URL} -RUN npm run build:packages && npm run build --workspace=apps/ui - -# Production stage - serve with nginx -FROM nginx:alpine - -# Copy built files -COPY --from=builder /app/apps/ui/dist /usr/share/nginx/html - -# Copy nginx config for SPA routing -COPY apps/ui/nginx.conf /etc/nginx/conf.d/default.conf - -EXPOSE 80 - -CMD ["nginx", "-g", "daemon off;"] diff --git a/docker-compose.yml b/docker-compose.yml index 0cbc28c2..bdf5ff19 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,8 @@ services: ui: build: context: . - dockerfile: apps/ui/Dockerfile + dockerfile: Dockerfile + target: ui container_name: automaker-ui restart: unless-stopped ports: @@ -25,7 +26,8 @@ services: server: build: context: . - dockerfile: apps/server/Dockerfile + dockerfile: Dockerfile + target: server container_name: automaker-server restart: unless-stopped ports: From bad4393ddac450cb0757263d6bb7a83bdaddb44a Mon Sep 17 00:00:00 2001 From: Kacper Date: Sun, 28 Dec 2025 20:03:39 +0100 Subject: [PATCH 3/7] fix: improve gh auth detection to work with GH_TOKEN env var MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use gh api user to verify authentication instead of gh auth status, which can return non-zero even when GH_TOKEN is valid (due to stale config file entries). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../src/routes/setup/routes/gh-status.ts | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/apps/server/src/routes/setup/routes/gh-status.ts b/apps/server/src/routes/setup/routes/gh-status.ts index 4d36561c..1d1312fe 100644 --- a/apps/server/src/routes/setup/routes/gh-status.ts +++ b/apps/server/src/routes/setup/routes/gh-status.ts @@ -94,23 +94,30 @@ async function getGhStatus(): Promise { // Version command failed } - // Check authentication status + // Check authentication status by actually making an API call + // gh auth status can return non-zero even when GH_TOKEN is valid try { - const { stdout } = await execAsync('gh auth status', { env: execEnv }); - // If this succeeds without error, we're authenticated - status.authenticated = true; - - // Try to extract username from output - const userMatch = - stdout.match(/Logged in to [^\s]+ account ([^\s]+)/i) || - stdout.match(/Logged in to [^\s]+ as ([^\s]+)/i); - if (userMatch) { - status.user = userMatch[1]; + const { stdout } = await execAsync('gh api user --jq ".login"', { env: execEnv }); + const user = stdout.trim(); + if (user) { + status.authenticated = true; + status.user = user; } - } catch (error: unknown) { - // Auth status returns non-zero if not authenticated - const err = error as { stderr?: string }; - if (err.stderr?.includes('not logged in')) { + } catch { + // API call failed - try gh auth status as fallback + try { + const { stdout } = await execAsync('gh auth status', { env: execEnv }); + status.authenticated = true; + + // Try to extract username from output + const userMatch = + stdout.match(/Logged in to [^\s]+ account ([^\s]+)/i) || + stdout.match(/Logged in to [^\s]+ as ([^\s]+)/i); + if (userMatch) { + status.user = userMatch[1]; + } + } catch { + // Auth status returns non-zero if not authenticated status.authenticated = false; } } From 35b3d3931ec65186b7d414a06758e16fad8afc71 Mon Sep 17 00:00:00 2001 From: Kacper Date: Sun, 28 Dec 2025 20:26:11 +0100 Subject: [PATCH 4/7] fix: add bash for terminal support and ARM64 gh CLI support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Install bash in Alpine for terminal feature to work - Add dynamic architecture detection for GitHub CLI download (supports x86_64 and aarch64/arm64) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- Dockerfile | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1b6bb0a7..26bab190 100644 --- a/Dockerfile +++ b/Dockerfile @@ -53,13 +53,19 @@ RUN npm run build:packages && npm run build --workspace=apps/server # ============================================================================= FROM node:20-alpine AS server -# Install git, curl, and GitHub CLI (pinned version for reproducible builds) -RUN apk add --no-cache git curl && \ +# Install git, curl, bash (for terminal), and GitHub CLI (pinned version, multi-arch) +RUN apk add --no-cache git curl bash && \ GH_VERSION="2.63.2" && \ - curl -L "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz" -o gh.tar.gz && \ + ARCH=$(uname -m) && \ + case "$ARCH" in \ + x86_64) GH_ARCH="amd64" ;; \ + aarch64|arm64) GH_ARCH="arm64" ;; \ + *) echo "Unsupported architecture: $ARCH" && exit 1 ;; \ + esac && \ + curl -L "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_${GH_ARCH}.tar.gz" -o gh.tar.gz && \ tar -xzf gh.tar.gz && \ - mv "gh_${GH_VERSION}_linux_amd64/bin/gh" /usr/local/bin/gh && \ - rm -rf gh.tar.gz "gh_${GH_VERSION}_linux_amd64" + mv gh_${GH_VERSION}_linux_${GH_ARCH}/bin/gh /usr/local/bin/gh && \ + rm -rf gh.tar.gz gh_${GH_VERSION}_linux_${GH_ARCH} # Install Claude CLI globally RUN npm install -g @anthropic-ai/claude-code From 789b8075425a049c67ad9dedd1bd91df1832de84 Mon Sep 17 00:00:00 2001 From: Kacper Date: Sun, 28 Dec 2025 20:32:49 +0100 Subject: [PATCH 5/7] fix: configure git safe.directory for mounted volumes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use system-level gitconfig to set safe.directory='*' so it works with mounted volumes and isn't overwritten by user's mounted .gitconfig. Fixes git "dubious ownership" errors when working with projects mounted from the host. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Dockerfile b/Dockerfile index 26bab190..26cf0d6c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -92,6 +92,10 @@ COPY --from=server-builder /app/node_modules ./node_modules # Create data and projects directories RUN mkdir -p /data /projects && chown automaker:automaker /data /projects +# Configure git for mounted volumes (ownership mismatch between host/container) +# Use --system so it's not overwritten by mounted user .gitconfig +RUN git config --system --add safe.directory '*' + # Switch to non-root user USER automaker From a526869f212283c906c4e650a66699e0664b3390 Mon Sep 17 00:00:00 2001 From: Kacper Date: Sun, 28 Dec 2025 20:34:43 +0100 Subject: [PATCH 6/7] fix: configure git to use gh as credential helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add system-level git config to use `gh auth git-credential` for HTTPS authentication. This allows git push/pull to work automatically using the GH_TOKEN environment variable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- Dockerfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 26cf0d6c..a5f7a806 100644 --- a/Dockerfile +++ b/Dockerfile @@ -92,9 +92,11 @@ COPY --from=server-builder /app/node_modules ./node_modules # Create data and projects directories RUN mkdir -p /data /projects && chown automaker:automaker /data /projects -# Configure git for mounted volumes (ownership mismatch between host/container) +# Configure git for mounted volumes and authentication # Use --system so it's not overwritten by mounted user .gitconfig -RUN git config --system --add safe.directory '*' +RUN git config --system --add safe.directory '*' && \ + # Use gh as credential helper (works with GH_TOKEN env var) + git config --system credential.helper '!gh auth git-credential' # Switch to non-root user USER automaker From 0a21c11a3587588e1cdc5d95a5af190acbf9ecaf Mon Sep 17 00:00:00 2001 From: Kacper Date: Sun, 28 Dec 2025 20:53:35 +0100 Subject: [PATCH 7/7] chore: update Dockerfile to use Node.js 22 and improve health check - Upgraded base and server images in Dockerfile from Node.js 20 to 22-alpine for better performance and security. - Replaced wget with curl in the health check command for improved reliability. - Enhanced README with detailed Docker deployment instructions, including configuration for API key and Claude CLI authentication, and examples for working with projects and GitHub CLI authentication. This update ensures a more secure and efficient Docker setup for the application. --- Dockerfile | 8 +- README.md | 101 +++++++++++++++++- .../src/routes/setup/routes/gh-status.ts | 9 +- 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index a5f7a806..e7a0237a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ # ============================================================================= # BASE STAGE - Common setup for all builds (DRY: defined once, used by all) # ============================================================================= -FROM node:20-alpine AS base +FROM node:22-alpine AS base # Install build dependencies for native modules (node-pty) RUN apk add --no-cache python3 make g++ @@ -51,7 +51,7 @@ RUN npm run build:packages && npm run build --workspace=apps/server # ============================================================================= # SERVER PRODUCTION STAGE # ============================================================================= -FROM node:20-alpine AS server +FROM node:22-alpine AS server # Install git, curl, bash (for terminal), and GitHub CLI (pinned version, multi-arch) RUN apk add --no-cache git curl bash && \ @@ -109,9 +109,9 @@ ENV DATA_DIR=/data # Expose port EXPOSE 3008 -# Health check +# Health check (using curl since it's already installed, more reliable than busybox wget) HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:3008/api/health || exit 1 + CMD curl -f http://localhost:3008/api/health || exit 1 # Start server CMD ["node", "apps/server/dist/index.js"] diff --git a/README.md b/README.md index f9cabfd0..cd575d1b 100644 --- a/README.md +++ b/README.md @@ -223,14 +223,111 @@ npm run build:electron:linux # Linux (AppImage + DEB, x64) #### Docker Deployment +Docker provides the most secure way to run Automaker by isolating it from your host filesystem. + ```bash -# Build and run with Docker Compose (recommended for security) +# Build and run with Docker Compose docker-compose up -d -# Access at http://localhost:3007 +# Access UI at http://localhost:3007 # API at http://localhost:3008 + +# View logs +docker-compose logs -f + +# Stop containers +docker-compose down ``` +##### Configuration + +Create a `.env` file in the project root if using API key authentication: + +```bash +# Optional: Anthropic API key (not needed if using Claude CLI authentication) +ANTHROPIC_API_KEY=sk-ant-... +``` + +**Note:** Most users authenticate via Claude CLI instead of API keys. See [Claude CLI Authentication](#claude-cli-authentication-optional) below. + +##### Working with Projects (Host Directory Access) + +By default, the container is isolated from your host filesystem. To work on projects from your host machine, create a `docker-compose.override.yml` file (gitignored): + +```yaml +services: + server: + volumes: + # Mount your project directories + - /path/to/your/project:/projects/your-project +``` + +##### Claude CLI Authentication (Optional) + +To use Claude Code CLI authentication instead of an API key, mount your Claude CLI config directory: + +```yaml +services: + server: + volumes: + # Linux/macOS + - ~/.claude:/home/automaker/.claude + # Windows + - C:/Users/YourName/.claude:/home/automaker/.claude +``` + +**Note:** The Claude CLI config must be writable (do not use `:ro` flag) as the CLI writes debug files. + +##### GitHub CLI Authentication (For Git Push/PR Operations) + +To enable git push and GitHub CLI operations inside the container: + +```yaml +services: + server: + volumes: + # Mount GitHub CLI config + # Linux/macOS + - ~/.config/gh:/home/automaker/.config/gh + # Windows + - 'C:/Users/YourName/AppData/Roaming/GitHub CLI:/home/automaker/.config/gh' + + # Mount git config for user identity (name, email) + - ~/.gitconfig:/home/automaker/.gitconfig:ro + environment: + # GitHub token (required on Windows where tokens are in Credential Manager) + # Get your token with: gh auth token + - GH_TOKEN=${GH_TOKEN} +``` + +Then add `GH_TOKEN` to your `.env` file: + +```bash +GH_TOKEN=gho_your_github_token_here +``` + +##### Complete docker-compose.override.yml Example + +```yaml +services: + server: + volumes: + # Your projects + - /path/to/project1:/projects/project1 + - /path/to/project2:/projects/project2 + + # Authentication configs + - ~/.claude:/home/automaker/.claude + - ~/.config/gh:/home/automaker/.config/gh + - ~/.gitconfig:/home/automaker/.gitconfig:ro + environment: + - GH_TOKEN=${GH_TOKEN} +``` + +##### Architecture Support + +The Docker image supports both AMD64 and ARM64 architectures. The GitHub CLI and Claude CLI are automatically downloaded for the correct architecture during build. + ### Testing #### End-to-End Tests (Playwright) diff --git a/apps/server/src/routes/setup/routes/gh-status.ts b/apps/server/src/routes/setup/routes/gh-status.ts index 1d1312fe..e48b5c25 100644 --- a/apps/server/src/routes/setup/routes/gh-status.ts +++ b/apps/server/src/routes/setup/routes/gh-status.ts @@ -96,15 +96,22 @@ async function getGhStatus(): Promise { // Check authentication status by actually making an API call // gh auth status can return non-zero even when GH_TOKEN is valid + let apiCallSucceeded = false; try { const { stdout } = await execAsync('gh api user --jq ".login"', { env: execEnv }); const user = stdout.trim(); if (user) { status.authenticated = true; status.user = user; + apiCallSucceeded = true; } + // If stdout is empty, fall through to gh auth status fallback } catch { - // API call failed - try gh auth status as fallback + // API call failed - fall through to gh auth status fallback + } + + // Fallback: try gh auth status if API call didn't succeed + if (!apiCallSucceeded) { try { const { stdout } = await execAsync('gh auth status', { env: execEnv }); status.authenticated = true;