• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 24791541718

22 Apr 2026 05:01PM UTC coverage: 92.911% (-0.02%) from 92.926%
24791541718

Pull #23133

github

web-flow
Merge d56db518e into 3d0987454
Pull Request #23133: Add buildctl engine

408 of 440 new or added lines in 13 files covered. (92.73%)

2 existing lines in 2 files now uncovered.

91882 of 98892 relevant lines covered (92.91%)

4.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.87
/src/python/pants/backend/docker/subsystems/docker_options.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
10✔
5

6
import logging
10✔
7
import sys
10✔
8
from typing import Any, cast
10✔
9

10
from pants.backend.docker.engine_types import (
10✔
11
    DockerBuildEngine,
12
    DockerEngines,
13
    DockerPushEngine,
14
    DockerRunEngine,
15
)
16
from pants.backend.docker.package_types import DockerPushOnPackageBehavior
10✔
17
from pants.backend.docker.registries import DockerRegistries
10✔
18
from pants.core.util_rules.search_paths import ExecutableSearchPathsOptionMixin
10✔
19
from pants.option.option_types import (
10✔
20
    BoolOption,
21
    DataclassOption,
22
    DictOption,
23
    EnumOption,
24
    ShellStrListOption,
25
    StrListOption,
26
    StrOption,
27
    WorkspacePathOption,
28
)
29
from pants.option.subsystem import Subsystem
10✔
30
from pants.util.docutil import bin_name
10✔
31
from pants.util.memo import memoized_method
10✔
32
from pants.util.strutil import bullet_list, softwrap
10✔
33

34
doc_links = {
10✔
35
    "docker_env_vars": (
36
        "https://docs.docker.com/engine/reference/commandline/cli/#environment-variables"
37
    ),
38
}
39

40
logger = logging.getLogger(__name__)
10✔
41

42

43
class DockerOptions(Subsystem):
10✔
44
    options_scope = "docker"
10✔
45
    help = "Options for interacting with Docker."
10✔
46

47
    class EnvironmentAware(ExecutableSearchPathsOptionMixin, Subsystem.EnvironmentAware):
10✔
48
        _env_vars = ShellStrListOption(
10✔
49
            help=softwrap(
50
                """
51
                Environment variables to set for `docker` invocations.
52

53
                Entries are either strings in the form `ENV_VAR=value` to set an explicit value;
54
                or just `ENV_VAR` to copy the value from Pants's own environment.
55
                """
56
            ),
57
            advanced=True,
58
        )
59
        executable_search_paths_help = softwrap(
10✔
60
            """
61
            The PATH value that will be used to find the Docker client and any tools required.
62
            """
63
        )
64

65
        @property
10✔
66
        def env_vars(self) -> tuple[str, ...]:
10✔
67
            return tuple(sorted(set(self._env_vars)))
5✔
68

69
    _registries = DictOption[Any](
70
        help=softwrap(
71
            """
72
            Configure Docker registries. The schema for a registry entry is as follows:
73

74
                {
75
                    "registry-alias": {
76
                        "address": "registry-domain:port",
77
                        "default": bool,
78
                        "extra_image_tags": [],
79
                        "skip_push": bool,
80
                        "repository": str,
81
                        "use_local_alias": bool,
82
                    },
83
                    ...
84
                }
85

86
            If no registries are provided in a `docker_image` target, then all default
87
            addresses will be used, if any.
88

89
            The `docker_image.registries` may be provided with a list of registry addresses
90
            and registry aliases prefixed with `@` to be used instead of the defaults.
91

92
            A configured registry is marked as default either by setting `default = true`
93
            or with an alias of `"default"`.
94

95
            A `docker_image` may be pushed to a subset of registries using the per registry
96
            `skip_push` option rather then the all or nothing toggle of the field option `skip_push`
97
            on the `docker_image` target.
98

99
            Any image tags that should only be added for specific registries may be provided as the
100
            `extra_image_tags` option. The tags may use value formatting the same as for the
101
            `image_tags` field of the `docker_image` target.
102

103
            When a registry provides a `repository` value, it will be used instead of the
104
            `docker_image.repository` or the default repository. Using the placeholders
105
            `{target_repository}` or `{default_repository}` those overridden values may be
106
            incorporated into the registry specific repository value.
107

108
            If `use_local_alias` is true, a built image is additionally tagged locally using the
109
            registry alias as the value for repository (i.e. the additional image tag is not pushed)
110
            and will be used for any `pants run` requests.
111
            """
112
        ),
113
        fromfile=True,
114
    )
115
    default_repository = StrOption(
10✔
116
        help=softwrap(
117
            f"""
118
            Configure the default repository name used in the Docker image tag.
119

120
            The value is formatted and may reference these variables (in addition to the normal
121
            placeholders derived from the Dockerfile and build args etc):
122

123
            {bullet_list(["name", "directory", "parent_directory", "full_directory", "target_repository"])}
124

125
            Example: `--default-repository="{{directory}}/{{name}}"`.
126

127
            The `name` variable is the `docker_image`'s target name.
128

129
            With the directory variables available, given a sample repository path of `baz/foo/bar/BUILD`,
130
            then `directory` is `bar`, `parent_directory` is `foo` and `full_directory` will be `baz/foo/bar`.
131

132
            Use the `repository` field to set this value directly on a `docker_image` target.
133

134
            Registries may override the repository value for a specific registry.
135

136
            Any registries or tags are added to the image name as required, and should
137
            not be part of the repository name.
138
            """
139
        ),
140
        default="{name}",
141
    )
142
    default_context_root = WorkspacePathOption(
10✔
143
        default="",
144
        help=softwrap(
145
            """
146
            Provide a default Docker build context root path for `docker_image` targets that
147
            does not specify their own `context_root` field.
148

149
            The context root is relative to the build root by default, but may be prefixed
150
            with `./` to be relative to the directory of the BUILD file of the `docker_image`.
151

152
            Examples:
153

154
                --default-context-root=src/docker
155
                --default-context-root=./relative_to_the_build_file
156
            """
157
        ),
158
    )
159
    engine = DataclassOption(
10✔
160
        default=DockerEngines(),
161
        mutually_exclusive_group="engines",
162
        help=softwrap(
163
            """
164
        The engines to use for Docker builds and runs.
165

166
        Valid values for `build` are:
167

168
        - `docker`: Use the Docker CLI to build images. (https://docs.docker.com/reference/cli/docker/buildx/build/)
169
        - `buildkit`: Invoke buildkit directly to build images. (https://github.com/moby/buildkit/blob/master/docs/reference/buildctl.md#build)
170
        - `podman`: Use Podman to build images. (https://docs.podman.io/en/latest/markdown/podman-build.1.html)
171

172
        Valid values for `push` are:
173

174
        - `docker`: Use the Docker CLI to push images. (https://docs.docker.com/reference/cli/docker/image/push/)
175
        - `podman`: Use Podman to push images. (https://docs.podman.io/en/latest/markdown/podman-push.1.html)
176

177
        Valid values for `run` are:
178

179
        - `docker`: Use the Docker CLI to run containers. (https://docs.docker.com/reference/cli/docker/run/)
180
        - `podman`: Use Podman to run containers. (https://docs.podman.io/en/latest/markdown/podman-run.1.html)
181
        """
182
        ),
183
    )
184
    use_buildx = BoolOption(
10✔
185
        default=True,
186
        help=softwrap(
187
            """
188
            DEPRECATED: Use [docker.engine].build = "docker" instead.
189

190
            See here for using the legacy builder: https://docs.docker.com/reference/cli/docker/build-legacy/
191

192
            Use [buildx](https://github.com/docker/buildx#buildx) (and BuildKit) for builds.
193
            """
194
        ),
195
        deprecation_start_version="2.31.0",
196
        mutually_exclusive_group="engines",
197
    )
198

199
    def _experimental_enable_podman_warning[
10✔
200
        E: DockerBuildEngine | DockerRunEngine | DockerPushEngine
201
    ](self, engine_type: type[E], engine_opt: str) -> E:
202
        experimental_enable_podman = self.options.get("experimental_enable_podman", None)
2✔
203
        match experimental_enable_podman:
2✔
204
            case None:
2✔
205
                return cast(E, getattr(self.engine, engine_opt))
2✔
NEW
206
            case True:
×
NEW
207
                engine = cast(E, engine_type.PODMAN)
×
NEW
208
            case False:
×
NEW
209
                engine = cast(E, engine_type.DOCKER)
×
NEW
210
        logger.warning(
×
211
            f'`[docker].experimental_enable_podman` is deprecated. Use `[docker.engine].{engine_opt} = "{engine.value}"` instead.'
212
        )
NEW
213
        return engine
×
214

215
    @property
10✔
216
    def build_engine(self) -> DockerBuildEngine:
10✔
217
        use_buildx = self.options.get("use_buildx")
5✔
218
        if use_buildx is not None:
5✔
219
            warning = '`[docker].use_buildx` is deprecated. Buildx is now the default Docker build engine. Use `[docker.engine].build = "docker"` instead.'
4✔
220
            if not use_buildx:
4✔
NEW
221
                warning += (
×
222
                    " To use the legacy engine, add `DOCKER_BUILDKIT=0` to `[docker].env_vars`."
223
                )
224
            logger.warning(warning)
4✔
225
            return DockerBuildEngine.DOCKER
4✔
226
        return self._experimental_enable_podman_warning(DockerBuildEngine, "build")
2✔
227

228
    @property
10✔
229
    def run_engine(self) -> DockerRunEngine:
10✔
NEW
230
        return self._experimental_enable_podman_warning(DockerRunEngine, "run")
×
231

232
    @property
10✔
233
    def push_engine(self) -> DockerPushEngine:
10✔
234
        return self._experimental_enable_podman_warning(DockerPushEngine, "push")
1✔
235

236
    _build_args = ShellStrListOption(
10✔
237
        help=softwrap(
238
            f"""
239
            Global build arguments (for Docker `--build-arg` options) to use for all
240
            `docker build` invocations.
241

242
            Entries are either strings in the form `ARG_NAME=value` to set an explicit value;
243
            or just `ARG_NAME` to copy the value from Pants's own environment.
244

245
            Example:
246

247
                [{options_scope}]
248
                build_args = ["VAR1=value", "VAR2"]
249

250

251
            Use the `extra_build_args` field on a `docker_image` target for additional
252
            image specific build arguments.
253
            """
254
        ),
255
    )
256
    build_target_stage = StrOption(
10✔
257
        default=None,
258
        help=softwrap(
259
            """
260
            Global default value for `target_stage` on `docker_image` targets, overriding
261
            the field value on the targets, if there is a matching stage in the `Dockerfile`.
262

263
            This is useful to provide from the command line, to specify the target stage to
264
            build for at execution time.
265
            """
266
        ),
267
    )
268
    build_hosts = DictOption[str](
10✔
269
        default={},
270
        help=softwrap(
271
            f"""
272
            Hosts entries to be added to the `/etc/hosts` file in all built images.
273

274
            Example:
275

276
                [{options_scope}]
277
                build_hosts = {{"docker": "10.180.0.1", "docker2": "10.180.0.2"}}
278

279
            Use the `extra_build_hosts` field on a `docker_image` target for additional
280
            image specific host entries.
281
            """
282
        ),
283
    )
284
    build_no_cache = BoolOption(
10✔
285
        default=False,
286
        help="Do not use the Docker cache when building images.",
287
    )
288
    build_verbose = BoolOption(
10✔
289
        default=False,
290
        help="Whether to log the Docker output to the console. If false, only the image ID is logged.",
291
    )
292
    run_args = ShellStrListOption(
10✔
293
        default=["--interactive", "--tty"] if sys.stdout.isatty() else [],
294
        help=softwrap(
295
            f"""
296
            Additional arguments to use for `docker run` invocations.
297

298
            Example:
299

300
                $ {bin_name()} run --{options_scope}-run-args="-p 127.0.0.1:80:8080/tcp\
301
                    --name demo" src/example:image -- [image entrypoint args]
302

303
            To provide the top-level options to the `docker` client, use
304
            `[{options_scope}].env_vars` to configure the
305
            [Environment variables]({doc_links["docker_env_vars"]}) as appropriate.
306

307
            The arguments for the image entrypoint may be passed on the command line after a
308
            double dash (`--`), or using the `--run-args` option.
309

310
            Defaults to `--interactive --tty` when stdout is connected to a terminal.
311
            """
312
        ),
313
    )
314
    publish_noninteractively = BoolOption(
10✔
315
        default=False,
316
        help=softwrap(
317
            """
318
            If true, publish images non-interactively. This allows for pushes to be parallelized, but requires
319
            docker to be pre-authenticated to the registries to which it is pushing.
320
            """
321
        ),
322
    )
323
    _tools = StrListOption(
10✔
324
        default=[],
325
        help=softwrap(
326
            """
327
            List any additional executable tools required for Docker to work. The paths to
328
            these tools will be included in the PATH used in the execution sandbox, so that
329
            they may be used by the Docker client.
330
            """
331
        ),
332
        advanced=True,
333
    )
334

335
    _optional_tools = StrListOption(
10✔
336
        help=softwrap(
337
            """
338
            List any additional executables which are not mandatory for Docker to work, but which
339
            should be included if available. The paths to these tools will be included in the
340
            PATH used in the execution sandbox, so that they may be used by the Docker client.
341
            """
342
        ),
343
        advanced=True,
344
    )
345

346
    tailor = BoolOption(
10✔
347
        default=True,
348
        help="If true, add `docker_image` targets with the `tailor` goal.",
349
        advanced=True,
350
    )
351

352
    suggest_renames = BoolOption(
10✔
353
        default=True,
354
        help=softwrap(
355
            """
356
            When true and, the `docker_image` build fails, enrich the logs with suggestions
357
            for renaming source file COPY instructions where possible.
358
            """
359
        ),
360
        advanced=True,
361
    )
362

363
    push_on_package = EnumOption(
10✔
364
        default=DockerPushOnPackageBehavior.WARN,
365
        help=softwrap(
366
            """
367
            The behavior when a docker_image target would push to a registry during packaging
368
            (e.g., when output has push=true or type=registry).
369

370
            Options:
371
            - allow: Allow pushes during packaging
372
            - warn: Log a warning but continue with the push (default)
373
            - ignore: Skip building images that would push
374
            - error: Raise an error if an image would push
375
            """
376
        ),
377
    )
378

379
    @property
10✔
380
    def build_args(self) -> tuple[str, ...]:
10✔
381
        return tuple(sorted(set(self._build_args)))
6✔
382

383
    @property
10✔
384
    def tools(self) -> tuple[str, ...]:
10✔
385
        return tuple(sorted(set(self._tools)))
4✔
386

387
    @property
10✔
388
    def optional_tools(self) -> tuple[str, ...]:
10✔
389
        return tuple(sorted(set(self._optional_tools)))
4✔
390

391
    @memoized_method
10✔
392
    def registries(self) -> DockerRegistries:
10✔
393
        return DockerRegistries.from_dict(self._registries)
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc