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

pantsbuild / pants / 18791134616

24 Oct 2025 08:18PM UTC coverage: 75.519% (-4.8%) from 80.282%
18791134616

Pull #22794

github

web-flow
Merge 098c595a0 into 7971a20bf
Pull Request #22794: Use self-hosted MacOS Intel runner

65803 of 87134 relevant lines covered (75.52%)

3.07 hits per line

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

51.16
/src/python/pants/backend/helm/dependency_inference/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
7✔
5

6
import logging
7✔
7
import os
7✔
8
from collections.abc import Iterable
7✔
9
from dataclasses import dataclass
7✔
10

11
from pants.backend.helm.resolve import artifacts
7✔
12
from pants.backend.helm.resolve.artifacts import ThirdPartyHelmArtifactMapping
7✔
13
from pants.backend.helm.resolve.remotes import HelmRemotes
7✔
14
from pants.backend.helm.subsystems.helm import HelmSubsystem
7✔
15
from pants.backend.helm.target_types import (
7✔
16
    AllHelmChartTargets,
17
    HelmChartDependenciesField,
18
    HelmChartMetaSourceField,
19
    HelmChartTarget,
20
)
21
from pants.backend.helm.target_types import rules as helm_target_types_rules
7✔
22
from pants.backend.helm.util_rules import chart_metadata
7✔
23
from pants.backend.helm.util_rules.chart_metadata import (
7✔
24
    HelmChartDependency,
25
    parse_chart_metadata_from_field,
26
)
27
from pants.engine.addresses import Address
7✔
28
from pants.engine.internals.graph import determine_explicitly_provided_dependencies
7✔
29
from pants.engine.internals.selectors import concurrently
7✔
30
from pants.engine.rules import collect_rules, implicitly, rule
7✔
31
from pants.engine.target import (
7✔
32
    DependenciesRequest,
33
    FieldSet,
34
    InferDependenciesRequest,
35
    InferredDependencies,
36
)
37
from pants.engine.unions import UnionRule
7✔
38
from pants.util.frozendict import FrozenDict
7✔
39
from pants.util.logging import LogLevel
7✔
40
from pants.util.ordered_set import OrderedSet
7✔
41
from pants.util.strutil import bullet_list, pluralize
7✔
42

43
logger = logging.getLogger(__name__)
7✔
44

45

46
class DuplicateHelmChartNamesFound(Exception):
7✔
47
    def __init__(self, duplicates: Iterable[tuple[str, Address]]) -> None:
7✔
48
        super().__init__(
×
49
            f"Found more than one `{HelmChartTarget.alias}` target using the same chart name:\n\n"
50
            f"{bullet_list([f'{addr} -> {name}' for name, addr in duplicates])}"
51
        )
52

53

54
class UnknownHelmChartDependency(Exception):
7✔
55
    def __init__(self, address: Address, dependency: HelmChartDependency) -> None:
7✔
56
        super().__init__(
×
57
            f"Can not find any declared artifact for dependency '{dependency.name}' "
58
            f"declared at `Chart.yaml` in Helm chart at address: {address}"
59
        )
60

61

62
class FirstPartyHelmChartMapping(FrozenDict[str, Address]):
7✔
63
    pass
7✔
64

65

66
@rule
7✔
67
async def first_party_helm_chart_mapping(
7✔
68
    all_helm_chart_tgts: AllHelmChartTargets,
69
) -> FirstPartyHelmChartMapping:
70
    charts_metadata = await concurrently(
×
71
        parse_chart_metadata_from_field(tgt[HelmChartMetaSourceField])
72
        for tgt in all_helm_chart_tgts
73
    )
74

75
    name_addr_mapping: dict[str, Address] = {}
×
76
    duplicate_chart_names: OrderedSet[tuple[str, Address]] = OrderedSet()
×
77

78
    for meta, tgt in zip(charts_metadata, all_helm_chart_tgts):
×
79
        if meta.name in name_addr_mapping:
×
80
            duplicate_chart_names.add((meta.name, name_addr_mapping[meta.name]))
×
81
            duplicate_chart_names.add((meta.name, tgt.address))
×
82
            continue
×
83

84
        name_addr_mapping[meta.name] = tgt.address
×
85

86
    if duplicate_chart_names:
×
87
        raise DuplicateHelmChartNamesFound(duplicate_chart_names)
×
88

89
    return FirstPartyHelmChartMapping(name_addr_mapping)
×
90

91

92
@dataclass(frozen=True)
7✔
93
class HelmChartDependenciesInferenceFieldSet(FieldSet):
7✔
94
    required_fields = (HelmChartMetaSourceField, HelmChartDependenciesField)
7✔
95

96
    source: HelmChartMetaSourceField
7✔
97
    dependencies: HelmChartDependenciesField
7✔
98

99

100
class InferHelmChartDependenciesRequest(InferDependenciesRequest):
7✔
101
    infer_from = HelmChartDependenciesInferenceFieldSet
7✔
102

103

104
def resolve_dependency_url(remotes: HelmRemotes, dependency: HelmChartDependency) -> str | None:
7✔
105
    if not dependency.repository:
×
106
        registry = remotes.default_registry
×
107
        if registry:
×
108
            return os.path.join(registry.address, dependency.name)
×
109
        return None
×
110
    else:
111
        return os.path.join(dependency.repository, dependency.name)
×
112

113

114
@rule(desc="Inferring Helm chart dependencies", level=LogLevel.DEBUG)
7✔
115
async def infer_chart_dependencies_via_metadata(
7✔
116
    request: InferHelmChartDependenciesRequest,
117
    first_party_mapping: FirstPartyHelmChartMapping,
118
    third_party_mapping: ThirdPartyHelmArtifactMapping,
119
    subsystem: HelmSubsystem,
120
) -> InferredDependencies:
121
    address = request.field_set.address
×
122

123
    # Parse Chart.yaml for explicitly set dependencies.
124
    explicitly_provided_deps, metadata = await concurrently(
×
125
        determine_explicitly_provided_dependencies(
126
            **implicitly(DependenciesRequest(request.field_set.dependencies))
127
        ),
128
        parse_chart_metadata_from_field(request.field_set.source),
129
    )
130

131
    remotes = subsystem.remotes()
×
132

133
    # Associate dependencies in Chart.yaml with addresses.
134
    dependencies: OrderedSet[Address] = OrderedSet()
×
135
    for chart_dep in metadata.dependencies:
×
136
        candidate_addrs = []
×
137

138
        first_party_dep = first_party_mapping.get(chart_dep.name)
×
139
        if first_party_dep:
×
140
            candidate_addrs.append(first_party_dep)
×
141

142
        dependency_url = resolve_dependency_url(remotes, chart_dep)
×
143
        third_party_dep = third_party_mapping.get(dependency_url) if dependency_url else None
×
144
        if third_party_dep:
×
145
            candidate_addrs.append(third_party_dep)
×
146

147
        if not candidate_addrs:
×
148
            raise UnknownHelmChartDependency(address, chart_dep)
×
149

150
        matches = frozenset(candidate_addrs).difference(explicitly_provided_deps.includes)
×
151

152
        explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference(
×
153
            matches,
154
            address,
155
            context=f"The Helm chart {address} declares `{chart_dep.name}` as dependency",
156
            import_reference="helm dependency",
157
        )
158

159
        maybe_disambiguated = explicitly_provided_deps.disambiguated(matches)
×
160
        if maybe_disambiguated:
×
161
            dependencies.add(maybe_disambiguated)
×
162

163
    logger.debug(
×
164
        f"Inferred {pluralize(len(dependencies), 'dependency')} for target at address: {address}"
165
    )
166
    return InferredDependencies(dependencies)
×
167

168

169
def rules():
7✔
170
    return [
6✔
171
        *collect_rules(),
172
        *artifacts.rules(),
173
        *helm_target_types_rules(),
174
        *chart_metadata.rules(),
175
        UnionRule(InferDependenciesRequest, InferHelmChartDependenciesRequest),
176
    ]
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