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

1"""Parser for hadolint output.""" 

2 

3import re 

4 

5from lintro.parsers.base_parser import strip_ansi_codes 

6from lintro.parsers.hadolint.hadolint_issue import HadolintIssue 

7 

8 

9def parse_hadolint_output(output: str) -> list[HadolintIssue]: 

10 """Parse hadolint output into a list of HadolintIssue objects. 

11 

12 Hadolint outputs in the format: 

13 filename:line code level: message 

14 

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` 

21 

22 Args: 

23 output: The raw output from hadolint 

24 

25 Returns: 

26 List of HadolintIssue objects 

27 """ 

28 issues: list[HadolintIssue] = [] 

29 

30 # Skip empty output 

31 if not output.strip(): 

32 return issues 

33 

34 # Strip ANSI codes for consistent parsing across environments 

35 output = strip_ansi_codes(output) 

36 

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 ) 

41 

42 lines: list[str] = output.splitlines() 

43 

44 for line in lines: 

45 line = line.strip() 

46 if not line: 

47 continue 

48 

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() 

57 

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 ) 

68 

69 return issues