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

pantsbuild / pants / 25441711719

06 May 2026 02:31PM UTC coverage: 92.915%. Remained the same
25441711719

push

github

web-flow
use sha pin (with comment) format for generated actions (#23312)

Per the GitHub Action best practices we recently enabled at #23249, we
should pin each action to a SHA so that the reference is actually
immutable.

This will -- I hope -- knock out a large chunk of the 421 alerts we
currently get from zizmor. The next followup would then be upgrades and
harmonizing the generated and none-generated pins.

Notice: This idea was suggested by Claude while going over pinact output
and I was surprised to see that post processing the yaml wasn't too
gross.

92206 of 99237 relevant lines covered (92.91%)

4.04 hits per line

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

97.06
/src/python/pants/backend/python/lint/first_party_plugins.py
1
# Copyright 2025 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3
from dataclasses import dataclass
5✔
4
from typing import ClassVar, TypeVar
5✔
5

6
from pants.backend.python.target_types import (
5✔
7
    InterpreterConstraintsField,
8
    PythonRequirementsField,
9
    PythonResolveField,
10
)
11
from pants.backend.python.util_rules.pex_requirements import PexRequirements
5✔
12
from pants.backend.python.util_rules.python_sources import (
5✔
13
    PythonSourceFilesRequest,
14
    strip_python_sources,
15
)
16
from pants.engine.addresses import UnparsedAddressInputs
5✔
17
from pants.engine.fs import EMPTY_DIGEST, AddPrefix, Digest
5✔
18
from pants.engine.internals.graph import resolve_unparsed_address_inputs
5✔
19
from pants.engine.internals.graph import transitive_targets as transitive_targets_get
5✔
20
from pants.engine.intrinsics import add_prefix
5✔
21
from pants.engine.rules import implicitly
5✔
22
from pants.engine.target import TransitiveTargetsRequest
5✔
23
from pants.util.ordered_set import FrozenOrderedSet, OrderedSet
5✔
24

25

26
@dataclass(frozen=True)
5✔
27
class BaseFirstPartyPlugins:
5✔
28
    requirement_strings: FrozenOrderedSet[str]
29
    interpreter_constraints_and_resolve_fields: FrozenOrderedSet[
30
        tuple[InterpreterConstraintsField, PythonResolveField]
31
    ]
32
    sources_digest: Digest
33

34
    PREFIX: ClassVar[str] = "__plugins"
5✔
35

36
    def __bool__(self) -> bool:
5✔
37
        return self.sources_digest != EMPTY_DIGEST
3✔
38

39

40
FPP = TypeVar("FPP", bound=BaseFirstPartyPlugins)
5✔
41

42

43
async def resolve_first_party_plugins(
5✔
44
    source_plugins: UnparsedAddressInputs, fpp_type: type[FPP]
45
) -> FPP:
46
    if not source_plugins:
5✔
47
        return fpp_type(FrozenOrderedSet(), FrozenOrderedSet(), EMPTY_DIGEST)
×
48

49
    plugin_target_addresses = await resolve_unparsed_address_inputs(source_plugins, **implicitly())
5✔
50
    transitive_targets = await transitive_targets_get(
5✔
51
        TransitiveTargetsRequest(plugin_target_addresses), **implicitly()
52
    )
53

54
    requirements_fields: OrderedSet[PythonRequirementsField] = OrderedSet()
5✔
55
    interpreter_constraints_and_resolve_fields: OrderedSet[
5✔
56
        tuple[InterpreterConstraintsField, PythonResolveField]
57
    ] = OrderedSet()
58
    for tgt in transitive_targets.closure:
5✔
59
        if tgt.has_field(PythonRequirementsField):
2✔
60
            requirements_fields.add(tgt[PythonRequirementsField])
2✔
61
        if tgt.has_field(InterpreterConstraintsField):
2✔
62
            interpreter_constraints_and_resolve_fields.add(
2✔
63
                (tgt[InterpreterConstraintsField], tgt[PythonResolveField])
64
            )
65

66
    # NB: Flake8 and Pylint source plugins must be explicitly loaded via PYTHONPATH (i.e. PEX_EXTRA_SYS_PATH).
67
    # The value must point to the plugin's directory, rather than to a parent's directory, because
68
    # `flake8:local-plugins` values take a module name rather than a path to the module;
69
    # i.e. `plugin`, but not `path/to/plugin`.
70
    # (This means users must have specified the parent directory as a source root.)
71
    stripped_sources = await strip_python_sources(
5✔
72
        **implicitly(PythonSourceFilesRequest(transitive_targets.closure))
73
    )
74
    prefixed_sources = await add_prefix(
5✔
75
        AddPrefix(stripped_sources.stripped_source_files.snapshot.digest, fpp_type.PREFIX)
76
    )
77

78
    return fpp_type(
5✔
79
        requirement_strings=PexRequirements.req_strings_from_requirement_fields(
80
            requirements_fields,
81
        ),
82
        interpreter_constraints_and_resolve_fields=FrozenOrderedSet(
83
            interpreter_constraints_and_resolve_fields
84
        ),
85
        sources_digest=prefixed_sources,
86
    )
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