Skip to content

Commit 2682638

Browse files
authored
Merge pull request #232 from shanejbrown/cleanup_intermediate_images
Always clean up intermediate multiplatform images on exit
2 parents bbe264c + 4029a0b commit 2682638

4 files changed

Lines changed: 80 additions & 2 deletions

File tree

buildrunner/docker/multiplatform_image_builder.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,29 @@ def __enter__(self):
112112
return self
113113

114114
def __exit__(self, exc_type, exc_value, traceback):
115+
self._cleanup_intermediate_images()
115116
if self._local_registry_is_running:
116117
self._stop_local_registry()
117118

119+
def _cleanup_intermediate_images(self):
120+
"""Remove all intermediate multiplatform images from the local Docker daemon."""
121+
docker_client = new_client()
122+
for built_image in self._built_images:
123+
for platform_image in built_image.built_images:
124+
try:
125+
LOGGER.info(
126+
f"Removing intermediate mp image {platform_image.image_ref}"
127+
)
128+
docker_client.remove_image(
129+
platform_image.image_ref,
130+
noprune=False,
131+
force=True,
132+
)
133+
except Exception as err: # pylint: disable=broad-except
134+
LOGGER.warning(
135+
f"Failed to remove intermediate mp image {platform_image.image_ref}: {err}"
136+
)
137+
118138
def set_cache_from(self, cache_from):
119139
self._cache_from = cache_from
120140

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "uv_build"
44

55
[project]
66
name = "buildrunner"
7-
version = "3.24"
7+
version = "3.25"
88
description = "Docker-based build tool"
99
readme = "README.rst"
1010
requires-python = ">=3.9"

tests/test_multiplatform.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,3 +826,61 @@ def test_push_calls(mock_docker, mock_config, tagged_images, expected_call_count
826826
mpib.push()
827827

828828
assert mock_docker.call_count == expected_call_count
829+
830+
831+
@pytest.mark.serial
832+
@patch("buildrunner.docker.multiplatform_image_builder.new_client")
833+
@patch("buildrunner.docker.multiplatform_image_builder.docker.image.remove")
834+
@patch("buildrunner.docker.multiplatform_image_builder.docker.push")
835+
@patch(
836+
"buildrunner.docker.multiplatform_image_builder.docker.buildx.imagetools.inspect"
837+
)
838+
@patch("buildrunner.docker.multiplatform_image_builder.docker.buildx.build")
839+
def test_cleanup_intermediate_images_on_exit(
840+
mock_build,
841+
mock_imagetools_inspect,
842+
mock_push,
843+
mock_pow_remove,
844+
mock_new_client,
845+
):
846+
mock_docker_client = MagicMock()
847+
mock_new_client.return_value = mock_docker_client
848+
mock_imagetools_inspect.return_value = MagicMock()
849+
mock_imagetools_inspect.return_value.config.digest = "fakedigest"
850+
851+
platforms = ["linux/amd64", "linux/arm64"]
852+
test_path = f"{TEST_DIR}/test-files/multiplatform"
853+
854+
with MultiplatformImageBuilder() as mpib:
855+
built_image = mpib.build_multiple_images(
856+
platforms=platforms,
857+
path=test_path,
858+
file=f"{test_path}/Dockerfile",
859+
use_threading=False,
860+
)
861+
image_refs = [img.image_ref for img in built_image.built_images]
862+
assert len(image_refs) == 2
863+
864+
remove_calls = mock_docker_client.remove_image.call_args_list
865+
removed_refs = [c[0][0] for c in remove_calls]
866+
for ref in image_refs:
867+
assert ref in removed_refs, f"Expected {ref} to be removed on exit"
868+
869+
870+
def test_cleanup_intermediate_images_on_exit_integration():
871+
test_path = f"{TEST_DIR}/test-files/multiplatform"
872+
platforms = ["linux/arm64"]
873+
874+
with MultiplatformImageBuilder() as mpib:
875+
built_image = mpib.build_multiple_images(
876+
platforms=platforms,
877+
path=test_path,
878+
file=f"{test_path}/Dockerfile",
879+
use_threading=False,
880+
)
881+
image_ref = built_image.built_images[0].image_ref
882+
found = docker.image.list(filters={"reference": image_ref})
883+
assert len(found) == 1, "Image should exist before exit"
884+
885+
found = docker.image.list(filters={"reference": image_ref})
886+
assert len(found) == 0, "Intermediate image should be removed after exit"

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)