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

pantsbuild / pants / 18198316586

02 Oct 2025 03:50PM UTC coverage: 78.82% (-1.4%) from 80.265%
18198316586

push

github

web-flow
Bump serde from 1.0.226 to 1.0.228 in /src/rust (#22723)

Bumps [serde](https://github.com/serde-rs/serde) from 1.0.226 to
1.0.228.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/serde/releases">serde's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.228</h2>
<ul>
<li>Allow building documentation with
<code>RUSTDOCFLAGS='--cfg=docsrs'</code> set for the whole dependency
graph (<a
href="https://redirect.github.com/serde-rs/serde/issues/2995">#2995</a>)</li>
</ul>
<h2>v1.0.227</h2>
<ul>
<li>Documentation improvements (<a
href="https://redirect.github.com/serde-rs/serde/issues/2991">#2991</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/serde-rs/serde/commit/a866b336f"><code>a866b33</code></a>
Release 1.0.228</li>
<li><a
href="https://github.com/serde-rs/serde/commit/5adc9e816"><code>5adc9e8</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2995">#2995</a>
from dtolnay/rustdocflags</li>
<li><a
href="https://github.com/serde-rs/serde/commit/ab581789f"><code>ab58178</code></a>
Workaround for RUSTDOCFLAGS='--cfg=docsrs'</li>
<li><a
href="https://github.com/serde-rs/serde/commit/415d9fc56"><code>415d9fc</code></a>
Release 1.0.227</li>
<li><a
href="https://github.com/serde-rs/serde/commit/7c58427e1"><code>7c58427</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2991">#2991</a>
from dtolnay/inlinecoredoc</li>
<li><a
href="https://github.com/serde-rs/serde/commit/9d3410e3f"><code>9d3410e</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2992">#2992</a>
from dtolnay/inplaceseed</li>
<li><a
href="https://github.com/serde-rs/serde/commit/2fb6748bf1ff93... (continued)

73576 of 93347 relevant lines covered (78.82%)

2.9 hits per line

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

52.33
/src/python/pants/backend/helm/util_rules/chart_metadata.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
8✔
5

6
import dataclasses
8✔
7
from dataclasses import dataclass
8✔
8
from enum import Enum
8✔
9
from typing import Any, cast
8✔
10

11
import yaml
8✔
12

13
from pants.backend.helm.target_types import HelmChartMetaSourceField
8✔
14
from pants.backend.helm.util_rules.sources import HelmChartRootRequest, find_chart_source_root
8✔
15
from pants.backend.helm.utils.yaml import snake_case_attr_dict
8✔
16
from pants.base.glob_match_error_behavior import GlobMatchErrorBehavior
8✔
17
from pants.engine.engine_aware import EngineAwareParameter
8✔
18
from pants.engine.fs import (
8✔
19
    CreateDigest,
20
    Digest,
21
    DigestSubset,
22
    FileContent,
23
    GlobExpansionConjunction,
24
    PathGlobs,
25
)
26
from pants.engine.internals.graph import hydrate_sources
8✔
27
from pants.engine.internals.native_engine import RemovePrefix
8✔
28
from pants.engine.internals.selectors import concurrently
8✔
29
from pants.engine.intrinsics import (
8✔
30
    create_digest,
31
    digest_subset_to_digest,
32
    get_digest_contents,
33
    remove_prefix,
34
)
35
from pants.engine.rules import collect_rules, implicitly, rule
8✔
36
from pants.engine.target import HydrateSourcesRequest
8✔
37
from pants.util.frozendict import FrozenDict
8✔
38
from pants.util.strutil import bullet_list
8✔
39

40

41
class ChartType(Enum):
8✔
42
    """Type of Helm Chart."""
43

44
    APPLICATION = "application"
8✔
45
    LIBRARY = "library"
8✔
46

47

48
class InvalidChartTypeValueError(ValueError):
8✔
49
    def __init__(self, value: str) -> None:
8✔
50
        super().__init__(
×
51
            f"Invalid value '{value}' for Helm Chart `type`. Valid values are: {[t.value for t in list(ChartType)]}"
52
        )
53

54

55
class MissingChartMetadataException(Exception):
8✔
56
    pass
8✔
57

58

59
class AmbiguousChartMetadataException(Exception):
8✔
60
    pass
8✔
61

62

63
@dataclass(frozen=True)
8✔
64
class HelmChartDependency:
8✔
65
    name: str
8✔
66
    repository: str | None = None
8✔
67
    version: str | None = None
8✔
68
    alias: str | None = None
8✔
69
    condition: str | None = None
8✔
70
    tags: tuple[str, ...] = dataclasses.field(default_factory=tuple)
8✔
71
    import_values: tuple[str, ...] = dataclasses.field(default_factory=tuple)
8✔
72

73
    @classmethod
8✔
74
    def from_dict(cls, d: dict[str, Any]) -> HelmChartDependency:
8✔
75
        attrs = snake_case_attr_dict(d)
×
76

77
        tags = attrs.pop("tags", [])
×
78
        import_values = attrs.pop("import_values", [])
×
79

80
        return cls(tags=tuple(tags), import_values=tuple(import_values), **attrs)
×
81

82
    def to_json_dict(self) -> dict[str, Any]:
8✔
83
        d: dict[str, Any] = {"name": self.name}
×
84
        if self.repository:
×
85
            d["repository"] = self.repository.rstrip("/")
×
86
        if self.version:
×
87
            d["version"] = self.version
×
88
        if self.alias:
×
89
            d["alias"] = self.alias
×
90
        if self.condition:
×
91
            d["condition"] = self.condition
×
92
        if self.tags:
×
93
            d["tags"] = list(self.tags)
×
94
        if self.import_values:
×
95
            d["import-values"] = list(self.import_values)
×
96
        return d
×
97

98

99
@dataclass(frozen=True)
8✔
100
class HelmChartMaintainer:
8✔
101
    name: str
8✔
102
    email: str | None = None
8✔
103
    url: str | None = None
8✔
104

105
    @classmethod
8✔
106
    def from_dict(cls, d: dict[str, Any]) -> HelmChartMaintainer:
8✔
107
        return cls(**d)
×
108

109
    def to_json_dict(self) -> dict[str, Any]:
8✔
110
        d = {"name": self.name}
×
111
        if self.email:
×
112
            d["email"] = self.email
×
113
        if self.url:
×
114
            d["url"] = self.url
×
115
        return d
×
116

117

118
DEFAULT_API_VERSION = "v2"
8✔
119

120

121
@dataclass(frozen=True)
8✔
122
class HelmChartMetadata:
8✔
123
    name: str
8✔
124
    version: str
8✔
125
    api_version: str = DEFAULT_API_VERSION
8✔
126
    type: ChartType = ChartType.APPLICATION
8✔
127
    kube_version: str | None = None
8✔
128
    app_version: str | None = None
8✔
129
    icon: str | None = None
8✔
130
    description: str | None = None
8✔
131
    dependencies: tuple[HelmChartDependency, ...] = dataclasses.field(default_factory=tuple)
8✔
132
    keywords: tuple[str, ...] = dataclasses.field(default_factory=tuple)
8✔
133
    sources: tuple[str, ...] = dataclasses.field(default_factory=tuple)
8✔
134
    home: str | None = None
8✔
135
    maintainers: tuple[HelmChartMaintainer, ...] = dataclasses.field(default_factory=tuple)
8✔
136
    deprecated: bool | None = None
8✔
137
    annotations: FrozenDict[str, str] = dataclasses.field(default_factory=FrozenDict)
8✔
138

139
    @classmethod
8✔
140
    def from_dict(cls, d: dict[str, Any]) -> HelmChartMetadata:
8✔
141
        chart_type: ChartType | None = None
×
142
        type_str = d.pop("type", None)
×
143
        if type_str:
×
144
            try:
×
145
                chart_type = ChartType(type_str)
×
146
            except KeyError:
×
147
                raise InvalidChartTypeValueError(type_str)
×
148

149
        # If the `apiVersion` is missing in the original `dict`, then we assume we are dealing with `v1` charts.
150
        api_version = d.pop("apiVersion", "v1")
×
151
        dependencies = [HelmChartDependency.from_dict(d) for d in d.pop("dependencies", [])]
×
152
        maintainers = [HelmChartMaintainer.from_dict(d) for d in d.pop("maintainers", [])]
×
153
        keywords = d.pop("keywords", [])
×
154
        sources = d.pop("sources", [])
×
155
        annotations = d.pop("annotations", {})
×
156

157
        attrs = snake_case_attr_dict(d)
×
158

159
        return cls(
×
160
            api_version=api_version,
161
            dependencies=tuple(dependencies),
162
            maintainers=tuple(maintainers),
163
            keywords=tuple(keywords),
164
            type=chart_type or ChartType.APPLICATION,
165
            annotations=FrozenDict(annotations),
166
            sources=tuple(sources),
167
            **attrs,
168
        )
169

170
    @classmethod
8✔
171
    def from_bytes(cls, content: bytes) -> HelmChartMetadata:
8✔
172
        return cls.from_dict(yaml.safe_load(content))
×
173

174
    @property
8✔
175
    def artifact_name(self) -> str:
8✔
176
        return f"{self.name}-{self.version}"
1✔
177

178
    def to_json_dict(self) -> dict[str, Any]:
8✔
179
        d: dict[str, Any] = {
×
180
            "apiVersion": self.api_version,
181
            "name": self.name,
182
            "version": self.version,
183
        }
184
        if self.api_version != "v1":
×
185
            d["type"] = self.type.value
×
186
        if self.icon:
×
187
            d["icon"] = self.icon
×
188
        if self.description:
×
189
            d["description"] = self.description
×
190
        if self.app_version:
×
191
            d["appVersion"] = self.app_version
×
192
        if self.kube_version:
×
193
            d["kubeVersion"] = self.kube_version
×
194
        if self.dependencies:
×
195
            d["dependencies"] = [item.to_json_dict() for item in self.dependencies]
×
196
        if self.maintainers:
×
197
            d["maintainers"] = [item.to_json_dict() for item in self.maintainers]
×
198
        if self.annotations:
×
199
            d["annotations"] = dict(self.annotations.items())
×
200
        if self.keywords:
×
201
            d["keywords"] = list(self.keywords)
×
202
        if self.sources:
×
203
            d["sources"] = list(self.sources)
×
204
        if self.home:
×
205
            d["home"] = self.home
×
206
        if self.deprecated:
×
207
            d["deprecated"] = self.deprecated
×
208
        return d
×
209

210
    def to_yaml(self) -> str:
8✔
211
        return cast("str", yaml.dump(self.to_json_dict()))
×
212

213

214
HELM_CHART_METADATA_FILENAMES = ["Chart.yaml", "Chart.yml"]
8✔
215

216

217
@dataclass(frozen=True)
8✔
218
class ParseHelmChartMetadataDigest(EngineAwareParameter):
8✔
219
    """Request to parse the Helm chart definition file (i.e. `Chart.yaml`) from the given digest.
220

221
    The definition file is expected to be at the root of the digest.
222
    """
223

224
    digest: Digest
8✔
225
    description_of_origin: str
8✔
226

227
    def debug_hint(self) -> str | None:
8✔
228
        return self.description_of_origin
×
229

230

231
@rule
8✔
232
async def parse_chart_metadata_from_digest(
8✔
233
    request: ParseHelmChartMetadataDigest,
234
) -> HelmChartMetadata:
235
    subset = await digest_subset_to_digest(
×
236
        DigestSubset(
237
            request.digest,
238
            PathGlobs(
239
                HELM_CHART_METADATA_FILENAMES,
240
                glob_match_error_behavior=GlobMatchErrorBehavior.error,
241
                conjunction=GlobExpansionConjunction.any_match,
242
                description_of_origin=request.description_of_origin,
243
            ),
244
        )
245
    )
246

247
    file_contents = await get_digest_contents(subset)
×
248

249
    if len(file_contents) == 0:
×
250
        raise MissingChartMetadataException(
×
251
            f"Could not find any file that matched with either {HELM_CHART_METADATA_FILENAMES} in target at {request.description_of_origin}."
252
        )
253
    if len(file_contents) > 1:
×
254
        raise AmbiguousChartMetadataException(
×
255
            f"Found more than one Helm chart metadata file at '{request.description_of_origin}':\n{bullet_list([f.path for f in file_contents])}"
256
        )
257

258
    return HelmChartMetadata.from_bytes(file_contents[0].content)
×
259

260

261
@rule
8✔
262
async def parse_chart_metadata_from_field(field: HelmChartMetaSourceField) -> HelmChartMetadata:
8✔
263
    chart_root, source_files = await concurrently(
×
264
        find_chart_source_root(HelmChartRootRequest(field)),
265
        hydrate_sources(
266
            HydrateSourcesRequest(
267
                field, for_sources_types=(HelmChartMetaSourceField,), enable_codegen=True
268
            ),
269
            **implicitly(),
270
        ),
271
    )
272

273
    metadata_digest = await remove_prefix(
×
274
        RemovePrefix(source_files.snapshot.digest, chart_root.path)
275
    )
276

277
    return await parse_chart_metadata_from_digest(
×
278
        ParseHelmChartMetadataDigest(
279
            metadata_digest,
280
            description_of_origin=f"the `helm_chart` {field.address.spec}",
281
        )
282
    )
283

284

285
@rule
8✔
286
async def render_chart_metadata(metadata: HelmChartMetadata) -> Digest:
8✔
287
    yaml_contents = bytes(metadata.to_yaml(), "utf-8")
×
288
    return await create_digest(
×
289
        CreateDigest([FileContent(HELM_CHART_METADATA_FILENAMES[0], yaml_contents)])
290
    )
291

292

293
def rules():
8✔
294
    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

© 2025 Coveralls, Inc