Skip to content

Commit 99ab8b7

Browse files
authored
Merge pull request hud-evals#186 from hud-evals/j/browser-improvements
fix: browser improvements, playwright hotfix.
2 parents 22981ce + 91f9416 commit 99ab8b7

File tree

8 files changed

+414
-52
lines changed

8 files changed

+414
-52
lines changed

environments/browser/Dockerfile

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,8 @@
11
# syntax=docker/dockerfile:1
2-
FROM ubuntu:24.04 AS setup
2+
FROM hudevals/hud-browser-base:latest AS setup
33

4-
# Update and install core dependencies (including working Chromium browser)
5-
RUN apt-get update -y \
6-
&& apt-get install -y --no-install-recommends \
7-
vim \
8-
openssl \
9-
ca-certificates \
10-
curl \
11-
wget \
12-
sudo \
13-
bash \
14-
net-tools \
15-
novnc \
16-
x11vnc \
17-
xvfb \
18-
xfce4 \
19-
locales \
20-
libpq5 \
21-
sqlite3 \
22-
dbus-x11 \
23-
xfce4-terminal \
24-
xfonts-base \
25-
xdotool \
26-
psmisc \
27-
scrot \
28-
pm-utils \
29-
build-essential \
30-
unzip \
31-
xauth \
32-
gnupg \
33-
gpg \
34-
jq \
35-
git \
36-
build-essential \
37-
nodejs \
38-
npm
39-
40-
RUN update-ca-certificates
41-
42-
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
43-
ENV PATH="/root/.local/bin:$PATH"
44-
45-
# Set working directory
464
WORKDIR /app
475

48-
# Install git for dependency installation
49-
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
50-
51-
# Install Playwright
52-
RUN uv pip install --system --break-system-packages playwright
53-
RUN python3 -m playwright install chromium --with-deps
54-
556
# Layer 1: Install server dependencies
567
COPY server/pyproject.toml /app/server/
578
RUN cd /app/server && uv pip install --system --break-system-packages .
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# syntax=docker/dockerfile:1
2+
# Local development Dockerfile that uses local hud-python
3+
FROM hudevals/hud-browser-base:latest AS setup
4+
5+
WORKDIR /app
6+
7+
# Layer 0: Install local hud-python
8+
# Copy local hud-python source (build context is repo root)
9+
COPY hud /app/hud-python/hud/
10+
COPY pyproject.toml /app/hud-python/
11+
COPY README.md /app/hud-python/
12+
COPY LICENSE /app/hud-python/
13+
14+
# Install local hud-python
15+
RUN cd /app/hud-python && uv pip install --system --break-system-packages -e .
16+
17+
# Layer 1: Install server dependencies
18+
COPY environments/browser/server/pyproject.toml /app/server/
19+
RUN cd /app/server && uv pip install --system --break-system-packages .
20+
21+
# Layer 2: Install environment dependencies
22+
COPY environments/browser/environment/pyproject.toml /app/environment/
23+
RUN cd /app/environment && uv pip install --system --break-system-packages .
24+
25+
# Layer 3: Copy source code (changes here don't invalidate dependency layers)
26+
COPY environments/browser/server/ /app/server/
27+
COPY environments/browser/environment/ /app/environment/
28+
29+
# Auto-discover and install/build all frontend apps
30+
RUN set -e; \
31+
for pkg in $(find /app/environment -type f -path '*/frontend/package.json'); do \
32+
app_dir=$(dirname "$pkg"); \
33+
echo "Installing dependencies in $app_dir"; \
34+
if [ -f "$app_dir/package-lock.json" ]; then \
35+
(cd "$app_dir" && npm ci --no-audit --no-fund); \
36+
else \
37+
(cd "$app_dir" && npm install --no-audit --no-fund); \
38+
fi; \
39+
done && \
40+
for pkg in $(find /app/environment -type f -path '*/frontend/package.json'); do \
41+
app_dir=$(dirname "$pkg"); \
42+
if [ -f "$app_dir/next.config.js" ]; then \
43+
echo "Building Next.js app in $app_dir"; \
44+
(cd "$app_dir" && npm run build); \
45+
fi; \
46+
done
47+
48+
# Make scripts executable
49+
RUN find /app/environment -name "*.py" -type f -exec chmod +x {} \;
50+
51+
# Environment configuration
52+
ENV MCP_TRANSPORT="stdio"
53+
ENV HUD_LOG_STREAM="stderr"
54+
ENV PYTHONUNBUFFERED="1"
55+
ENV PYTHONWARNINGS="ignore::SyntaxWarning:pyautogui"
56+
ENV DISPLAY=":1"
57+
ENV PYTHONPATH=/app
58+
59+
# Expose ports
60+
EXPOSE 8000 8080 3000-3200 5000-5200
61+
62+
# Simple startup: HUD_DEV=1 enables hot-reload; otherwise run production
63+
CMD ["sh", "-c", "\
64+
if [ \"${HUD_DEV:-0}\" = \"1\" ]; then \
65+
uvicorn environment.server:app --host 0.0.0.0 --port 8000 --reload --log-level warning >&2 & \
66+
sleep 5 && cd /app/server && exec hud dev server.main --stdio; \
67+
else \
68+
uvicorn environment.server:app --host 0.0.0.0 --port 8000 --log-level warning >&2 & \
69+
sleep 5 && cd /app/server && exec python3 -m server.main; \
70+
fi\
71+
"]
72+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# syntax=docker/dockerfile:1
2+
FROM ubuntu:24.04 AS setup
3+
4+
# Update and install core dependencies (including working Chromium browser)
5+
RUN apt-get update -y \
6+
&& apt-get install -y --no-install-recommends \
7+
vim \
8+
openssl \
9+
ca-certificates \
10+
curl \
11+
wget \
12+
sudo \
13+
bash \
14+
net-tools \
15+
novnc \
16+
x11vnc \
17+
xvfb \
18+
xfce4 \
19+
locales \
20+
libpq5 \
21+
sqlite3 \
22+
dbus-x11 \
23+
xfce4-terminal \
24+
xfonts-base \
25+
xdotool \
26+
psmisc \
27+
scrot \
28+
pm-utils \
29+
build-essential \
30+
unzip \
31+
xauth \
32+
gnupg \
33+
gpg \
34+
jq \
35+
git \
36+
build-essential \
37+
nodejs \
38+
npm
39+
40+
RUN update-ca-certificates
41+
42+
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
43+
ENV PATH="/root/.local/bin:$PATH"
44+
45+
# Install git for dependency installation
46+
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
47+
48+
# Install Playwright
49+
RUN uv pip install --system --break-system-packages playwright
50+
RUN python3 -m playwright install chromium --with-deps
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Browser Base Image
2+
3+
Base Docker image for browser environments with Playwright, Chromium, and VNC support.
4+
5+
## Build
6+
7+
```bash
8+
docker build -t browser-base:latest .
9+
```
10+
11+
## Test with VNC Access
12+
13+
### 1. Start the container
14+
15+
```bash
16+
docker run -it --rm \
17+
-p 6080:6080 \
18+
-p 5900:5900 \
19+
-e DISPLAY=:1 \
20+
browser-base:latest \
21+
bash
22+
```
23+
24+
### 2. Inside the container, start display servers
25+
26+
```bash
27+
Xvfb :1 -screen 0 1920x1080x24 > /dev/null 2>&1 &
28+
x11vnc -display :1 -nopw -listen 0.0.0.0 -forever > /dev/null 2>&1 &
29+
/usr/share/novnc/utils/novnc_proxy --vnc localhost:5900 --listen 6080 > /dev/null 2>&1 &
30+
```
31+
32+
### 3. Test Playwright
33+
34+
```bash
35+
python3 -c "
36+
from playwright.sync_api import sync_playwright
37+
with sync_playwright() as p:
38+
browser = p.chromium.launch(headless=False)
39+
page = browser.new_page()
40+
page.goto('https://example.com')
41+
print('Title:', page.title())
42+
input('Press Enter to close...')
43+
browser.close()
44+
"
45+
```
46+
47+
### 4. View in browser
48+
49+
Open `http://localhost:6080/vnc.html` to see Chromium running.
50+
51+
## What's Included
52+
53+
- Ubuntu 24.04
54+
- Desktop environment (Xvfb, x11vnc, noVNC, xfce4)
55+
- Node.js & npm
56+
- Python 3 with uv package manager
57+
- Playwright with Chromium
58+
- Development tools (git, curl, wget, etc.)

environments/browser/server/tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ async def launch_app(app_name: str) -> str:
5151

5252
# Automatically navigate to the app after launching
5353
try:
54-
await playwright(action="navigate", url=app_url)
54+
await playwright(action="navigate", url=app_url, wait_for_load_state="networkidle")
5555
# Give the page a moment to fully load
5656
await asyncio.sleep(1)
5757
return f"Launched {app_name} at {app_url} and navigated to it"

hud/tools/playwright.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,14 @@ async def _ensure_browser(self) -> None:
225225
if self._browser_context is None:
226226
raise RuntimeError("Browser context failed to initialize")
227227

228-
self.page = await self._browser_context.new_page()
228+
# Reuse existing page if available (for CDP connections), otherwise create new one
229+
pages = self._browser_context.pages
230+
if pages:
231+
self.page = pages[0]
232+
logger.info("Reusing existing browser page")
233+
else:
234+
self.page = await self._browser_context.new_page()
235+
logger.info("Created new browser page")
229236
logger.info("Playwright browser launched successfully")
230237

231238
async def navigate(

scripts/build-and-push-env.sh

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/bin/bash
2+
3+
# Multi-architecture Docker build and push script for hud environments
4+
# Usage: ./build-and-push-env.sh <environment|directory> <version>
5+
# Example: ./build-and-push-env.sh browser 0.1.8
6+
# ./build-and-push-env.sh environments/browser 0.1.8
7+
# ./build-and-push-env.sh /full/path/to/env 0.1.8
8+
9+
set -e
10+
11+
ENV_INPUT=$1
12+
VERSION=$2
13+
14+
if [ -z "$ENV_INPUT" ] || [ -z "$VERSION" ]; then
15+
echo "Error: Missing required arguments"
16+
echo "Usage: $0 <environment|directory> <version>"
17+
echo "Example: $0 browser 0.1.8"
18+
echo " $0 environments/browser 0.1.8"
19+
exit 1
20+
fi
21+
22+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
23+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
24+
25+
# Check if input is a directory path or just a name
26+
if [[ "$ENV_INPUT" == *"/"* ]] || [ -d "$ENV_INPUT" ] || [ -d "$PROJECT_ROOT/$ENV_INPUT" ]; then
27+
# It's a directory path
28+
if [ -d "$ENV_INPUT" ]; then
29+
ENV_PATH="$ENV_INPUT"
30+
else
31+
ENV_PATH="$PROJECT_ROOT/$ENV_INPUT"
32+
fi
33+
ENVIRONMENT=$(basename "$ENV_PATH")
34+
else
35+
# It's just an environment name
36+
ENVIRONMENT="$ENV_INPUT"
37+
ENV_PATH="$PROJECT_ROOT/environments/${ENVIRONMENT}"
38+
fi
39+
40+
IMAGE_NAME="hudevals/hud-${ENVIRONMENT}"
41+
42+
echo "Building and pushing hud-${ENVIRONMENT} version ${VERSION}"
43+
echo "Environment path: $ENV_PATH"
44+
45+
if [ ! -d "$ENV_PATH" ]; then
46+
echo "Error: Environment directory not found: $ENV_PATH"
47+
exit 1
48+
fi
49+
50+
if [ ! -f "$ENV_PATH/Dockerfile" ]; then
51+
echo "Error: Dockerfile not found in $ENV_PATH"
52+
exit 1
53+
fi
54+
55+
cd "$ENV_PATH"
56+
echo "Working directory: $(pwd)"
57+
58+
echo "Building ARM64 image..."
59+
docker build --platform linux/arm64 -t $IMAGE_NAME:$VERSION-arm64 .
60+
61+
echo "Building AMD64 image..."
62+
docker build --platform linux/amd64 -t $IMAGE_NAME:$VERSION-amd64 .
63+
64+
echo "Tagging images as latest..."
65+
docker tag $IMAGE_NAME:$VERSION-arm64 $IMAGE_NAME:latest-arm64
66+
docker tag $IMAGE_NAME:$VERSION-amd64 $IMAGE_NAME:latest-amd64
67+
68+
echo "Pushing ARM64 image..."
69+
docker push $IMAGE_NAME:$VERSION-arm64
70+
docker push $IMAGE_NAME:latest-arm64
71+
72+
echo "Pushing AMD64 image..."
73+
docker push $IMAGE_NAME:$VERSION-amd64
74+
docker push $IMAGE_NAME:latest-amd64
75+
76+
echo "Creating and pushing multi-arch manifest..."
77+
docker manifest create $IMAGE_NAME:$VERSION \
78+
--amend $IMAGE_NAME:$VERSION-arm64 \
79+
--amend $IMAGE_NAME:$VERSION-amd64
80+
81+
docker manifest create $IMAGE_NAME:latest \
82+
--amend $IMAGE_NAME:latest-arm64 \
83+
--amend $IMAGE_NAME:latest-amd64
84+
85+
echo "Pushing manifests..."
86+
docker manifest push $IMAGE_NAME:$VERSION
87+
docker manifest push $IMAGE_NAME:latest
88+
89+
echo "Successfully built and pushed:"
90+
echo " - $IMAGE_NAME:$VERSION (multi-arch)"
91+
echo " - $IMAGE_NAME:latest (multi-arch)"
92+
echo " - $IMAGE_NAME:$VERSION-arm64"
93+
echo " - $IMAGE_NAME:$VERSION-amd64"
94+
echo " - $IMAGE_NAME:latest-arm64"
95+
echo " - $IMAGE_NAME:latest-amd64"
96+
97+
echo "Done!"

0 commit comments

Comments
 (0)