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

pantsbuild / pants / 24103052497

07 Apr 2026 08:33PM UTC coverage: 52.311% (-40.6%) from 92.909%
24103052497

Pull #23228

github

web-flow
Merge 18fdfb0fd into b05152cd9
Pull Request #23228: Add persistent dependency inference cache for incremental --changed-dependents

31 of 136 new or added lines in 2 files covered. (22.79%)

23028 existing lines in 605 files now uncovered.

31671 of 60544 relevant lines covered (52.31%)

1.05 hits per line

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

48.68
/src/python/pants/backend/docker/goals/publish.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
2✔
5

6
import logging
2✔
7
from collections import defaultdict
2✔
8
from dataclasses import dataclass
2✔
9
from itertools import chain
2✔
10
from typing import DefaultDict, cast
2✔
11

12
from pants.backend.docker.goals.package_image import (
2✔
13
    DockerPackageFieldSet,
14
    GetImageRefsRequest,
15
    get_docker_image_build_process,
16
    get_image_refs,
17
)
18
from pants.backend.docker.package_types import BuiltDockerImage
2✔
19
from pants.backend.docker.registries import DockerRegistryOptions
2✔
20
from pants.backend.docker.subsystems.docker_options import DockerOptions
2✔
21
from pants.backend.docker.target_types import DockerImageRegistriesField, DockerImageSkipPushField
2✔
22
from pants.backend.docker.util_rules.docker_binary import DockerBinary
2✔
23
from pants.core.goals.package import PackageFieldSet
2✔
24
from pants.core.goals.publish import (
2✔
25
    CheckSkipRequest,
26
    CheckSkipResult,
27
    PublishFieldSet,
28
    PublishOutputData,
29
    PublishPackages,
30
    PublishProcesses,
31
    PublishRequest,
32
)
33
from pants.core.util_rules.env_vars import environment_vars_subset
2✔
34
from pants.engine.env_vars import EnvironmentVarsRequest
2✔
35
from pants.engine.process import InteractiveProcess, Process
2✔
36
from pants.engine.rules import collect_rules, implicitly, rule
2✔
37
from pants.engine.unions import UnionRule
2✔
38

39
logger = logging.getLogger(__name__)
2✔
40

41

42
class PublishDockerImageRequest(PublishRequest):
2✔
43
    pass
2✔
44

45

46
@dataclass(frozen=True)
2✔
47
class PublishDockerImageFieldSet(PublishFieldSet, DockerPackageFieldSet):
2✔
48
    publish_request_type = PublishDockerImageRequest
2✔
49
    required_fields = (  # type: ignore[assignment]
2✔
50
        *DockerPackageFieldSet.required_fields,
51
        DockerImageRegistriesField,
52
    )
53

54
    skip_push: DockerImageSkipPushField
2✔
55

56
    def make_skip_request(
2✔
57
        self, package_fs: PackageFieldSet
58
    ) -> PublishDockerImageSkipRequest | None:
59
        return (
×
60
            PublishDockerImageSkipRequest(publish_fs=self, package_fs=package_fs)
61
            if isinstance(package_fs, DockerPackageFieldSet)
62
            else None
63
        )
64

65
    def get_output_data(self) -> PublishOutputData:
2✔
UNCOV
66
        return PublishOutputData(
×
67
            {
68
                "publisher": "docker",
69
                "registries": self.registries.value or (),
70
                **super().get_output_data(),
71
            }
72
        )
73

74

75
class PublishDockerImageSkipRequest(CheckSkipRequest[PublishDockerImageFieldSet]):
2✔
76
    package_fs: DockerPackageFieldSet
2✔
77

78

79
@rule
2✔
80
async def check_if_skip_push(
2✔
81
    request: PublishDockerImageSkipRequest, options: DockerOptions
82
) -> CheckSkipResult:
UNCOV
83
    skip_registries = {
×
84
        registry for registry in options.registries().registries.values() if registry.skip_push
85
    }
UNCOV
86
    if skip_registries or request.publish_fs.skip_push.value:
×
UNCOV
87
        image_refs = await get_image_refs(
×
88
            GetImageRefsRequest(field_set=request.package_fs, build_upstream_images=False),
89
            **implicitly(),
90
        )
UNCOV
91
        if request.publish_fs.skip_push.value:
×
UNCOV
92
            return CheckSkipResult.skip(
×
93
                names=[tag.full_name for registry in image_refs for tag in registry.tags],
94
                description=f"(by `{request.publish_fs.skip_push.alias}` on {request.address})",
95
                data=request.publish_fs.get_output_data(),
96
            )
UNCOV
97
        if all(image_ref.registry in skip_registries for image_ref in image_refs):
×
UNCOV
98
            output_data = request.publish_fs.get_output_data()
×
UNCOV
99
            return CheckSkipResult(
×
100
                PublishPackages(
101
                    names=tuple(tag.full_name for tag in image_ref.tags),
102
                    description=f"(by skip_push on @{cast(DockerRegistryOptions, image_ref.registry).alias})",
103
                    data=output_data,
104
                )
105
                for image_ref in image_refs
106
            )
UNCOV
107
    return (
×
108
        CheckSkipResult.skip(skip_packaging_only=True)
109
        if request.package_fs.pushes_on_package()
110
        else CheckSkipResult.no_skip()
111
    )
112

113

114
@rule
2✔
115
async def push_docker_images(
2✔
116
    request: PublishDockerImageRequest,
117
    docker: DockerBinary,
118
    options: DockerOptions,
119
    options_env_aware: DockerOptions.EnvironmentAware,
120
) -> PublishProcesses:
UNCOV
121
    if cast(DockerPackageFieldSet, request.field_set).pushes_on_package():
×
UNCOV
122
        build_process = await get_docker_image_build_process(request.field_set, **implicitly())
×
UNCOV
123
        return PublishProcesses(
×
124
            [
125
                PublishPackages(
126
                    names=build_process.tags,
127
                    process=build_process.process
128
                    if options.publish_noninteractively
129
                    else InteractiveProcess.from_process(build_process.process),
130
                )
131
            ]
132
        )
133

UNCOV
134
    tags = tuple(
×
135
        chain.from_iterable(
136
            cast(BuiltDockerImage, image).tags
137
            for pkg in request.packages
138
            for image in pkg.artifacts
139
        )
140
    )
141

UNCOV
142
    env = await environment_vars_subset(
×
143
        EnvironmentVarsRequest(options_env_aware.env_vars), **implicitly()
144
    )
UNCOV
145
    skip_push_reasons: DefaultDict[str, DefaultDict[str, set[str]]] = defaultdict(
×
146
        lambda: defaultdict(set)
147
    )
UNCOV
148
    jobs: list[PublishPackages] = []
×
UNCOV
149
    refs: list[str] = []
×
UNCOV
150
    processes: list[Process | InteractiveProcess] = []
×
151

UNCOV
152
    for tag in tags:
×
UNCOV
153
        for registry in options.registries().registries.values():
×
UNCOV
154
            if registry.skip_push and tag.startswith(f"{registry.address}/"):
×
UNCOV
155
                skip_push_reasons["skip_push"][registry.alias].add(tag)
×
UNCOV
156
                break
×
UNCOV
157
            if registry.use_local_alias and tag.startswith(f"{registry.alias}/"):
×
158
                skip_push_reasons["use_local_alias"][registry.alias].add(tag)
×
159
                break
×
160
        else:
UNCOV
161
            refs.append(tag)
×
UNCOV
162
            push_process = docker.push_image(tag, env)
×
UNCOV
163
            if options.publish_noninteractively:
×
164
                processes.append(push_process)
×
165
            else:
UNCOV
166
                processes.append(InteractiveProcess.from_process(push_process))
×
167

UNCOV
168
    for ref, process in zip(refs, processes):
×
UNCOV
169
        jobs.append(
×
170
            PublishPackages(
171
                names=(ref,),
172
                process=process,
173
            )
174
        )
175

UNCOV
176
    for reason, skip_push in skip_push_reasons.items():
×
UNCOV
177
        for name, skip_tags in skip_push.items():
×
UNCOV
178
            jobs.append(
×
179
                PublishPackages(
180
                    names=tuple(skip_tags),
181
                    description=f"(by `{reason}` on registry @{name})",
182
                ),
183
            )
184

UNCOV
185
    return PublishProcesses(jobs)
×
186

187

188
def rules():
2✔
189
    return (
2✔
190
        *collect_rules(),
191
        *PublishDockerImageFieldSet.rules(),
192
        UnionRule(CheckSkipRequest, PublishDockerImageSkipRequest),
193
    )
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