Coverage for lintro / parsers / hadolint / hadolint_parser.py: 100%
19 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Parser for hadolint output."""
3import re
5from lintro.parsers.base_parser import strip_ansi_codes
6from lintro.parsers.hadolint.hadolint_issue import HadolintIssue
9def parse_hadolint_output(output: str) -> list[HadolintIssue]:
10 """Parse hadolint output into a list of HadolintIssue objects.
12 Hadolint outputs in the format:
13 filename:line code level: message
15 Example outputs:
16 Dockerfile:1 DL3006 error: Always tag the version of an image explicitly
17 Dockerfile:3 DL3009 warning: Delete the apt-get lists after installing
18 something
19 Dockerfile:5 DL3015 info: Avoid additional packages by specifying
20 `--no-install-recommends`
22 Args:
23 output: The raw output from hadolint
25 Returns:
26 List of HadolintIssue objects
27 """
28 issues: list[HadolintIssue] = []
30 # Skip empty output
31 if not output.strip():
32 return issues
34 # Strip ANSI codes for consistent parsing across environments
35 output = strip_ansi_codes(output)
37 # Pattern for hadolint output: filename:line code level: message
38 pattern: re.Pattern[str] = re.compile(
39 r"^(.+?):(\d+)\s+([A-Z]+\d+)\s+(error|warning|info|style):\s+(.+)$",
40 )
42 lines: list[str] = output.splitlines()
44 for line in lines:
45 line = line.strip()
46 if not line:
47 continue
49 match: re.Match[str] | None = pattern.match(line)
50 if match:
51 file: str
52 line_num: str
53 code: str
54 level: str
55 message: str
56 file, line_num, code, level, message = match.groups()
58 issues.append(
59 HadolintIssue(
60 file=file,
61 line=int(line_num),
62 column=0, # hadolint doesn't provide column in this format
63 level=level,
64 code=code,
65 message=message.strip(),
66 ),
67 )
69 return issues