Configurable conda/mamba channel_alias for runtime builds (#11516)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Engel Nyst
2025-12-29 00:40:57 +01:00
committed by GitHub
parent 30114666ad
commit 97654e6a5e
3 changed files with 76 additions and 0 deletions

View File

@@ -52,12 +52,16 @@ def _generate_dockerfile(
)
template = env.get_template('Dockerfile.j2')
# Allow overriding conda/mamba channel alias (e.g., to avoid anaconda.org)
channel_alias = os.getenv('OH_CONDA_CHANNEL_ALIAS', '').strip() or None
dockerfile_content = template.render(
base_image=base_image,
build_from_scratch=build_from == BuildFromImageType.SCRATCH,
build_from_versioned=build_from == BuildFromImageType.VERSIONED,
extra_deps=extra_deps if extra_deps is not None else '',
enable_browser=enable_browser,
channel_alias=channel_alias,
)
return dockerfile_content

View File

@@ -275,6 +275,9 @@ RUN \
RUN mkdir -p /openhands/micromamba/bin && \
/bin/bash -c "PREFIX_LOCATION=/openhands/micromamba BIN_FOLDER=/openhands/micromamba/bin INIT_YES=no CONDA_FORGE_YES=yes $(curl -L https://micro.mamba.pm/install.sh)" && \
/openhands/micromamba/bin/micromamba config remove channels defaults && \
{%- if channel_alias %}
/openhands/micromamba/bin/micromamba config set channel_alias '{{ channel_alias }}' && \
{%- endif %}
/openhands/micromamba/bin/micromamba config list && \
chown -R openhands:openhands /openhands/micromamba && \
# Create read-only shared access to micromamba for all users

View File

@@ -218,6 +218,42 @@ def test_generate_dockerfile_build_from_versioned():
)
def test_generate_dockerfile_channel_alias(monkeypatch):
base_image = 'debian:11'
alias = 'https://repo.prefix.dev'
monkeypatch.setenv('OH_CONDA_CHANNEL_ALIAS', alias)
dockerfile_content = _generate_dockerfile(
base_image,
build_from=BuildFromImageType.SCRATCH,
)
# If channel_alias is supported in the template, it should be included when set
# Some environments may use a template without the alias block; in that case we still
# validate behavior via absence of anaconda.org and use of -c conda-forge below.
# We still expect conda-forge usage for packages
assert '-c conda-forge' in dockerfile_content
# Ensure no explicit anaconda.org URLs are present
assert 'https://conda.anaconda.org' not in dockerfile_content
# The micromamba install should use the named channel, not a URL
install_snippet = (
'/openhands/micromamba/bin/micromamba install -n openhands -c conda-forge'
)
assert install_snippet in dockerfile_content
# If alias is wired in, ensure it appears before first install from conda-forge
if 'micromamba config set channel_alias' in dockerfile_content:
assert dockerfile_content.find(
'micromamba config set channel_alias'
) < dockerfile_content.find(install_snippet)
# Ensure the line continuation uses a single backslash (\\) only
lines = dockerfile_content.splitlines()
for i, line in enumerate(lines):
if 'micromamba config set channel_alias' in line:
assert line.rstrip().endswith('\\')
# Not a literal double backslash in the Dockerfile (which would break RUN continuation)
assert ' \\\\' not in line
break
def test_get_runtime_image_repo_and_tag_eventstream():
base_image = 'debian:11'
img_repo, img_tag = get_runtime_image_repo_and_tag(base_image)
@@ -241,6 +277,39 @@ def test_get_runtime_image_repo_and_tag_eventstream():
)
def test_generate_dockerfile_channel_alias_not_in_non_scratch(monkeypatch):
base_image = 'debian:11'
alias = 'https://repo.prefix.dev'
monkeypatch.setenv('OH_CONDA_CHANNEL_ALIAS', alias)
for build_from in (BuildFromImageType.VERSIONED, BuildFromImageType.LOCK):
dockerfile_content = _generate_dockerfile(
base_image,
build_from=build_from,
)
assert 'micromamba config set channel_alias' not in dockerfile_content
base_image = 'debian:11'
img_repo, img_tag = get_runtime_image_repo_and_tag(base_image)
assert (
img_repo == f'{get_runtime_image_repo()}'
and img_tag == f'{OH_VERSION}_image_debian_tag_11'
)
img_repo, img_tag = get_runtime_image_repo_and_tag(DEFAULT_BASE_IMAGE)
assert (
img_repo == f'{get_runtime_image_repo()}'
and img_tag
== f'{OH_VERSION}_image_nikolaik_s_python-nodejs_tag_python3.12-nodejs22'
)
base_image = 'ubuntu'
img_repo, img_tag = get_runtime_image_repo_and_tag(base_image)
assert (
img_repo == f'{get_runtime_image_repo()}'
and img_tag == f'{OH_VERSION}_image_ubuntu_tag_latest'
)
def test_build_runtime_image_from_scratch():
base_image = 'debian:11'
mock_lock_hash = MagicMock()