hadolint-2.5.0: src/Hadolint/Formatter/SonarQube.hs
module Hadolint.Formatter.SonarQube
( formatResult,
printResults
)
where
import qualified Control.Foldl as Foldl
import Data.Aeson hiding (Result)
import qualified Data.ByteString.Lazy.Char8 as B
import Data.Sequence (Seq)
import qualified Data.Text as Text
import Hadolint.Formatter.Format
( Result (..),
errorPosition,
errorMessage
)
import Hadolint.Rule
( CheckFailure (..),
DLSeverity (..),
unRuleCode
)
import Text.Megaparsec (TraversableStream)
import Text.Megaparsec.Error
import Text.Megaparsec.Pos
( sourceColumn,
sourceLine,
sourceName,
unPos
)
import Text.Megaparsec.Stream (VisualStream)
data SonarQubeFormat s e
= SonarQubeCheck Text.Text CheckFailure
| SonarQubeError (ParseErrorBundle s e)
instance (VisualStream s,
TraversableStream s,
ShowErrorComponent e) => ToJSON (SonarQubeFormat s e) where
toJSON (SonarQubeCheck filename CheckFailure {..}) =
object
[ "engineId" .= Text.pack "Hadolint",
"ruleId" .= unRuleCode code,
"severity" .= toSeverity severity,
"type" .= toType severity,
"primaryLocation" .= object
[ "message" .= message,
"filePath" .= filename,
"textRange" .= object
[ "startLine" .= line,
"endLine" .= line,
"startColumn" .= (1 :: Int),
"endColumn" .= (1 :: Int)
]
]
]
toJSON (SonarQubeError err) =
object
[ "engineId" .= Text.pack "Hadolint",
"ruleId" .= Text.pack "DL1000",
"severity" .= Text.pack "BLOCKER",
"type" .= Text.pack "BUG",
"primaryLocation" .= object
[ "message" .= errorMessage err,
"filePath" .= Text.pack (sourceName pos),
"textRange" .= object
[ "startLine" .= linenumber,
"endLine" .= linenumber,
"startColumn" .= column,
"endColumn" .= column
]
]
]
where
pos = errorPosition err
linenumber = unPos $ sourceLine pos
column = unPos $ sourceColumn pos
formatResult :: Result s e -> Seq (SonarQubeFormat s e)
formatResult (Result filename errors checks) = allMessages
where
allMessages = errorMessages <> checkMessages
errorMessages = fmap SonarQubeError errors
checkMessages = fmap (SonarQubeCheck filename) checks
printResults :: (VisualStream s,
TraversableStream s,
ShowErrorComponent e,
Foldable f) => f (Result s e) -> IO ()
printResults results = B.putStr . encode $ object [ "issues" .= flattened ]
where
flattened = Foldl.fold (Foldl.premap formatResult Foldl.mconcat) results
toType :: DLSeverity -> Text.Text
toType DLErrorC = "BUG"
toType _ = "CODE_SMELL"
toSeverity :: DLSeverity -> Text.Text
toSeverity DLErrorC = "CRITICAL"
toSeverity DLWarningC = "MAJOR"
toSeverity DLInfoC = "MINOR"
toSeverity _ = "INFO"