Skip to content

Commit 8618383

Browse files
author
Chris Toshok
authored
build a runner container for us to use (#14)
1 parent 68b4f21 commit 8618383

File tree

3 files changed

+152
-4
lines changed

3 files changed

+152
-4
lines changed

.github/workflows/ghcr-build.yml

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,66 @@ jobs:
8989
echo "hash_from_app_image=$hash_from_app_image" >> $GITHUB_OUTPUT
9090
echo "Hash from app image: $hash_from_app_image"
9191
92+
# Builds the replay.io runner image
93+
ghcr_build_runner:
94+
name: Build Runner Image
95+
runs-on: ubuntu-latest
96+
permissions:
97+
contents: read
98+
packages: write
99+
outputs:
100+
hash_from_runner_image: ${{ steps.get_hash_in_runner_image.outputs.hash_from_runner_image }}
101+
steps:
102+
- name: Checkout
103+
uses: actions/checkout@v4
104+
- name: Free Disk Space (Ubuntu)
105+
uses: jlumbroso/free-disk-space@main
106+
with:
107+
# this might remove tools that are actually needed,
108+
# if set to "true" but frees about 6 GB
109+
tool-cache: true
110+
# all of these default to true, but feel free to set to
111+
# "false" if necessary for your workflow
112+
android: true
113+
dotnet: true
114+
haskell: true
115+
large-packages: true
116+
docker-images: false
117+
swap-storage: true
118+
- name: Set up QEMU
119+
uses: docker/[email protected]
120+
with:
121+
image: tonistiigi/binfmt:latest
122+
- name: Login to GHCR
123+
uses: docker/login-action@v3
124+
with:
125+
registry: ghcr.io
126+
username: ${{ github.repository_owner }}
127+
password: ${{ secrets.GITHUB_TOKEN }}
128+
- name: Set up Docker Buildx
129+
id: buildx
130+
uses: docker/setup-buildx-action@v3
131+
- name: Build and push runner image
132+
if: "!github.event.pull_request.head.repo.fork"
133+
run: |
134+
./containers/build.sh -i runner -o ${{ github.repository_owner }} --push
135+
- name: Build runner image
136+
if: "github.event.pull_request.head.repo.fork"
137+
run: |
138+
./containers/build.sh -i runner -o ${{ github.repository_owner }} --load
139+
- name: Get hash in Runner Image
140+
id: get_hash_in_runner_image
141+
run: |
142+
# Lowercase the repository owner
143+
export REPO_OWNER=${{ github.repository_owner }}
144+
REPO_OWNER=$(echo $REPO_OWNER | tr '[:upper:]' '[:lower:]')
145+
# Run the build script in the runner image
146+
docker run -e SANDBOX_USER_ID=0 -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/${REPO_OWNER}/runner:${{ env.RELEVANT_SHA }} /bin/bash -c "mkdir -p containers/runtime; python3 openhands/runtime/utils/runtime_build.py --base_image ${{ env.BASE_IMAGE_FOR_HASH_EQUIVALENCE_TEST }} --build_folder containers/runtime --force_rebuild" 2>&1 | tee docker-outputs.txt
147+
# Get the hash from the build script
148+
hash_from_runner_image=$(cat docker-outputs.txt | grep "Hash for docker build directory" | awk -F "): " '{print $2}' | uniq | head -n1)
149+
echo "hash_from_runner_image=$hash_from_runner_image" >> $GITHUB_OUTPUT
150+
echo "Hash from runner image: $hash_from_runner_image"
151+
92152
# Builds the runtime Docker images
93153
ghcr_build_runtime:
94154
name: Build Image
@@ -169,10 +229,10 @@ jobs:
169229
name: runtime-${{ matrix.base_image.tag }}
170230
path: /tmp/runtime-${{ matrix.base_image.tag }}.tar
171231

172-
verify_hash_equivalence_in_runtime_and_app:
173-
name: Verify Hash Equivalence in Runtime and Docker images
232+
verify_hash_equivalence_in_runtime_and_runner_and_app:
233+
name: Verify Hash Equivalence in App, Runner, and Runtime images
174234
runs-on: ubuntu-latest
175-
needs: [ghcr_build_runtime, ghcr_build_app]
235+
needs: [ghcr_build_runtime, ghcr_build_runner, ghcr_build_app]
176236
strategy:
177237
fail-fast: false
178238
matrix:
@@ -200,6 +260,10 @@ jobs:
200260
run: |
201261
echo "Hash from app image: ${{ needs.ghcr_build_app.outputs.hash_from_app_image }}"
202262
echo "hash_from_app_image=${{ needs.ghcr_build_app.outputs.hash_from_app_image }}" >> $GITHUB_ENV
263+
- name: Get hash in Runner Image
264+
run: |
265+
echo "Hash from runner image: ${{ needs.ghcr_build_runner.outputs.hash_from_runner_image }}"
266+
echo "hash_from_runner_image=${{ needs.ghcr_build_runner.outputs.hash_from_runner_image }}" >> $GITHUB_ENV
203267
204268
- name: Get hash using code (development mode)
205269
run: |
@@ -211,8 +275,9 @@ jobs:
211275
- name: Compare hashes
212276
run: |
213277
echo "Hash from App Image: ${{ env.hash_from_app_image }}"
278+
echo "Hash from Runner Image: ${{ env.hash_from_runner_image }}"
214279
echo "Hash from Code: ${{ env.hash_from_code }}"
215-
if [ "${{ env.hash_from_app_image }}" = "${{ env.hash_from_code }}" ]; then
280+
if [ "${{ env.hash_from_app_image }}" = "${{ env.hash_from_code }}" -a "${{ env.hash_from_runner_image }}" = "${{ env.hash_from_code }}"]; then
216281
echo "Hashes match!"
217282
else
218283
echo "Hashes do not match!"

containers/runner/Dockerfile

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
ARG OPENHANDS_BUILD_VERSION=dev
2+
3+
FROM python:3.12.3-slim AS backend-builder
4+
5+
WORKDIR /runner
6+
ENV PYTHONPATH='/runner'
7+
8+
ENV POETRY_NO_INTERACTION=1 \
9+
POETRY_VIRTUALENVS_IN_PROJECT=1 \
10+
POETRY_VIRTUALENVS_CREATE=1 \
11+
POETRY_CACHE_DIR=/tmp/poetry_cache
12+
13+
RUN apt-get update -y \
14+
&& apt-get install -y curl make git build-essential \
15+
&& python3 -m pip install poetry==1.8.2 --break-system-packages
16+
17+
COPY ./pyproject.toml ./poetry.lock ./
18+
RUN touch README.md
19+
RUN export POETRY_CACHE_DIR && poetry install --without evaluation,llama-index --no-root && rm -rf $POETRY_CACHE_DIR
20+
21+
FROM python:3.12.3-slim AS openhands-runner
22+
23+
WORKDIR /runner
24+
25+
ARG OPENHANDS_BUILD_VERSION #re-declare for this section
26+
27+
ENV RUN_AS_OPENHANDS=true
28+
# A random number--we need this to be different from the user's UID on the host machine
29+
ENV OPENHANDS_USER_ID=42420
30+
ENV SANDBOX_LOCAL_RUNTIME_URL=http://host.docker.internal
31+
ENV USE_HOST_NETWORK=false
32+
ENV WORKSPACE_BASE=/opt/workspace_base
33+
ENV OPENHANDS_BUILD_VERSION=$OPENHANDS_BUILD_VERSION
34+
ENV SANDBOX_USER_ID=0
35+
RUN mkdir -p $WORKSPACE_BASE
36+
37+
RUN apt-get update -y \
38+
&& apt-get install -y curl ssh sudo
39+
40+
# Default is 1000, but OSX is often 501
41+
RUN sed -i 's/^UID_MIN.*/UID_MIN 499/' /etc/login.defs
42+
# Default is 60000, but we've seen up to 200000
43+
RUN sed -i 's/^UID_MAX.*/UID_MAX 1000000/' /etc/login.defs
44+
45+
RUN groupadd runner
46+
RUN useradd -l -m -u $OPENHANDS_USER_ID -s /bin/bash openhands && \
47+
usermod -aG runner openhands && \
48+
usermod -aG sudo openhands && \
49+
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
50+
RUN chown -R openhands:runner /runner && chmod -R 770 /runner
51+
RUN sudo chown -R openhands:runner $WORKSPACE_BASE && sudo chmod -R 770 $WORKSPACE_BASE
52+
USER openhands
53+
54+
ENV VIRTUAL_ENV=/runner/.venv \
55+
PATH="/runner/.venv/bin:$PATH" \
56+
PYTHONPATH='/runner'
57+
58+
COPY --chown=openhands:runner --chmod=770 --from=backend-builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
59+
60+
COPY --chown=openhands:runner --chmod=770 ./openhands ./openhands
61+
COPY --chown=openhands:runner --chmod=777 ./openhands/runtime/plugins ./openhands/runtime/plugins
62+
COPY --chown=openhands:runner --chmod=770 ./openhands/agenthub ./openhands/agenthub
63+
COPY --chown=openhands:runner ./pyproject.toml ./pyproject.toml
64+
COPY --chown=openhands:runner ./poetry.lock ./poetry.lock
65+
COPY --chown=openhands:runner ./README.md ./README.md
66+
COPY --chown=openhands:runner ./MANIFEST.in ./MANIFEST.in
67+
COPY --chown=openhands:runner ./LICENSE ./LICENSE
68+
69+
# This is run as "openhands" user, and will create __pycache__ with openhands:openhands ownership
70+
RUN python openhands/core/download.py # No-op to download assets
71+
# Add this line to set group ownership of all files/directories not already in "runner" group
72+
# openhands:openhands -> openhands:runner
73+
RUN find /runner \! -group runner -exec chgrp runner {} +
74+
75+
USER root
76+
77+
WORKDIR /runner
78+
79+
CMD [ "sleep", "infinity" ]

containers/runner/config.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
DOCKER_REGISTRY=ghcr.io
2+
DOCKER_ORG=all-hands-ai
3+
DOCKER_IMAGE=runner
4+
DOCKER_BASE_DIR="."

0 commit comments

Comments
 (0)