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

pantsbuild / pants / 22898465085

10 Mar 2026 10:37AM UTC coverage: 92.932%. Remained the same
22898465085

Pull #23032

github

web-flow
Merge 4bf2e958f into 2804a4673
Pull Request #23032: Bugfix: Add support for pull option in podman

87 of 93 new or added lines in 4 files covered. (93.55%)

2 existing lines in 2 files now uncovered.

91041 of 97965 relevant lines covered (92.93%)

4.06 hits per line

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

98.13
/src/python/pants/backend/docker/target_types.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 os
10✔
7
import re
10✔
8
import warnings
10✔
9
from abc import ABC, abstractmethod
10✔
10
from collections.abc import Callable, Iterator
10✔
11
from dataclasses import dataclass
10✔
12
from typing import ClassVar, cast, final
10✔
13

14
from pants.backend.docker.registries import ALL_DEFAULT_REGISTRIES
10✔
15
from pants.backend.docker.subsystems.docker_options import DockerOptions
10✔
16
from pants.base.build_environment import get_buildroot
10✔
17
from pants.core.goals.package import OutputPathField
10✔
18
from pants.core.goals.run import RestartableField
10✔
19
from pants.engine.addresses import Address
10✔
20
from pants.engine.collection import Collection
10✔
21
from pants.engine.environment import EnvironmentName
10✔
22
from pants.engine.fs import GlobMatchErrorBehavior
10✔
23
from pants.engine.rules import collect_rules, rule
10✔
24
from pants.engine.target import (
10✔
25
    COMMON_TARGET_FIELDS,
26
    AllTargets,
27
    AsyncFieldMixin,
28
    BoolField,
29
    Dependencies,
30
    DictStringToStringField,
31
    Field,
32
    InvalidFieldException,
33
    ListOfDictStringToStringField,
34
    OptionalSingleSourceField,
35
    StringField,
36
    StringOrBoolField,
37
    StringSequenceField,
38
    Target,
39
    Targets,
40
)
41
from pants.engine.unions import union
10✔
42
from pants.util.docutil import bin_name, doc_url
10✔
43
from pants.util.frozendict import FrozenDict
10✔
44
from pants.util.strutil import help_text, softwrap
10✔
45

46
# Common help text to be applied to each field that supports value interpolation.
47
_interpolation_help = (
10✔
48
    "{kind} may use placeholders in curly braces to be interpolated. The placeholders are derived "
49
    "from various sources, such as the Dockerfile instructions and build args."
50
)
51

52

53
class DockerImageBuildArgsField(StringSequenceField):
10✔
54
    alias = "extra_build_args"
10✔
55
    default = ()
10✔
56
    help = help_text(
10✔
57
        """
58
        Build arguments (`--build-arg`) to use when building this image.
59
        Entries are either strings in the form `ARG_NAME=value` to set an explicit value;
60
        or just `ARG_NAME` to copy the value from Pants's own environment.
61

62
        Use `[docker].build_args` to set default build args for all images.
63
        """
64
    )
65

66

67
class DockerImageContextRootField(StringField):
10✔
68
    alias = "context_root"
10✔
69
    help = help_text(
10✔
70
        """
71
        Specify which directory to use as the Docker build context root. This affects the file
72
        paths to use for the `COPY` and `ADD` instructions. For example, whether
73
        `COPY files/f.txt` should look for the file relative to the build root:
74
        `<build root>/files/f.txt` vs relative to the BUILD file:
75
        `<build root>/path_to_build_file/files/f.txt`.
76

77
        Specify the `context_root` path as `files` for relative to build root, or as `./files`
78
        for relative to the BUILD file.
79

80
        If `context_root` is not specified, it defaults to `[docker].default_context_root`.
81
        """
82
    )
83

84
    @classmethod
10✔
85
    def compute_value(cls, raw_value: str | None, address: Address) -> str | None:
10✔
86
        value_or_default = super().compute_value(raw_value, address=address)
9✔
87
        if isinstance(value_or_default, str) and value_or_default.startswith("/"):
9✔
88
            val = value_or_default.strip("/")
1✔
89
            raise InvalidFieldException(
1✔
90
                softwrap(
91
                    f"""
92
                    The `{cls.alias}` field in target {address} must be a relative path, but was
93
                    {value_or_default!r}. Use {val!r} for a path relative to the build root, or
94
                    {"./" + val!r} for a path relative to the BUILD file
95
                    (i.e. {os.path.join(address.spec_path, val)!r}).
96
                    """
97
                )
98
            )
99
        return value_or_default
9✔
100

101

102
class DockerImageSourceField(OptionalSingleSourceField):
10✔
103
    none_is_valid_value = True
10✔
104
    default = "Dockerfile"
10✔
105

106
    # When the default glob value is in effect, we don't want the normal glob match error behavior
107
    # to kick in for a missing Dockerfile, in case there are `instructions` provided, in which case
108
    # we generate the Dockerfile instead. If there are no `instructions`, or there are both
109
    # `instructions` and a Dockerfile hydrated from the `source` glob, we error out with a message
110
    # to the user.
111
    default_glob_match_error_behavior = GlobMatchErrorBehavior.ignore
10✔
112

113
    help = help_text(
10✔
114
        """
115
        The Dockerfile to use when building the Docker image.
116

117
        Use the `instructions` field instead if you prefer not having the Dockerfile in your
118
        source tree.
119
        """
120
    )
121

122

123
class DockerImageInstructionsField(StringSequenceField):
10✔
124
    alias = "instructions"
10✔
125
    required = False
10✔
126
    help = help_text(
10✔
127
        """
128
        The `Dockerfile` content, typically one instruction per list item.
129

130
        Use the `source` field instead if you prefer having the Dockerfile in your source tree.
131

132
        Example:
133

134
            # example/BUILD
135
            docker_image(
136
              instructions=[
137
                "FROM base/image:1.0",
138
                "RUN echo example",
139
              ],
140
            )
141
        """
142
    )
143

144

145
class DockerImageTagsField(StringSequenceField):
10✔
146
    alias = "image_tags"
10✔
147
    default = ("latest",)
10✔
148
    help = help_text(
10✔
149
        f"""
150

151
        Any tags to apply to the Docker image name (the version is usually applied as a tag).
152

153
        {_interpolation_help.format(kind="tag")}
154

155
        See {doc_url("docs/docker/tagging-docker-images")}.
156
        """
157
    )
158

159

160
class DockerImageTargetStageField(StringField):
10✔
161
    alias = "target_stage"
10✔
162
    help = help_text(
10✔
163
        """
164
        Specify target build stage, rather than building the entire `Dockerfile`.
165

166
        When using multi-stage build, you may name your stages, and can target them when building
167
        to only selectively build a certain stage. See also the `--docker-build-target-stage`
168
        option.
169

170
        Read more about [multi-stage Docker builds](https://docs.docker.com/develop/develop-images/multistage-build/#stop-at-a-specific-build-stage)
171
        """
172
    )
173

174

175
class DockerImageDependenciesField(Dependencies):
10✔
176
    supports_transitive_excludes = True
10✔
177

178

179
class DockerImageRegistriesField(StringSequenceField):
10✔
180
    alias = "registries"
10✔
181
    default = (ALL_DEFAULT_REGISTRIES,)
10✔
182
    help = help_text(
10✔
183
        """
184
        List of addresses or configured aliases to any Docker registries to use for the
185
        built image.
186

187
        The address is a domain name with optional port for your registry, and any registry
188
        aliases are prefixed with `@` for addresses in the `[docker].registries` configuration
189
        section.
190

191
        By default, all configured registries with `default = true` are used.
192

193
        Example:
194

195
            # pants.toml
196
            [docker.registries.my-registry-alias]
197
            address = "myregistrydomain:port"
198
            default = false # optional
199

200
            # example/BUILD
201
            docker_image(
202
                registries = [
203
                    "@my-registry-alias",
204
                    "myregistrydomain:port",
205
                ],
206
            )
207

208
        The above example shows two valid `registry` options: using an alias to a configured
209
        registry and the address to a registry verbatim in the BUILD file.
210
        """
211
    )
212

213

214
class DockerImageRepositoryField(StringField):
10✔
215
    alias = "repository"
10✔
216
    help = help_text(
10✔
217
        f"""
218
        The repository name for the Docker image. e.g. `"<repository>/<name>"`.
219

220
        It uses the `[docker].default_repository` by default.
221

222
        {_interpolation_help.format(kind="Repository")}
223

224
        Additional placeholders for the repository field are: `name`, `directory`,
225
        `parent_directory`, and `default_repository`.
226

227
        Registries may also configure the repository value for specific registries.
228

229
        See the documentation for `[docker].default_repository` for more information.
230
        """
231
    )
232

233

234
class DockerImageSkipPushField(BoolField):
10✔
235
    alias = "skip_push"
10✔
236
    default = False
10✔
237
    help = f"If true, do not push this image to registries when running `{bin_name()} publish`."
10✔
238

239

240
OptionValueFormatter = Callable[[str], str]
10✔
241

242

243
class DockerBuildOptionFieldMixin(ABC):
10✔
244
    """Inherit this mixin class to provide options to `docker build`."""
245

246
    docker_build_option: ClassVar[str]
10✔
247

248
    @abstractmethod
10✔
249
    def option_values(
10✔
250
        self, *, value_formatter: OptionValueFormatter, global_build_hosts_options: dict
251
    ) -> Iterator[str]:
252
        """Subclasses must implement this, to turn their `self.value` into none, one or more option
253
        values."""
254

255
    @final
10✔
256
    def options(
10✔
257
        self, value_formatter: OptionValueFormatter, global_build_hosts_options, **kwargs
258
    ) -> Iterator[str]:
259
        for value in self.option_values(
5✔
260
            value_formatter=value_formatter, global_build_hosts_options=global_build_hosts_options
261
        ):
262
            yield from (self.docker_build_option, value)
1✔
263

264

265
class DockerImageBuildImageLabelsOptionField(DockerBuildOptionFieldMixin, DictStringToStringField):
10✔
266
    alias = "image_labels"
10✔
267
    help = help_text(
10✔
268
        f"""
269
        Provide image metadata.
270

271
        {_interpolation_help.format(kind="Label value")}
272

273
        See [Docker labels](https://docs.docker.com/config/labels-custom-metadata/#manage-labels-on-objects)
274
        for more information.
275
        """
276
    )
277
    docker_build_option = "--label"
10✔
278

279
    def option_values(self, value_formatter: OptionValueFormatter, **kwargs) -> Iterator[str]:
10✔
280
        for label, value in (self.value or {}).items():
5✔
281
            yield f"{label}={value_formatter(value)}"
1✔
282

283

284
class DockerImageBuildImageExtraHostsField(DockerBuildOptionFieldMixin, DictStringToStringField):
10✔
285
    alias = "extra_build_hosts"
10✔
286
    help = help_text(
10✔
287
        """
288
        Extra hosts entries to be added to a container's `/etc/hosts` file.
289

290
        Use `[docker].build_hosts` to set default host entries for all images.
291
        """
292
    )
293
    docker_build_option = "--add-host"
10✔
294

295
    def option_values(
10✔
296
        self, value_formatter: OptionValueFormatter, global_build_hosts_options: dict = {}
297
    ) -> Iterator[str]:
298
        if self.value:
5✔
299
            merged_values = {**global_build_hosts_options, **self.value}
1✔
300
            for label, value in merged_values.items():
1✔
301
                yield f"{label}:{value_formatter(value)}"
1✔
302

303

304
class DockerBuildOptionFieldMultiValueDictMixin(DictStringToStringField):
10✔
305
    """Inherit this mixin class to provide options in the form of `--flag=key1=value1,key2=value2`
306
    to `docker build`."""
307

308
    docker_build_option: ClassVar[str]
10✔
309

310
    @final
10✔
311
    def options(self, value_formatter: OptionValueFormatter, **kwargs) -> Iterator[str]:
10✔
312
        if self.value:
1✔
313
            yield f"{self.docker_build_option}=" + ",".join(
1✔
314
                f"{key}={value_formatter(value)}" for key, value in self.value.items()
315
            )
316

317

318
class DockerBuildOptionFieldListOfMultiValueDictMixin(ListOfDictStringToStringField):
10✔
319
    """Inherit this mixin class to provide multiple key-value options to docker build:
320

321
    `--flag=key1=value1,key2=value2 --flag=key3=value3,key4=value4`
322
    """
323

324
    docker_build_option: ClassVar[str]
10✔
325

326
    @final
10✔
327
    def options(self, value_formatter: OptionValueFormatter, **kwargs) -> Iterator[str]:
10✔
328
        if self.value:
1✔
329
            for item in self.value:
1✔
330
                yield f"{self.docker_build_option}=" + ",".join(
1✔
331
                    f"{key}={value_formatter(value)}" for key, value in item.items()
332
                )
333

334

335
class DockerBuildKitOptionField:
10✔
336
    """Mixin to indicate a BuildKit-specific option."""
337

338
    @abstractmethod
339
    def options(self, value_formatter: OptionValueFormatter) -> Iterator[str]: ...
340

341
    required_help = "This option requires BuildKit to be enabled via the Docker subsystem options."
10✔
342

343

344
class DockerImageBuildImageCacheToField(
10✔
345
    DockerBuildOptionFieldMultiValueDictMixin, DictStringToStringField, DockerBuildKitOptionField
346
):
347
    alias = "cache_to"
10✔
348
    help = help_text(
10✔
349
        f"""
350
        Export image build cache to an external cache destination.
351

352
        Note that Docker [supports](https://docs.docker.com/build/cache/backends/#multiple-caches)
353
        multiple cache sources - Pants will pass these as multiple `--cache_from` arguments to the
354
        Docker CLI. Docker will only use the first cache hit (i.e. the image exists) in the build.
355

356
        {DockerBuildKitOptionField.required_help}
357

358
        Example:
359

360
            docker_image(
361
                name="example-local-cache-backend",
362
                cache_to={{
363
                    "type": "local",
364
                    "dest": "/tmp/docker-cache/example"
365
                }},
366
                cache_from=[{{
367
                    "type": "local",
368
                    "src": "/tmp/docker-cache/example"
369
                }}]
370
            )
371

372
        {_interpolation_help.format(kind="Values")}
373
        """
374
    )
375
    docker_build_option = "--cache-to"
10✔
376

377

378
class DockerImageBuildImageCacheFromField(
10✔
379
    DockerBuildOptionFieldListOfMultiValueDictMixin,
380
    ListOfDictStringToStringField,
381
    DockerBuildKitOptionField,
382
):
383
    alias = "cache_from"
10✔
384
    help = help_text(
10✔
385
        f"""
386
        Use external cache sources when building the image.
387

388
        {DockerBuildKitOptionField.required_help}
389

390
        Example:
391

392
            docker_image(
393
                name="example-local-cache-backend",
394
                cache_to={{
395
                    "type": "local",
396
                    "dest": "/tmp/docker-cache/primary"
397
                }},
398
                cache_from=[
399
                    {{
400
                        "type": "local",
401
                        "src": "/tmp/docker-cache/primary"
402
                    }},
403
                    {{
404
                        "type": "local",
405
                        "src": "/tmp/docker-cache/secondary"
406
                    }}
407
                ]
408
            )
409

410
        {_interpolation_help.format(kind="Values")}
411
        """
412
    )
413
    docker_build_option = "--cache-from"
10✔
414

415

416
class DockerImageBuildImageOutputField(
10✔
417
    DockerBuildOptionFieldMultiValueDictMixin, DictStringToStringField, DockerBuildKitOptionField
418
):
419
    alias = "output"
10✔
420
    default = FrozenDict({"type": "docker"})
10✔
421
    help = help_text(
10✔
422
        f"""
423
        Sets the export action for the build result.
424

425
        {DockerBuildKitOptionField.required_help}
426

427
        When using `pants publish` to publish Docker images to a registry, the output type
428
        must be 'docker', as `publish` expects that the built images exist in the local
429
        image store.
430

431
        {_interpolation_help.format(kind="Values")}
432
        """
433
    )
434
    docker_build_option = "--output"
10✔
435

436

437
class DockerImageBuildSecretsOptionField(
10✔
438
    AsyncFieldMixin, DockerBuildOptionFieldMixin, DictStringToStringField
439
):
440
    alias = "secrets"
10✔
441
    help = help_text(
10✔
442
        """
443
        Secret files to expose to the build (only if BuildKit enabled).
444

445
        Secrets may use absolute paths, or paths relative to your build root, or the BUILD file
446
        if prefixed with `./`. Paths to your home directory will be automatically expanded.
447
        The id should be valid as used by the Docker build `--secret` option.
448
        See [Docker secrets](https://docs.docker.com/engine/swarm/secrets/) for more
449
        information.
450

451
        Example:
452

453
            docker_image(
454
                secrets={
455
                    "mysecret": "/var/secrets/some-secret",
456
                    "repo-secret": "src/proj/secrets/some-secret",
457
                    "home-dir-secret": "~/.config/some-secret",
458
                    "target-secret": "./secrets/some-secret",
459
                }
460
            )
461
        """
462
    )
463

464
    docker_build_option = "--secret"
10✔
465

466
    def option_values(self, **kwargs) -> Iterator[str]:
10✔
467
        # os.path.join() discards preceding parts if encountering an abs path, e.g. if the secret
468
        # `path` is an absolute path, the `buildroot` and `spec_path` will not be considered.  Also,
469
        # an empty path part is ignored.
470
        for secret, path in (self.value or {}).items():
6✔
471
            full_path = os.path.join(
2✔
472
                get_buildroot(),
473
                self.address.spec_path if re.match(r"\.{1,2}/", path) else "",
474
                os.path.expanduser(path),
475
            )
476

477
            yield f"id={secret},src={os.path.normpath(full_path)}"
2✔
478

479

480
class DockerImageBuildSSHOptionField(DockerBuildOptionFieldMixin, StringSequenceField):
10✔
481
    alias = "ssh"
10✔
482
    default = ()
10✔
483
    help = help_text(
10✔
484
        """
485
        SSH agent socket or keys to expose to the build (only if BuildKit enabled)
486
        (format: `default|<id>[=<socket>|<key>[,<key>]]`)
487

488
        The exposed agent and/or keys can then be used in your `Dockerfile` by mounting them in
489
        your `RUN` instructions:
490

491
            RUN --mount=type=ssh ...
492

493
        See [Docker documentation](https://docs.docker.com/develop/develop-images/build_enhancements/#using-ssh-to-access-private-data-in-builds)
494
        for more information.
495
        """
496
    )
497

498
    docker_build_option = "--ssh"
10✔
499

500
    def option_values(self, **kwargs) -> Iterator[str]:
10✔
501
        yield from cast("tuple[str]", self.value)
5✔
502

503

504
class DockerBuildOptionFieldValueMixin(Field):
10✔
505
    """Inherit this mixin class to provide unary options (i.e. option in the form of `--flag=value`)
506
    to `docker build`."""
507

508
    docker_build_option: ClassVar[str]
10✔
509

510
    @final
10✔
511
    def options(self, *args, **kwargs) -> Iterator[str]:
10✔
512
        if self.value is not None:
5✔
513
            yield f"{self.docker_build_option}={self.value}"
1✔
514

515

516
class DockerBuildOptionFieldMultiValueMixin(StringSequenceField):
10✔
517
    """Inherit this mixin class to provide options in the form of `--flag=value1,value2` to `docker
518
    build`."""
519

520
    docker_build_option: ClassVar[str]
10✔
521

522
    @final
10✔
523
    def options(self, *args, **kwargs) -> Iterator[str]:
10✔
524
        if self.value:
5✔
525
            yield f"{self.docker_build_option}={','.join(list(self.value))}"
1✔
526

527

528
class DockerImageBuildPullOptionField(StringOrBoolField):
10✔
529
    alias = "pull"
10✔
530
    default = None
10✔
531
    valid_choices = ("always", "missing", "never", "newer")
10✔
532
    help = help_text(
10✔
533
        """
534
        Pull policy for the image.
535

536
        For Docker: accepts boolean (true to always pull, false to use cached).
537
        For Podman: accepts boolean or string policy ("always", "missing", "never", "newer").
538
        Default: false for Docker, "missing" for Podman.
539

540
        NOTE: This option cannot be used on images that build off of "transitive" base images
541
        referenced by address (i.e. `FROM path/to/your/base/Dockerfile`).
542
        """
543
    )
544
    docker_build_option = "--pull"
10✔
545

546
    def options(self, value_formatter, global_build_hosts_options=None, **kwargs):
10✔
547
        # Determine backend type from DockerBinary (which is resolved based on
548
        # the [docker].experimental_enable_podman option). When experimental_enable_podman=true,
549
        # the docker_binary will be 'podman' and is_podman will be True.
550
        docker_binary = kwargs.get("docker") or kwargs.get("docker_binary")
5✔
551
        is_podman = (
5✔
552
            getattr(docker_binary, "is_podman", False) if docker_binary is not None else False
553
        )
554

555
        val = self.value
5✔
556
        if val is None:
5✔
557
            # Use defaults based on backend
558
            val = "missing" if is_podman else False
4✔
559

560
        if isinstance(val, str):
5✔
561
            # String policies are only supported by Podman
562
            if not is_podman:
1✔
563
                raise InvalidFieldException(
1✔
564
                    f"The {self.alias!r} field was set to string value {val!r}, "
565
                    f"but string pull policies are only supported by Podman, not Docker. "
566
                    f"Use a boolean value (true/false) for Docker."
567
                )
568
            yield f"{self.docker_build_option}={value_formatter(val)}"
1✔
569
        else:
570
            # Boolean value
571
            if is_podman:
4✔
572
                # Convert boolean to Podman policy string
NEW
573
                warnings.warn(
×
574
                    f"Using boolean values for the 'pull' field with Podman is deprecated. "
575
                    f"Please use string values instead: 'always', 'missing', 'never', or 'newer'. "
576
                    f"Boolean {val} is being converted to 'always' if val else 'missing' policy.",
577
                    DeprecationWarning,
578
                    stacklevel=2,
579
                )
NEW
580
                policy = "always" if val else "missing"
×
NEW
581
                yield f"{self.docker_build_option}={policy}"
×
582
            else:
583
                # Docker: emit explicit boolean value with capital first letter
584
                yield f"{self.docker_build_option}={str(val).capitalize()}"
4✔
585

586

587
class DockerBuildOptionFlagFieldMixin(BoolField, ABC):
10✔
588
    """Inherit this mixin class to provide optional flags (i.e. add `--flag` only when the value is
589
    `True`) to `docker build`."""
590

591
    docker_build_option: ClassVar[str]
10✔
592

593
    @final
10✔
594
    def options(self, *args, **kwargs) -> Iterator[str]:
10✔
595
        if self.value:
5✔
596
            yield f"{self.docker_build_option}"
1✔
597

598

599
class DockerImageBuildSquashOptionField(DockerBuildOptionFlagFieldMixin):
10✔
600
    alias = "squash"
10✔
601
    default = False
10✔
602
    help = help_text(
10✔
603
        """
604
        If true, then docker will squash newly built layers into a single new layer.
605

606
        Note that this option is only supported on a Docker daemon with experimental features enabled.
607
        """
608
    )
609
    docker_build_option = "--squash"
10✔
610

611

612
class DockerImageBuildNetworkOptionField(DockerBuildOptionFieldValueMixin, StringField):
10✔
613
    alias = "build_network"
10✔
614
    default = None
10✔
615
    help = help_text(
10✔
616
        """
617
        Sets the networking mode for the run commands during build.
618
        Supported standard values are: bridge, host, none, and container:<name|id>.
619
        Any other value is taken as a custom network's name to which the container should connect to.
620
        """
621
    )
622
    docker_build_option = "--network"
10✔
623

624

625
class DockerImageBuildPlatformOptionField(
10✔
626
    DockerBuildOptionFieldMultiValueMixin, StringSequenceField
627
):
628
    alias = "build_platform"
10✔
629
    default = None
10✔
630
    help = help_text(
10✔
631
        """
632
        Set the target platform(s) for the build.
633
        """
634
    )
635
    docker_build_option = "--platform"
10✔
636

637

638
class DockerImageRunExtraArgsField(StringSequenceField):
10✔
639
    alias: ClassVar[str] = "extra_run_args"
10✔
640
    default = ()
10✔
641
    help = help_text(
10✔
642
        lambda: f"Extra arguments to pass into the invocation of `docker run`. These are in addition to those at the `[{DockerOptions.options_scope}].run_args`"
643
    )
644

645

646
class DockerImageTarget(Target):
10✔
647
    alias = "docker_image"
10✔
648
    core_fields = (
10✔
649
        *COMMON_TARGET_FIELDS,
650
        DockerImageBuildArgsField,
651
        DockerImageDependenciesField,
652
        DockerImageSourceField,
653
        DockerImageInstructionsField,
654
        DockerImageContextRootField,
655
        DockerImageTagsField,
656
        DockerImageRegistriesField,
657
        DockerImageRepositoryField,
658
        DockerImageBuildImageLabelsOptionField,
659
        DockerImageBuildImageExtraHostsField,
660
        DockerImageBuildSecretsOptionField,
661
        DockerImageBuildSSHOptionField,
662
        DockerImageSkipPushField,
663
        DockerImageTargetStageField,
664
        DockerImageBuildPullOptionField,
665
        DockerImageBuildSquashOptionField,
666
        DockerImageBuildNetworkOptionField,
667
        DockerImageBuildPlatformOptionField,
668
        DockerImageBuildImageCacheToField,
669
        DockerImageBuildImageCacheFromField,
670
        DockerImageBuildImageOutputField,
671
        DockerImageRunExtraArgsField,
672
        OutputPathField,
673
        RestartableField,
674
    )
675
    help = help_text(
676
        """
677
        The `docker_image` target describes how to build and tag a Docker image.
678

679
        Any dependencies, as inferred or explicitly specified, will be included in the Docker
680
        build context, after being packaged if applicable.
681

682
        By default, it will use a Dockerfile from the same directory as the BUILD file this target
683
        is defined in. Point at another file with the `source` field, or use the `instructions`
684
        field to have the Dockerfile contents verbatim directly in the BUILD file.
685

686
        Dependencies on upstream/base images defined by another `docker_image` are inferred if
687
        referenced by a build argument with a default value of the target address.
688

689
        Example:
690

691
            # src/docker/downstream/Dockerfile
692
            ARG BASE=src/docker/upstream:image
693
            FROM $BASE
694
            ...
695

696
        """
697
    )
698

699

700
@union(in_scope_types=[EnvironmentName])
10✔
701
@dataclass(frozen=True)
10✔
702
class DockerImageTagsRequest:
10✔
703
    """A request to provide additional image tags."""
704

705
    target: Target
10✔
706

707
    @classmethod
10✔
708
    def is_applicable(cls, target: Target) -> bool:
10✔
709
        """Whether to provide additional tags for this target or not."""
710
        return True
1✔
711

712

713
class DockerImageTags(Collection[str]):
10✔
714
    """Additional image tags to apply to built Docker images."""
715

716

717
@rule(polymorphic=True)
10✔
718
async def get_docker_image_tags(
10✔
719
    req: DockerImageTagsRequest, env_name: EnvironmentName
720
) -> DockerImageTags:
721
    raise NotImplementedError()
×
722

723

724
class AllDockerImageTargets(Targets):
10✔
725
    pass
10✔
726

727

728
@rule
10✔
729
async def all_docker_targets(all_targets: AllTargets) -> AllDockerImageTargets:
10✔
730
    return AllDockerImageTargets(
6✔
731
        [tgt for tgt in all_targets if tgt.has_field(DockerImageSourceField)]
732
    )
733

734

735
def rules():
10✔
736
    return collect_rules()
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