about summary refs log tree commit diff
path: root/nixpkgs/maintainers
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2021-09-27 16:00:58 +0000
committerAlyssa Ross <hi@alyssa.is>2021-09-27 16:00:58 +0000
commitc504e5d19d940926b3ddcf62c983d66f49f3cbb2 (patch)
treeec955e58bcac2cb93b9f8c10786b23f61d40cd7e /nixpkgs/maintainers
parent72789cefce7b17419815f600fbd18238d89afcc9 (diff)
parent1737f98af6667560e3e4f930312f9b5002649d04 (diff)
downloadnixlib-c504e5d19d940926b3ddcf62c983d66f49f3cbb2.tar
nixlib-c504e5d19d940926b3ddcf62c983d66f49f3cbb2.tar.gz
nixlib-c504e5d19d940926b3ddcf62c983d66f49f3cbb2.tar.bz2
nixlib-c504e5d19d940926b3ddcf62c983d66f49f3cbb2.tar.lz
nixlib-c504e5d19d940926b3ddcf62c983d66f49f3cbb2.tar.xz
nixlib-c504e5d19d940926b3ddcf62c983d66f49f3cbb2.tar.zst
nixlib-c504e5d19d940926b3ddcf62c983d66f49f3cbb2.zip
Merge commit '1737f98af6667560e3e4f930312f9b5002649d04'
Conflicts:
	nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
	nixpkgs/pkgs/applications/networking/irc/weechat/scripts/default.nix
	nixpkgs/pkgs/development/node-packages/default.nix
	nixpkgs/pkgs/development/python-modules/priority/deadline.patch
Diffstat (limited to 'nixpkgs/maintainers')
-rw-r--r--nixpkgs/maintainers/maintainer-list.nix119
-rw-r--r--nixpkgs/maintainers/scripts/haskell/dependencies.nix10
-rwxr-xr-xnixpkgs/maintainers/scripts/haskell/hydra-report.hs148
-rw-r--r--nixpkgs/maintainers/scripts/haskell/maintainer-handles.nix7
-rwxr-xr-xnixpkgs/maintainers/scripts/haskell/merge-and-open-pr.sh118
-rwxr-xr-xnixpkgs/maintainers/scripts/rebuild-amount.sh165
-rw-r--r--nixpkgs/maintainers/team-list.nix8
7 files changed, 456 insertions, 119 deletions
diff --git a/nixpkgs/maintainers/maintainer-list.nix b/nixpkgs/maintainers/maintainer-list.nix
index 628a42ce1f00..892f671d3b0d 100644
--- a/nixpkgs/maintainers/maintainer-list.nix
+++ b/nixpkgs/maintainers/maintainer-list.nix
@@ -462,6 +462,12 @@
     githubId = 2335822;
     name = "Alexandre Esteves";
   };
+  alexnortung = {
+    name = "alexnortung";
+    email = "alex_nortung@live.dk";
+    github = "alexnortung";
+    githubId = 1552267;
+  };
   alexvorobiev = {
     email = "alexander.vorobiev@gmail.com";
     github = "alexvorobiev";
@@ -730,6 +736,12 @@
     githubId = 1771266;
     name = "Vo Anh Duy";
   };
+  anirrudh = {
+    email = "anik597@gmail.com";
+    github = "anirrudh";
+    githubId = 6091755;
+    name = "Anirrudh Krishnan";
+  };
   ankhers = {
     email = "me@ankhers.dev";
     github = "ankhers";
@@ -1376,6 +1388,12 @@
     githubId = 28444296;
     name = "Benjamin Hougland";
   };
+  billewanick = {
+    email = "bill@ewanick.com";
+    github = "billewanick";
+    githubId = 13324165;
+    name = "Bill Ewanick";
+  };
   binarin = {
     email = "binarin@binarin.ru";
     github = "binarin";
@@ -1841,6 +1859,12 @@
     githubId = 1762540;
     name = "Changlin Li";
   };
+  chanley = {
+    email = "charlieshanley@gmail.com";
+    github = "charlieshanley";
+    githubId = 8228888;
+    name = "Charlie Hanley";
+  };
   CharlesHD = {
     email = "charleshdespointes@gmail.com";
     github = "CharlesHD";
@@ -1897,6 +1921,12 @@
     githubId = 3086255;
     name = "Barry Moore II";
   };
+  chisui = {
+    email = "chisui.pd@gmail.com";
+    github = "chisui";
+    githubId = 4526429;
+    name = "Philipp Dargel";
+  };
   chivay = {
     email = "hubert.jasudowicz@gmail.com";
     github = "chivay";
@@ -2719,6 +2749,12 @@
     githubId = 40633781;
     name = "Sergei S.";
   };
+  dit7ya = {
+    email = "7rat13@gmail.com";
+    github = "dit7ya";
+    githubId = 14034137;
+    name = "Mostly Void";
+  };
   dizfer = {
     email = "david@izquierdofernandez.com";
     github = "dizfer";
@@ -4275,6 +4311,12 @@
     githubId = 731722;
     name = "Ryan Scheel";
   };
+  hawkw = {
+    email = "eliza@elizas.website";
+    github = "hawkw";
+    githubId = 2796466;
+    name = "Eliza Weisman";
+  };
   hax404 = {
     email = "hax404foogit@hax404.de";
     github = "hax404";
@@ -4417,6 +4459,12 @@
       fingerprint = "D618 7A03 A40A 3D56 62F5  4B46 03EF BF83 9A5F DC15";
     }];
   };
+  hleboulanger = {
+    email = "hleboulanger@protonmail.com";
+    name = "Harold Leboulanger";
+    github = "thbkrhsw";
+    githubId = 33122;
+  };
   hlolli = {
     email = "hlolli@gmail.com";
     github = "hlolli";
@@ -4541,6 +4589,16 @@
     githubId = 2789926;
     name = "Imran Hossain";
   };
+  iagoq = {
+    email = "18238046+iagocq@users.noreply.github.com";
+    github = "iagocq";
+    githubId = 18238046;
+    name = "Iago Manoel Brito";
+    keys = [{
+      longkeyid = "rsa4096/0x35D39F9A9A1BC8DA";
+      fingerprint = "DF90 9D58 BEE4 E73A 1B8C  5AF3 35D3 9F9A 9A1B C8DA";
+    }];
+  };
   iammrinal0 = {
     email = "nixpkgs@mrinalpurohit.in";
     github = "iammrinal0";
@@ -4661,6 +4719,12 @@
     githubId = 993484;
     name = "Greg Hale";
   };
+  imgabe = {
+    email = "gabrielpmonte@hotmail.com";
+    github = "imgabe";
+    githubId = 24387926;
+    name = "Gabriel Pereira";
+  };
   imlonghao = {
     email = "nixos@esd.cc";
     github = "imlonghao";
@@ -4787,6 +4851,12 @@
     githubId = 26877687;
     name = "Yurii Izorkin";
   };
+  j0xaf = {
+    email = "j0xaf@j0xaf.de";
+    name = "Jörn Gersdorf";
+    github = "j0xaf";
+    githubId = 932697;
+  };
   j0hax = {
     name = "Johannes Arnold";
     email = "johannes.arnold@stud.uni-hannover.de";
@@ -5131,6 +5201,12 @@
     githubId = 9866621;
     name = "Jack";
   };
+  jkarlson = {
+    email = "jekarlson@gmail.com";
+    github = "jkarlson";
+    githubId = 1204734;
+    name = "Emil Karlson";
+  };
   jlesquembre = {
     email = "jl@lafuente.me";
     github = "jlesquembre";
@@ -5448,6 +5524,12 @@
     githubId = 39434424;
     name = "Felix Springer";
   };
+  junjihashimoto = {
+    email = "junji.hashimoto@gmail.com";
+    github = "junjihashimoto";
+    githubId = 2469618;
+    name = "Junji Hashimoto";
+  };
   justinas = {
     email = "justinas@justinas.org";
     github = "justinas";
@@ -7603,6 +7685,12 @@
     githubId = 6455574;
     name = "Matt Votava";
   };
+  mvs = {
+    email = "mvs@nya.yt";
+    github = "illdefined";
+    githubId = 772914;
+    name = "Mikael Voss";
+  };
   maxwilson = {
     email = "nixpkgs@maxwilson.dev";
     github = "mwilsoncoding";
@@ -8582,7 +8670,7 @@
     githubId = 1719781;
     name = "Pablo Ovelleiro Corral";
     keys = [{
-      longkeyid = "sa4096/0x823A6154426408D3";
+      longkeyid = "rsa4096/0x823A6154426408D3";
       fingerprint = "D03B 218C AE77 1F77 D7F9  20D9 823A 6154 4264 08D3";
     }];
   };
@@ -9140,6 +9228,12 @@
     githubId = 546296;
     name = "Eric Ren";
   };
+  renesat = {
+    name = "Ivan Smolyakov";
+    email = "smol.ivan97@gmail.com";
+    github = "renesat";
+    githubId = 11363539;
+  };
   renzo = {
     email = "renzocarbonara@gmail.com";
     github = "k0001";
@@ -9804,12 +9898,6 @@
     githubId = 11613056;
     name = "Scott Dier";
   };
-  sdll = {
-    email = "sasha.delly@gmail.com";
-    github = "sdll";
-    githubId = 17913919;
-    name = "Sasha Illarionov";
-  };
   SeanZicari = {
     email = "sean.zicari@gmail.com";
     github = "SeanZicari";
@@ -12470,6 +12558,16 @@
       fingerprint = "61AE D40F 368B 6F26 9DAE  3892 6861 6B2D 8AC4 DCC5";
     }];
   };
+  zenithal = {
+    name = "zenithal";
+    email = "i@zenithal.me";
+    github = "ZenithalHourlyRate";
+    githubId = 19512674;
+    keys = [{
+      longkeyid = "rsa4096/0x87E17EEF9B18B6C9";
+      fingerprint = "1127 F188 280A E312 3619  3329 87E1 7EEF 9B18 B6C9";
+    }];
+  };
   zeri = {
     name = "zeri";
     email = "68825133+zeri42@users.noreply.github.com";
@@ -12498,4 +12596,11 @@
     github = "rski";
     githubId = 2960312;
   };
+  mbprtpmnr = {
+    name = "mbprtpmnr";
+    email = "mbprtpmnr@pm.me";
+    github = "mbprtpmnr";
+    githubId = 88109321;
+  };
+
 }
diff --git a/nixpkgs/maintainers/scripts/haskell/dependencies.nix b/nixpkgs/maintainers/scripts/haskell/dependencies.nix
new file mode 100644
index 000000000000..f0620902c0ee
--- /dev/null
+++ b/nixpkgs/maintainers/scripts/haskell/dependencies.nix
@@ -0,0 +1,10 @@
+# Nix script to calculate the Haskell dependencies of every haskellPackage. Used by ./hydra-report.hs.
+let
+  pkgs = import ../../.. {};
+  inherit (pkgs) lib;
+  getDeps = _: pkg: {
+    deps = builtins.filter (x: !isNull x) (map (x: x.pname or null) (pkg.propagatedBuildInputs or []));
+    broken = (pkg.meta.hydraPlatforms or [null]) == [];
+  };
+in
+  lib.mapAttrs getDeps pkgs.haskellPackages
diff --git a/nixpkgs/maintainers/scripts/haskell/hydra-report.hs b/nixpkgs/maintainers/scripts/haskell/hydra-report.hs
index fd6430d43c9a..0d22a670366d 100755
--- a/nixpkgs/maintainers/scripts/haskell/hydra-report.hs
+++ b/nixpkgs/maintainers/scripts/haskell/hydra-report.hs
@@ -26,6 +26,8 @@ Because step 1) is quite expensive and takes roughly ~5 minutes the result is ca
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TupleSections #-}
 {-# OPTIONS_GHC -Wall #-}
+{-# LANGUAGE ViewPatterns #-}
+{-# LANGUAGE TupleSections #-}
 
 import Control.Monad (forM_, (<=<))
 import Control.Monad.Trans (MonadIO (liftIO))
@@ -41,7 +43,7 @@ import Data.List.NonEmpty (NonEmpty, nonEmpty)
 import qualified Data.List.NonEmpty as NonEmpty
 import Data.Map.Strict (Map)
 import qualified Data.Map.Strict as Map
-import Data.Maybe (fromMaybe, mapMaybe)
+import Data.Maybe (fromMaybe, mapMaybe, isNothing)
 import Data.Monoid (Sum (Sum, getSum))
 import Data.Sequence (Seq)
 import qualified Data.Sequence as Seq
@@ -70,6 +72,12 @@ import System.Directory (XdgDirectory (XdgCache), getXdgDirectory)
 import System.Environment (getArgs)
 import System.Process (readProcess)
 import Prelude hiding (id)
+import Data.List (sortOn)
+import Control.Concurrent.Async (concurrently)
+import Control.Exception (evaluate)
+import qualified Data.IntMap.Strict as IntMap
+import qualified Data.IntSet as IntSet
+import Data.Bifunctor (second)
 
 newtype JobsetEvals = JobsetEvals
    { evals :: Seq Eval
@@ -134,20 +142,17 @@ hydraEvalCommand = "hydra-eval-jobs"
 hydraEvalParams :: [String]
 hydraEvalParams = ["-I", ".", "pkgs/top-level/release-haskell.nix"]
 
-handlesCommand :: FilePath
-handlesCommand = "nix-instantiate"
-
-handlesParams :: [String]
-handlesParams = ["--eval", "--strict", "--json", "-"]
+nixExprCommand :: FilePath
+nixExprCommand = "nix-instantiate"
 
-handlesExpression :: String
-handlesExpression = "with import ./. {}; with lib; zipAttrsWith (_: builtins.head) (mapAttrsToList (_: v: if v ? github then { \"${v.email}\" = v.github; } else {}) (import maintainers/maintainer-list.nix))"
+nixExprParams :: [String]
+nixExprParams = ["--eval", "--strict", "--json"]
 
 -- | This newtype is used to parse a Hydra job output from @hydra-eval-jobs@.
 -- The only field we are interested in is @maintainers@, which is why this
 -- is just a newtype.
 --
--- Note that there are occassionally jobs that don't have a maintainers
+-- Note that there are occasionally jobs that don't have a maintainers
 -- field, which is why this has to be @Maybe Text@.
 newtype Maintainers = Maintainers { maintainers :: Maybe Text }
   deriving stock (Generic, Show)
@@ -195,13 +200,49 @@ type EmailToGitHubHandles = Map Text Text
 -- @@
 type MaintainerMap = Map Text (NonEmpty Text)
 
--- | Generate a mapping of Hydra job names to maintainer GitHub handles.
+-- | Information about a package which lists its dependencies and whether the
+-- package is marked broken.
+data DepInfo = DepInfo {
+   deps :: Set Text,
+   broken :: Bool
+}
+   deriving stock (Generic, Show)
+   deriving anyclass (FromJSON, ToJSON)
+
+-- | Map from package names to their DepInfo. This is the data we get out of a
+-- nix call.
+type DependencyMap = Map Text DepInfo
+
+-- | Map from package names to its broken state, number of reverse dependencies (fst) and
+-- unbroken reverse dependencies (snd).
+type ReverseDependencyMap = Map Text (Int, Int)
+
+-- | Calculate the (unbroken) reverse dependencies of a package by transitively
+-- going through all packages if it’s a dependency of them.
+calculateReverseDependencies :: DependencyMap -> ReverseDependencyMap
+calculateReverseDependencies depMap = Map.fromDistinctAscList $ zip keys (zip (rdepMap False) (rdepMap True))
+ where
+    -- This code tries to efficiently invert the dependency map and calculate
+    -- it’s transitive closure by internally identifying every pkg with it’s index
+    -- in the package list and then using memoization.
+    keys = Map.keys depMap
+    pkgToIndexMap = Map.fromDistinctAscList (zip keys [0..])
+    intDeps = zip [0..] $ (\DepInfo{broken,deps} -> (broken,mapMaybe (`Map.lookup` pkgToIndexMap) $ Set.toList deps)) <$> Map.elems depMap
+    rdepMap onlyUnbroken = IntSet.size <$> resultList
+     where
+       resultList = go <$> [0..]
+       oneStepMap = IntMap.fromListWith IntSet.union $ (\(key,(_,deps)) -> (,IntSet.singleton key) <$> deps) <=< filter (\(_, (broken,_)) -> not (broken && onlyUnbroken)) $ intDeps
+       go pkg = IntSet.unions (oneStep:((resultList !!) <$> IntSet.toList oneStep))
+        where oneStep = IntMap.findWithDefault mempty pkg oneStepMap
+
+-- | Generate a mapping of Hydra job names to maintainer GitHub handles. Calls
+-- hydra-eval-jobs and the nix script ./maintainer-handles.nix.
 getMaintainerMap :: IO MaintainerMap
 getMaintainerMap = do
    hydraJobs :: HydraJobs <-
-      readJSONProcess hydraEvalCommand hydraEvalParams "" "Failed to decode hydra-eval-jobs output: "
+      readJSONProcess hydraEvalCommand hydraEvalParams "Failed to decode hydra-eval-jobs output: "
    handlesMap :: EmailToGitHubHandles <-
-      readJSONProcess handlesCommand handlesParams handlesExpression "Failed to decode nix output for lookup of github handles: "
+      readJSONProcess nixExprCommand ("maintainers/scripts/haskell/maintainer-handles.nix":nixExprParams) "Failed to decode nix output for lookup of github handles: "
    pure $ Map.mapMaybe (splitMaintainersToGitHubHandles handlesMap) hydraJobs
    where
    -- Split a comma-spearated string of Maintainers into a NonEmpty list of
@@ -211,6 +252,12 @@ getMaintainerMap = do
    splitMaintainersToGitHubHandles handlesMap (Maintainers maint) =
       nonEmpty .  mapMaybe (`Map.lookup` handlesMap) .  Text.splitOn ", " $ fromMaybe "" maint
 
+-- | Get the a map of all dependencies of every package by calling the nix
+-- script ./dependencies.nix.
+getDependencyMap :: IO DependencyMap
+getDependencyMap =
+   readJSONProcess nixExprCommand ("maintainers/scripts/haskell/dependencies.nix":nixExprParams) "Failed to decode nix output for lookup of dependencies: "
+
 -- | Run a process that produces JSON on stdout and and decode the JSON to a
 -- data type.
 --
@@ -219,11 +266,10 @@ readJSONProcess
    :: FromJSON a
    => FilePath -- ^ Filename of executable.
    -> [String] -- ^ Arguments
-   -> String -- ^ stdin to pass to the process
    -> String -- ^ String to prefix to JSON-decode error.
    -> IO a
-readJSONProcess exe args input err = do
-   output <- readProcess exe args input
+readJSONProcess exe args err = do
+   output <- readProcess exe args ""
    let eitherDecodedOutput = eitherDecodeStrict' . encodeUtf8 . Text.pack $ output
    case eitherDecodedOutput of
      Left decodeErr -> error $ err <> decodeErr <> "\nRaw: '" <> take 1000 output <> "'"
@@ -264,7 +310,13 @@ platformIcon (Platform x) = case x of
 data BuildResult = BuildResult {state :: BuildState, id :: Int} deriving (Show, Eq, Ord)
 newtype Platform = Platform {platform :: Text} deriving (Show, Eq, Ord)
 newtype Table row col a = Table (Map (row, col) a)
-type StatusSummary = Map Text (Table Text Platform BuildResult, Set Text)
+data SummaryEntry = SummaryEntry {
+   summaryBuilds :: Table Text Platform BuildResult,
+   summaryMaintainers :: Set Text,
+   summaryReverseDeps :: Int,
+   summaryUnbrokenReverseDeps :: Int
+}
+type StatusSummary = Map Text SummaryEntry
 
 instance (Ord row, Ord col, Semigroup a) => Semigroup (Table row col a) where
    Table l <> Table r = Table (Map.unionWith (<>) l r)
@@ -275,11 +327,11 @@ instance Functor (Table row col) where
 instance Foldable (Table row col) where
    foldMap f (Table a) = foldMap f a
 
-buildSummary :: MaintainerMap -> Seq Build -> StatusSummary
-buildSummary maintainerMap = foldl (Map.unionWith unionSummary) Map.empty . fmap toSummary
+buildSummary :: MaintainerMap -> ReverseDependencyMap -> Seq Build -> StatusSummary
+buildSummary maintainerMap reverseDependencyMap = foldl (Map.unionWith unionSummary) Map.empty . fmap toSummary
   where
-   unionSummary (Table l, l') (Table r, r') = (Table $ Map.union l r, l' <> r')
-   toSummary Build{finished, buildstatus, job, id, system} = Map.singleton name (Table (Map.singleton (set, Platform system) (BuildResult state id)), maintainers)
+   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)
      where
       state :: BuildState
       state = case (finished, buildstatus) of
@@ -297,6 +349,7 @@ buildSummary maintainerMap = foldl (Map.unionWith unionSummary) Map.empty . fmap
       name = maybe packageName NonEmpty.last splitted
       set = maybe "" (Text.intercalate "." . NonEmpty.init) splitted
       maintainers = maybe mempty (Set.fromList . toList) (Map.lookup job maintainerMap)
+      (reverseDeps, unbrokenReverseDeps) = Map.findWithDefault (0,0) name reverseDependencyMap
 
 readBuildReports :: IO (Eval, UTCTime, Seq Build)
 readBuildReports = do
@@ -339,25 +392,29 @@ makeSearchLink evalId linkLabel query = "[" <> linkLabel <> "](" <> "https://hyd
 statusToNumSummary :: StatusSummary -> NumSummary
 statusToNumSummary = fmap getSum . foldMap (fmap Sum . jobTotals)
 
-jobTotals :: (Table Text Platform BuildResult, a) -> Table Platform BuildState Int
-jobTotals (Table mapping, _) = getSum <$> Table (Map.foldMapWithKey (\(_, platform) (BuildResult buildstate _) -> Map.singleton (platform, buildstate) (Sum 1)) mapping)
+jobTotals :: SummaryEntry -> Table Platform BuildState Int
+jobTotals (summaryBuilds -> Table mapping) = getSum <$> Table (Map.foldMapWithKey (\(_, platform) (BuildResult buildstate _) -> Map.singleton (platform, buildstate) (Sum 1)) mapping)
 
 details :: Text -> [Text] -> [Text]
 details summary content = ["<details><summary>" <> summary <> " </summary>", ""] <> content <> ["</details>", ""]
 
-printBuildSummary :: Eval -> UTCTime -> StatusSummary -> Text
+printBuildSummary :: Eval -> UTCTime -> StatusSummary -> [(Text, Int)] -> Text
 printBuildSummary
    Eval{id, jobsetevalinputs = JobsetEvalInputs{nixpkgs = Nixpkgs{revision}}}
    fetchTime
-   summary =
+   summary
+   topBrokenRdeps =
       Text.unlines $
-         headline <> totals
+         headline <> [""] <> tldr <> (("  * "<>) <$> (errors <> warnings)) <> [""]
+            <> totals
             <> optionalList "#### Maintained packages with build failure" (maintainedList fails)
             <> optionalList "#### Maintained packages with failed dependency" (maintainedList failedDeps)
             <> optionalList "#### Maintained packages with unknown error" (maintainedList unknownErr)
             <> optionalHideableList "#### Unmaintained packages with build failure" (unmaintainedList fails)
             <> optionalHideableList "#### Unmaintained packages with failed dependency" (unmaintainedList failedDeps)
             <> optionalHideableList "#### Unmaintained packages with unknown error" (unmaintainedList unknownErr)
+            <> optionalHideableList "#### Top 50 broken packages, sorted by number of reverse dependencies" (brokenLine <$> topBrokenRdeps)
+            <> ["","*:arrow_heading_up:: The number of packages that depend (directly or indirectly) on this package (if any). If two numbers are shown the first (lower) number considers only packages which currently have enabled hydra jobs, i.e. are not marked broken. The second (higher) number considers all packages.*",""]
             <> footer
      where
       footer = ["*Report generated with [maintainers/scripts/haskell/hydra-report.hs](https://github.com/NixOS/nixpkgs/blob/haskell-updates/maintainers/scripts/haskell/hydra-report.sh)*"]
@@ -365,7 +422,7 @@ printBuildSummary
          [ "#### Build summary"
          , ""
          ]
-            <> printTable "Platform" (\x -> makeSearchLink id (platform x <> " " <> platformIcon x) ("." <> platform x)) (\x -> showT x <> " " <> icon x) showT (statusToNumSummary summary)
+            <> printTable "Platform" (\x -> makeSearchLink id (platform x <> " " <> platformIcon x) ("." <> platform x)) (\x -> showT x <> " " <> icon x) showT numSummary
       headline =
          [ "### [haskell-updates build report from hydra](https://hydra.nixos.org/jobset/nixpkgs/haskell-updates)"
          , "*evaluation ["
@@ -380,24 +437,49 @@ printBuildSummary
             <> Text.pack (formatTime defaultTimeLocale "%Y-%m-%d %H:%M UTC" fetchTime)
             <> "*"
          ]
-      jobsByState predicate = Map.filter (predicate . foldl' min Success . fmap state . fst) summary
+      brokenLine (name, rdeps) = "[" <> name <> "](https://search.nixos.org/packages?channel=unstable&show=haskellPackages." <> name <> "&query=haskellPackages." <> name <> ") :arrow_heading_up: " <> Text.pack (show rdeps)
+      numSummary = statusToNumSummary summary
+      jobsByState predicate = Map.filter (predicate . worstState) summary
+      worstState = foldl' min Success . fmap state . summaryBuilds
       fails = jobsByState (== Failed)
       failedDeps = jobsByState (== DependencyFailed)
       unknownErr = jobsByState (\x -> x > DependencyFailed && x < TimedOut)
-      withMaintainer = Map.mapMaybe (\(x, m) -> (x,) <$> nonEmpty (Set.toList m))
-      withoutMaintainer = Map.mapMaybe (\(x, m) -> if Set.null m then Just x else Nothing)
+      withMaintainer = Map.mapMaybe (\e -> (summaryBuilds e,) <$> nonEmpty (Set.toList (summaryMaintainers e)))
+      withoutMaintainer = Map.mapMaybe (\e -> if Set.null (summaryMaintainers e) then Just e else Nothing)
       optionalList heading list = if null list then mempty else [heading] <> list
       optionalHideableList heading list = if null list then mempty else [heading] <> details (showT (length list) <> " job(s)") list
       maintainedList = showMaintainedBuild <=< Map.toList . withMaintainer
-      unmaintainedList = showBuild <=< Map.toList . withoutMaintainer
-      showBuild (name, table) = printJob id name (table, "")
+      unmaintainedList = showBuild <=< sortOn (\(snd -> x) -> (negate (summaryUnbrokenReverseDeps x), negate (summaryReverseDeps x))) . Map.toList . withoutMaintainer
+      showBuild (name, entry) = printJob id name (summaryBuilds entry, Text.pack (if summaryReverseDeps entry > 0 then " :arrow_heading_up: " <> show (summaryUnbrokenReverseDeps entry) <>" | "<> show (summaryReverseDeps entry) else ""))
       showMaintainedBuild (name, (table, maintainers)) = printJob id name (table, Text.intercalate " " (fmap ("@" <>) (toList maintainers)))
+      tldr = case (errors, warnings) of
+               ([],[]) -> [":green_circle: **Ready to merge**"]
+               ([],_) -> [":yellow_circle: **Potential issues**"]
+               _ -> [":red_circle: **Branch not mergeable**"]
+      warnings =
+         if' (Unfinished > maybe Success worstState maintainedJob) "`maintained` jobset failed." <>
+         if' (Unfinished == maybe Success worstState mergeableJob) "`mergeable` jobset is not finished." <>
+         if' (Unfinished == maybe Success worstState maintainedJob) "`maintained` jobset is not finished."
+      errors =
+         if' (isNothing mergeableJob) "No `mergeable` job found." <>
+         if' (isNothing maintainedJob) "No `maintained` job found." <>
+         if' (Unfinished > maybe Success worstState mergeableJob) "`mergeable` jobset failed." <>
+         if' (outstandingJobs (Platform "x86_64-linux") > 100) "Too much outstanding jobs on x86_64-linux." <>
+         if' (outstandingJobs (Platform "aarch64-linux") > 100) "Too much outstanding jobs on aarch64-linux."
+      if' p e = if p then [e] else mempty
+      outstandingJobs platform | Table m <- numSummary = Map.findWithDefault 0 (platform, Unfinished) m
+      maintainedJob = Map.lookup "maintained" summary
+      mergeableJob = Map.lookup "mergeable" summary
 
 printMaintainerPing :: IO ()
 printMaintainerPing = do
-   maintainerMap <- getMaintainerMap
+   (maintainerMap, (reverseDependencyMap, topBrokenRdeps)) <- concurrently getMaintainerMap do
+      depMap <- getDependencyMap
+      rdepMap <- evaluate . calculateReverseDependencies $ depMap
+      let tops = take 50 . sortOn (negate . snd) . fmap (second fst) . filter (\x -> maybe False broken $ Map.lookup (fst x) depMap) . Map.toList $ rdepMap
+      pure (rdepMap, tops)
    (eval, fetchTime, buildReport) <- readBuildReports
-   putStrLn (Text.unpack (printBuildSummary eval fetchTime (buildSummary maintainerMap buildReport)))
+   putStrLn (Text.unpack (printBuildSummary eval fetchTime (buildSummary maintainerMap reverseDependencyMap buildReport) topBrokenRdeps))
 
 printMarkBrokenList :: IO ()
 printMarkBrokenList = do
diff --git a/nixpkgs/maintainers/scripts/haskell/maintainer-handles.nix b/nixpkgs/maintainers/scripts/haskell/maintainer-handles.nix
new file mode 100644
index 000000000000..08c6bc4c96af
--- /dev/null
+++ b/nixpkgs/maintainers/scripts/haskell/maintainer-handles.nix
@@ -0,0 +1,7 @@
+# Nix script to lookup maintainer github handles from their email address. Used by ./hydra-report.hs.
+let
+  pkgs = import ../../.. {};
+  maintainers = import ../../maintainer-list.nix;
+  inherit (pkgs) lib;
+  mkMailGithubPair = _: maintainer: if maintainer ? github then { "${maintainer.email}" = maintainer.github; } else {};
+in lib.zipAttrsWith (_: builtins.head) (lib.mapAttrsToList mkMailGithubPair maintainers)
diff --git a/nixpkgs/maintainers/scripts/haskell/merge-and-open-pr.sh b/nixpkgs/maintainers/scripts/haskell/merge-and-open-pr.sh
new file mode 100755
index 000000000000..d73c09122350
--- /dev/null
+++ b/nixpkgs/maintainers/scripts/haskell/merge-and-open-pr.sh
@@ -0,0 +1,118 @@
+#! /usr/bin/env nix-shell
+#! nix-shell -i bash -p git gh -I nixpkgs=.
+#
+# Script to merge the currently open haskell-updates PR into master, bump the
+# Stackage version and Hackage versions, and open the next haskell-updates PR.
+
+set -eu -o pipefail
+
+# exit after printing first argument to this function
+function die {
+  # echo the first argument
+  echo "ERROR: $1"
+  echo "Aborting!"
+
+  exit 1
+}
+
+function help {
+  echo "Usage: $0 HASKELL_UPDATES_PR_NUM"
+  echo "Merge the currently open haskell-updates PR into master, and open the next one."
+  echo
+  echo "  -h, --help                print this help"
+  echo "  HASKELL_UPDATES_PR_NUM    number of the currently open PR on NixOS/nixpkgs"
+  echo "                            for the haskell-updates branch"
+  echo
+  echo "Example:"
+  echo "  \$ $0 137340"
+
+  exit 1
+}
+
+# Read in the current haskell-updates PR number from the command line.
+while [[ $# -gt 0 ]]; do
+  key="$1"
+
+  case $key in
+    -h|--help)
+      help
+      ;;
+    *)
+      curr_haskell_updates_pr_num="$1"
+      shift
+      ;;
+  esac
+done
+
+if [[ -z "${curr_haskell_updates_pr_num-}" ]] ; then
+  die "You must pass the current haskell-updates PR number as the first argument to this script."
+fi
+
+# Make sure you have gh authentication setup.
+if ! gh auth status 2>/dev/null ; then
+  die "You must setup the \`gh\` command.  Run \`gh auth login\`."
+fi
+
+# Fetch nixpkgs to get an up-to-date origin/haskell-updates branch.
+echo "Fetching origin..."
+git fetch origin >/dev/null
+
+# Make sure we are currently on a local haskell-updates branch.
+curr_branch="$(git rev-parse --abbrev-ref HEAD)"
+if [[ "$curr_branch" != "haskell-updates" ]]; then
+    die "Current branch is not called \"haskell-updates\"."
+fi
+
+# Make sure our local haskell-updates branch is on the same commit as
+# origin/haskell-updates.
+curr_branch_commit="$(git rev-parse haskell-updates)"
+origin_haskell_updates_commit="$(git rev-parse origin/haskell-updates)"
+if [[ "$curr_branch_commit" != "$origin_haskell_updates_commit" ]]; then
+    die "Current branch is not at the same commit as origin/haskell-updates"
+fi
+
+# Merge the current open haskell-updates PR.
+echo "Merging https://github.com/NixOS/nixpkgs/pull/${curr_haskell_updates_pr_num}..."
+gh pr merge --repo NixOS/nixpkgs --merge "$curr_haskell_updates_pr_num"
+
+# Update stackage, Hackage hashes, and regenerate Haskell package set
+echo "Updating Stackage..."
+./maintainers/scripts/haskell/update-stackage.sh --do-commit
+echo "Updating Hackage hashes..."
+./maintainers/scripts/haskell/update-hackage.sh --do-commit
+echo "Regenerating Hackage packages..."
+./maintainers/scripts/haskell/regenerate-hackage-packages.sh --do-commit
+
+# Push these new commits to the haskell-updates branch
+echo "Pushing commits just created to the haskell-updates branch"
+git push
+
+# Open new PR
+new_pr_body=$(cat <<EOF
+### This Merge
+
+This PR is the regular merge of the \`haskell-updates\` branch into \`master\`.
+
+This branch is being continually built and tested by hydra at https://hydra.nixos.org/jobset/nixpkgs/haskell-updates.
+
+We roughly aim to merge these \`haskell-updates\` PRs at least once every two weeks. See the @NixOS/haskell [team calendar](https://cloud.maralorn.de/apps/calendar/p/Mw5WLnzsP7fC4Zky) for who is currently in charge of this branch.
+
+### haskellPackages Workflow Summary
+
+Our workflow is currently described in [\`pkgs/development/haskell-modules/HACKING.md\`](https://github.com/NixOS/nixpkgs/blob/haskell-updates/pkgs/development/haskell-modules/HACKING.md).
+
+The short version is this:
+* We regularly update the Stackage and Hackage pins on \`haskell-updates\` (normally at the beginning of a merge window).
+* The community fixes builds of Haskell packages on that branch.
+* We aim at at least one merge of \`haskell-updates\` into \`master\` every two weeks.
+* We only do the merge if the [\`mergeable\`](https://hydra.nixos.org/job/nixpkgs/haskell-updates/mergeable) job is succeeding on hydra.
+* If a [\`maintained\`](https://hydra.nixos.org/job/nixpkgs/haskell-updates/maintained) package is still broken at the time of merge, we will only merge if the maintainer has been pinged 7 days in advance. (If you care about a Haskell package, become a maintainer!)
+
+---
+
+This is the follow-up to #${curr_haskell_updates_pr_num}. Come to [#haskell:nixos.org](https://matrix.to/#/#haskell:nixos.org) if you have any questions.
+EOF
+)
+
+echo "Opening a PR for the next haskell-updates merge cycle"
+gh pr create --repo NixOS/nixpkgs --base master --head haskell-updates --title "haskellPackages: update stackage and hackage" --body "$new_pr_body"
diff --git a/nixpkgs/maintainers/scripts/rebuild-amount.sh b/nixpkgs/maintainers/scripts/rebuild-amount.sh
index ca89e08073e8..bedd352db5f3 100755
--- a/nixpkgs/maintainers/scripts/rebuild-amount.sh
+++ b/nixpkgs/maintainers/scripts/rebuild-amount.sh
@@ -4,123 +4,130 @@ set -e
 # --print: avoid dependency on environment
 optPrint=
 if [ "$1" == "--print" ]; then
-	optPrint=true
-	shift
+    optPrint=true
+    shift
 fi
 
 if [ "$#" != 1 ] && [ "$#" != 2 ]; then
-	cat <<-EOF
-	Usage: $0 [--print] commit-spec [commit-spec]
-	    You need to be in a git-controlled nixpkgs tree.
-	    The current state of the tree will be used if the second commit is missing.
-	EOF
-	exit 1
+    cat <<EOF
+    Usage: $0 [--print] from-commit-spec [to-commit-spec]
+        You need to be in a git-controlled nixpkgs tree.
+        The current state of the tree will be used if the second commit is missing.
+
+        Examples:
+          effect of latest commit:
+              $ $0 HEAD^
+              $ $0 --print HEAD^
+          effect of the whole patch series for 'staging' branch:
+              $ $0 origin/staging staging
+EOF
+    exit 1
 fi
 
 # A slightly hacky way to get the config.
 parallel="$(echo 'config.rebuild-amount.parallel or false' | nix-repl . 2>/dev/null \
-			| grep -v '^\(nix-repl.*\)\?$' | tail -n 1 || true)"
+            | grep -v '^\(nix-repl.*\)\?$' | tail -n 1 || true)"
 
-echo "Estimating rebuild amount by counting changed Hydra jobs."
+echo "Estimating rebuild amount by counting changed Hydra jobs (parallel=${parallel:-unset})."
 
 toRemove=()
 
 cleanup() {
-	rm -rf "${toRemove[@]}"
+    rm -rf "${toRemove[@]}"
 }
 trap cleanup EXIT SIGINT SIGQUIT ERR
 
 MKTEMP='mktemp --tmpdir nix-rebuild-amount-XXXXXXXX'
 
 nixexpr() {
-	cat <<-EONIX
-		let
-		  lib = import $1/lib;
-		  hydraJobs = import $1/pkgs/top-level/release.nix
-		    # Compromise: accuracy vs. resources needed for evaluation.
-		    { supportedSystems = cfg.systems or [ "x86_64-linux" "x86_64-darwin" ]; };
-		  cfg = (import $1 {}).config.rebuild-amount or {};
-
-		  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)
-	EONIX
+    cat <<EONIX
+        let
+          lib = import $1/lib;
+          hydraJobs = import $1/pkgs/top-level/release.nix
+            # Compromise: accuracy vs. resources needed for evaluation.
+            { supportedSystems = cfg.systems or [ "x86_64-linux" "x86_64-darwin" ]; };
+          cfg = (import $1 {}).config.rebuild-amount or {};
+
+          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)
+EONIX
 }
 
 # Output packages in tree $2 that weren't in $1.
 # Changing the output hash or name is taken as a change.
 # Extra nix-env parameters can be in $3
 newPkgs() {
-	# We use files instead of pipes, as running multiple nix-env processes
-	# could eat too much memory for a standard 4GiB machine.
-	local -a list
-	for i in 1 2; do
-		local l="$($MKTEMP)"
-		list[$i]="$l"
-		toRemove+=("$l")
-
-		local expr="$($MKTEMP)"
-		toRemove+=("$expr")
-		nixexpr "${!i}" > "$expr"
-
-		nix-env -f "$expr" -qaP --no-name --out-path --show-trace $3 \
-			| sort > "${list[$i]}" &
-
-		if [ "$parallel" != "true" ]; then
-			wait
-		fi
-	done
-
-	wait
-	comm -13 "${list[@]}"
+    # We use files instead of pipes, as running multiple nix-env processes
+    # could eat too much memory for a standard 4GiB machine.
+    local -a list
+    for i in 1 2; do
+        local l="$($MKTEMP)"
+        list[$i]="$l"
+        toRemove+=("$l")
+
+        local expr="$($MKTEMP)"
+        toRemove+=("$expr")
+        nixexpr "${!i}" > "$expr"
+
+        nix-env -f "$expr" -qaP --no-name --out-path --show-trace $3 \
+            | sort > "${list[$i]}" &
+
+        if [ "$parallel" != "true" ]; then
+            wait
+        fi
+    done
+
+    wait
+    comm -13 "${list[@]}"
 }
 
 # Prepare nixpkgs trees.
 declare -a tree
 for i in 1 2; do
-	if [ -n "${!i}" ]; then # use the given commit
-		dir="$($MKTEMP -d)"
-		tree[$i]="$dir"
-		toRemove+=("$dir")
-
-		git clone --shared --no-checkout --quiet . "${tree[$i]}"
-		(cd "${tree[$i]}" && git checkout --quiet "${!i}")
-	else #use the current tree
-		tree[$i]="$(pwd)"
-	fi
+    if [ -n "${!i}" ]; then # use the given commit
+        dir="$($MKTEMP -d)"
+        tree[$i]="$dir"
+        toRemove+=("$dir")
+
+        git clone --shared --no-checkout --quiet . "${tree[$i]}"
+        (cd "${tree[$i]}" && git checkout --quiet "${!i}")
+    else #use the current tree
+        tree[$i]="$(pwd)"
+    fi
 done
 
 newlist="$($MKTEMP)"
 toRemove+=("$newlist")
 # Notes:
-#	- the evaluation is done on x86_64-linux, like on Hydra.
-#	- using $newlist file so that newPkgs() isn't in a sub-shell (because of toRemove)
+#    - the evaluation is done on x86_64-linux, like on Hydra.
+#    - using $newlist file so that newPkgs() isn't in a sub-shell (because of toRemove)
 newPkgs "${tree[1]}" "${tree[2]}" '--argstr system "x86_64-linux"' > "$newlist"
 
 # Hacky: keep only the last word of each attribute path and sort.
 sed -n 's/\([^. ]*\.\)*\([^. ]*\) .*$/\2/p' < "$newlist" \
-	| sort | uniq -c
+    | sort | uniq -c
 
 if [ -n "$optPrint" ]; then
-	echo
-	cat "$newlist"
+    echo
+    cat "$newlist"
 fi
diff --git a/nixpkgs/maintainers/team-list.nix b/nixpkgs/maintainers/team-list.nix
index df0c9ce7cf61..9171c10da460 100644
--- a/nixpkgs/maintainers/team-list.nix
+++ b/nixpkgs/maintainers/team-list.nix
@@ -164,6 +164,14 @@ with lib.maintainers; {
     scope = "Maintain Kodi and related packages.";
   };
 
+  mate = {
+    members = [
+      j03
+      romildo
+    ];
+    scope = "Maintain Mate desktop environment and related packages.";
+  };
+
   matrix = {
     members = [
       ma27