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

pantsbuild / pants / 23559966294

25 Mar 2026 07:27PM UTC coverage: 92.882% (-0.04%) from 92.918%
23559966294

Pull #23133

github

web-flow
Merge b6f2381e0 into 9b3c1562e
Pull Request #23133: Add buildctl engine

289 of 337 new or added lines in 13 files covered. (85.76%)

2 existing lines in 2 files now uncovered.

91640 of 98663 relevant lines covered (92.88%)

4.06 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 `run` are:
173

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

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

187
            Use [buildx](https://github.com/docker/buildx#buildx) (and BuildKit) for builds.
188
            """
189
        ),
190
        deprecation_start_version="2.31.0",
191
        mutually_exclusive_group="engines",
192
    )
193

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

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

223
    @property
10✔
224
    def run_engine(self) -> DockerRunEngine:
10✔
NEW
225
        return self._experimental_enable_podman_warning(DockerRunEngine, "run")
×
226

227
    @property
10✔
228
    def push_engine(self) -> DockerPushEngine:
10✔
229
        return self._experimental_enable_podman_warning(DockerPushEngine, "push")
1✔
230

231
    _build_args = ShellStrListOption(
10✔
232
        help=softwrap(
233
            f"""
234
            Global build arguments (for Docker `--build-arg` options) to use for all
235
            `docker build` invocations.
236

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

240
            Example:
241

242
                [{options_scope}]
243
                build_args = ["VAR1=value", "VAR2"]
244

245

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

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

269
            Example:
270

271
                [{options_scope}]
272
                build_hosts = {{"docker": "10.180.0.1", "docker2": "10.180.0.2"}}
273

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

293
            Example:
294

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

298
            To provide the top-level options to the `docker` client, use
299
            `[{options_scope}].env_vars` to configure the
300
            [Environment variables]({doc_links["docker_env_vars"]}) as appropriate.
301

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

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

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

341
    tailor = BoolOption(
10✔
342
        default=True,
343
        help="If true, add `docker_image` targets with the `tailor` goal.",
344
        advanced=True,
345
    )
346

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

358
    push_on_package = EnumOption(
10✔
359
        default=DockerPushOnPackageBehavior.WARN,
360
        help=softwrap(
361
            """
362
            The behavior when a docker_image target would push to a registry during packaging
363
            (e.g., when output has push=true or type=registry).
364

365
            Options:
366
            - allow: Allow pushes during packaging
367
            - warn: Log a warning but continue with the push (default)
368
            - ignore: Skip building images that would push
369
            - error: Raise an error if an image would push
370
            """
371
        ),
372
    )
373

374
    @property
10✔
375
    def build_args(self) -> tuple[str, ...]:
10✔
376
        return tuple(sorted(set(self._build_args)))
6✔
377

378
    @property
10✔
379
    def tools(self) -> tuple[str, ...]:
10✔
380
        return tuple(sorted(set(self._tools)))
4✔
381

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

386
    @memoized_method
10✔
387
    def registries(self) -> DockerRegistries:
10✔
388
        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