diff --git a/.env.example b/.env.example index 1646e8f..1b049f1 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ -OPENAI_API_KEY=sk \ No newline at end of file +OPENAI_API_KEY=sk +CHROME_PATH=/usr/bin/chromium \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15de53b..fa1cb0c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,8 @@ concurrency: env: PYTHON_VERSION: "3.13" + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} jobs: determine_changes: @@ -69,3 +71,47 @@ jobs: - name: "Validate project metadata" run: uvx --from 'validate-pyproject[all,store]' validate-pyproject pyproject.toml + + build-and-publish: + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + # attestations: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + id: push + uses: docker/build-push-action@v6 + with: + context: ./ + file: ./Dockerfile + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + VNC_PASSWORD=${{ secrets.VNC_PASSWORD }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3b49110 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,70 @@ +FROM ghcr.io/astral-sh/uv:bookworm-slim AS builder + +ARG VNC_PASSWORD=browser-use + +ENV UV_COMPILE_BYTECODE=1 \ + UV_LINK_MODE=copy \ + UV_PYTHON_INSTALL_DIR=/python \ + UV_PYTHON_PREFERENCE=only-managed + +# Install build dependencies and clean up in the same layer +RUN apt-get update -y && \ + apt-get install --no-install-recommends -y clang && \ + rm -rf /var/lib/apt/lists/* + +# Install Python before the project for caching +RUN uv python install 3.13 + +WORKDIR /app +RUN --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=uv.lock,target=uv.lock \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ + uv sync --frozen --no-install-project --no-dev +ADD . /app +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-dev + +FROM debian:bookworm-slim AS runtime + +# Install required packages including Chromium and clean up in the same layer +RUN apt-get update && \ + apt-get install --no-install-recommends -y \ + xfce4 \ + dbus-x11 \ + tigervnc-standalone-server \ + tigervnc-tools \ + nodejs \ + npm \ + chromium \ + chromium-driver \ + fonts-freefont-ttf \ + fonts-ipafont-gothic \ + fonts-wqy-zenhei \ + fonts-thai-tlwg \ + fonts-kacst \ + fonts-symbola \ + fonts-noto-color-emoji && \ + npm i -g proxy-login-automator && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* && \ + rm -rf /var/cache/apt/* + +# Copy only necessary files from builder +COPY --from=builder --chown=python:python /python /python +COPY --from=builder --chown=app:app /app /app + +ENV PATH="/app/.venv/bin:$PATH" \ + DISPLAY=:0 \ + CHROME_BIN=/usr/bin/chromium \ + CHROMIUM_FLAGS="--no-sandbox --headless --disable-gpu --disable-software-rasterizer --disable-dev-shm-usage" + +# Combine VNC setup commands to reduce layers +RUN mkdir -p ~/.vnc && \ + echo ${VNC_PASSWORD} | vncpasswd -f > /root/.vnc/passwd && \ + chmod 600 /root/.vnc/passwd && \ + printf '#!/bin/sh\nunset SESSION_MANAGER\nunset DBUS_SESSION_BUS_ADDRESS\nstartxfce4' > /root/.vnc/xstartup && \ + chmod +x /root/.vnc/xstartup && \ + printf '#!/bin/bash\nvncserver -depth 24 -geometry 1920x1080 -localhost no -PasswordFile /root/.vnc/passwd :0\nproxy-login-automator\npython /app/server --transport sse --port 8000' > /app/boot.sh && \ + chmod +x /app/boot.sh + +ENTRYPOINT ["/bin/bash", "/app/boot.sh"] diff --git a/server/server.py b/server/server.py index 43ca076..e411bdd 100644 --- a/server/server.py +++ b/server/server.py @@ -1,3 +1,4 @@ +import os import anyio import click import asyncio @@ -5,7 +6,7 @@ from datetime import datetime from langchain_openai import ChatOpenAI from browser_use import Agent -from browser_use.browser.browser import Browser +from browser_use.browser.browser import Browser, BrowserConfig import mcp.types as types from mcp.server.lowlevel import Server from dotenv import load_dotenv @@ -31,7 +32,18 @@ ) # Initialize browser and context -browser = Browser() +browser = Browser( + config=BrowserConfig( + chrome_instance_path=os.environ.get("CHROME_PATH"), + extra_chromium_args=[ + "--no-sandbox", + "--disable-gpu", + "--disable-software-rasterizer", + "--disable-dev-shm-usage", + "--remote-debugging-port=9222", + ], + ) +) context = BrowserContext(browser=browser, config=config) # Initialize LLM