Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 1 addition & 50 deletions environments/browser/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,57 +1,8 @@
# syntax=docker/dockerfile:1
FROM ubuntu:24.04 AS setup
FROM hudevals/hud-browser-base:latest AS setup

# Update and install core dependencies (including working Chromium browser)
RUN apt-get update -y \
&& apt-get install -y --no-install-recommends \
vim \
openssl \
ca-certificates \
curl \
wget \
sudo \
bash \
net-tools \
novnc \
x11vnc \
xvfb \
xfce4 \
locales \
libpq5 \
sqlite3 \
dbus-x11 \
xfce4-terminal \
xfonts-base \
xdotool \
psmisc \
scrot \
pm-utils \
build-essential \
unzip \
xauth \
gnupg \
gpg \
jq \
git \
build-essential \
nodejs \
npm

RUN update-ca-certificates

RUN curl -LsSf https://astral.sh/uv/install.sh | sh
ENV PATH="/root/.local/bin:$PATH"

# Set working directory
WORKDIR /app

# Install git for dependency installation
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*

# Install Playwright
RUN uv pip install --system --break-system-packages playwright
RUN python3 -m playwright install chromium --with-deps

# Layer 1: Install server dependencies
COPY server/pyproject.toml /app/server/
RUN cd /app/server && uv pip install --system --break-system-packages .
Expand Down
72 changes: 72 additions & 0 deletions environments/browser/Dockerfile.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# syntax=docker/dockerfile:1
# Local development Dockerfile that uses local hud-python
FROM hudevals/hud-browser-base:latest AS setup

WORKDIR /app

# Layer 0: Install local hud-python
# Copy local hud-python source (build context is repo root)
COPY hud /app/hud-python/hud/
COPY pyproject.toml /app/hud-python/
COPY README.md /app/hud-python/
COPY LICENSE /app/hud-python/

# Install local hud-python
RUN cd /app/hud-python && uv pip install --system --break-system-packages -e .

# Layer 1: Install server dependencies
COPY environments/browser/server/pyproject.toml /app/server/
RUN cd /app/server && uv pip install --system --break-system-packages .

# Layer 2: Install environment dependencies
COPY environments/browser/environment/pyproject.toml /app/environment/
RUN cd /app/environment && uv pip install --system --break-system-packages .

# Layer 3: Copy source code (changes here don't invalidate dependency layers)
COPY environments/browser/server/ /app/server/
COPY environments/browser/environment/ /app/environment/

# Auto-discover and install/build all frontend apps
RUN set -e; \
for pkg in $(find /app/environment -type f -path '*/frontend/package.json'); do \
app_dir=$(dirname "$pkg"); \
echo "Installing dependencies in $app_dir"; \
if [ -f "$app_dir/package-lock.json" ]; then \
(cd "$app_dir" && npm ci --no-audit --no-fund); \
else \
(cd "$app_dir" && npm install --no-audit --no-fund); \
fi; \
done && \
for pkg in $(find /app/environment -type f -path '*/frontend/package.json'); do \
app_dir=$(dirname "$pkg"); \
if [ -f "$app_dir/next.config.js" ]; then \
echo "Building Next.js app in $app_dir"; \
(cd "$app_dir" && npm run build); \
fi; \
done

# Make scripts executable
RUN find /app/environment -name "*.py" -type f -exec chmod +x {} \;

# Environment configuration
ENV MCP_TRANSPORT="stdio"
ENV HUD_LOG_STREAM="stderr"
ENV PYTHONUNBUFFERED="1"
ENV PYTHONWARNINGS="ignore::SyntaxWarning:pyautogui"
ENV DISPLAY=":1"
ENV PYTHONPATH=/app

# Expose ports
EXPOSE 8000 8080 3000-3200 5000-5200

# Simple startup: HUD_DEV=1 enables hot-reload; otherwise run production
CMD ["sh", "-c", "\
if [ \"${HUD_DEV:-0}\" = \"1\" ]; then \
uvicorn environment.server:app --host 0.0.0.0 --port 8000 --reload --log-level warning >&2 & \
sleep 5 && cd /app/server && exec hud dev server.main --stdio; \
else \
uvicorn environment.server:app --host 0.0.0.0 --port 8000 --log-level warning >&2 & \
sleep 5 && cd /app/server && exec python3 -m server.main; \
fi\
"]

50 changes: 50 additions & 0 deletions environments/browser/browser-base/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# syntax=docker/dockerfile:1
FROM ubuntu:24.04 AS setup

# Update and install core dependencies (including working Chromium browser)
RUN apt-get update -y \
&& apt-get install -y --no-install-recommends \
vim \
openssl \
ca-certificates \
curl \
wget \
sudo \
bash \
net-tools \
novnc \
x11vnc \
xvfb \
xfce4 \
locales \
libpq5 \
sqlite3 \
dbus-x11 \
xfce4-terminal \
xfonts-base \
xdotool \
psmisc \
scrot \
pm-utils \
build-essential \
unzip \
xauth \
gnupg \
gpg \
jq \
git \
build-essential \
nodejs \
npm

RUN update-ca-certificates

RUN curl -LsSf https://astral.sh/uv/install.sh | sh
ENV PATH="/root/.local/bin:$PATH"

# Install git for dependency installation
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*

# Install Playwright
RUN uv pip install --system --break-system-packages playwright
RUN python3 -m playwright install chromium --with-deps
58 changes: 58 additions & 0 deletions environments/browser/browser-base/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Browser Base Image

Base Docker image for browser environments with Playwright, Chromium, and VNC support.

## Build

```bash
docker build -t browser-base:latest .
```

## Test with VNC Access

### 1. Start the container

```bash
docker run -it --rm \
-p 6080:6080 \
-p 5900:5900 \
-e DISPLAY=:1 \
browser-base:latest \
bash
```

### 2. Inside the container, start display servers

```bash
Xvfb :1 -screen 0 1920x1080x24 > /dev/null 2>&1 &
x11vnc -display :1 -nopw -listen 0.0.0.0 -forever > /dev/null 2>&1 &
/usr/share/novnc/utils/novnc_proxy --vnc localhost:5900 --listen 6080 > /dev/null 2>&1 &
```

### 3. Test Playwright

```bash
python3 -c "
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto('https://example.com')
print('Title:', page.title())
input('Press Enter to close...')
browser.close()
"
```

### 4. View in browser

Open `http://localhost:6080/vnc.html` to see Chromium running.

## What's Included

- Ubuntu 24.04
- Desktop environment (Xvfb, x11vnc, noVNC, xfce4)
- Node.js & npm
- Python 3 with uv package manager
- Playwright with Chromium
- Development tools (git, curl, wget, etc.)
2 changes: 1 addition & 1 deletion environments/browser/server/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async def launch_app(app_name: str) -> str:

# Automatically navigate to the app after launching
try:
await playwright(action="navigate", url=app_url)
await playwright(action="navigate", url=app_url, wait_for_load_state="networkidle")
# Give the page a moment to fully load
await asyncio.sleep(1)
return f"Launched {app_name} at {app_url} and navigated to it"
Expand Down
9 changes: 8 additions & 1 deletion hud/tools/playwright.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,14 @@ async def _ensure_browser(self) -> None:
if self._browser_context is None:
raise RuntimeError("Browser context failed to initialize")

self.page = await self._browser_context.new_page()
# Reuse existing page if available (for CDP connections), otherwise create new one
pages = self._browser_context.pages
if pages:
self.page = pages[0]
logger.info("Reusing existing browser page")
else:
self.page = await self._browser_context.new_page()
logger.info("Created new browser page")
logger.info("Playwright browser launched successfully")

async def navigate(
Expand Down
97 changes: 97 additions & 0 deletions scripts/build-and-push-env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/bin/bash

# Multi-architecture Docker build and push script for hud environments
# Usage: ./build-and-push-env.sh <environment|directory> <version>
# Example: ./build-and-push-env.sh browser 0.1.8
# ./build-and-push-env.sh environments/browser 0.1.8
# ./build-and-push-env.sh /full/path/to/env 0.1.8

set -e

ENV_INPUT=$1
VERSION=$2

if [ -z "$ENV_INPUT" ] || [ -z "$VERSION" ]; then
echo "Error: Missing required arguments"
echo "Usage: $0 <environment|directory> <version>"
echo "Example: $0 browser 0.1.8"
echo " $0 environments/browser 0.1.8"
exit 1
fi

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

# Check if input is a directory path or just a name
if [[ "$ENV_INPUT" == *"/"* ]] || [ -d "$ENV_INPUT" ] || [ -d "$PROJECT_ROOT/$ENV_INPUT" ]; then
# It's a directory path
if [ -d "$ENV_INPUT" ]; then
ENV_PATH="$ENV_INPUT"
else
ENV_PATH="$PROJECT_ROOT/$ENV_INPUT"
fi
ENVIRONMENT=$(basename "$ENV_PATH")
else
# It's just an environment name
ENVIRONMENT="$ENV_INPUT"
ENV_PATH="$PROJECT_ROOT/environments/${ENVIRONMENT}"
fi

IMAGE_NAME="hudevals/hud-${ENVIRONMENT}"

echo "Building and pushing hud-${ENVIRONMENT} version ${VERSION}"
echo "Environment path: $ENV_PATH"

if [ ! -d "$ENV_PATH" ]; then
echo "Error: Environment directory not found: $ENV_PATH"
exit 1
fi

if [ ! -f "$ENV_PATH/Dockerfile" ]; then
echo "Error: Dockerfile not found in $ENV_PATH"
exit 1
fi

cd "$ENV_PATH"
echo "Working directory: $(pwd)"

echo "Building ARM64 image..."
docker build --platform linux/arm64 -t $IMAGE_NAME:$VERSION-arm64 .

echo "Building AMD64 image..."
docker build --platform linux/amd64 -t $IMAGE_NAME:$VERSION-amd64 .

echo "Tagging images as latest..."
docker tag $IMAGE_NAME:$VERSION-arm64 $IMAGE_NAME:latest-arm64
docker tag $IMAGE_NAME:$VERSION-amd64 $IMAGE_NAME:latest-amd64

echo "Pushing ARM64 image..."
docker push $IMAGE_NAME:$VERSION-arm64
docker push $IMAGE_NAME:latest-arm64

echo "Pushing AMD64 image..."
docker push $IMAGE_NAME:$VERSION-amd64
docker push $IMAGE_NAME:latest-amd64

echo "Creating and pushing multi-arch manifest..."
docker manifest create $IMAGE_NAME:$VERSION \
--amend $IMAGE_NAME:$VERSION-arm64 \
--amend $IMAGE_NAME:$VERSION-amd64

docker manifest create $IMAGE_NAME:latest \
--amend $IMAGE_NAME:latest-arm64 \
--amend $IMAGE_NAME:latest-amd64

echo "Pushing manifests..."
docker manifest push $IMAGE_NAME:$VERSION
docker manifest push $IMAGE_NAME:latest

echo "Successfully built and pushed:"
echo " - $IMAGE_NAME:$VERSION (multi-arch)"
echo " - $IMAGE_NAME:latest (multi-arch)"
echo " - $IMAGE_NAME:$VERSION-arm64"
echo " - $IMAGE_NAME:$VERSION-amd64"
echo " - $IMAGE_NAME:latest-arm64"
echo " - $IMAGE_NAME:latest-amd64"

echo "Done!"
Loading
Loading