{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} module Outpaths ( currentOutpathSet, ResultLine, dummyOutpathSetBefore, dummyOutpathSetAfter, numPackageRebuilds, outpathReport, ) where import Data.List (sort) import qualified Data.Set as S import qualified Data.Text as T import qualified Data.Text.IO as T import qualified Data.Vector as V import OurPrelude import Text.Parsec (parse) import Text.Parser.Char import Text.Parser.Combinators outPathsExpr :: Text outPathsExpr = [interpolate| { checkMeta , path ? ./. }: let lib = import (path + "/lib"); hydraJobs = import (path + "/pkgs/top-level/release.nix") # Compromise: accuracy vs. resources needed for evaluation. { supportedSystems = [ "aarch64-linux" "i686-linux" "x86_64-linux" "x86_64-darwin" ]; nixpkgsArgs = { config = { allowBroken = true; allowUnfree = true; allowInsecurePredicate = x: true; checkMeta = checkMeta; handleEvalIssue = reason: errormsg: let fatalErrors = [ "unknown-meta" "broken-outputs" ]; in if builtins.elem reason fatalErrors then abort errormsg else true; inHydra = true; }; }; }; recurseIntoAttrs = attrs: attrs // { recurseForDerivations = true; }; # hydraJobs leaves recurseForDerivations as empty attrmaps; # that would break nix-env and we also need to recurse everywhere. tweak = lib.mapAttrs (name: val: if name == "recurseForDerivations" then true else if lib.isAttrs val && val.type or null != "derivation" then recurseIntoAttrs (tweak val) else val ); # Some of these contain explicit references to platform(s) we want to avoid; # some even (transitively) depend on ~/.nixpkgs/config.nix (!) blacklist = [ "tarball" "metrics" "manual" "darwin-tested" "unstable" "stdenvBootstrapTools" "moduleSystem" "lib-tests" # these just confuse the output ]; in tweak (builtins.removeAttrs hydraJobs blacklist) |] outPath :: MonadIO m => ExceptT Text m Text outPath = do liftIO $ putStrLn "Writing outpaths.nix..." liftIO $ T.writeFile "./outpaths.nix" outPathsExpr liftIO $ putStrLn "Evaluating outpaths..." ourReadProcessInterleaved_ "nix-env -f ./outpaths.nix -qaP --no-name --out-path --arg checkMeta true --show-trace" data Outpath = Outpath { mayName :: Maybe Text, storePath :: Text } deriving (Eq, Ord, Show) data ResultLine = ResultLine { package :: Text, architecture :: Text, outpaths :: Vector Outpath } deriving (Eq, Ord, Show) -- Example query result line: -- testInput :: Text -- testInput = -- "haskellPackages.amazonka-dynamodb-streams.x86_64-linux doc=/nix/store/m4rpsc9nx0qcflh9ni6qdlg6hbkwpicc-amazonka-dynamodb-streams-1.6.0-doc;/nix/store/rvd4zydr22a7j5kgnmg5x6695c7bgqbk-amazonka-dynamodb-streams-1.6.0\nhaskellPackages.agum.x86_64-darwin doc=/nix/store/n526rc0pa5h0krdzsdni5agcpvcd3cb9-agum-2.7-doc;/nix/store/s59r75svbjm724q5iaprq4mln5k6wcr9-agum-2.7" currentOutpathSet :: MonadIO m => ExceptT Text m (Set ResultLine) currentOutpathSet = do op <- outPath parse parseResults "outpath" op & fmapL tshow & hoistEither dummyOutpathSetBefore :: Text -> Set ResultLine dummyOutpathSetBefore attrPath = S.singleton (ResultLine attrPath "x86-64" (V.singleton (Outpath (Just "attrPath") "fakepath"))) dummyOutpathSetAfter :: Text -> Set ResultLine dummyOutpathSetAfter attrPath = S.singleton (ResultLine attrPath "x86-64" (V.singleton (Outpath (Just "attrPath") "fakepath-edited"))) parseResults :: CharParsing m => m (Set ResultLine) parseResults = S.fromList <$> parseResultLine `sepEndBy` newline parseResultLine :: CharParsing m => m ResultLine parseResultLine = ResultLine <$> (T.dropWhileEnd (== '.') <$> parseAttrpath) <*> parseArchitecture <* spaces <*> parseOutpaths parseAttrpath :: CharParsing m => m Text parseAttrpath = T.concat <$> many (try parseAttrpathPart) parseAttrpathPart :: CharParsing m => m Text parseAttrpathPart = T.snoc <$> (T.pack <$> many (noneOf ". ")) <*> char '.' parseArchitecture :: CharParsing m => m Text parseArchitecture = T.pack <$> many (noneOf " ") parseOutpaths :: CharParsing m => m (Vector Outpath) parseOutpaths = V.fromList <$> (parseOutpath `sepBy1` char ';') parseOutpath :: CharParsing m => m Outpath parseOutpath = Outpath <$> optional (try (T.pack <$> (many (noneOf "=\n") <* char '='))) <*> (T.pack <$> many (noneOf ";\n")) packageRebuilds :: Set ResultLine -> Vector Text packageRebuilds = S.toList >>> fmap package >>> sort >>> V.fromList >>> V.uniq numPackageRebuilds :: Set ResultLine -> Int numPackageRebuilds diff = V.length $ packageRebuilds diff archRebuilds :: Text -> Set ResultLine -> Int archRebuilds arch = S.toList >>> fmap architecture >>> filter (== arch) >>> length darwinRebuilds :: Set ResultLine -> Int darwinRebuilds = archRebuilds "x86_64-darwin" linuxRebuilds :: Set ResultLine -> Int linuxRebuilds = archRebuilds "x86_64-linux" linux32bRebuilds :: Set ResultLine -> Int linux32bRebuilds = archRebuilds "i686-linux" armRebuilds :: Set ResultLine -> Int armRebuilds = archRebuilds "aarch64-linux" outpathReport :: Set ResultLine -> Text outpathReport diff = let pkg = tshow $ V.length $ packageRebuilds diff firstFifty = T.unlines $ V.toList $ V.take 50 $ packageRebuilds diff darwin = tshow $ darwinRebuilds diff linux = tshow $ linuxRebuilds diff linux32b = tshow $ linux32bRebuilds diff arm = tshow $ armRebuilds diff numPaths = tshow $ S.size diff in [interpolate| $numPaths total rebuild path(s) $pkg package rebuild(s) $linux x86_64-linux rebuild(s) $linux32b i686-linux rebuild(s) $darwin x86_64-darwin rebuild(s) $arm aarch64-linux rebuild(s) First fifty rebuilds by attrpath $firstFifty |]