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

pantsbuild / pants / 19015773527

02 Nov 2025 05:33PM UTC coverage: 17.872% (-62.4%) from 80.3%
19015773527

Pull #22816

github

web-flow
Merge a12d75757 into 6c024e162
Pull Request #22816: Update Pants internal Python to 3.14

4 of 5 new or added lines in 3 files covered. (80.0%)

28452 existing lines in 683 files now uncovered.

9831 of 55007 relevant lines covered (17.87%)

0.18 hits per line

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

0.0
/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).
UNCOV
3
from __future__ import annotations
×
4

UNCOV
5
from dataclasses import dataclass
×
UNCOV
6
from typing import Any
×
7

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

31

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

UNCOV
36
    source: DockerImageSourceField
×
37

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

42

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

48

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

59

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

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

76
    input_digest = await merge_digests(
×
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(
×
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)
×
109

110

UNCOV
111
def rules():
×
UNCOV
112
    return [
×
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