diff options
Diffstat (limited to 'src/ansiblelint/formatters')
-rw-r--r-- | src/ansiblelint/formatters/__init__.py | 61 |
1 files changed, 52 insertions, 9 deletions
diff --git a/src/ansiblelint/formatters/__init__.py b/src/ansiblelint/formatters/__init__.py index 9ddca00..187d803 100644 --- a/src/ansiblelint/formatters/__init__.py +++ b/src/ansiblelint/formatters/__init__.py @@ -1,4 +1,5 @@ """Output formatters.""" + from __future__ import annotations import hashlib @@ -14,6 +15,7 @@ from ansiblelint.version import __version__ if TYPE_CHECKING: from ansiblelint.errors import MatchError + from ansiblelint.rules import BaseRule # type: ignore[attr-defined] T = TypeVar("T", bound="BaseFormatter") # type: ignore[type-arg] @@ -27,6 +29,7 @@ class BaseFormatter(Generic[T]): ---- base_dir (str|Path): reference directory against which display relative path. display_relative_path (bool): whether to show path as relative or absolute + """ def __init__(self, base_dir: str | Path, display_relative_path: bool) -> None: @@ -143,7 +146,7 @@ class CodeclimateJSONFormatter(BaseFormatter[Any]): """Format a list of match errors as a JSON string.""" if not isinstance(matches, list): msg = f"The {self.__class__} was expecting a list of MatchError." - raise RuntimeError(msg) + raise TypeError(msg) result = [] for match in matches: @@ -210,7 +213,7 @@ class SarifFormatter(BaseFormatter[Any]): """Format a list of match errors as a JSON string.""" if not isinstance(matches, list): msg = f"The {self.__class__} was expecting a list of MatchError." - raise RuntimeError(msg) + raise TypeError(msg) root_path = Path(str(self.base_dir)).as_uri() root_path = root_path + "/" if not root_path.endswith("/") else root_path @@ -264,7 +267,7 @@ class SarifFormatter(BaseFormatter[Any]): "text": str(match.message), }, "defaultConfiguration": { - "level": self._to_sarif_level(match), + "level": self.get_sarif_rule_severity_level(match.rule), }, "help": { "text": str(match.rule.description), @@ -275,12 +278,21 @@ class SarifFormatter(BaseFormatter[Any]): return rule def _to_sarif_result(self, match: MatchError) -> dict[str, Any]: + # https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790898 + if match.level not in ("warning", "error", "note", "none"): + msg = "Unexpected failure to map '%s' level to SARIF." + raise RuntimeError( + msg, + match.level, + ) + result: dict[str, Any] = { "ruleId": match.tag, + "level": self.get_sarif_result_severity_level(match), "message": { - "text": str(match.details) - if str(match.details) - else str(match.message), + "text": ( + str(match.details) if str(match.details) else str(match.message) + ), }, "locations": [ { @@ -303,6 +315,37 @@ class SarifFormatter(BaseFormatter[Any]): return result @staticmethod - def _to_sarif_level(match: MatchError) -> str: - # sarif accepts only 4 levels: error, warning, note, none - return match.level + def get_sarif_rule_severity_level(rule: BaseRule) -> str: + """General SARIF severity level for a rule. + + Note: Can differ from an actual result/match severity. + Possible values: "none", "note", "warning", "error" + + see: https://github.com/oasis-tcs/sarif-spec/blob/123e95847b13fbdd4cbe2120fa5e33355d4a042b/Schemata/sarif-schema-2.1.0.json#L1934-L1939 + """ + if rule.severity in ["VERY_HIGH", "HIGH"]: + return "error" + + if rule.severity in ["MEDIUM", "LOW", "VERY_LOW"]: + return "warning" + + if rule.severity == "INFO": + return "note" + + return "none" + + @staticmethod + def get_sarif_result_severity_level(match: MatchError) -> str: + """SARIF severity level for an actual result/match. + + Possible values: "none", "note", "warning", "error" + + see: https://github.com/oasis-tcs/sarif-spec/blob/123e95847b13fbdd4cbe2120fa5e33355d4a042b/Schemata/sarif-schema-2.1.0.json#L2066-L2071 + """ + if not match.level: + return "none" + + if match.level in ["warning", "error"]: + return match.level + + return "note" |