Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
### Merge requirements satisfied?
- [ ] I have updated the documentation or no documentation changes are required.
- [ ] I have added tests to cover my changes.
- [ ] I have updated the base version in ``BASE_VERSION`` (if appropriate).
- [ ] I have updated the base ``version`` in [pyproject.toml](../pyproject.toml) (if appropriate).

1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

* Add python 3.13 support
* Migrate to uv for dependency management
* Optimize Docker build context: source directory is no longer copied when path is not explicitly specified, improving build performance when using only inject or dockerfile attributes

* ... undocumented versions, see GitHub tagged releases ...

Expand Down
7 changes: 6 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,12 @@ shows the different configuration options available:
# contained in the path configuration above.
#
# NOTE: you do not need to specify a path attribute if you inject all
# of the files you need, including a Dockerfile
# of the files you need, including a Dockerfile. When the path attribute
# is NOT explicitly specified (either as 'path: .' or 'build: .'), the
# source directory will NOT be copied into the build context. This
# optimization improves build performance by avoiding unnecessary file
# copying. Any files needed by the Dockerfile must be explicitly injected
# when not specifying a path.
#
# NOTE: if the destination is a directory then it must be indicated with
# an ending "/" or a "." component.
Expand Down
7 changes: 6 additions & 1 deletion buildrunner/docker/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def build_image(
buildargs=None,
platform=None,
target=None,
copy_source_path=True,
):
"""
Build a Docker image using the DockerBuilder class.
Expand All @@ -64,6 +65,7 @@ def build_image(
buildargs (dict): Build arguments to pass to the Docker client.
platform (str): Platform to build the image for.
target (str): Target stage to build.
copy_source_path (bool): Whether to copy the source path into the build context.
Returns:
str: The ID of the built image.
"""
Expand All @@ -77,6 +79,7 @@ def build_image(
timeout=timeout,
docker_registry=docker_registry,
temp_dir=temp_dir,
copy_source_path=copy_source_path,
)
try:
exit_code = builder.build(
Expand Down Expand Up @@ -112,12 +115,14 @@ def __init__(
timeout=None,
docker_registry=None,
temp_dir=None,
copy_source_path=True,
): # pylint: disable=too-many-arguments
self.path = path
self.inject = inject
self.temp_dir = temp_dir
self.dockerfile = None
self.cleanup_dockerfile = False
self.copy_source_path = copy_source_path

self.dockerfile, self.cleanup_dockerfile = get_dockerfile(
dockerfile, self.temp_dir
Expand Down Expand Up @@ -184,7 +189,7 @@ def build(

_fileobj = tempfile.NamedTemporaryFile(dir=self.temp_dir)
with tarfile.open(mode="w", fileobj=_fileobj) as tfile:
if self.path:
if self.path and self.copy_source_path:
tfile.add(self.path, arcname=".")
if self.inject:
for to_inject, dest in self.inject.items():
Expand Down
27 changes: 20 additions & 7 deletions buildrunner/docker/multiplatform_image_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ def _build_with_inject(
dockerfile: str,
build_args: dict,
build_kwargs: dict,
copy_source_path: bool = True,
) -> None:
if not path or not os.path.isdir(path):
LOGGER.warning(
Expand All @@ -217,12 +218,18 @@ def _build_with_inject(
dir=self._temp_dir, prefix=dir_prefix
) as tmp_dir:
context_dir = os.path.join(tmp_dir, f"{dir_prefix}/")
shutil.copytree(
path,
context_dir,
ignore=shutil.ignore_patterns(dir_prefix, ".git"),
symlinks=True,
)

if copy_source_path:
# Copy the entire source path as the build context
shutil.copytree(
path,
context_dir,
ignore=shutil.ignore_patterns(dir_prefix, ".git"),
symlinks=True,
)
else:
# Create an empty context directory
os.makedirs(context_dir, exist_ok=True)

for src, dest in inject.items():
src_path = os.path.join(path, src)
Expand All @@ -236,7 +243,7 @@ def _build_with_inject(
# Check to see if the dest dir exists, if not create it
dest_dir = os.path.dirname(dest_path)
if not os.path.isdir(dest_dir):
os.mkdir(dest_dir)
os.makedirs(dest_dir, exist_ok=True)

# Copy source to destination
if os.path.isdir(src_path):
Expand Down Expand Up @@ -296,6 +303,7 @@ def _build_single_image(
cache: Optional[bool] = None,
pull: bool = False,
secrets: Optional[List[str]] = None,
copy_source_path: bool = True,
) -> None:
"""
Builds a single image for the given platform.
Expand All @@ -310,6 +318,7 @@ def _build_single_image(
build_args (dict): The build args to pass to docker.
inject (dict): The files to inject into the build context.
secrets (List[str]): The secrets to pass to docker.
copy_source_path (bool): Whether to copy the source path into the build context.
"""
assert os.path.isdir(path) and os.path.exists(dockerfile), (
f"Either path {path} ({os.path.isdir(path)}) or file "
Expand Down Expand Up @@ -361,6 +370,7 @@ def _build_single_image(
dockerfile=dockerfile,
build_args=build_args,
build_kwargs=build_kwargs,
copy_source_path=copy_source_path,
)
else:
logs_itr = docker.buildx.build(
Expand Down Expand Up @@ -429,6 +439,7 @@ def build_multiple_images(
cache: Optional[bool] = None,
pull: bool = False,
secrets: Optional[List[str]] = None,
copy_source_path: bool = True,
) -> BuiltImageInfo:
"""
Builds multiple images for the given platforms. One image will be built for each platform.
Expand All @@ -441,6 +452,7 @@ def build_multiple_images(
:arg inject: The files to inject into the build context. Defaults to None.
:arg cache: If true, enables cache, defaults to False
:arg pull: If true, pulls image before build, defaults to False
:arg copy_source_path: Whether to copy the source path into the build context. Defaults to True.
:return: A BuiltImageInfo instance that describes the built images for each platform and can be used to track
final images as well
"""
Expand Down Expand Up @@ -525,6 +537,7 @@ def build_multiple_images(
cache,
pull,
secrets,
copy_source_path,
)
LOGGER.debug(f"Building {repo} for {platform}")
if use_threading:
Expand Down
4 changes: 4 additions & 0 deletions buildrunner/steprunner/tasks/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ def __init__(

self._import = step.import_param
self.path = step.path
# Track whether path was explicitly specified in the config
self._path_explicitly_specified = step.path is not None
self.dockerfile = step.dockerfile
self.target = step.target
self.nocache = step.no_cache
Expand Down Expand Up @@ -239,6 +241,7 @@ def run(self, context):
cache=not self.nocache,
pull=self.pull,
secrets=self.step.secrets,
copy_source_path=self._path_explicitly_specified,
)

# Set expected number of platforms
Expand Down Expand Up @@ -275,6 +278,7 @@ def run(self, context):
buildargs=self.buildargs,
platform=self.platform,
target=self.target,
copy_source_path=self._path_explicitly_specified,
)
context["image"] = image
self.step_runner.build_runner.generated_images.append(image)
Expand Down
3 changes: 3 additions & 0 deletions examples/build/inject/buildrunner.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# This example demonstrates how to use the inject field to inject a file into the build context.
# Note: This example does NOT specify a 'path' attribute. When path is not explicitly specified,
# the source directory is NOT copied into the build context, which improves build performance by
# avoiding unnecessary file copying. Only the explicitly injected files are included in the build context.
# See buildrunner documentation for more information.
steps:
build-step-with-inject:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "uv_build"

[project]
name = "buildrunner"
version = "3.18"
version = "3.19"
description = "Docker-based build tool"
readme = "README.rst"
requires-python = ">=3.9"
Expand Down
6 changes: 4 additions & 2 deletions tests/test-files/test-multi-platform-none-path.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ steps:
repository: adobe/buildrunner-mp-path-test
tags: [ 'latest' ]

# Test to may sure MP builds work without specifying a path
# Path is derived from the dockerfile path
# Test to make sure MP builds work without specifying a path
# Files needed by the Dockerfile are injected explicitly
build-container-single-platform-dockerfile:
build:
dockerfile: tests/test-files/multiplatform/Dockerfile.none.path
inject:
tests/test-files/multiplatform/buildrunner-path-test.txt: .
platforms:
- linux/amd64
- linux/arm64
Expand Down
Loading
Loading