• 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.92
/src/python/pants/backend/docker/lint/hadolint/rules.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3
from __future__ import annotations
3✔
4

5
from dataclasses import dataclass
3✔
6
from typing import Any
3✔
7

8
from pants.backend.docker.lint.hadolint.skip_field import SkipHadolintField
3✔
9
from pants.backend.docker.lint.hadolint.subsystem import Hadolint
3✔
10
from pants.backend.docker.subsystems.dockerfile_parser import (
3✔
11
    DockerfileInfo,
12
    DockerfileInfoRequest,
13
    parse_dockerfile,
14
)
15
from pants.backend.docker.target_types import DockerImageSourceField
3✔
16
from pants.core.goals.lint import LintResult, LintTargetsRequest
3✔
17
from pants.core.goals.resolves import ExportableTool
3✔
18
from pants.core.util_rules.config_files import find_config_file
3✔
19
from pants.core.util_rules.external_tool import download_external_tool
3✔
20
from pants.core.util_rules.partitions import PartitionerType
3✔
21
from pants.engine.fs import MergeDigests
3✔
22
from pants.engine.intrinsics import execute_process, merge_digests
3✔
23
from pants.engine.platform import Platform
3✔
24
from pants.engine.process import Process
3✔
25
from pants.engine.rules import collect_rules, concurrently, implicitly, rule
3✔
26
from pants.engine.target import FieldSet, Target
3✔
27
from pants.engine.unions import UnionRule
3✔
28
from pants.util.logging import LogLevel
3✔
29
from pants.util.strutil import pluralize
3✔
30

31

32
@dataclass(frozen=True)
3✔
33
class HadolintFieldSet(FieldSet):
3✔
34
    required_fields = (DockerImageSourceField,)
3✔
35

36
    source: DockerImageSourceField
3✔
37

38
    @classmethod
3✔
39
    def opt_out(cls, tgt: Target) -> bool:
3✔
40
        return tgt.get(SkipHadolintField).value
×
41

42

43
class HadolintRequest(LintTargetsRequest):
3✔
44
    field_set_type = HadolintFieldSet
3✔
45
    tool_subsystem = Hadolint  # type: ignore[assignment]
3✔
46
    partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
3✔
47

48

49
def generate_argv(
3✔
50
    dockerfile_infos: tuple[DockerfileInfo, ...], hadolint: Hadolint
51
) -> tuple[str, ...]:
52
    args = []
3✔
53
    if hadolint.config:
3✔
54
        args.append(f"--config={hadolint.config}")
1✔
55
    args.extend(hadolint.args)
3✔
56
    args.extend(info.source for info in dockerfile_infos)
3✔
57
    return tuple(args)
3✔
58

59

60
@rule(desc="Lint with Hadolint", level=LogLevel.DEBUG)
3✔
61
async def run_hadolint(
3✔
62
    request: HadolintRequest.Batch[HadolintFieldSet, Any],
63
    hadolint: Hadolint,
64
    platform: Platform,
65
) -> LintResult:
66
    downloaded_hadolint, config_files = await concurrently(
3✔
67
        download_external_tool(hadolint.get_request(platform)),
68
        find_config_file(hadolint.config_request()),
69
    )
70

71
    dockerfile_infos = await concurrently(
3✔
72
        parse_dockerfile(DockerfileInfoRequest(field_set.address), **implicitly())
73
        for field_set in request.elements
74
    )
75

76
    input_digest = await merge_digests(
3✔
77
        MergeDigests(
78
            (
79
                downloaded_hadolint.digest,
80
                config_files.snapshot.digest,
81
                *(info.digest for info in dockerfile_infos),
82
            )
83
        )
84
    )
85
    process_result = await execute_process(
3✔
86
        Process(
87
            argv=[downloaded_hadolint.exe, *generate_argv(dockerfile_infos, hadolint)],
88
            # Hadolint tries to read a configuration file from a few locations on the system:
89
            # https://github.com/hadolint/hadolint/blob/43d2bfe9f71dea9ddd203d5bdbd2cc1fb512e4dd/src/Hadolint/Config/Configfile.hs#L75-L101
90
            #
91
            # We don't want it to do this in order to have reproducible results machine to machine
92
            # and there is also the problem that on some machines, an unset (as opposed to empty)
93
            # HOME env var crashes hadolint with SIGSEGV.
94
            # See: https://github.com/hadolint/hadolint/issues/741
95
            #
96
            # As such, we set HOME to blank so no system configuration is found and, as a side
97
            # benefit, we don't crash.
98
            #
99
            # See https://github.com/pantsbuild/pants/issues/13735 for more details.
100
            env={"HOME": ""},
101
            input_digest=input_digest,
102
            description=f"Run `hadolint` on {pluralize(len(dockerfile_infos), 'Dockerfile')}.",
103
            level=LogLevel.DEBUG,
104
        ),
105
        **implicitly(),
106
    )
107

108
    return LintResult.create(request, process_result)
3✔
109

110

111
def rules():
3✔
112
    return [
3✔
113
        *collect_rules(),
114
        *HadolintRequest.rules(),
115
        UnionRule(ExportableTool, Hadolint),
116
    ]
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