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

pantsbuild / pants / 18913303678

29 Oct 2025 03:29PM UTC coverage: 80.004% (-0.3%) from 80.283%
18913303678

push

github

web-flow
Updating 3rd party lockfiles for PBS script and MyPy (#22796)

Also small bumps to fastapi and starlette, which updated over the last few days. Starlette updated for features, and then a security vulnerability.

Fastapi bumped just for starlette.

88 of 93 new or added lines in 73 files covered. (94.62%)

221 existing lines in 15 files now uncovered.

77334 of 96663 relevant lines covered (80.0%)

5.2 hits per line

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

50.38
/src/python/pants/backend/helm/util_rules/chart.py
1
# Copyright 2022 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 dataclasses
6✔
7
import logging
6✔
8
from collections.abc import Iterable
6✔
9
from dataclasses import dataclass
6✔
10
from typing import Any
6✔
11

12
from pants.backend.helm.dependency_inference import chart as chart_inference
6✔
13
from pants.backend.helm.resolve import fetch
6✔
14
from pants.backend.helm.resolve.artifacts import ResolvedHelmArtifact
6✔
15
from pants.backend.helm.resolve.fetch import FetchedHelmArtifact, FetchHelmArtifactRequest
6✔
16
from pants.backend.helm.subsystems.helm import HelmSubsystem
6✔
17
from pants.backend.helm.target_types import (
6✔
18
    HelmArtifactFieldSet,
19
    HelmChartFieldSet,
20
    HelmChartTarget,
21
    HelmDeploymentFieldSet,
22
)
23
from pants.backend.helm.util_rules import chart_metadata, sources
6✔
24
from pants.backend.helm.util_rules.chart_metadata import (
6✔
25
    HELM_CHART_METADATA_FILENAMES,
26
    HelmChartDependency,
27
    HelmChartMetadata,
28
    ParseHelmChartMetadataDigest,
29
    parse_chart_metadata_from_digest,
30
    parse_chart_metadata_from_field,
31
    render_chart_metadata,
32
)
33
from pants.backend.helm.util_rules.sources import HelmChartSourceFilesRequest, get_helm_source_files
6✔
34
from pants.engine.addresses import Address
6✔
35
from pants.engine.engine_aware import EngineAwareParameter, EngineAwareReturnType
6✔
36
from pants.engine.fs import (
6✔
37
    EMPTY_DIGEST,
38
    AddPrefix,
39
    Digest,
40
    DigestSubset,
41
    FileDigest,
42
    MergeDigests,
43
    PathGlobs,
44
    Snapshot,
45
)
46
from pants.engine.internals.build_files import resolve_address
6✔
47
from pants.engine.internals.graph import resolve_target, resolve_targets
6✔
48
from pants.engine.intrinsics import (
6✔
49
    add_prefix,
50
    digest_subset_to_digest,
51
    digest_to_snapshot,
52
    merge_digests,
53
)
54
from pants.engine.rules import collect_rules, concurrently, implicitly, rule
6✔
55
from pants.engine.target import DependenciesRequest, Target, WrappedTargetRequest
6✔
56
from pants.util.frozendict import FrozenDict
6✔
57
from pants.util.logging import LogLevel
6✔
58
from pants.util.ordered_set import OrderedSet
6✔
59
from pants.util.strutil import pluralize, softwrap
6✔
60

61
logger = logging.getLogger(__name__)
6✔
62

63

64
class InvalidHelmChartTarget(ValueError):
6✔
65
    def __init__(self, target: Target) -> None:
6✔
66
        super().__init__(f"The target {target.address} is not a `{HelmChartTarget.alias}`.")
×
67

68

69
@dataclass(frozen=True)
6✔
70
class HelmChart(EngineAwareReturnType):
6✔
71
    address: Address
6✔
72
    info: HelmChartMetadata
6✔
73
    snapshot: Snapshot
6✔
74
    artifact: ResolvedHelmArtifact | None = None
6✔
75

76
    @property
6✔
77
    def name(self) -> str:
6✔
78
        return self.info.name
×
79

80
    @property
6✔
81
    def description(self) -> str:
6✔
82
        return self.name
×
83

84
    @property
6✔
85
    def immutable_input_digests(self) -> FrozenDict[str, Digest]:
6✔
86
        return FrozenDict({self.name: self.snapshot.digest})
×
87

88
    def level(self) -> LogLevel | None:
6✔
89
        return LogLevel.DEBUG
×
90

91
    def message(self) -> str | None:
6✔
92
        msg = f"Built Helm chart '{self.info.name}' from target at {self.address}"
×
93
        if self.artifact:
×
94
            msg += f" (resolved using URL {self.artifact.chart_url})."
×
95
        return msg
×
96

97
    def metadata(self) -> dict[str, Any] | None:
6✔
98
        meta: dict[str, Any] = {"name": self.info.name, "address": self.address.spec}
×
99
        if self.artifact:
×
100
            meta["artifact"] = self.artifact
×
101
        return meta
×
102

103
    def artifacts(self) -> dict[str, FileDigest | Snapshot] | None:
6✔
104
        return {"snapshot": self.snapshot}
×
105

106

107
@dataclass(frozen=True)
6✔
108
class HelmChartRequest(EngineAwareParameter):
6✔
109
    field_set: HelmChartFieldSet
6✔
110

111
    @classmethod
6✔
112
    def from_target(cls, target: Target) -> HelmChartRequest:
6✔
UNCOV
113
        if not HelmChartFieldSet.is_applicable(target):
×
114
            raise InvalidHelmChartTarget(target)
×
UNCOV
115
        return cls(HelmChartFieldSet.create(target))
×
116

117
    def debug_hint(self) -> str | None:
6✔
118
        return self.field_set.address.spec
×
119

120
    def metadata(self) -> dict[str, Any] | None:
6✔
121
        return {"address": self.field_set.address.spec}
×
122

123

124
@rule(desc="Compile third parth Helm chart", level=LogLevel.DEBUG)
6✔
125
async def create_chart_from_artifact(fetched_artifact: FetchedHelmArtifact) -> HelmChart:
6✔
126
    metadata = await parse_chart_metadata_from_digest(
×
127
        ParseHelmChartMetadataDigest(
128
            fetched_artifact.snapshot.digest,
129
            description_of_origin=f"the `helm_artifact` {fetched_artifact.address}",
130
        )
131
    )
132
    return HelmChart(
×
133
        fetched_artifact.address,
134
        metadata,
135
        fetched_artifact.snapshot,
136
        artifact=fetched_artifact.artifact,
137
    )
138

139

140
async def _merge_subchart_digests(charts: Iterable[HelmChart]) -> Digest:
6✔
141
    prefixed_chart_digests = await concurrently(
×
142
        add_prefix(AddPrefix(chart.snapshot.digest, chart.name)) for chart in charts
143
    )
144
    merged_digests = await merge_digests(MergeDigests(prefixed_chart_digests))
×
145
    return await add_prefix(AddPrefix(merged_digests, "charts"))
×
146

147

148
async def _find_charts_by_targets(
6✔
149
    targets: Iterable[Target], *, description_of_origin: str
150
) -> tuple[HelmChart, ...]:
151
    # These `Get`s are not ported yet to call-by-name due to the rule cycle with`get_helm_chart`
152
    # which calls this function.
153
    requests = [
×
154
        *(
155
            get_helm_chart(HelmChartRequest.from_target(target), **implicitly())
156
            for target in targets
157
            if HelmChartFieldSet.is_applicable(target)
158
        ),
159
        *(
160
            create_chart_from_artifact(
161
                **implicitly(
162
                    {
163
                        FetchHelmArtifactRequest.from_target(
164
                            target,
165
                            description_of_origin=description_of_origin,
166
                        ): FetchHelmArtifactRequest
167
                    }
168
                )
169
            )
170
            for target in targets
171
            if HelmArtifactFieldSet.is_applicable(target)
172
        ),
173
    ]
174
    return await concurrently(requests)
×
175

176

177
@rule(desc="Compile Helm chart", level=LogLevel.DEBUG)
6✔
178
async def get_helm_chart(request: HelmChartRequest, subsystem: HelmSubsystem) -> HelmChart:
6✔
179
    dependencies, source_files, chart_info = await concurrently(
×
180
        resolve_targets(**implicitly(DependenciesRequest(request.field_set.dependencies))),
181
        get_helm_source_files(
182
            HelmChartSourceFilesRequest.for_field_set(
183
                request.field_set,
184
                include_metadata=False,
185
                include_resources=True,
186
                include_files=True,
187
            )
188
        ),
189
        parse_chart_metadata_from_field(request.field_set.chart),
190
    )
191

192
    if request.field_set.version.value:
×
193
        chart_info = dataclasses.replace(chart_info, version=request.field_set.version.value)
×
194

195
    subcharts_digest = EMPTY_DIGEST
×
196
    subcharts = await _find_charts_by_targets(
×
197
        dependencies, description_of_origin=f"the `helm_chart` {request.field_set.address}"
198
    )
199
    if subcharts:
×
200
        logger.debug(
×
201
            softwrap(
202
                f"""
203
                Found {pluralize(len(subcharts), "subchart")} as direct dependencies
204
                on Helm chart at: {request.field_set.address}.
205
                """
206
            )
207
        )
208

209
        subcharts_digest = await _merge_subchart_digests(subcharts)
×
210

211
        # Update subchart dependencies in the metadata and re-render it.
212
        remotes = subsystem.remotes()
×
213
        subchart_map: dict[str, HelmChart] = {chart.info.name: chart for chart in subcharts}
×
214
        updated_dependencies: OrderedSet[HelmChartDependency] = OrderedSet()
×
215
        for dep in chart_info.dependencies:
×
216
            updated_dep = dep
×
217

218
            if not dep.repository and remotes.default_registry:
×
219
                # If the dependency hasn't specified a repository, then we choose the registry with the 'default' alias.
220
                default_remote = remotes.default_registry
×
221
                updated_dep = dataclasses.replace(updated_dep, repository=default_remote.address)
×
222
            elif dep.repository and dep.repository.startswith("@"):
×
223
                remote = next(remotes.get(dep.repository))
×
224
                updated_dep = dataclasses.replace(updated_dep, repository=remote.address)
×
225

226
            if dep.name in subchart_map:
×
227
                updated_dep = dataclasses.replace(
×
228
                    updated_dep, version=subchart_map[dep.name].info.version
229
                )
230

231
            updated_dependencies.add(updated_dep)
×
232

233
        # Include the explicitly provided subchats in the set of dependencies if not already present.
234
        updated_dependencies_names = {dep.name for dep in updated_dependencies}
×
235
        remaining_subcharts = [
×
236
            chart for chart in subcharts if chart.info.name not in updated_dependencies_names
237
        ]
238
        for chart in remaining_subcharts:
×
239
            if chart.artifact:
×
240
                dependency = HelmChartDependency(
×
241
                    name=chart.artifact.name,
242
                    version=chart.artifact.version,
243
                    repository=chart.artifact.location_url,
244
                )
245
            else:
246
                dependency = HelmChartDependency(name=chart.info.name, version=chart.info.version)
×
247
            updated_dependencies.add(dependency)
×
248

249
        # Update metadata with the information about charts' dependencies.
250
        chart_info = dataclasses.replace(chart_info, dependencies=tuple(updated_dependencies))
×
251

252
    # Re-render the Chart.yaml file with the updated dependencies.
253
    metadata_digest, sources_without_metadata = await concurrently(
×
254
        render_chart_metadata(chart_info),
255
        digest_subset_to_digest(
256
            DigestSubset(
257
                source_files.snapshot.digest,
258
                PathGlobs(
259
                    ["**/*", *(f"!**/{filename}" for filename in HELM_CHART_METADATA_FILENAMES)]
260
                ),
261
            )
262
        ),
263
    )
264

265
    # Merge all digests that conform chart's content.
266
    chart_snapshot = await digest_to_snapshot(
×
267
        **implicitly(MergeDigests([metadata_digest, sources_without_metadata, subcharts_digest]))
268
    )
269

270
    return HelmChart(address=request.field_set.address, info=chart_info, snapshot=chart_snapshot)
×
271

272

273
@dataclass(frozen=True)
6✔
274
class FindHelmDeploymentChart(EngineAwareParameter):
6✔
275
    field_set: HelmDeploymentFieldSet
6✔
276

277
    def debug_hint(self) -> str | None:
6✔
278
        return self.field_set.address.spec
×
279

280

281
@rule(desc="Find Helm deployment's chart", level=LogLevel.DEBUG)
6✔
282
async def find_chart_for_deployment(request: FindHelmDeploymentChart) -> HelmChart:
6✔
283
    address_input = request.field_set.chart.to_address_input()
×
284
    address = await resolve_address(**implicitly(address_input))
×
285
    wrapped_target = await resolve_target(
×
286
        WrappedTargetRequest(address, address_input.description_of_origin), **implicitly()
287
    )
288

289
    found_charts = await _find_charts_by_targets(
×
290
        [wrapped_target.target],
291
        description_of_origin=f"the `helm_deployment` {request.field_set.address}",
292
    )
293
    assert len(found_charts) == 1
×
294
    return found_charts[0]
×
295

296

297
def rules():
6✔
298
    return [
6✔
299
        *collect_rules(),
300
        *sources.rules(),
301
        *chart_metadata.rules(),
302
        *chart_inference.rules(),
303
        *fetch.rules(),
304
    ]
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

© 2025 Coveralls, Inc