Skip to content

Commit 900c3f3

Browse files
committed
feat: share host conda package cache with Docker containers
Mount the host's conda package cache directory read-only inside the Docker build container. This allows the container to reuse cached repodata and packages instead of re-downloading them from scratch. Changes: - Add _get_host_pkgs_dir() helper to locate the host's pkgs directory - Add share_host_cache parameter to RecipeBuilder (default: True) - Mount host pkgs dir as read-only volume in build_recipe() - Update BUILD_SCRIPT_TEMPLATE to prepend the mounted cache to pkgs_dirs - Add --no-share-host-cache CLI flag for opt-out The :ro mount prevents any corruption of the host cache. Conda handles read-only pkgs_dirs gracefully. If the host pkgs dir is not found, the feature is silently skipped with no change in behavior. Saves 30-90s per Docker build by avoiding redundant repodata downloads.
1 parent 975acdb commit 900c3f3

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

bioconda_utils/cli.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,11 @@ def do_lint(
619619
action="store_true",
620620
help="Disable live logging during the build process",
621621
)
622+
@arg(
623+
"--no-share-host-cache",
624+
action="store_true",
625+
help="Do not share the host's conda package cache with Docker containers.",
626+
)
622627
@arg("--exclude", nargs="+", help="Packages to exclude during this run")
623628
@arg(
624629
"--subdag-depth",
@@ -652,6 +657,7 @@ def build(
652657
record_build_failures=False,
653658
skiplist_leafs=False,
654659
disable_live_logs=False,
660+
no_share_host_cache=False,
655661
exclude=None,
656662
subdag_depth=None,
657663
):
@@ -694,6 +700,7 @@ def build(
694700
keep_image=keep_image,
695701
build_image=build_image,
696702
docker_base_image=docker_base_image,
703+
share_host_cache=not no_share_host_cache,
697704
)
698705
else:
699706
docker_builder = None

bioconda_utils/docker_utils.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,23 @@
6666
logger = logging.getLogger(__name__)
6767

6868

69+
def _get_host_pkgs_dir():
70+
"""Return the host's primary conda package cache directory."""
71+
try:
72+
from conda.base.context import context
73+
if context.pkgs_dirs:
74+
return context.pkgs_dirs[0]
75+
except Exception:
76+
pass
77+
conda_path = shutil.which("conda")
78+
if conda_path:
79+
root = os.path.dirname(os.path.dirname(os.path.realpath(conda_path)))
80+
pkgs_dir = os.path.join(root, "pkgs")
81+
if os.path.isdir(pkgs_dir):
82+
return pkgs_dir
83+
return None
84+
85+
6986
# ----------------------------------------------------------------------------
7087
# BUILD_SCRIPT_TEMPLATE
7188
# ----------------------------------------------------------------------------
@@ -83,6 +100,12 @@
83100
#!/bin/bash
84101
set -eo pipefail
85102
103+
# Share host's repodata/package cache if mounted (read-only)
104+
if [ -d "{self.container_pkgs_cache}" ]; then
105+
conda config --prepend pkgs_dirs {self.container_pkgs_cache}
106+
conda config --append pkgs_dirs /opt/conda/pkgs
107+
fi
108+
86109
# Add the host's mounted conda-bld dir so that we can use its contents as
87110
# dependencies for building this recipe.
88111
#
@@ -167,6 +190,7 @@ def __init__(
167190
build_image=False,
168191
image_build_dir=None,
169192
docker_base_image=None,
193+
share_host_cache=True,
170194
):
171195
"""
172196
Class to handle building a custom docker container that can be used for
@@ -248,6 +272,10 @@ def __init__(
248272
249273
docker_base_image : str or None
250274
Name of base image that can be used in **dockerfile_template**.
275+
276+
share_host_cache : bool
277+
If True, mount the host's conda package cache read-only in the
278+
container so repodata does not need to be re-downloaded.
251279
"""
252280
self.requirements = requirements
253281
self.conda_build_args = ""
@@ -295,6 +323,17 @@ def __init__(
295323
if not os.path.exists(self.pkg_dir):
296324
os.makedirs(self.pkg_dir)
297325
shutil.copyfile(config_file.path, dst_file)
326+
# Set up host package cache sharing
327+
self.container_pkgs_cache = "/opt/host-conda-pkgs-cache"
328+
if share_host_cache:
329+
self.host_pkgs_dir = _get_host_pkgs_dir()
330+
if self.host_pkgs_dir:
331+
logger.info("Will share host conda pkgs cache: %s", self.host_pkgs_dir)
332+
else:
333+
logger.debug("Host conda pkgs dir not found, skipping cache sharing")
334+
else:
335+
self.host_pkgs_dir = None
336+
298337
if self.build_image:
299338
self._build_image()
300339

@@ -479,6 +518,9 @@ def build_recipe(self, recipe_dir, build_args, env, noarch=False, live_logs=True
479518
"-v",
480519
"{0}:{1}".format(recipe_dir, self.container_recipe),
481520
]
521+
# Mount host's conda pkgs cache read-only to avoid re-downloading repodata
522+
if self.host_pkgs_dir:
523+
cmd += ["-v", f"{self.host_pkgs_dir}:{self.container_pkgs_cache}:ro"]
482524
cmd += env_list
483525
if self.build_image:
484526
cmd += [self.docker_temp_image]

0 commit comments

Comments
 (0)