[Arch] Add runtime image build CI & clean up runtime build using jinja2 template (#3055)

* test_runtime_client.py to test _execute_bash()

* runtime_build and runtime tweaks

* fix in docker script

* revert bash changes

* use sandbox_config.update_source_code to control source code update

* add od_version to the sandbox tag

* add doc instruction for update source code

* do not remove whole poetry folder;
add mamba clean

* add missing newlines

* cleanup runtime dockerfile into jinja template

* make prep temp file a separate function;
make that function accessible through cli

* modify `runtime_build.py` so it can generate directory for building docker img

* add dockerfile and sdist of runtime to gitignore since it will be dynamically generated

* add runtime to build

* do not rebuild new image when an `od_runtime` is provided

* use default container_image for testing if possible

* move runtime tests to ghcr runtime workflow

* update docker base dir for runtime

* fix unittest

* fix image name

* fix image name for test case

* rename to make it consistent

---------

Co-authored-by: tobitege <tobitege@gmx.de>
This commit is contained in:
Xingyao Wang 2024-07-24 21:56:12 +08:00 committed by GitHub
parent 547c510848
commit 405c8a0456
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 442 additions and 167 deletions

242
.github/workflows/ghcr-runtime.yml vendored Normal file
View File

@ -0,0 +1,242 @@
name: Build Publish and Test Runtime Image
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
on:
push:
branches:
- main
tags:
- '*'
pull_request:
workflow_dispatch:
inputs:
reason:
description: 'Reason for manual trigger'
required: true
default: ''
jobs:
ghcr_build_runtime:
runs-on: ubuntu-latest
outputs:
tags: ${{ steps.capture-tags.outputs.tags }}
permissions:
contents: read
packages: write
strategy:
matrix:
image: ["od_runtime"]
base_image: ["ubuntu:22.04"]
platform: ["amd64", "arm64"]
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/setup-qemu-action@v3
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "poetry"
- name: Install Python dependencies using Poetry
run: make install-python-dependencies
- name: Create source distribution and Dockerfile
run: poetry run python3 opendevin/runtime/utils/runtime_build.py --base_image ${{ matrix.base_image }} --build_folder containers/runtime
- name: Build and export image
id: build
run: ./containers/build.sh ${{ matrix.image }} ${{ github.repository_owner }} ${{ matrix.platform }}
- name: Capture tags
id: capture-tags
run: |
tags=$(cat tags.txt)
echo "tags=$tags"
echo "tags=$tags" >> $GITHUB_OUTPUT
- name: Upload Docker image as artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.image }}-docker-image-${{ matrix.platform }}
path: /tmp/${{ matrix.image }}_image_${{ matrix.platform }}.tar
test-for-runtime:
name: Test for Runtime
runs-on: ubuntu-latest
needs: ghcr_build_runtime
env:
PERSIST_SANDBOX: "false"
steps:
- uses: actions/checkout@v4
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# when 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
swap-storage: true
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "poetry"
- name: Install Python dependencies using Poetry
run: make install-python-dependencies
- name: Download Runtime Docker image
uses: actions/download-artifact@v4
with:
name: od_runtime-docker-image-amd64
path: /tmp/
- name: Load Runtime image and run runtime tests
run: |
# Load the Docker image and capture the output
output=$(docker load -i /tmp/od_runtime_image_amd64.tar)
# Extract the first image name from the output
image_name=$(echo "$output" | grep -oP 'Loaded image: \K.*' | head -n 1)
# Print the full name of the image
echo "Loaded Docker image: $image_name"
SANDBOX_CONTAINER_IMAGE=$image_name TEST_IN_CI=true poetry run pytest --cov=agenthub --cov=opendevin --cov-report=xml -s ./tests/unit/test_runtime.py
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
ghcr_push:
runs-on: ubuntu-latest
# don't push if runtime tests fail
needs: [ghcr_build_runtime, test-for-runtime]
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
env:
tags: ${{ needs.ghcr_build_runtime.outputs.tags }}
permissions:
contents: read
packages: write
strategy:
matrix:
image: ["od_runtime"]
platform: ["amd64", "arm64"]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Login to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Download Docker images
uses: actions/download-artifact@v4
with:
name: ${{ matrix.image }}-docker-image-${{ matrix.platform }}
path: /tmp/${{ matrix.platform }}
- name: Load images and push to registry
run: |
mv /tmp/${{ matrix.platform }}/${{ matrix.image }}_image_${{ matrix.platform }}.tar .
loaded_image=$(docker load -i ${{ matrix.image }}_image_${{ matrix.platform }}.tar | grep "Loaded image:" | head -n 1 | awk '{print $3}')
echo "loaded image = $loaded_image"
tags=$(echo ${tags} | tr ' ' '\n')
image_name=$(echo "ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}" | tr '[:upper:]' '[:lower:]')
echo "image name = $image_name"
for tag in $tags; do
echo "tag = $tag"
docker tag $loaded_image $image_name:${tag}_${{ matrix.platform }}
docker push $image_name:${tag}_${{ matrix.platform }}
done
create_manifest:
runs-on: ubuntu-latest
needs: [ghcr_build_runtime, ghcr_push]
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
env:
tags: ${{ needs.ghcr_build_runtime.outputs.tags }}
strategy:
matrix:
image: ["od_runtime"]
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Login to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push multi-platform manifest
run: |
image_name=$(echo "ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}" | tr '[:upper:]' '[:lower:]')
echo "image name = $image_name"
tags=$(echo ${tags} | tr ' ' '\n')
for tag in $tags; do
echo 'tag = $tag'
docker buildx imagetools create --tag $image_name:$tag \
$image_name:${tag}_amd64 \
$image_name:${tag}_arm64
done

View File

@ -1,64 +0,0 @@
name: Run Runtime Tests
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
on:
push:
branches:
- main
paths-ignore:
- '**/*.md'
- 'frontend/**'
- 'docs/**'
- 'evaluation/**'
pull_request:
env:
PERSIST_SANDBOX : "false"
jobs:
test-for-runtime:
name: Test for Runtime
runs-on: ubuntu-latest
env:
PERSIST_SANDBOX: "false"
steps:
- uses: actions/checkout@v4
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# when 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
swap-storage: true
- name: Install poetry via pipx
run: pipx install poetry
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "poetry"
- name: Install Python dependencies using Poetry
run: make install-python-dependencies
- name: Run runtime tests
run: |
TEST_IN_CI=true poetry run pytest --cov=agenthub --cov=opendevin --cov-report=xml -s ./tests/unit/test_runtime.py
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

4
.gitignore vendored
View File

@ -220,3 +220,7 @@ image_build_logs
run_instance_logs
od_runtime_*.tar
# docker build
containers/runtime/Dockerfile
containers/runtime/project.tar.gz

View File

@ -27,11 +27,14 @@ echo "Tags: ${tags[@]}"
if [[ "$image_name" == "opendevin" ]]; then
dir="./containers/app"
elif [[ "$image_name" == "od_runtime" ]]; then
dir="./containers/runtime"
else
dir="./containers/$image_name"
fi
if [[ ! -f "$dir/Dockerfile" ]]; then
if [[ (! -f "$dir/Dockerfile") && "$image_name" != "od_runtime" ]]; then
# Allow runtime to be built without a Dockerfile
echo "No Dockerfile found"
exit 1
fi
@ -46,6 +49,11 @@ if [[ -n "$org_name" ]]; then
DOCKER_ORG="$org_name"
fi
# If $DOCKER_IMAGE_TAG is set, add it to the tags
if [[ -n "$DOCKER_IMAGE_TAG" ]]; then
tags+=("$DOCKER_IMAGE_TAG")
fi
DOCKER_REPOSITORY="$DOCKER_REGISTRY/$DOCKER_ORG/$DOCKER_IMAGE"
DOCKER_REPOSITORY=${DOCKER_REPOSITORY,,} # lowercase
echo "Repo: $DOCKER_REPOSITORY"

View File

@ -0,0 +1,11 @@
# Dynamic constructed Dockerfile
This folder builds runtime image (sandbox), which will use a `Dockerfile` that is dynamically generated depends on the `base_image` AND a [Python source distribution](https://docs.python.org/3.10/distutils/sourcedist.html) that's based on the current commit of `opendevin`.
The following command will generate Dockerfile for `ubuntu:22.04` and the source distribution `.tar` into `containers/runtime`.
```bash
poetry run python3 opendevin/runtime/utils/runtime_build.py \
--base_image ubuntu:22.04 \
--build_folder containers/runtime
```

View File

@ -0,0 +1,8 @@
DOCKER_REGISTRY=ghcr.io
DOCKER_ORG=opendevin
DOCKER_BASE_DIR="./containers/runtime"
# These two variables will be appended by the runtime_build.py script
# DOCKER_IMAGE=
# DOCKER_IMAGE_TAG=
DOCKER_IMAGE=od_runtime
DOCKER_IMAGE_TAG=od_v0.8.1_image_ubuntu_tag_22.04

View File

@ -6,6 +6,7 @@ import tempfile
import docker
import toml
from jinja2 import Environment, FileSystemLoader
import opendevin
from opendevin.core.logger import opendevin_logger as logger
@ -64,75 +65,42 @@ def _put_source_code_to_dir(temp_dir: str) -> str:
def _generate_dockerfile(
base_image: str, source_code_dirname: str, skip_init: bool = False
) -> str:
"""Generate the Dockerfile content for the eventstream runtime image based on user-provided base image.
NOTE: This is only tested on debian yet.
"""
if skip_init:
dockerfile_content = f'FROM {base_image}\n'
else:
# Ubuntu 22.x has libgl1-mesa-glx, but 24.x and above have libgl1!
if 'ubuntu' in base_image and (
base_image.endswith(':latest') or base_image.endswith(':24.04')
):
LIBGL_MESA = 'libgl1'
else:
LIBGL_MESA = 'libgl1-mesa-glx'
dockerfile_content = (
f'FROM {base_image}\n'
# Install necessary packages and clean up in one layer
f'RUN apt-get update && apt-get install -y wget sudo apt-utils {LIBGL_MESA} libasound2-plugins && \\\n'
f' apt-get clean && rm -rf /var/lib/apt/lists/*\n'
# Create necessary directories
f'RUN mkdir -p /opendevin && mkdir -p /opendevin/logs && chmod 777 /opendevin/logs && \\\n'
f' echo "" > /opendevin/bash.bashrc\n'
# Install Miniforge3
f'RUN if [ ! -d /opendevin/miniforge3 ]; then \\\n'
f' wget --progress=bar:force -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" && \\\n'
f' bash Miniforge3.sh -b -p /opendevin/miniforge3 && \\\n'
f' rm Miniforge3.sh && \\\n'
f' chmod -R g+w /opendevin/miniforge3 && \\\n'
f' bash -c ". /opendevin/miniforge3/etc/profile.d/conda.sh && conda config --set changeps1 False && conda config --append channels conda-forge"; \\\n'
f' fi\n'
'RUN /opendevin/miniforge3/bin/mamba install python=3.11 -y\n'
'RUN /opendevin/miniforge3/bin/mamba install conda-forge::poetry -y\n'
"""Generate the Dockerfile content for the eventstream runtime image based on user-provided base image."""
env = Environment(
loader=FileSystemLoader(
searchpath=os.path.join(os.path.dirname(__file__), 'runtime_templates')
)
# Copy the project directory to the container
dockerfile_content += 'COPY project.tar.gz /opendevin\n'
# Remove /opendevin/code if it exists
dockerfile_content += (
'RUN if [ -d /opendevin/code ]; then rm -rf /opendevin/code; fi\n'
)
# Unzip the tarball to /opendevin/code
dockerfile_content += (
'RUN cd /opendevin && tar -xzvf project.tar.gz && rm project.tar.gz\n'
)
dockerfile_content += f'RUN mv /opendevin/{source_code_dirname} /opendevin/code\n'
# ALTERNATIVE, but maybe not complete? (toml error!)
dockerfile_content += (
'RUN cd /opendevin/code && '
'/opendevin/miniforge3/bin/mamba run -n base poetry env use python3.11 && '
'/opendevin/miniforge3/bin/mamba run -n base poetry install --no-interaction --no-root\n'
'RUN /opendevin/miniforge3/bin/mamba run -n base poetry cache clear --all . && \\\n'
'apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* &&\\\n'
'/opendevin/miniforge3/bin/mamba clean --all\n'
)
# For browser (update if needed)
dockerfile_content += (
'RUN apt-get update && \\\n'
' cd /opendevin/code && \\\n'
' /opendevin/miniforge3/bin/mamba run -n base poetry run pip install playwright && \\\n'
' /opendevin/miniforge3/bin/mamba run -n base poetry run playwright install --with-deps chromium && \\\n'
' apt-get clean && \\\n'
' rm -rf /var/lib/apt/lists/*\n'
template = env.get_template('Dockerfile.j2')
dockerfile_content = template.render(
base_image=base_image,
source_code_dirname=source_code_dirname,
skip_init=skip_init,
)
return dockerfile_content
def prep_docker_build_folder(
dir_path: str,
base_image: str,
skip_init: bool = False,
):
"""Prepares the docker build folder by copying the source code and generating the Dockerfile."""
source_code_dirname = _put_source_code_to_dir(dir_path)
dockerfile_content = _generate_dockerfile(
base_image, source_code_dirname, skip_init=skip_init
)
logger.info(
(
f'===== Dockerfile content =====\n'
f'{dockerfile_content}\n'
f'==============================='
)
)
with open(os.path.join(dir_path, 'Dockerfile'), 'w') as file:
file.write(dockerfile_content)
def _build_sandbox_image(
base_image: str,
target_image_name: str,
@ -141,26 +109,13 @@ def _build_sandbox_image(
):
try:
with tempfile.TemporaryDirectory() as temp_dir:
source_code_dirname = _put_source_code_to_dir(temp_dir)
dockerfile_content = _generate_dockerfile(
base_image, source_code_dirname, skip_init=skip_init
)
if skip_init:
logger.info(
f'Reusing existing od_sandbox image [{target_image_name}] but will update the source code in it.'
)
else:
logger.info(f'Building agnostic sandbox image: {target_image_name}')
logger.info(
(
f'===== Dockerfile content =====\n'
f'{dockerfile_content}\n'
f'==============================='
)
)
with open(f'{temp_dir}/Dockerfile', 'w') as file:
file.write(dockerfile_content)
prep_docker_build_folder(temp_dir, base_image, skip_init=skip_init)
api_client = docker_client.api
build_logs = api_client.build(
path=temp_dir,
@ -193,7 +148,7 @@ def _build_sandbox_image(
raise e
def _get_new_image_name(base_image: str, dev_mode: bool = False) -> str:
def get_new_image_name(base_image: str, dev_mode: bool = False) -> str:
if dev_mode:
if 'od_runtime' not in base_image:
raise ValueError(
@ -201,6 +156,10 @@ def _get_new_image_name(base_image: str, dev_mode: bool = False) -> str:
)
# remove the 'od_runtime' prefix from the base_image
return base_image.replace('od_runtime', 'od_runtime_dev')
elif 'od_runtime' in base_image:
# if the base image is a valid od_runtime image, we will use it as is
logger.info(f'Using existing od_runtime image [{base_image}]')
return base_image
else:
prefix = 'od_runtime'
if ':' not in base_image:
@ -231,8 +190,13 @@ def build_runtime_image(
This is only used for **eventstream runtime**.
"""
new_image_name = _get_new_image_name(base_image)
logger.info(f'New image name: {new_image_name}')
new_image_name = get_new_image_name(base_image)
if base_image == new_image_name:
logger.info(
f'Using existing od_runtime image [{base_image}]. Will NOT build a new image.'
)
else:
logger.info(f'New image name: {new_image_name}')
# Ensure new_image_name contains a colon
if ':' not in new_image_name:
@ -264,7 +228,7 @@ def build_runtime_image(
# e.g., od_runtime:ubuntu_tag_latest -> od_runtime_dev:ubuntu_tag_latest
logger.info('Image exists, but updating source code requested')
base_image = new_image_name
new_image_name = _get_new_image_name(base_image, dev_mode=True)
new_image_name = get_new_image_name(base_image, dev_mode=True)
skip_init = True # since we only need to update the source code
else:
@ -302,15 +266,42 @@ def build_runtime_image(
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--base_image', type=str, default='ubuntu:22.04')
parser.add_argument('--update_source_code', type=bool, default=False)
parser.add_argument('--save_to_local_store', type=bool, default=False)
parser.add_argument('--update_source_code', action='store_true')
parser.add_argument('--save_to_local_store', action='store_true')
parser.add_argument('--build_folder', type=str, default=None)
args = parser.parse_args()
client = docker.from_env()
image_name = build_runtime_image(
args.base_image,
client,
update_source_code=args.update_source_code,
save_to_local_store=args.save_to_local_store,
)
print(f'\nBUILT Image: {image_name}\n')
if args.build_folder is not None:
build_folder = args.build_folder
assert os.path.exists(
build_folder
), f'Build folder {build_folder} does not exist'
logger.info(
f'Will prepare a build folder by copying the source code and generating the Dockerfile: {build_folder}'
)
new_image_path = get_new_image_name(args.base_image)
prep_docker_build_folder(
build_folder, args.base_image, skip_init=args.update_source_code
)
new_image_name, new_image_tag = new_image_path.split(':')
with open(os.path.join(build_folder, 'config.sh'), 'a') as file:
file.write(
(
f'DOCKER_IMAGE={new_image_name}\n'
f'DOCKER_IMAGE_TAG={new_image_tag}\n'
)
)
logger.info(
f'`config.sh` is updated with the new image name [{new_image_name}] and tag [{new_image_tag}]'
)
logger.info(f'Dockerfile and source distribution are ready in {build_folder}')
else:
logger.info('Building image in a temporary folder')
client = docker.from_env()
image_name = build_runtime_image(
args.base_image,
client,
update_source_code=args.update_source_code,
save_to_local_store=args.save_to_local_store,
)
print(f'\nBUILT Image: {image_name}\n')

View File

@ -0,0 +1,66 @@
{% if skip_init %}
FROM {{ base_image }}
{% else %}
# ================================================================
# START: Build Runtime Image from Scratch
# ================================================================
FROM {{ base_image }}
{% if 'ubuntu' in base_image and (base_image.endswith(':latest') or base_image.endswith(':24.04')) %}
{% set LIBGL_MESA = 'libgl1' %}
{% else %}
{% set LIBGL_MESA = 'libgl1-mesa-glx' %}
{% endif %}
# Install necessary packages and clean up in one layer
RUN apt-get update && \
apt-get install -y wget sudo apt-utils {{ LIBGL_MESA }} libasound2-plugins && \
apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Create necessary directories
RUN mkdir -p /opendevin && \
mkdir -p /opendevin/logs && \
chmod 777 /opendevin/logs && \
echo "" > /opendevin/bash.bashrc
RUN if [ ! -d /opendevin/miniforge3 ]; then \
wget --progress=bar:force -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" && \
bash Miniforge3.sh -b -p /opendevin/miniforge3 && \
rm Miniforge3.sh && \
chmod -R g+w /opendevin/miniforge3 && \
bash -c ". /opendevin/miniforge3/etc/profile.d/conda.sh && conda config --set changeps1 False && conda config --append channels conda-forge"; \
fi
# Install Python and Poetry
RUN /opendevin/miniforge3/bin/mamba install python=3.11 -y
RUN /opendevin/miniforge3/bin/mamba install conda-forge::poetry -y
# ================================================================
# END: Build Runtime Image from Scratch
# ================================================================
{% endif %}
# ================================================================
# START: Copy Project and Install/Update Dependencies
# ================================================================
COPY project.tar.gz /opendevin
RUN if [ -d /opendevin/code ]; then rm -rf /opendevin/code; fi
RUN cd /opendevin && tar -xzvf project.tar.gz && rm project.tar.gz
RUN mv /opendevin/{{ source_code_dirname }} /opendevin/code
# Install/Update Dependencies
# 1. Install pyproject.toml via poetry
# 2. Install playwright and chromium
# 3. Clear poetry, apt, mamba caches
RUN cd /opendevin/code && \
/opendevin/miniforge3/bin/mamba run -n base poetry env use python3.11 && \
/opendevin/miniforge3/bin/mamba run -n base poetry install --no-interaction --no-root && \
apt-get update && \
/opendevin/miniforge3/bin/mamba run -n base poetry run pip install playwright && \
/opendevin/miniforge3/bin/mamba run -n base poetry run playwright install --with-deps chromium && \
/opendevin/miniforge3/bin/mamba run -n base poetry cache clear --all . && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
/opendevin/miniforge3/bin/mamba clean --all
# ================================================================
# END: Copy Project and Install/Update Dependencies
# ================================================================

View File

@ -8,6 +8,7 @@ from unittest.mock import patch
import pytest
from opendevin.core.config import SandboxConfig
from opendevin.core.logger import opendevin_logger as logger
from opendevin.events import EventStream
from opendevin.events.action import (
CmdRunAction,
@ -32,6 +33,14 @@ async def _load_runtime(box_class, event_stream, plugins, sid):
sandbox_config = SandboxConfig(
use_host_network=False,
)
container_image = sandbox_config.container_image
# NOTE: we will use the default container image specified in the config.sandbox
# if it is an official od_runtime image.
if 'od_runtime' not in container_image:
container_image = 'ubuntu:22.04'
logger.warning(
f'`sandbox_config.container_image` is not an od_runtime image. Will use `{container_image}` as the container image for testing.'
)
if box_class == EventStreamRuntime:
runtime = EventStreamRuntime(
sandbox_config=sandbox_config,
@ -39,7 +48,7 @@ async def _load_runtime(box_class, event_stream, plugins, sid):
sid=sid,
# NOTE: we probably don't have a default container image `/sandbox` for the event stream runtime
# Instead, we will pre-build a suite of container images with OD-runtime-cli installed.
container_image='ubuntu:22.04',
container_image=container_image,
plugins=plugins,
)
await runtime.ainit()

View File

@ -9,9 +9,9 @@ import toml
from opendevin.runtime.utils.runtime_build import (
_generate_dockerfile,
_get_new_image_name,
_put_source_code_to_dir,
build_runtime_image,
get_new_image_name,
)
RUNTIME_IMAGE_PREFIX = 'od_runtime'
@ -95,44 +95,44 @@ def test_generate_dockerfile_skip_init():
def test_get_new_image_name_eventstream():
base_image = 'debian:11'
new_image_name = _get_new_image_name(base_image)
new_image_name = get_new_image_name(base_image)
assert new_image_name == f'{RUNTIME_IMAGE_PREFIX}:debian_tag_11'
base_image = 'ubuntu:22.04'
new_image_name = _get_new_image_name(base_image)
new_image_name = get_new_image_name(base_image)
assert new_image_name == f'{RUNTIME_IMAGE_PREFIX}:ubuntu_tag_22.04'
base_image = 'ubuntu'
new_image_name = _get_new_image_name(base_image)
new_image_name = get_new_image_name(base_image)
assert new_image_name == f'{RUNTIME_IMAGE_PREFIX}:ubuntu_tag_latest'
def test_get_new_image_name_eventstream_dev_mode():
base_image = f'{RUNTIME_IMAGE_PREFIX}:debian_tag_11'
new_image_name = _get_new_image_name(base_image, dev_mode=True)
new_image_name = get_new_image_name(base_image, dev_mode=True)
assert new_image_name == f'{RUNTIME_IMAGE_PREFIX}_dev:debian_tag_11'
base_image = f'{RUNTIME_IMAGE_PREFIX}:ubuntu_tag_22.04'
new_image_name = _get_new_image_name(base_image, dev_mode=True)
new_image_name = get_new_image_name(base_image, dev_mode=True)
assert new_image_name == f'{RUNTIME_IMAGE_PREFIX}_dev:ubuntu_tag_22.04'
base_image = f'{RUNTIME_IMAGE_PREFIX}:ubuntu_tag_latest'
new_image_name = _get_new_image_name(base_image, dev_mode=True)
new_image_name = get_new_image_name(base_image, dev_mode=True)
assert new_image_name == f'{RUNTIME_IMAGE_PREFIX}_dev:ubuntu_tag_latest'
def test_get_new_image_name_eventstream_dev_invalid_base_image():
with pytest.raises(ValueError):
base_image = 'debian:11'
_get_new_image_name(base_image, dev_mode=True)
get_new_image_name(base_image, dev_mode=True)
with pytest.raises(ValueError):
base_image = 'ubuntu:22.04'
_get_new_image_name(base_image, dev_mode=True)
get_new_image_name(base_image, dev_mode=True)
with pytest.raises(ValueError):
base_image = 'ubuntu:latest'
_get_new_image_name(base_image, dev_mode=True)
get_new_image_name(base_image, dev_mode=True)
@patch('opendevin.runtime.utils.runtime_build._build_sandbox_image')