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

pantsbuild / pants / 21264706899

22 Jan 2026 09:00PM UTC coverage: 80.255% (+1.6%) from 78.666%
21264706899

Pull #23031

github

web-flow
Merge 8385604a3 into d250c80fe
Pull Request #23031: Enable publish without package

32 of 60 new or added lines in 6 files covered. (53.33%)

2 existing lines in 2 files now uncovered.

78788 of 98172 relevant lines covered (80.26%)

3.36 hits per line

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

51.67
/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
6✔
5

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

12
from pants.backend.docker.goals.package_image import BuiltDockerImage
6✔
13
from pants.backend.docker.subsystems.docker_options import DockerOptions
6✔
14
from pants.backend.docker.target_types import DockerImageRegistriesField, DockerImageSkipPushField
6✔
15
from pants.backend.docker.util_rules.docker_binary import DockerBinary
6✔
16
from pants.core.goals.package import PackageFieldSet
6✔
17
from pants.core.goals.publish import (
6✔
18
    PublishFieldSet,
19
    PublishOutputData,
20
    PublishPackages,
21
    PublishProcesses,
22
    PublishRequest,
23
)
24
from pants.core.util_rules.env_vars import environment_vars_subset
6✔
25
from pants.engine.env_vars import EnvironmentVarsRequest
6✔
26
from pants.engine.process import InteractiveProcess, Process
6✔
27
from pants.engine.rules import collect_rules, implicitly, rule
6✔
28

29
logger = logging.getLogger(__name__)
6✔
30

31

32
class PublishDockerImageRequest(PublishRequest):
6✔
33
    pass
6✔
34

35

36
@dataclass(frozen=True)
6✔
37
class PublishDockerImageFieldSet(PublishFieldSet):
6✔
38
    publish_request_type = PublishDockerImageRequest
6✔
39
    required_fields = (DockerImageRegistriesField,)
6✔
40

41
    registries: DockerImageRegistriesField
6✔
42
    skip_push: DockerImageSkipPushField
6✔
43

44
    def get_output_data(self) -> PublishOutputData:
6✔
45
        return PublishOutputData(
×
46
            {
47
                "publisher": "docker",
48
                "registries": self.registries.value or (),
49
                **super().get_output_data(),
50
            }
51
        )
52

53
    def package_before_publish(self, package_fs: PackageFieldSet) -> bool:
6✔
NEW
54
        return not self.skip_push.value
×
55

56

57
@rule
6✔
58
async def push_docker_images(
6✔
59
    request: PublishDockerImageRequest,
60
    docker: DockerBinary,
61
    options: DockerOptions,
62
    options_env_aware: DockerOptions.EnvironmentAware,
63
) -> PublishProcesses:
64
    tags = tuple(
×
65
        chain.from_iterable(
66
            cast(BuiltDockerImage, image).tags
67
            for pkg in request.packages
68
            for image in pkg.artifacts
69
        )
70
    )
71

72
    if request.field_set.skip_push.value:
×
73
        return PublishProcesses(
×
74
            [
75
                PublishPackages(
76
                    names=tags,
77
                    description=f"(by `{request.field_set.skip_push.alias}` on {request.field_set.address})",
78
                ),
79
            ]
80
        )
81

82
    env = await environment_vars_subset(
×
83
        EnvironmentVarsRequest(options_env_aware.env_vars), **implicitly()
84
    )
85
    skip_push_reasons: DefaultDict[str, DefaultDict[str, set[str]]] = defaultdict(
×
86
        lambda: defaultdict(set)
87
    )
88
    jobs: list[PublishPackages] = []
×
89
    refs: list[str] = []
×
90
    processes: list[Process | InteractiveProcess] = []
×
91

92
    for tag in tags:
×
93
        for registry in options.registries().registries.values():
×
94
            if registry.skip_push and tag.startswith(f"{registry.address}/"):
×
95
                skip_push_reasons["skip_push"][registry.alias].add(tag)
×
96
                break
×
97
            if registry.use_local_alias and tag.startswith(f"{registry.alias}/"):
×
98
                skip_push_reasons["use_local_alias"][registry.alias].add(tag)
×
99
                break
×
100
        else:
101
            refs.append(tag)
×
102
            push_process = docker.push_image(tag, env)
×
103
            if options.publish_noninteractively:
×
104
                processes.append(push_process)
×
105
            else:
106
                processes.append(InteractiveProcess.from_process(push_process))
×
107

108
    for ref, process in zip(refs, processes):
×
109
        jobs.append(
×
110
            PublishPackages(
111
                names=(ref,),
112
                process=process,
113
            )
114
        )
115

116
    for reason, skip_push in skip_push_reasons.items():
×
117
        for name, skip_tags in skip_push.items():
×
118
            jobs.append(
×
119
                PublishPackages(
120
                    names=tuple(skip_tags),
121
                    description=f"(by `{reason}` on registry @{name})",
122
                ),
123
            )
124

125
    return PublishProcesses(jobs)
×
126

127

128
def rules():
6✔
129
    return (
6✔
130
        *collect_rules(),
131
        *PublishDockerImageFieldSet.rules(),
132
    )
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