Skip to content

Commit

Permalink
build a runner container for us to use (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
toshok authored Jan 8, 2025
1 parent 68b4f21 commit 8618383
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 4 deletions.
73 changes: 69 additions & 4 deletions .github/workflows/ghcr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,66 @@ jobs:
echo "hash_from_app_image=$hash_from_app_image" >> $GITHUB_OUTPUT
echo "Hash from app image: $hash_from_app_image"
# Builds the replay.io runner image
ghcr_build_runner:
name: Build Runner Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
hash_from_runner_image: ${{ steps.get_hash_in_runner_image.outputs.hash_from_runner_image }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: true
# all of these default to true, but feel free to set to
# "false" if necessary for your workflow
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: false
swap-storage: true
- name: Set up QEMU
uses: docker/[email protected]
with:
image: tonistiigi/binfmt:latest
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
- name: Build and push runner image
if: "!github.event.pull_request.head.repo.fork"
run: |
./containers/build.sh -i runner -o ${{ github.repository_owner }} --push
- name: Build runner image
if: "github.event.pull_request.head.repo.fork"
run: |
./containers/build.sh -i runner -o ${{ github.repository_owner }} --load
- name: Get hash in Runner Image
id: get_hash_in_runner_image
run: |
# Lowercase the repository owner
export REPO_OWNER=${{ github.repository_owner }}
REPO_OWNER=$(echo $REPO_OWNER | tr '[:upper:]' '[:lower:]')
# Run the build script in the runner image
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
# Get the hash from the build script
hash_from_runner_image=$(cat docker-outputs.txt | grep "Hash for docker build directory" | awk -F "): " '{print $2}' | uniq | head -n1)
echo "hash_from_runner_image=$hash_from_runner_image" >> $GITHUB_OUTPUT
echo "Hash from runner image: $hash_from_runner_image"
# Builds the runtime Docker images
ghcr_build_runtime:
name: Build Image
Expand Down Expand Up @@ -169,10 +229,10 @@ jobs:
name: runtime-${{ matrix.base_image.tag }}
path: /tmp/runtime-${{ matrix.base_image.tag }}.tar

verify_hash_equivalence_in_runtime_and_app:
name: Verify Hash Equivalence in Runtime and Docker images
verify_hash_equivalence_in_runtime_and_runner_and_app:
name: Verify Hash Equivalence in App, Runner, and Runtime images
runs-on: ubuntu-latest
needs: [ghcr_build_runtime, ghcr_build_app]
needs: [ghcr_build_runtime, ghcr_build_runner, ghcr_build_app]
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -200,6 +260,10 @@ jobs:
run: |
echo "Hash from app image: ${{ needs.ghcr_build_app.outputs.hash_from_app_image }}"
echo "hash_from_app_image=${{ needs.ghcr_build_app.outputs.hash_from_app_image }}" >> $GITHUB_ENV
- name: Get hash in Runner Image
run: |
echo "Hash from runner image: ${{ needs.ghcr_build_runner.outputs.hash_from_runner_image }}"
echo "hash_from_runner_image=${{ needs.ghcr_build_runner.outputs.hash_from_runner_image }}" >> $GITHUB_ENV
- name: Get hash using code (development mode)
run: |
Expand All @@ -211,8 +275,9 @@ jobs:
- name: Compare hashes
run: |
echo "Hash from App Image: ${{ env.hash_from_app_image }}"
echo "Hash from Runner Image: ${{ env.hash_from_runner_image }}"
echo "Hash from Code: ${{ env.hash_from_code }}"
if [ "${{ env.hash_from_app_image }}" = "${{ env.hash_from_code }}" ]; then
if [ "${{ env.hash_from_app_image }}" = "${{ env.hash_from_code }}" -a "${{ env.hash_from_runner_image }}" = "${{ env.hash_from_code }}"]; then
echo "Hashes match!"
else
echo "Hashes do not match!"
Expand Down
79 changes: 79 additions & 0 deletions containers/runner/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
ARG OPENHANDS_BUILD_VERSION=dev

FROM python:3.12.3-slim AS backend-builder

WORKDIR /runner
ENV PYTHONPATH='/runner'

ENV POETRY_NO_INTERACTION=1 \
POETRY_VIRTUALENVS_IN_PROJECT=1 \
POETRY_VIRTUALENVS_CREATE=1 \
POETRY_CACHE_DIR=/tmp/poetry_cache

RUN apt-get update -y \
&& apt-get install -y curl make git build-essential \
&& python3 -m pip install poetry==1.8.2 --break-system-packages

COPY ./pyproject.toml ./poetry.lock ./
RUN touch README.md
RUN export POETRY_CACHE_DIR && poetry install --without evaluation,llama-index --no-root && rm -rf $POETRY_CACHE_DIR

FROM python:3.12.3-slim AS openhands-runner

WORKDIR /runner

ARG OPENHANDS_BUILD_VERSION #re-declare for this section

ENV RUN_AS_OPENHANDS=true
# A random number--we need this to be different from the user's UID on the host machine
ENV OPENHANDS_USER_ID=42420
ENV SANDBOX_LOCAL_RUNTIME_URL=http://host.docker.internal
ENV USE_HOST_NETWORK=false
ENV WORKSPACE_BASE=/opt/workspace_base
ENV OPENHANDS_BUILD_VERSION=$OPENHANDS_BUILD_VERSION
ENV SANDBOX_USER_ID=0
RUN mkdir -p $WORKSPACE_BASE

RUN apt-get update -y \
&& apt-get install -y curl ssh sudo

# Default is 1000, but OSX is often 501
RUN sed -i 's/^UID_MIN.*/UID_MIN 499/' /etc/login.defs
# Default is 60000, but we've seen up to 200000
RUN sed -i 's/^UID_MAX.*/UID_MAX 1000000/' /etc/login.defs

RUN groupadd runner
RUN useradd -l -m -u $OPENHANDS_USER_ID -s /bin/bash openhands && \
usermod -aG runner openhands && \
usermod -aG sudo openhands && \
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
RUN chown -R openhands:runner /runner && chmod -R 770 /runner
RUN sudo chown -R openhands:runner $WORKSPACE_BASE && sudo chmod -R 770 $WORKSPACE_BASE
USER openhands

ENV VIRTUAL_ENV=/runner/.venv \
PATH="/runner/.venv/bin:$PATH" \
PYTHONPATH='/runner'

COPY --chown=openhands:runner --chmod=770 --from=backend-builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}

COPY --chown=openhands:runner --chmod=770 ./openhands ./openhands
COPY --chown=openhands:runner --chmod=777 ./openhands/runtime/plugins ./openhands/runtime/plugins
COPY --chown=openhands:runner --chmod=770 ./openhands/agenthub ./openhands/agenthub
COPY --chown=openhands:runner ./pyproject.toml ./pyproject.toml
COPY --chown=openhands:runner ./poetry.lock ./poetry.lock
COPY --chown=openhands:runner ./README.md ./README.md
COPY --chown=openhands:runner ./MANIFEST.in ./MANIFEST.in
COPY --chown=openhands:runner ./LICENSE ./LICENSE

# This is run as "openhands" user, and will create __pycache__ with openhands:openhands ownership
RUN python openhands/core/download.py # No-op to download assets
# Add this line to set group ownership of all files/directories not already in "runner" group
# openhands:openhands -> openhands:runner
RUN find /runner \! -group runner -exec chgrp runner {} +

USER root

WORKDIR /runner

CMD [ "sleep", "infinity" ]
4 changes: 4 additions & 0 deletions containers/runner/config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DOCKER_REGISTRY=ghcr.io
DOCKER_ORG=all-hands-ai
DOCKER_IMAGE=runner
DOCKER_BASE_DIR="."

0 comments on commit 8618383

Please sign in to comment.