main
  1/*
  2Selection of packages with `passthru.updateScript`, shared by the
  3runner (scripts/update.nix) and scripts/update-package --list.
  4
  5Usage:
  6
  7    nix-instantiate --eval --json --strict \
  8      -E 'map (p: p.attrPath) (import ./scripts/update-packages.nix {})'
  9*/
 10{
 11  pkgs ? import <nixpkgs> {},
 12  # Space-separated list of package attribute paths to update.
 13  # Empty string means "update everything".
 14  packages ? "",
 15}: let
 16  inherit (pkgs) lib;
 17  nurPkgs = import ../default.nix {inherit pkgs;};
 18
 19  updateScriptOf = pkg: let
 20    script =
 21      (
 22        if pkg.passthru or null != null
 23        then pkg.passthru
 24        else {}
 25      ).updateScript or null;
 26  in
 27    if script == null
 28    then null
 29    else if lib.isDerivation script
 30    then [(toString script)]
 31    else if lib.isAttrs script
 32    then
 33      if script ? command
 34      then map toString (lib.toList script.command)
 35      else null
 36    else map toString (lib.toList script);
 37
 38  reservedNames = [
 39    "lib"
 40    "overlays"
 41    "nixosModules"
 42    "homeModules"
 43    "darwinModules"
 44    "flakeModules"
 45  ];
 46
 47  collect = prefix: attrs: let
 48    names = builtins.tryEval (builtins.attrNames attrs);
 49  in
 50    if !names.success
 51    then []
 52    else
 53      builtins.concatLists (
 54        map (
 55          name: let
 56            result = builtins.tryEval attrs.${name};
 57          in
 58            if !result.success
 59            then []
 60            else if lib.isDerivation result.value
 61            then let
 62              script = updateScriptOf result.value;
 63              meta = result.value.meta or null;
 64              position =
 65                if meta != null
 66                then meta.position or null
 67                else null;
 68            in
 69              lib.optional (script != null && !(meta.nurRenamed or false) && !(meta.nurDeprecated or false)) {
 70                inherit name;
 71                attrPath = lib.concatStringsSep "." (prefix ++ [name]);
 72                inherit script;
 73                pname = result.value.pname or (lib.getName result.value);
 74                pkgName = result.value.name;
 75                oldVersion = result.value.version or "";
 76                inherit position;
 77              }
 78            else if lib.isAttrs result.value
 79            then collect (prefix ++ [name]) result.value
 80            else []
 81        )
 82        names.value
 83      );
 84
 85  allPackages = collect [] (lib.filterAttrs (name: _: !builtins.elem name reservedNames) nurPkgs);
 86
 87  deduplicated = let
 88    isClean = p: !(p.meta.nurRenamed or false) && !(p.meta.nurDeprecated or false);
 89    grouped =
 90      lib.foldl (
 91        acc: cur: let
 92          key = builtins.hashString "sha256" (
 93            lib.concatStringsSep "\n" (cur.script ++ [(toString cur.position)])
 94          );
 95          prev = acc.${key} or null;
 96          keep =
 97            prev
 98            == null
 99            || (isClean cur && !isClean prev)
100            || (isClean cur == isClean prev && lib.hasInfix "." cur.attrPath && !lib.hasInfix "." prev.attrPath);
101        in
102          acc
103          // {
104            ${key} =
105              if keep
106              then cur
107              else prev;
108          }
109      ) {}
110      allPackages;
111  in
112    map (k: grouped.${k}) (builtins.attrNames grouped);
113
114  matchesSelection = p: sel: p.attrPath == sel || lib.hasSuffix ".${sel}" p.attrPath;
115
116  selected =
117    if packages != ""
118    then lib.filter (p: lib.any (sel: matchesSelection p sel) (lib.splitString " " packages)) deduplicated
119    else deduplicated;
120in
121  selected