From 2838cff67caa0654eb319156344adc8405755a65 Mon Sep 17 00:00:00 2001 From: Ken Date: Tue, 26 May 2026 18:41:33 +0000 Subject: [PATCH] feat: Dockerfile with all-in-one container --- Dockerfile | 68 ++++++++++++++++++ tests/test_dockerfile.sh | 149 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 Dockerfile create mode 100755 tests/test_dockerfile.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1824908 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,68 @@ +FROM python:3.13-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + xvfb \ + x11vnc \ + websockify \ + novnc \ + curl \ + libnss3 \ + libatk1.0-0 \ + libatk-bridge2.0-0 \ + libcups2 \ + libdrm2 \ + libxkbcommon0 \ + libxcomposite1 \ + libxdamage1 \ + libxrandr2 \ + libgbm1 \ + libpango-1.0-0 \ + libcairo2 \ + libasound2 \ + libxshmfence1 \ + libgtk-3-0 \ + fonts-liberation \ + && rm -rf /var/lib/apt/lists/* + +# Install Node.js 22.x from nodesource.com +RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get install -y --no-install-recommends nodejs \ + && rm -rf /var/lib/apt/lists/* + +# Install @anthropic-ai/playwright-cli globally via npm +RUN npm install -g @anthropic-ai/playwright-cli + +# Install uv via pip +RUN pip install uv + +# Copy backend dependency files and install Python dependencies +COPY backend/pyproject.toml backend/uv.lock /app/backend/ +WORKDIR /app/backend +RUN uv sync --frozen + +# Install Playwright chromium +RUN uv run playwright install chromium + +# Install amplifierd +RUN uv pip install amplifierd + +# Copy application files +COPY backend/*.py /app/backend/ +COPY bundle/ /app/bundle/ +COPY frontend/dist/ /app/frontend/dist/ +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +# Create artifacts directory +RUN mkdir -p /app/artifacts + +WORKDIR /app + +EXPOSE 8080 6080 + +ENV DISPLAY=:99 +ENV AMPLIFIERD_URL=http://localhost:8410 +ENV BUNDLE_NAME=research-workbench + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/tests/test_dockerfile.sh b/tests/test_dockerfile.sh new file mode 100755 index 0000000..51d9211 --- /dev/null +++ b/tests/test_dockerfile.sh @@ -0,0 +1,149 @@ +#!/bin/bash +# test_dockerfile.sh - Verify Dockerfile matches spec for task-7-dockerfile +# RED: Run before Dockerfile exists (should fail) +# GREEN: Run after Dockerfile is created (should pass) + +REPO=/home/ken/workspace/research-workbench +DOCKERFILE="$REPO/Dockerfile" +PASS=0 +FAIL=0 + +check_file() { + if [ -f "$1" ]; then + echo "PASS: file '$1' exists" + ((PASS++)) + else + echo "FAIL: file '$1' should exist but is missing" + ((FAIL++)) + fi +} + +check_contains() { + local pattern="$1" + local label="$2" + if grep -qF -- "$pattern" "$DOCKERFILE" 2>/dev/null; then + echo "PASS: Dockerfile contains $label" + ((PASS++)) + else + echo "FAIL: Dockerfile missing $label" + ((FAIL++)) + fi +} + +check_regex() { + local pattern="$1" + local label="$2" + if grep -qE "$pattern" "$DOCKERFILE" 2>/dev/null; then + echo "PASS: Dockerfile contains $label" + ((PASS++)) + else + echo "FAIL: Dockerfile missing $label" + ((FAIL++)) + fi +} + +echo "=== Checking Dockerfile exists ===" +check_file "$DOCKERFILE" + +echo "" +echo "=== Checking base image ===" +check_contains "FROM python:3.13-slim" "FROM python:3.13-slim" + +echo "" +echo "=== Checking system dependencies ===" +check_contains "xvfb" "xvfb" +check_contains "x11vnc" "x11vnc" +check_contains "websockify" "websockify" +check_contains "novnc" "novnc" +check_contains "curl" "curl" +check_contains "libnss3" "libnss3" +check_contains "libatk1.0-0" "libatk1.0-0" +check_contains "libatk-bridge2.0-0" "libatk-bridge2.0-0" +check_contains "libcups2" "libcups2" +check_contains "libdrm2" "libdrm2" +check_contains "libxkbcommon0" "libxkbcommon0" +check_contains "libxcomposite1" "libxcomposite1" +check_contains "libxdamage1" "libxdamage1" +check_contains "libxrandr2" "libxrandr2" +check_contains "libgbm1" "libgbm1" +check_contains "libpango-1.0-0" "libpango-1.0-0" +check_contains "libcairo2" "libcairo2" +check_contains "libasound2" "libasound2" +check_contains "libxshmfence1" "libxshmfence1" +check_contains "libgtk-3-0" "libgtk-3-0" +check_contains "fonts-liberation" "fonts-liberation" +check_contains "--no-install-recommends" "--no-install-recommends" +check_contains "rm -rf /var/lib/apt/lists/*" "rm /var/lib/apt/lists/*" + +echo "" +echo "=== Checking Node.js setup ===" +check_contains "nodesource" "Node.js nodesource setup" +check_regex "22\.x" "Node.js 22.x" +check_contains "nodejs" "nodejs package" + +echo "" +echo "=== Checking npm global packages ===" +check_contains "@anthropic-ai/playwright-cli" "@anthropic-ai/playwright-cli" +check_contains "npm install -g" "npm install -g" + +echo "" +echo "=== Checking uv installation ===" +check_contains "pip install uv" "uv via pip" + +echo "" +echo "=== Checking backend setup ===" +check_contains "COPY backend/pyproject.toml" "COPY backend/pyproject.toml" +check_contains "backend/uv.lock" "backend/uv.lock" +check_contains "/app/backend/" "destination /app/backend/" +check_contains "WORKDIR /app/backend" "WORKDIR /app/backend" +check_contains "uv sync --frozen" "uv sync --frozen" + +echo "" +echo "=== Checking Playwright chromium ===" +check_contains "uv run playwright install chromium" "playwright install chromium" + +echo "" +echo "=== Checking amplifierd ===" +check_contains "uv pip install amplifierd" "uv pip install amplifierd" + +echo "" +echo "=== Checking COPY instructions ===" +check_regex "COPY backend/\*\.py" "COPY backend/*.py" +check_contains "COPY bundle/" "COPY bundle/" +check_contains "/app/bundle" "destination /app/bundle" +check_contains "COPY frontend/dist/" "COPY frontend/dist/" +check_contains "/app/frontend/dist" "destination /app/frontend/dist" +check_contains "COPY entrypoint.sh" "COPY entrypoint.sh" +check_contains "/entrypoint.sh" "destination /entrypoint.sh" +check_contains "chmod +x /entrypoint.sh" "chmod +x entrypoint.sh" + +echo "" +echo "=== Checking /app/artifacts directory ===" +check_contains "/app/artifacts" "/app/artifacts" + +echo "" +echo "=== Checking WORKDIR /app ===" +check_regex "^WORKDIR /app$" "WORKDIR /app" + +echo "" +echo "=== Checking EXPOSE ports ===" +check_contains "EXPOSE 8080" "EXPOSE 8080" +check_contains "6080" "EXPOSE 6080" + +echo "" +echo "=== Checking ENV variables ===" +check_contains "DISPLAY=:99" "DISPLAY=:99" +check_contains "AMPLIFIERD_URL=http://localhost:8410" "AMPLIFIERD_URL" +check_contains "BUNDLE_NAME=research-workbench" "BUNDLE_NAME" + +echo "" +echo "=== Checking ENTRYPOINT ===" +check_contains "ENTRYPOINT" "ENTRYPOINT directive" +check_contains "/entrypoint.sh" "/entrypoint.sh in ENTRYPOINT" + +echo "" +echo "==============================" +echo "Results: $PASS passed, $FAIL failed" +echo "==============================" + +[ $FAIL -eq 0 ] && exit 0 || exit 1