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

pantsbuild / pants / 24055979590

06 Apr 2026 11:17PM UTC coverage: 52.37% (-40.5%) from 92.908%
24055979590

Pull #23225

github

web-flow
Merge 67474653c into 542ca048d
Pull Request #23225: Add --test-show-all-batch-targets to expose all targets in batched pytest

6 of 17 new or added lines in 2 files covered. (35.29%)

23030 existing lines in 605 files now uncovered.

31643 of 60422 relevant lines covered (52.37%)

1.05 hits per line

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

95.83
/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
2✔
4

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

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

31

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

36
    source: DockerImageSourceField
2✔
37

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

42

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

48

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

59

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

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

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

110

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