diff --git a/docs/notes/2.32.x.md b/docs/notes/2.32.x.md index a161ceeee9f..ef7833eb33f 100644 --- a/docs/notes/2.32.x.md +++ b/docs/notes/2.32.x.md @@ -32,6 +32,8 @@ The plugin API's `Get()` and `MultiGet()` constructs, deprecated in 2.30, are no The option `[docker].push_on_package` can be used to prevent Docker images from being pushed during packaging, i.e. when `--output` contains `push=True` or `type=registry`. +New `extra_args_for_docker_build` field on `docker_image` allowing adding extra arguments that are passed into `docker build ...` invocation when building images with `pants package`. This can also be configured globally via `[docker].args_for_docker_build` in `pants.toml`. + #### Helm When `pants publish` is invoked, Pants will now skip packaging for `helm_chart` targets if either `skip_push=True` or the target has no registries. diff --git a/src/python/pants/backend/docker/goals/package_image.py b/src/python/pants/backend/docker/goals/package_image.py index 35f4acb3985..88a692053e5 100644 --- a/src/python/pants/backend/docker/goals/package_image.py +++ b/src/python/pants/backend/docker/goals/package_image.py @@ -29,6 +29,7 @@ DockerBuildOptionFieldMultiValueMixin, DockerBuildOptionFieldValueMixin, DockerBuildOptionFlagFieldMixin, + DockerImageBuildExtraArgsForDockerBuild, DockerImageBuildImageOutputField, DockerImageContextRootField, DockerImageRegistriesField, @@ -89,6 +90,7 @@ class DockerPackageFieldSet(PackageFieldSet): target_stage: DockerImageTargetStageField output_path: OutputPathField output: DockerImageBuildImageOutputField + extra_args_for_docker_build: DockerImageBuildExtraArgsForDockerBuild def pushes_on_package(self) -> bool: """Returns True if this docker_image target would push to a registry during packaging.""" @@ -330,6 +332,7 @@ def get_build_options( global_target_stage_option: str | None, global_build_hosts_options: dict | None, global_build_no_cache_option: bool | None, + global_args_for_docker_build: tuple[str, ...], use_buildx_option: bool, target: Target, ) -> Iterator[str]: @@ -393,6 +396,9 @@ def get_build_options( if global_build_no_cache_option: yield "--no-cache" + yield from global_args_for_docker_build + yield from field_set.extra_args_for_docker_build.value or () + @rule async def build_docker_image( @@ -482,6 +488,7 @@ async def build_docker_image( global_target_stage_option=options.build_target_stage, global_build_hosts_options=options.build_hosts, global_build_no_cache_option=options.build_no_cache, + global_args_for_docker_build=options.args_for_docker_build, use_buildx_option=options.use_buildx, target=wrapped_target.target, ) diff --git a/src/python/pants/backend/docker/goals/package_image_test.py b/src/python/pants/backend/docker/goals/package_image_test.py index 71f6670c70b..824dd515350 100644 --- a/src/python/pants/backend/docker/goals/package_image_test.py +++ b/src/python/pants/backend/docker/goals/package_image_test.py @@ -187,6 +187,7 @@ def mock_get_info_file(request: CreateDigest) -> Digest: opts.setdefault("env_vars", []) opts.setdefault("suggest_renames", True) opts.setdefault("push_on_package", DockerPushOnPackageBehavior.WARN) + opts.setdefault("args_for_docker_build", []) docker_options = create_subsystem( DockerOptions, @@ -947,6 +948,54 @@ def check_docker_proc(process: Process): ) +def test_docker_extra_args_for_docker_build(rule_runner: RuleRunner) -> None: + rule_runner.write_files( + { + "docker/test/BUILD": dedent( + """\ + docker_image( + name="img1", + extra_args_for_docker_build=[ + "--shm-size", + "256m", + ] + ) + """ + ), + } + ) + rule_runner.set_options( + [ + "--docker-args-for-docker-build=--progress plain", + "--docker-args-for-docker-build=--label my_label", + ], + ) + + def check_docker_proc(process: Process): + assert process.argv == ( + "/dummy/docker", + "build", + "--pull=False", + "--progress", + "plain", + "--label", + "my_label", + "--shm-size", + "256m", + "--tag", + "img1:latest", + "--file", + "docker/test/Dockerfile", + ".", + ) + + assert_build( + rule_runner, + Address("docker/test", target_name="img1"), + process_assertions=check_docker_proc, + ) + + def test_docker_build_secrets_option(rule_runner: RuleRunner) -> None: rule_runner.write_files( { diff --git a/src/python/pants/backend/docker/subsystems/docker_options.py b/src/python/pants/backend/docker/subsystems/docker_options.py index 1787b3a0adf..ed7c68609f8 100644 --- a/src/python/pants/backend/docker/subsystems/docker_options.py +++ b/src/python/pants/backend/docker/subsystems/docker_options.py @@ -233,6 +233,18 @@ def env_vars(self) -> tuple[str, ...]: """ ), ) + args_for_docker_build = ShellStrListOption( + default=[], + help=softwrap( + f""" + Additional arguments to use for `docker build` invocations. + + Example: + $ {bin_name()} package --{options_scope}-args-for-docker-build="--shm-size 256m"\ + src/example:image + """ + ), + ) publish_noninteractively = BoolOption( default=False, help=softwrap( diff --git a/src/python/pants/backend/docker/target_types.py b/src/python/pants/backend/docker/target_types.py index 47a24701821..5bad2fbab4e 100644 --- a/src/python/pants/backend/docker/target_types.py +++ b/src/python/pants/backend/docker/target_types.py @@ -588,6 +588,14 @@ class DockerImageBuildPlatformOptionField( docker_build_option = "--platform" +class DockerImageBuildExtraArgsForDockerBuild(StringSequenceField): + alias = "extra_args_for_docker_build" + default = () + help = help_text( + lambda: f"Extra arguments to pass into the invocation of `docker build`. These are in addition to those at the `[{DockerOptions.options_scope}].args_for_docker_build`" + ) + + class DockerImageRunExtraArgsField(StringSequenceField): alias: ClassVar[str] = "extra_run_args" default = () @@ -621,6 +629,7 @@ class DockerImageTarget(Target): DockerImageBuildImageCacheToField, DockerImageBuildImageCacheFromField, DockerImageBuildImageOutputField, + DockerImageBuildExtraArgsForDockerBuild, DockerImageRunExtraArgsField, OutputPathField, RestartableField,