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

pantsbuild / pants / 22361640215

24 Feb 2026 05:09PM UTC coverage: 92.342% (-0.6%) from 92.935%
22361640215

Pull #23133

github

web-flow
Merge fd48a7577 into 4d038bd74
Pull Request #23133: Add buildctl engine

194 of 282 new or added lines in 10 files covered. (68.79%)

452 existing lines in 24 files now uncovered.

89670 of 97106 relevant lines covered (92.34%)

4.01 hits per line

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

82.28
/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
10✔
9

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

29
doc_links = {
10✔
30
    "docker_env_vars": (
31
        "https://docs.docker.com/engine/reference/commandline/cli/#environment-variables"
32
    ),
33
}
34

35
logger = logging.getLogger(__name__)
10✔
36

37

38
class DockerOptions(Subsystem):
10✔
39
    options_scope = "docker"
10✔
40
    help = "Options for interacting with Docker."
10✔
41

42
    class EnvironmentAware(ExecutableSearchPathsOptionMixin, Subsystem.EnvironmentAware):
10✔
43
        _env_vars = ShellStrListOption(
10✔
44
            help=softwrap(
45
                """
46
                Environment variables to set for `docker` invocations.
47

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

60
        @property
10✔
61
        def env_vars(self) -> tuple[str, ...]:
10✔
62
            return tuple(sorted(set(self._env_vars)))
3✔
63

64
    _registries = DictOption[Any](
65
        help=softwrap(
66
            """
67
            Configure Docker registries. The schema for a registry entry is as follows:
68

69
                {
70
                    "registry-alias": {
71
                        "address": "registry-domain:port",
72
                        "default": bool,
73
                        "extra_image_tags": [],
74
                        "skip_push": bool,
75
                        "repository": str,
76
                        "use_local_alias": bool,
77
                    },
78
                    ...
79
                }
80

81
            If no registries are provided in a `docker_image` target, then all default
82
            addresses will be used, if any.
83

84
            The `docker_image.registries` may be provided with a list of registry addresses
85
            and registry aliases prefixed with `@` to be used instead of the defaults.
86

87
            A configured registry is marked as default either by setting `default = true`
88
            or with an alias of `"default"`.
89

90
            A `docker_image` may be pushed to a subset of registries using the per registry
91
            `skip_push` option rather then the all or nothing toggle of the field option `skip_push`
92
            on the `docker_image` target.
93

94
            Any image tags that should only be added for specific registries may be provided as the
95
            `extra_image_tags` option. The tags may use value formatting the same as for the
96
            `image_tags` field of the `docker_image` target.
97

98
            When a registry provides a `repository` value, it will be used instead of the
99
            `docker_image.repository` or the default repository. Using the placeholders
100
            `{target_repository}` or `{default_repository}` those overridden values may be
101
            incorporated into the registry specific repository value.
102

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

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

118
            {bullet_list(["name", "directory", "parent_directory", "full_directory", "target_repository"])}
119

120
            Example: `--default-repository="{{directory}}/{{name}}"`.
121

122
            The `name` variable is the `docker_image`'s target name.
123

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

127
            Use the `repository` field to set this value directly on a `docker_image` target.
128

129
            Registries may override the repository value for a specific registry.
130

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

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

147
            Examples:
148

149
                --default-context-root=src/docker
150
                --default-context-root=./relative_to_the_build_file
151
            """
152
        ),
153
    )
154
    engine = DataclassOption(
10✔
155
        default=DockerEngines(),
156
        mutually_exclusive_group="engines",
157
        help=softwrap(
158
            """
159
        The engines to use for Docker builds and runs.
160

161
        Valid values for `build` are:
162

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

167
        Valid values for `run` are:
168

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

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

182
            Use [buildx](https://github.com/docker/buildx#buildx) (and BuildKit) for builds.
183
            """
184
        ),
185
        deprecation_start_version="2.31.0",
186
        mutually_exclusive_group="engines",
187
    )
188

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

203
    @property
10✔
204
    def build_engine(self) -> DockerBuildEngine:
10✔
205
        use_buildx = self.options.get("use_buildx")
2✔
206
        if use_buildx is not None:
2✔
207
            warning = '`[docker].use_buildx` is deprecated. Buildx is now the default Docker build engine. Use `[docker.engine].build = "docker"` instead.'
2✔
208
            if not use_buildx:
2✔
NEW
209
                warning += (
×
210
                    " To use the legacy engine, add `DOCKER_BUILDKIT=0` to `[docker].env_vars`."
211
                )
212
            logger.warning(warning)
2✔
213
            return DockerBuildEngine.DOCKER
2✔
NEW
214
        return self._experimental_enable_podman_warning(DockerBuildEngine, "build")
×
215

216
    @property
10✔
217
    def run_engine(self) -> DockerRunEngine:
10✔
NEW
218
        return self._experimental_enable_podman_warning(DockerRunEngine, "run")
×
219

220
    @property
10✔
221
    def push_engine(self) -> DockerPushEngine:
10✔
NEW
222
        return self._experimental_enable_podman_warning(DockerPushEngine, "push")
×
223

224
    _build_args = ShellStrListOption(
10✔
225
        help=softwrap(
226
            f"""
227
            Global build arguments (for Docker `--build-arg` options) to use for all
228
            `docker build` invocations.
229

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

233
            Example:
234

235
                [{options_scope}]
236
                build_args = ["VAR1=value", "VAR2"]
237

238

239
            Use the `extra_build_args` field on a `docker_image` target for additional
240
            image specific build arguments.
241
            """
242
        ),
243
    )
244
    build_target_stage = StrOption(
10✔
245
        default=None,
246
        help=softwrap(
247
            """
248
            Global default value for `target_stage` on `docker_image` targets, overriding
249
            the field value on the targets, if there is a matching stage in the `Dockerfile`.
250

251
            This is useful to provide from the command line, to specify the target stage to
252
            build for at execution time.
253
            """
254
        ),
255
    )
256
    build_hosts = DictOption[str](
10✔
257
        default={},
258
        help=softwrap(
259
            f"""
260
            Hosts entries to be added to the `/etc/hosts` file in all built images.
261

262
            Example:
263

264
                [{options_scope}]
265
                build_hosts = {{"docker": "10.180.0.1", "docker2": "10.180.0.2"}}
266

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

286
            Example:
287

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

291
            To provide the top-level options to the `docker` client, use
292
            `[{options_scope}].env_vars` to configure the
293
            [Environment variables]({doc_links["docker_env_vars"]}) as appropriate.
294

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

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

323
    _optional_tools = StrListOption(
10✔
324
        help=softwrap(
325
            """
326
            List any additional executables which are not mandatory for Docker to work, but which
327
            should be included if available. The paths to these tools will be included in the
328
            PATH used in the execution sandbox, so that they may be used by the Docker client.
329
            """
330
        ),
331
        advanced=True,
332
    )
333

334
    tailor = BoolOption(
10✔
335
        default=True,
336
        help="If true, add `docker_image` targets with the `tailor` goal.",
337
        advanced=True,
338
    )
339

340
    suggest_renames = BoolOption(
10✔
341
        default=True,
342
        help=softwrap(
343
            """
344
            When true and, the `docker_image` build fails, enrich the logs with suggestions
345
            for renaming source file COPY instructions where possible.
346
            """
347
        ),
348
        advanced=True,
349
    )
350

351
    push_on_package = EnumOption(
10✔
352
        default=DockerPushOnPackageBehavior.WARN,
353
        help=softwrap(
354
            """
355
            The behavior when a docker_image target would push to a registry during packaging
356
            (e.g., when output has push=true or type=registry).
357

358
            Options:
359
            - allow: Allow pushes during packaging
360
            - warn: Log a warning but continue with the push (default)
361
            - ignore: Skip building images that would push
362
            - error: Raise an error if an image would push
363
            """
364
        ),
365
    )
366

367
    @property
10✔
368
    def build_args(self) -> tuple[str, ...]:
10✔
369
        return tuple(sorted(set(self._build_args)))
5✔
370

371
    @property
10✔
372
    def tools(self) -> tuple[str, ...]:
10✔
373
        return tuple(sorted(set(self._tools)))
3✔
374

375
    @property
10✔
376
    def optional_tools(self) -> tuple[str, ...]:
10✔
377
        return tuple(sorted(set(self._optional_tools)))
3✔
378

379
    @memoized_method
10✔
380
    def registries(self) -> DockerRegistries:
10✔
381
        return DockerRegistries.from_dict(self._registries)
3✔
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