From 63c5774cc57c734190bfcc0c28d2c6f5068bf66c Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:35:20 +0000 Subject: [PATCH 01/13] Add Docker and Docker Compose support for dev and prod - Added `Dockerfile.backend` for Python/FastAPI backend (with uv and deno). - Added `web/Dockerfile` for React/Vite frontend (multi-stage: dev, build, prod). - Added `web/nginx.conf` for production serving of frontend. - Added `docker-compose.yml` for immutable production deployment. - Added `docker-compose.dev.yml` for local development with hot-reloading. - Added `DOCKER.md` with instructions. - Updated `web/vite.config.ts` to support dynamic API URL via env var. - Updated `README.md` to link to Docker guide. Co-authored-by: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> --- DOCKER.md | 75 +++++ Dockerfile.backend | 38 +++ README.md | 2 + docker-compose.dev.yml | 22 ++ docker-compose.yml | 23 ++ web/Dockerfile | 25 ++ web/nginx.conf | 40 +++ web/package-lock.json | 664 +++++++++++++++++++++++++++++++++++++++++ web/vite.config.ts | 2 +- 9 files changed, 890 insertions(+), 1 deletion(-) create mode 100644 DOCKER.md create mode 100644 Dockerfile.backend create mode 100644 docker-compose.dev.yml create mode 100644 docker-compose.yml create mode 100644 web/Dockerfile create mode 100644 web/nginx.conf diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000..62ca516 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,75 @@ +# Docker Setup for AsyncReview + +This guide explains how to run AsyncReview using Docker, both for production (immutable usage) and local development. + +## Prerequisites + +- **Docker** and **Docker Compose** installed on your machine. +- A `.env` file with your API keys (see [README.md](README.md) or copy `.env.example`). + +## Quick Start (Production/User Mode) + +To run the application in a stable, immutable container environment: + +1. Ensure your `.env` file is populated. +2. Run: + +```bash +docker compose up --build +``` + +- The Web UI will be available at: http://localhost:3000 +- The Backend API will be available at: http://localhost:8000 + +To stop the application: +```bash +docker compose down +``` + +## Local Development + +To run the application in development mode with hot-reloading (changes to your code are immediately reflected): + +1. Run: + +```bash +docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build +``` + +- The Web UI (Vite dev server) will be available at: http://localhost:5173 +- The Backend API (Uvicorn with reload) will be available at: http://localhost:8000 + +*Note: In development mode, the `web/` directory and the root directory are mounted into the containers. Changes you make locally will trigger reloads.* + +## Running CLI Commands + +You can run the `cr` CLI tool inside the backend container. + +**In Production Mode:** +```bash +docker compose run --rm backend cr --help +docker compose run --rm backend cr review --url https://github.com/org/repo/pull/123 +``` + +**In Development Mode:** +```bash +docker compose -f docker-compose.yml -f docker-compose.dev.yml run --rm backend cr --help +``` + +## Troubleshooting + +### Port Conflicts +If port 3000, 8000, or 5173 are in use, you can modify the ports in `docker-compose.yml` or `docker-compose.dev.yml`. + +### Deno/Pyodide Issues +The backend container installs Deno and caches the Pyodide environment during the build. If you encounter issues, try rebuilding: +```bash +docker compose build --no-cache backend +``` + +### Dependencies +If you add new Python dependencies to `pyproject.toml`, you must rebuild the backend container: +```bash +docker compose build backend +``` +In development mode, since the source is mounted but dependencies are installed in the system python path, a rebuild is usually required to install new dependencies. diff --git a/Dockerfile.backend b/Dockerfile.backend new file mode 100644 index 0000000..1ecf053 --- /dev/null +++ b/Dockerfile.backend @@ -0,0 +1,38 @@ +# Base image +FROM python:3.11-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + curl \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +# Install uv +RUN curl -LsSf https://astral.sh/uv/install.sh | sh +ENV PATH="/root/.cargo/bin:$PATH" + +# Install Deno +ENV DENO_INSTALL="/root/.deno" +RUN curl -fsSL https://deno.land/install.sh | sh +ENV PATH="$DENO_INSTALL/bin:$PATH" + +# Set working directory +WORKDIR /app + +# Copy project files +COPY pyproject.toml README.md ./ +COPY cr/ ./cr/ +COPY cli/ ./cli/ + +# Install python dependencies +# We use --system to install into the container's system python environment +RUN uv pip install --system . + +# Cache Deno dependencies +RUN deno cache npm:pyodide/pyodide.js + +# Expose port +EXPOSE 8000 + +# Default command +CMD ["uvicorn", "cr.server:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/README.md b/README.md index e34a981..8e04749 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,8 @@ If you prefer manual configuration, point your agent to the skill definition fil To run the full backend server or web interface locally, please see the [Installation Guide](INSTALLATION.md). +To run using **Docker**, see the [Docker Guide](DOCKER.md). + ## License MIT diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..143d8d9 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,22 @@ +services: + backend: + volumes: + - ./:/app + # Use anonymous volumes to prevent host directories from overwriting container directories + - /app/.venv + - /app/.deno + command: uvicorn cr.server:app --reload --host 0.0.0.0 --port 8000 + environment: + - WATCHFILES_FORCE_POLLING=true + + frontend: + build: + target: dev + volumes: + - ./web:/app + - /app/node_modules + ports: + - "5173:5173" + environment: + - API_URL=http://backend:8000 + command: npm run dev -- --host diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d20d6a2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +services: + backend: + build: + context: . + dockerfile: Dockerfile.backend + ports: + - "8000:8000" + env_file: + - .env + environment: + - PYTHONUNBUFFERED=1 + restart: unless-stopped + + frontend: + build: + context: ./web + dockerfile: Dockerfile + target: prod + ports: + - "3000:80" + depends_on: + - backend + restart: unless-stopped diff --git a/web/Dockerfile b/web/Dockerfile new file mode 100644 index 0000000..a743189 --- /dev/null +++ b/web/Dockerfile @@ -0,0 +1,25 @@ +# Base stage +FROM node:20-alpine AS base +WORKDIR /app +COPY package*.json ./ +RUN npm ci + +# Development stage +FROM base AS dev +# Expose default Vite port +EXPOSE 5173 +# Command to run dev server, binding to all interfaces +CMD ["npm", "run", "dev", "--", "--host"] + +# Build stage +FROM base AS build +COPY . . +RUN npm run build + +# Production stage +FROM nginx:alpine AS prod +COPY --from=build /app/dist /usr/share/nginx/html +# We will copy a custom nginx config to handle SPA routing and API proxying +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/web/nginx.conf b/web/nginx.conf new file mode 100644 index 0000000..24eb313 --- /dev/null +++ b/web/nginx.conf @@ -0,0 +1,40 @@ +server { + listen 80; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + # Support SPA routing (redirect to index.html for non-existent files) + try_files $uri $uri/ /index.html; + } + + # Proxy API requests to the backend service + location /api/ { + proxy_pass http://backend:8000/api/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Support SSE (Server Sent Events) + proxy_buffering off; + proxy_cache off; + proxy_set_header Connection ''; + proxy_http_version 1.1; + chunked_transfer_encoding off; + } + + # Health check + location /health { + proxy_pass http://backend:8000/health; + } + + # Docs + location /docs { + proxy_pass http://backend:8000/docs; + } + location /openapi.json { + proxy_pass http://backend:8000/openapi.json; + } +} diff --git a/web/package-lock.json b/web/package-lock.json index 0209729..36df989 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -456,6 +456,70 @@ "node": ">=18" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/darwin-arm64": { "version": "0.21.5", "cpu": [ @@ -470,6 +534,294 @@ "node": ">=12" } }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "license": "MIT", @@ -526,6 +878,32 @@ "version": "1.0.0-beta.27", "license": "MIT" }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.56.0.tgz", + "integrity": "sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.56.0.tgz", + "integrity": "sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.56.0", "cpu": [ @@ -537,6 +915,292 @@ "darwin" ] }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.56.0.tgz", + "integrity": "sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.56.0.tgz", + "integrity": "sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.56.0.tgz", + "integrity": "sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.56.0.tgz", + "integrity": "sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.56.0.tgz", + "integrity": "sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.56.0.tgz", + "integrity": "sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.56.0.tgz", + "integrity": "sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.56.0.tgz", + "integrity": "sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.56.0.tgz", + "integrity": "sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.56.0.tgz", + "integrity": "sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.56.0.tgz", + "integrity": "sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.56.0.tgz", + "integrity": "sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.56.0.tgz", + "integrity": "sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.56.0.tgz", + "integrity": "sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.56.0.tgz", + "integrity": "sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.56.0.tgz", + "integrity": "sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.56.0.tgz", + "integrity": "sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.56.0.tgz", + "integrity": "sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.56.0.tgz", + "integrity": "sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.56.0.tgz", + "integrity": "sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.56.0.tgz", + "integrity": "sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.56.0.tgz", + "integrity": "sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@shikijs/core": { "version": "3.21.0", "license": "MIT", diff --git a/web/vite.config.ts b/web/vite.config.ts index dc0602a..eba9623 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -7,7 +7,7 @@ export default defineConfig({ port: 3000, proxy: { '/api': { - target: 'http://localhost:8000', + target: process.env.API_URL || 'http://localhost:8000', changeOrigin: true, }, }, From 0d71ba188c21232060a3c72f3b9b492c902932d5 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:44:42 +0000 Subject: [PATCH 02/13] Add Docker and Docker Compose support for dev and prod - Added `Dockerfile.backend` for Python/FastAPI backend (with uv and deno). - Added `web/Dockerfile` for React/Vite frontend (multi-stage: dev, build, prod). - Added `web/nginx.conf` for production serving of frontend. - Added `docker-compose.yml` for immutable production deployment (Frontend on port 3000 proxies to Backend). - Added `docker-compose.dev.yml` for local development (Hot-reload, ports 3000 and 8000 exposed). - Updated `Makefile` with `docker-up` and `docker-dev` convenience targets. - Added `DOCKER.md` with detailed instructions. - Updated `web/vite.config.ts` to support dynamic API URL via env var. - Updated `README.md` to link to Docker guide. Co-authored-by: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> --- DOCKER.md | 28 ++++++++++++++++++++-------- Makefile | 25 +++++++++++++++++++++++-- docker-compose.dev.yml | 2 ++ docker-compose.yml | 2 -- 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/DOCKER.md b/DOCKER.md index 62ca516..8be3216 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -7,19 +7,28 @@ This guide explains how to run AsyncReview using Docker, both for production (im - **Docker** and **Docker Compose** installed on your machine. - A `.env` file with your API keys (see [README.md](README.md) or copy `.env.example`). +**Note:** You *must* create a `.env` file before running any Docker commands. +```bash +cp .env.example .env +# Edit .env and add your GEMINI_API_KEY +``` + ## Quick Start (Production/User Mode) -To run the application in a stable, immutable container environment: +To run the application in a stable, immutable container environment using the convenience Makefile command: -1. Ensure your `.env` file is populated. -2. Run: +```bash +make docker-up +``` +Alternatively, using Docker Compose directly: ```bash docker compose up --build ``` -- The Web UI will be available at: http://localhost:3000 -- The Backend API will be available at: http://localhost:8000 +- **Web UI:** http://localhost:3000 +- **API:** Proxied via the Web UI at http://localhost:3000/api + - Note: The backend service is not directly exposed to the host in production mode. To stop the application: ```bash @@ -30,14 +39,17 @@ docker compose down To run the application in development mode with hot-reloading (changes to your code are immediately reflected): -1. Run: +```bash +make docker-dev +``` +Alternatively, using Docker Compose directly (requires both files): ```bash docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build ``` -- The Web UI (Vite dev server) will be available at: http://localhost:5173 -- The Backend API (Uvicorn with reload) will be available at: http://localhost:8000 +- **Web UI:** http://localhost:5173 (Vite dev server) +- **API:** http://localhost:8000 (Direct access for debugging) *Note: In development mode, the `web/` directory and the root directory are mounted into the containers. Changes you make locally will trigger reloads.* diff --git a/Makefile b/Makefile index bb1ec52..684e536 100644 --- a/Makefile +++ b/Makefile @@ -8,12 +8,16 @@ # make all - Build, install, and test # make clean - Clean build artifacts # +# Docker Usage: +# make docker-up - Run production environment (User mode) +# make docker-dev - Run development environment (Hot-reload) +# # Version Management: # make bump-patch - Bump patch version (0.5.0 -> 0.5.1) # make bump-minor - Bump minor version (0.5.0 -> 0.6.0) # make bump-major - Bump major version (0.5.0 -> 1.0.0) -.PHONY: all build install test publish clean bump-patch bump-minor bump-major version +.PHONY: all build install test publish clean bump-patch bump-minor bump-major version docker-up docker-dev # Version file - single source of truth VERSION_FILE := VERSION @@ -75,6 +79,20 @@ clean: @rm -rf .runtime_stage @echo "Done." +# ============================================================ +# Docker Helpers +# ============================================================ + +docker-up: + @if [ ! -f .env ]; then echo "Error: .env file not found. Copy .env.example to .env and fill in API keys."; exit 1; fi + @echo "==> Starting AsyncReview in Production Mode (Immutable)..." + @docker compose up --build + +docker-dev: + @if [ ! -f .env ]; then echo "Error: .env file not found. Copy .env.example to .env and fill in API keys."; exit 1; fi + @echo "==> Starting AsyncReview in Development Mode (Hot-Reload)..." + @docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build + # ============================================================ # Version Management # ============================================================ @@ -171,6 +189,10 @@ publish-npm: $(VERSION_FILE) help: @echo "AsyncReview Runtime Build System" @echo "" + @echo "Docker Commands (Preferred):" + @echo " make docker-up - Run production environment (User mode)" + @echo " make docker-dev - Run development environment (Hot-reload)" + @echo "" @echo "Build Commands:" @echo " make build - Build runtime v$(VERSION) for $(PLATFORM)" @echo " make install - Install built runtime locally" @@ -197,4 +219,3 @@ help: @echo "Dev Commands:" @echo " make build-npx - Build TypeScript only" @echo " make dev URL=... Q=... - Run dev mode" - diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 143d8d9..4de34f7 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -6,6 +6,8 @@ services: - /app/.venv - /app/.deno command: uvicorn cr.server:app --reload --host 0.0.0.0 --port 8000 + ports: + - "8000:8000" environment: - WATCHFILES_FORCE_POLLING=true diff --git a/docker-compose.yml b/docker-compose.yml index d20d6a2..6b20cf0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,8 +3,6 @@ services: build: context: . dockerfile: Dockerfile.backend - ports: - - "8000:8000" env_file: - .env environment: From 45e225a6cb7293543671206e3d76b2f598d2fd4d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:48:28 +0000 Subject: [PATCH 03/13] Add Docker and Docker Compose support for dev and prod - Added `Dockerfile.backend` for Python/FastAPI backend (with uv and deno). - Added `web/Dockerfile` for React/Vite frontend (multi-stage: dev, build, prod). - Added `web/nginx.conf` for production serving of frontend. - Added `docker-compose.yml` for immutable production deployment (Frontend on port 3000 proxies to Backend). - Added `docker-compose.dev.yml` for local development (Hot-reload, ports 3000 and 8000 exposed). - Updated `Makefile` with `docker-up` and `docker-dev` convenience targets. - Added `DOCKER.md` with detailed instructions. - Updated `web/vite.config.ts` to support dynamic API URL via env var. - Updated `README.md` to link to Docker guide. - Fixed TypeScript errors in `web/src/components/PRSummary.tsx` and `web/src/types.ts`. Co-authored-by: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> --- web/src/components/PRSummary.tsx | 2 +- web/src/types.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/components/PRSummary.tsx b/web/src/components/PRSummary.tsx index 927929b..df20969 100644 --- a/web/src/components/PRSummary.tsx +++ b/web/src/components/PRSummary.tsx @@ -104,7 +104,7 @@ export function PRSummary({ prInfo }: PRSummaryProps) { {activeTab === 'commits' && (
{prInfo.commitsList && prInfo.commitsList.length > 0 ? ( - prInfo.commitsList.map((commit, index) => ( + prInfo.commitsList.map((commit) => (
{/* Line connecting items, except last one */}
diff --git a/web/src/types.ts b/web/src/types.ts index 3b18df0..ded9057 100644 --- a/web/src/types.ts +++ b/web/src/types.ts @@ -24,6 +24,7 @@ export interface PRInfo { commits?: number additions?: number deletions?: number + changedFiles?: number commitsList?: Array<{ sha: string message: string From 529cc78e5aeeb1aa26f0fa0d9fd8752331e3fdbc Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:50:41 +0000 Subject: [PATCH 04/13] Fix uv installation in Dockerfile.backend Co-authored-by: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> --- Dockerfile.backend | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.backend b/Dockerfile.backend index 1ecf053..c5e1d65 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -8,8 +8,8 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* # Install uv -RUN curl -LsSf https://astral.sh/uv/install.sh | sh -ENV PATH="/root/.cargo/bin:$PATH" +# explicit path to ensure it's available +COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv # Install Deno ENV DENO_INSTALL="/root/.deno" From 33c29d8823d1d0b1ff5e5df0df23f4b9ae58bb19 Mon Sep 17 00:00:00 2001 From: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:07:52 +0000 Subject: [PATCH 05/13] Add Docker and Docker Compose support for dev and prod Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> From c8329287e5ccaaaa57991b784da426d5a3a7818b Mon Sep 17 00:00:00 2001 From: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> Date: Fri, 6 Feb 2026 07:38:48 +0000 Subject: [PATCH 06/13] Pin uv version in Dockerfile.backend Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- Dockerfile.backend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.backend b/Dockerfile.backend index c5e1d65..9b6c771 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -y \ # Install uv # explicit path to ensure it's available -COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv +COPY --from=ghcr.io/astral-sh/uv:0.5.29 /uv /bin/uv # Install Deno ENV DENO_INSTALL="/root/.deno" From 46ed5c399e3874e08e03f58b78f6c95f943fe335 Mon Sep 17 00:00:00 2001 From: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> Date: Fri, 6 Feb 2026 07:39:39 +0000 Subject: [PATCH 07/13] Pin Deno version in Dockerfile.backend Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- Dockerfile.backend | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile.backend b/Dockerfile.backend index 9b6c771..31e56fa 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -13,6 +13,7 @@ COPY --from=ghcr.io/astral-sh/uv:0.5.29 /uv /bin/uv # Install Deno ENV DENO_INSTALL="/root/.deno" +ENV DENO_VERSION="v1.40.3" RUN curl -fsSL https://deno.land/install.sh | sh ENV PATH="$DENO_INSTALL/bin:$PATH" From 172e619804adc01dbcfcadbff82fca11b614e006 Mon Sep 17 00:00:00 2001 From: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> Date: Fri, 6 Feb 2026 08:22:10 +0000 Subject: [PATCH 08/13] Optimize Docker build caching Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- Dockerfile.backend | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Dockerfile.backend b/Dockerfile.backend index 31e56fa..b9510a9 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -20,14 +20,21 @@ ENV PATH="$DENO_INSTALL/bin:$PATH" # Set working directory WORKDIR /app -# Copy project files +# Copy dependency definition only to leverage cache COPY pyproject.toml README.md ./ + +# Compile and install dependencies +# We generate requirements.txt from pyproject.toml and install it. +# This step will be cached unless pyproject.toml changes. +RUN uv pip compile pyproject.toml -o requirements.txt && \ + uv pip install --system -r requirements.txt + +# Copy source code COPY cr/ ./cr/ COPY cli/ ./cli/ -# Install python dependencies -# We use --system to install into the container's system python environment -RUN uv pip install --system . +# Install the project itself (without reinstalling dependencies) +RUN uv pip install --system --no-deps . # Cache Deno dependencies RUN deno cache npm:pyodide/pyodide.js From 888488cf03f3831ba7c0288acd3ef4c079c02349 Mon Sep 17 00:00:00 2001 From: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> Date: Fri, 6 Feb 2026 08:29:07 +0000 Subject: [PATCH 09/13] Enforce non-root execution in Docker containers Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- Dockerfile.backend | 30 +++++++++++++++++++++++------- docker-compose.yml | 2 +- web/Dockerfile | 28 ++++++++++++++++++++++++++-- web/nginx.conf | 2 +- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/Dockerfile.backend b/Dockerfile.backend index b9510a9..145a66c 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -7,15 +7,18 @@ RUN apt-get update && apt-get install -y \ unzip \ && rm -rf /var/lib/apt/lists/* +# Create a non-root user +RUN groupadd -r appuser && useradd -r -g appuser appuser + # Install uv -# explicit path to ensure it's available COPY --from=ghcr.io/astral-sh/uv:0.5.29 /uv /bin/uv # Install Deno -ENV DENO_INSTALL="/root/.deno" +# We install Deno to a global location or user's home +ENV DENO_INSTALL="/usr/local" ENV DENO_VERSION="v1.40.3" -RUN curl -fsSL https://deno.land/install.sh | sh -ENV PATH="$DENO_INSTALL/bin:$PATH" +# Install deno manually to /usr/local/bin so it's accessible to all users +RUN curl -fsSL https://deno.land/x/install/install.sh | sh -s ${DENO_VERSION} # Set working directory WORKDIR /app @@ -24,8 +27,8 @@ WORKDIR /app COPY pyproject.toml README.md ./ # Compile and install dependencies -# We generate requirements.txt from pyproject.toml and install it. -# This step will be cached unless pyproject.toml changes. +# We install to system python which is fine in docker, but we need to ensure permissions if we were to install more later. +# However, system install requires root. We do this BEFORE switching user. RUN uv pip compile pyproject.toml -o requirements.txt && \ uv pip install --system -r requirements.txt @@ -33,10 +36,23 @@ RUN uv pip compile pyproject.toml -o requirements.txt && \ COPY cr/ ./cr/ COPY cli/ ./cli/ -# Install the project itself (without reinstalling dependencies) +# Install the project itself RUN uv pip install --system --no-deps . # Cache Deno dependencies +# We need to run this as the user who will run the app, OR ensure the cache is accessible. +# If we run as appuser, Deno will cache to $HOME/.cache/deno +# So we should switch user first. + +# Change ownership of the app directory +RUN chown -R appuser:appuser /app + +# Switch to non-root user +USER appuser + +# Cache Deno dependencies (will be stored in /home/appuser/.cache/deno or similar) +# We need to ensure appuser has a home directory or set DENO_DIR +ENV DENO_DIR="/app/.deno_cache" RUN deno cache npm:pyodide/pyodide.js # Expose port diff --git a/docker-compose.yml b/docker-compose.yml index 6b20cf0..3bc94e0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,7 @@ services: dockerfile: Dockerfile target: prod ports: - - "3000:80" + - "3000:8080" depends_on: - backend restart: unless-stopped diff --git a/web/Dockerfile b/web/Dockerfile index a743189..76f5a10 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -18,8 +18,32 @@ RUN npm run build # Production stage FROM nginx:alpine AS prod + +# Copy build artifacts COPY --from=build /app/dist /usr/share/nginx/html -# We will copy a custom nginx config to handle SPA routing and API proxying + +# Copy custom nginx config COPY nginx.conf /etc/nginx/conf.d/default.conf -EXPOSE 80 + +# Configure permissions for non-root user +# Nginx alpine image uses 'nginx' user (uid 101) +# We need to ensure it can write to cache/pid directories if they are used, +# though with standard config it might write to /var/run which is root owned. +# More commonly we just need to ensure it can read the static files (which it can) +# and bind to the port (which is now 8080). + +# Update nginx.conf to use /tmp for pid/cache if strictly necessary, +# but usually just changing the port is enough for the main process +# if we don't need to write to system paths. +# However, standard nginx image tries to write to /var/cache/nginx and /var/run/nginx.pid +RUN chown -R nginx:nginx /var/cache/nginx && \ + chown -R nginx:nginx /var/log/nginx && \ + chown -R nginx:nginx /etc/nginx/conf.d && \ + touch /var/run/nginx.pid && \ + chown -R nginx:nginx /var/run/nginx.pid + +# Switch to non-root user +USER nginx + +EXPOSE 8080 CMD ["nginx", "-g", "daemon off;"] diff --git a/web/nginx.conf b/web/nginx.conf index 24eb313..fe238d3 100644 --- a/web/nginx.conf +++ b/web/nginx.conf @@ -1,5 +1,5 @@ server { - listen 80; + listen 8080; server_name localhost; location / { From 50fe29d24a453fab428e06f7147d6b469c98880b Mon Sep 17 00:00:00 2001 From: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> Date: Fri, 6 Feb 2026 08:33:55 +0000 Subject: [PATCH 10/13] Fix hot-reloading permissions in docker-compose.dev.yml Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- docker-compose.dev.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 4de34f7..4fe4c96 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,5 +1,7 @@ services: backend: + # Run as root in development to avoid permission issues with bind mounts + user: root volumes: - ./:/app # Use anonymous volumes to prevent host directories from overwriting container directories @@ -12,6 +14,8 @@ services: - WATCHFILES_FORCE_POLLING=true frontend: + # Run as root in development to ensure node_modules write access and hot reloading + user: root build: target: dev volumes: From 87854c11d4e672f7c55863a20d4955afadc88f0a Mon Sep 17 00:00:00 2001 From: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> Date: Fri, 6 Feb 2026 08:36:40 +0000 Subject: [PATCH 11/13] Pin base image in Dockerfile.backend Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- Dockerfile.backend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.backend b/Dockerfile.backend index 145a66c..b217227 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -1,5 +1,5 @@ # Base image -FROM python:3.11-slim +FROM python:3.11.8-slim-bookworm # Install system dependencies RUN apt-get update && apt-get install -y \ From a63f886d48a800842f1bb0ffa9eb0a82bb7d7007 Mon Sep 17 00:00:00 2001 From: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> Date: Fri, 6 Feb 2026 08:43:30 +0000 Subject: [PATCH 12/13] Harden Nginx security configuration Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- web/nginx.conf | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/web/nginx.conf b/web/nginx.conf index fe238d3..561a21c 100644 --- a/web/nginx.conf +++ b/web/nginx.conf @@ -2,6 +2,16 @@ server { listen 8080; server_name localhost; + # Security Headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + # Body Size Limit + client_max_body_size 10M; + location / { root /usr/share/nginx/html; index index.html index.htm; From f848bbe678a95c1b1a6ab264a9db781d7abc4e16 Mon Sep 17 00:00:00 2001 From: ngoyal16 <10145666+ngoyal16@users.noreply.github.com> Date: Sun, 8 Feb 2026 05:25:26 +0000 Subject: [PATCH 13/13] Add .dockerignore files Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .dockerignore | 32 ++++++++++++++++++++++++++++++++ web/.dockerignore | 13 +++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 .dockerignore create mode 100644 web/.dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e84ebf1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,32 @@ +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +env/ +venv/ +.venv/ +pip-log.txt +pip-delete-this-directory.txt +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.log +.git +.mypy_cache +.pytest_cache +.hypothes +.DS_Store +.env +.env.* +!.env.example + +# Web artifacts (not needed for backend build context, though we use specific COPY) +web/node_modules/ +web/dist/ +web/.next/ +web/coverage/ diff --git a/web/.dockerignore b/web/.dockerignore new file mode 100644 index 0000000..cabe303 --- /dev/null +++ b/web/.dockerignore @@ -0,0 +1,13 @@ +node_modules +dist +.env +.env.* +!.env.example +.git +.DS_Store +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.idea +.vscode +coverage