Merge pull request #217242 from maralorn/broken-reasons

maintainers/scripts/haskell/hydra-report: Add comments with error causes to broken list

authored by

maralorn and committed by
GitHub
994e845b 2827717f

+71 -31
+71 -31
maintainers/scripts/haskell/hydra-report.hs
··· 26 26 {-# LANGUAGE TupleSections #-} 27 27 {-# LANGUAGE ViewPatterns #-} 28 28 {-# OPTIONS_GHC -Wall #-} 29 + {-# LANGUAGE DataKinds #-} 29 30 30 31 import Control.Monad (forM_, (<=<)) 31 32 import Control.Monad.Trans (MonadIO (liftIO)) ··· 54 55 import Data.Time.Clock (UTCTime) 55 56 import GHC.Generics (Generic) 56 57 import Network.HTTP.Req ( 57 - GET (GET), 58 - NoReqBody (NoReqBody), 59 - defaultHttpConfig, 60 - header, 61 - https, 62 - jsonResponse, 63 - req, 64 - responseBody, 65 - responseTimeout, 66 - runReq, 67 - (/:), 58 + GET (GET), 59 + HttpResponse (HttpResponseBody), 60 + NoReqBody (NoReqBody), 61 + Option, 62 + Req, 63 + Scheme (Https), 64 + bsResponse, 65 + defaultHttpConfig, 66 + header, 67 + https, 68 + jsonResponse, 69 + req, 70 + responseBody, 71 + responseTimeout, 72 + runReq, 73 + (/:), 68 74 ) 69 75 import System.Directory (XdgDirectory (XdgCache), getXdgDirectory) 70 76 import System.Environment (getArgs) ··· 76 82 import qualified Data.IntMap.Strict as IntMap 77 83 import qualified Data.IntSet as IntSet 78 84 import Data.Bifunctor (second) 85 + import Data.Data (Proxy) 86 + import Data.ByteString (ByteString) 87 + import qualified Data.ByteString.Char8 as ByteString 88 + import Distribution.Simple.Utils (safeLast, fromUTF8BS) 79 89 80 90 newtype JobsetEvals = JobsetEvals 81 91 { evals :: Seq Eval ··· 123 133 124 134 getBuildReports :: IO () 125 135 getBuildReports = runReq defaultHttpConfig do 126 - evalMay <- Seq.lookup 0 . evals <$> myReq (https "hydra.nixos.org" /: "jobset" /: "nixpkgs" /: "haskell-updates" /: "evals") mempty 136 + evalMay <- Seq.lookup 0 . evals <$> hydraJSONQuery mempty ["jobset", "nixpkgs", "haskell-updates", "evals"] 127 137 eval@Eval{id} <- maybe (liftIO $ fail "No Evalution found") pure evalMay 128 138 liftIO . putStrLn $ "Fetching evaluation " <> show id <> " from Hydra. This might take a few minutes..." 129 - buildReports :: Seq Build <- myReq (https "hydra.nixos.org" /: "eval" /: showT id /: "builds") (responseTimeout 600000000) 139 + buildReports :: Seq Build <- hydraJSONQuery (responseTimeout 600000000) ["eval", showT id, "builds"] 130 140 liftIO do 131 141 fileName <- reportFileName 132 142 putStrLn $ "Finished fetching all builds from Hydra, saving report as " <> fileName 133 143 now <- getCurrentTime 134 144 encodeFile fileName (eval, now, buildReports) 135 - where 136 - myReq query option = responseBody <$> req GET query NoReqBody jsonResponse (header "User-Agent" "hydra-report.hs/v1 (nixpkgs;maintainers/scripts/haskell)" <> option) 145 + 146 + hydraQuery :: HttpResponse a => Proxy a -> Option 'Https -> [Text] -> Req (HttpResponseBody a) 147 + hydraQuery responseType option query = 148 + responseBody 149 + <$> req 150 + GET 151 + (foldl' (/:) (https "hydra.nixos.org") query) 152 + NoReqBody 153 + responseType 154 + (header "User-Agent" "hydra-report.hs/v1 (nixpkgs;maintainers/scripts/haskell)" <> option) 155 + 156 + hydraJSONQuery :: FromJSON a => Option 'Https -> [Text] -> Req a 157 + hydraJSONQuery = hydraQuery jsonResponse 158 + 159 + hydraPlainQuery :: [Text] -> Req ByteString 160 + hydraPlainQuery = hydraQuery bsResponse mempty 137 161 138 162 hydraEvalCommand :: FilePath 139 163 hydraEvalCommand = "hydra-eval-jobs" ··· 326 350 instance Foldable (Table row col) where 327 351 foldMap f (Table a) = foldMap f a 328 352 353 + getBuildState :: Build -> BuildState 354 + getBuildState Build{finished, buildstatus} = case (finished, buildstatus) of 355 + (0, _) -> Unfinished 356 + (_, Just 0) -> Success 357 + (_, Just 1) -> Failed 358 + (_, Just 2) -> DependencyFailed 359 + (_, Just 3) -> HydraFailure 360 + (_, Just 4) -> Canceled 361 + (_, Just 7) -> TimedOut 362 + (_, Just 11) -> OutputLimitExceeded 363 + (_, i) -> Unknown i 364 + 329 365 buildSummary :: MaintainerMap -> ReverseDependencyMap -> Seq Build -> StatusSummary 330 366 buildSummary maintainerMap reverseDependencyMap = foldl (Map.unionWith unionSummary) Map.empty . fmap toSummary 331 367 where 332 368 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) 333 - toSummary Build{finished, buildstatus, job, id, system} = Map.singleton name (SummaryEntry (Table (Map.singleton (set, Platform system) (BuildResult state id))) maintainers reverseDeps unbrokenReverseDeps) 369 + toSummary build@Build{job, id, system} = Map.singleton name (SummaryEntry (Table (Map.singleton (set, Platform system) (BuildResult (getBuildState build) id))) maintainers reverseDeps unbrokenReverseDeps) 334 370 where 335 - state :: BuildState 336 - state = case (finished, buildstatus) of 337 - (0, _) -> Unfinished 338 - (_, Just 0) -> Success 339 - (_, Just 1) -> Failed 340 - (_, Just 2) -> DependencyFailed 341 - (_, Just 3) -> HydraFailure 342 - (_, Just 4) -> Canceled 343 - (_, Just 7) -> TimedOut 344 - (_, Just 11) -> OutputLimitExceeded 345 - (_, i) -> Unknown i 346 371 packageName = fromMaybe job (Text.stripSuffix ("." <> system) job) 347 372 splitted = nonEmpty $ Text.splitOn "." packageName 348 373 name = maybe packageName NonEmpty.last splitted ··· 486 511 487 512 printMarkBrokenList :: IO () 488 513 printMarkBrokenList = do 489 - (_, _, buildReport) <- readBuildReports 490 - forM_ buildReport \Build{buildstatus, job} -> 491 - case (buildstatus, Text.splitOn "." job) of 492 - (Just 1, ["haskellPackages", name, "x86_64-linux"]) -> putStrLn $ " - " <> Text.unpack name 514 + (_, fetchTime, buildReport) <- readBuildReports 515 + runReq defaultHttpConfig $ forM_ buildReport \build@Build{job, id} -> 516 + case (getBuildState build, Text.splitOn "." job) of 517 + (Failed, ["haskellPackages", name, "x86_64-linux"]) -> do 518 + -- Fetch build log from hydra to figure out the cause of the error. 519 + build_log <- ByteString.lines <$> hydraPlainQuery ["build", showT id, "nixlog", "1", "raw"] 520 + -- We use the last probable error cause found in the build log file. 521 + let error_message = fromMaybe " failure " $ safeLast $ mapMaybe probableErrorCause build_log 522 + liftIO $ putStrLn $ " - " <> Text.unpack name <> " # " <> error_message <> " in job https://hydra.nixos.org/build/" <> show id <> " at " <> formatTime defaultTimeLocale "%Y-%m-%d" fetchTime 493 523 _ -> pure () 524 + 525 + {- | This function receives a line from a Nix Haskell builder build log and returns a possible error cause. 526 + | We might need to add other causes in the future if errors happen in unusual parts of the builder. 527 + -} 528 + probableErrorCause :: ByteString -> Maybe String 529 + probableErrorCause "Setup: Encountered missing or private dependencies:" = Just "dependency missing" 530 + probableErrorCause "running tests" = Just "test failure" 531 + probableErrorCause build_line | ByteString.isPrefixOf "Building" build_line = Just ("failure building " <> fromUTF8BS (fst $ ByteString.breakSubstring " for" $ ByteString.drop 9 build_line)) 532 + probableErrorCause build_line | ByteString.isSuffixOf "Phase" build_line = Just ("failure in " <> fromUTF8BS build_line) 533 + probableErrorCause _ = Nothing