mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-26 08:53:21 +00:00
Merge pull request #217242 from maralorn/broken-reasons
maintainers/scripts/haskell/hydra-report: Add comments with error causes to broken list
This commit is contained in:
commit
994e845bd0
@ -26,6 +26,7 @@ Because step 1) is quite expensive and takes roughly ~5 minutes the result is ca
|
|||||||
{-# LANGUAGE TupleSections #-}
|
{-# LANGUAGE TupleSections #-}
|
||||||
{-# LANGUAGE ViewPatterns #-}
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
{-# OPTIONS_GHC -Wall #-}
|
{-# OPTIONS_GHC -Wall #-}
|
||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
|
||||||
import Control.Monad (forM_, (<=<))
|
import Control.Monad (forM_, (<=<))
|
||||||
import Control.Monad.Trans (MonadIO (liftIO))
|
import Control.Monad.Trans (MonadIO (liftIO))
|
||||||
@ -54,17 +55,22 @@ import Data.Time (defaultTimeLocale, formatTime, getCurrentTime)
|
|||||||
import Data.Time.Clock (UTCTime)
|
import Data.Time.Clock (UTCTime)
|
||||||
import GHC.Generics (Generic)
|
import GHC.Generics (Generic)
|
||||||
import Network.HTTP.Req (
|
import Network.HTTP.Req (
|
||||||
GET (GET),
|
GET (GET),
|
||||||
NoReqBody (NoReqBody),
|
HttpResponse (HttpResponseBody),
|
||||||
defaultHttpConfig,
|
NoReqBody (NoReqBody),
|
||||||
header,
|
Option,
|
||||||
https,
|
Req,
|
||||||
jsonResponse,
|
Scheme (Https),
|
||||||
req,
|
bsResponse,
|
||||||
responseBody,
|
defaultHttpConfig,
|
||||||
responseTimeout,
|
header,
|
||||||
runReq,
|
https,
|
||||||
(/:),
|
jsonResponse,
|
||||||
|
req,
|
||||||
|
responseBody,
|
||||||
|
responseTimeout,
|
||||||
|
runReq,
|
||||||
|
(/:),
|
||||||
)
|
)
|
||||||
import System.Directory (XdgDirectory (XdgCache), getXdgDirectory)
|
import System.Directory (XdgDirectory (XdgCache), getXdgDirectory)
|
||||||
import System.Environment (getArgs)
|
import System.Environment (getArgs)
|
||||||
@ -76,6 +82,10 @@ import Control.Exception (evaluate)
|
|||||||
import qualified Data.IntMap.Strict as IntMap
|
import qualified Data.IntMap.Strict as IntMap
|
||||||
import qualified Data.IntSet as IntSet
|
import qualified Data.IntSet as IntSet
|
||||||
import Data.Bifunctor (second)
|
import Data.Bifunctor (second)
|
||||||
|
import Data.Data (Proxy)
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
|
import qualified Data.ByteString.Char8 as ByteString
|
||||||
|
import Distribution.Simple.Utils (safeLast, fromUTF8BS)
|
||||||
|
|
||||||
newtype JobsetEvals = JobsetEvals
|
newtype JobsetEvals = JobsetEvals
|
||||||
{ evals :: Seq Eval
|
{ evals :: Seq Eval
|
||||||
@ -123,17 +133,31 @@ showT = Text.pack . show
|
|||||||
|
|
||||||
getBuildReports :: IO ()
|
getBuildReports :: IO ()
|
||||||
getBuildReports = runReq defaultHttpConfig do
|
getBuildReports = runReq defaultHttpConfig do
|
||||||
evalMay <- Seq.lookup 0 . evals <$> myReq (https "hydra.nixos.org" /: "jobset" /: "nixpkgs" /: "haskell-updates" /: "evals") mempty
|
evalMay <- Seq.lookup 0 . evals <$> hydraJSONQuery mempty ["jobset", "nixpkgs", "haskell-updates", "evals"]
|
||||||
eval@Eval{id} <- maybe (liftIO $ fail "No Evalution found") pure evalMay
|
eval@Eval{id} <- maybe (liftIO $ fail "No Evalution found") pure evalMay
|
||||||
liftIO . putStrLn $ "Fetching evaluation " <> show id <> " from Hydra. This might take a few minutes..."
|
liftIO . putStrLn $ "Fetching evaluation " <> show id <> " from Hydra. This might take a few minutes..."
|
||||||
buildReports :: Seq Build <- myReq (https "hydra.nixos.org" /: "eval" /: showT id /: "builds") (responseTimeout 600000000)
|
buildReports :: Seq Build <- hydraJSONQuery (responseTimeout 600000000) ["eval", showT id, "builds"]
|
||||||
liftIO do
|
liftIO do
|
||||||
fileName <- reportFileName
|
fileName <- reportFileName
|
||||||
putStrLn $ "Finished fetching all builds from Hydra, saving report as " <> fileName
|
putStrLn $ "Finished fetching all builds from Hydra, saving report as " <> fileName
|
||||||
now <- getCurrentTime
|
now <- getCurrentTime
|
||||||
encodeFile fileName (eval, now, buildReports)
|
encodeFile fileName (eval, now, buildReports)
|
||||||
where
|
|
||||||
myReq query option = responseBody <$> req GET query NoReqBody jsonResponse (header "User-Agent" "hydra-report.hs/v1 (nixpkgs;maintainers/scripts/haskell)" <> option)
|
hydraQuery :: HttpResponse a => Proxy a -> Option 'Https -> [Text] -> Req (HttpResponseBody a)
|
||||||
|
hydraQuery responseType option query =
|
||||||
|
responseBody
|
||||||
|
<$> req
|
||||||
|
GET
|
||||||
|
(foldl' (/:) (https "hydra.nixos.org") query)
|
||||||
|
NoReqBody
|
||||||
|
responseType
|
||||||
|
(header "User-Agent" "hydra-report.hs/v1 (nixpkgs;maintainers/scripts/haskell)" <> option)
|
||||||
|
|
||||||
|
hydraJSONQuery :: FromJSON a => Option 'Https -> [Text] -> Req a
|
||||||
|
hydraJSONQuery = hydraQuery jsonResponse
|
||||||
|
|
||||||
|
hydraPlainQuery :: [Text] -> Req ByteString
|
||||||
|
hydraPlainQuery = hydraQuery bsResponse mempty
|
||||||
|
|
||||||
hydraEvalCommand :: FilePath
|
hydraEvalCommand :: FilePath
|
||||||
hydraEvalCommand = "hydra-eval-jobs"
|
hydraEvalCommand = "hydra-eval-jobs"
|
||||||
@ -326,23 +350,24 @@ instance Functor (Table row col) where
|
|||||||
instance Foldable (Table row col) where
|
instance Foldable (Table row col) where
|
||||||
foldMap f (Table a) = foldMap f a
|
foldMap f (Table a) = foldMap f a
|
||||||
|
|
||||||
|
getBuildState :: Build -> BuildState
|
||||||
|
getBuildState Build{finished, buildstatus} = case (finished, buildstatus) of
|
||||||
|
(0, _) -> Unfinished
|
||||||
|
(_, Just 0) -> Success
|
||||||
|
(_, Just 1) -> Failed
|
||||||
|
(_, Just 2) -> DependencyFailed
|
||||||
|
(_, Just 3) -> HydraFailure
|
||||||
|
(_, Just 4) -> Canceled
|
||||||
|
(_, Just 7) -> TimedOut
|
||||||
|
(_, Just 11) -> OutputLimitExceeded
|
||||||
|
(_, i) -> Unknown i
|
||||||
|
|
||||||
buildSummary :: MaintainerMap -> ReverseDependencyMap -> Seq Build -> StatusSummary
|
buildSummary :: MaintainerMap -> ReverseDependencyMap -> Seq Build -> StatusSummary
|
||||||
buildSummary maintainerMap reverseDependencyMap = foldl (Map.unionWith unionSummary) Map.empty . fmap toSummary
|
buildSummary maintainerMap reverseDependencyMap = foldl (Map.unionWith unionSummary) Map.empty . fmap toSummary
|
||||||
where
|
where
|
||||||
unionSummary (SummaryEntry (Table lb) lm lr lu) (SummaryEntry (Table rb) rm rr ru) = SummaryEntry (Table $ Map.union lb rb) (lm <> rm) (max lr rr) (max lu ru)
|
unionSummary (SummaryEntry (Table lb) lm lr lu) (SummaryEntry (Table rb) rm rr ru) = SummaryEntry (Table $ Map.union lb rb) (lm <> rm) (max lr rr) (max lu ru)
|
||||||
toSummary Build{finished, buildstatus, job, id, system} = Map.singleton name (SummaryEntry (Table (Map.singleton (set, Platform system) (BuildResult state id))) maintainers reverseDeps unbrokenReverseDeps)
|
toSummary build@Build{job, id, system} = Map.singleton name (SummaryEntry (Table (Map.singleton (set, Platform system) (BuildResult (getBuildState build) id))) maintainers reverseDeps unbrokenReverseDeps)
|
||||||
where
|
where
|
||||||
state :: BuildState
|
|
||||||
state = case (finished, buildstatus) of
|
|
||||||
(0, _) -> Unfinished
|
|
||||||
(_, Just 0) -> Success
|
|
||||||
(_, Just 1) -> Failed
|
|
||||||
(_, Just 2) -> DependencyFailed
|
|
||||||
(_, Just 3) -> HydraFailure
|
|
||||||
(_, Just 4) -> Canceled
|
|
||||||
(_, Just 7) -> TimedOut
|
|
||||||
(_, Just 11) -> OutputLimitExceeded
|
|
||||||
(_, i) -> Unknown i
|
|
||||||
packageName = fromMaybe job (Text.stripSuffix ("." <> system) job)
|
packageName = fromMaybe job (Text.stripSuffix ("." <> system) job)
|
||||||
splitted = nonEmpty $ Text.splitOn "." packageName
|
splitted = nonEmpty $ Text.splitOn "." packageName
|
||||||
name = maybe packageName NonEmpty.last splitted
|
name = maybe packageName NonEmpty.last splitted
|
||||||
@ -486,8 +511,23 @@ printMaintainerPing = do
|
|||||||
|
|
||||||
printMarkBrokenList :: IO ()
|
printMarkBrokenList :: IO ()
|
||||||
printMarkBrokenList = do
|
printMarkBrokenList = do
|
||||||
(_, _, buildReport) <- readBuildReports
|
(_, fetchTime, buildReport) <- readBuildReports
|
||||||
forM_ buildReport \Build{buildstatus, job} ->
|
runReq defaultHttpConfig $ forM_ buildReport \build@Build{job, id} ->
|
||||||
case (buildstatus, Text.splitOn "." job) of
|
case (getBuildState build, Text.splitOn "." job) of
|
||||||
(Just 1, ["haskellPackages", name, "x86_64-linux"]) -> putStrLn $ " - " <> Text.unpack name
|
(Failed, ["haskellPackages", name, "x86_64-linux"]) -> do
|
||||||
|
-- Fetch build log from hydra to figure out the cause of the error.
|
||||||
|
build_log <- ByteString.lines <$> hydraPlainQuery ["build", showT id, "nixlog", "1", "raw"]
|
||||||
|
-- We use the last probable error cause found in the build log file.
|
||||||
|
let error_message = fromMaybe " failure " $ safeLast $ mapMaybe probableErrorCause build_log
|
||||||
|
liftIO $ putStrLn $ " - " <> Text.unpack name <> " # " <> error_message <> " in job https://hydra.nixos.org/build/" <> show id <> " at " <> formatTime defaultTimeLocale "%Y-%m-%d" fetchTime
|
||||||
_ -> pure ()
|
_ -> pure ()
|
||||||
|
|
||||||
|
{- | This function receives a line from a Nix Haskell builder build log and returns a possible error cause.
|
||||||
|
| We might need to add other causes in the future if errors happen in unusual parts of the builder.
|
||||||
|
-}
|
||||||
|
probableErrorCause :: ByteString -> Maybe String
|
||||||
|
probableErrorCause "Setup: Encountered missing or private dependencies:" = Just "dependency missing"
|
||||||
|
probableErrorCause "running tests" = Just "test failure"
|
||||||
|
probableErrorCause build_line | ByteString.isPrefixOf "Building" build_line = Just ("failure building " <> fromUTF8BS (fst $ ByteString.breakSubstring " for" $ ByteString.drop 9 build_line))
|
||||||
|
probableErrorCause build_line | ByteString.isSuffixOf "Phase" build_line = Just ("failure in " <> fromUTF8BS build_line)
|
||||||
|
probableErrorCause _ = Nothing
|
||||||
|
Loading…
Reference in New Issue
Block a user