at 22.05-pre 7.7 kB view raw
1{ package ? null 2, maintainer ? null 3, predicate ? null 4, path ? null 5, max-workers ? null 6, include-overlays ? false 7, keep-going ? null 8, commit ? null 9}: 10 11# TODO: add assert statements 12 13let 14 pkgs = import ./../../default.nix ( 15 if include-overlays == false then 16 { overlays = []; } 17 else if include-overlays == true then 18 { } # Let Nixpkgs include overlays impurely. 19 else { overlays = include-overlays; } 20 ); 21 22 inherit (pkgs) lib; 23 24 /* Remove duplicate elements from the list based on some extracted value. O(n^2) complexity. 25 */ 26 nubOn = f: list: 27 if list == [] then 28 [] 29 else 30 let 31 x = lib.head list; 32 xs = lib.filter (p: f x != f p) (lib.drop 1 list); 33 in 34 [x] ++ nubOn f xs; 35 36 /* Recursively find all packages (derivations) in `pkgs` matching `cond` predicate. 37 38 Type: packagesWithPath :: AttrPath (AttrPath derivation bool) AttrSet List<AttrSet{attrPath :: str; package :: derivation; }> 39 AttrPath :: [str] 40 41 The packages will be returned as a list of named pairs comprising of: 42 - attrPath: stringified attribute path (based on `rootPath`) 43 - package: corresponding derivation 44 */ 45 packagesWithPath = rootPath: cond: pkgs: 46 let 47 packagesWithPathInner = path: pathContent: 48 let 49 result = builtins.tryEval pathContent; 50 51 dedupResults = lst: nubOn ({ package, attrPath }: package.updateScript) (lib.concatLists lst); 52 in 53 if result.success then 54 let 55 evaluatedPathContent = result.value; 56 in 57 if lib.isDerivation evaluatedPathContent then 58 lib.optional (cond path evaluatedPathContent) { attrPath = lib.concatStringsSep "." path; package = evaluatedPathContent; } 59 else if lib.isAttrs evaluatedPathContent then 60 # If user explicitly points to an attrSet or it is marked for recursion, we recur. 61 if path == rootPath || evaluatedPathContent.recurseForDerivations or false || evaluatedPathContent.recurseForRelease or false then 62 dedupResults (lib.mapAttrsToList (name: elem: packagesWithPathInner (path ++ [name]) elem) evaluatedPathContent) 63 else [] 64 else [] 65 else []; 66 in 67 packagesWithPathInner rootPath pkgs; 68 69 /* Recursively find all packages (derivations) in `pkgs` matching `cond` predicate. 70 */ 71 packagesWith = packagesWithPath []; 72 73 /* Recursively find all packages in `pkgs` with updateScript matching given predicate. 74 */ 75 packagesWithUpdateScriptMatchingPredicate = cond: 76 packagesWith (path: pkg: builtins.hasAttr "updateScript" pkg && cond path pkg); 77 78 /* Recursively find all packages in `pkgs` with updateScript by given maintainer. 79 */ 80 packagesWithUpdateScriptAndMaintainer = maintainer': 81 let 82 maintainer = 83 if ! builtins.hasAttr maintainer' lib.maintainers then 84 builtins.throw "Maintainer with name `${maintainer'} does not exist in `maintainers/maintainer-list.nix`." 85 else 86 builtins.getAttr maintainer' lib.maintainers; 87 in 88 packagesWithUpdateScriptMatchingPredicate (path: pkg: 89 (if builtins.hasAttr "maintainers" pkg.meta 90 then (if builtins.isList pkg.meta.maintainers 91 then builtins.elem maintainer pkg.meta.maintainers 92 else maintainer == pkg.meta.maintainers 93 ) 94 else false 95 ) 96 ); 97 98 /* Recursively find all packages under `path` in `pkgs` with updateScript. 99 */ 100 packagesWithUpdateScript = path: pkgs: 101 let 102 prefix = lib.splitString "." path; 103 pathContent = lib.attrByPath prefix null pkgs; 104 in 105 if pathContent == null then 106 builtins.throw "Attribute path `${path}` does not exist." 107 else 108 packagesWithPath prefix (path: pkg: builtins.hasAttr "updateScript" pkg) 109 pathContent; 110 111 /* Find a package under `path` in `pkgs` and require that it has an updateScript. 112 */ 113 packageByName = path: pkgs: 114 let 115 package = lib.attrByPath (lib.splitString "." path) null pkgs; 116 in 117 if package == null then 118 builtins.throw "Package with an attribute name `${path}` does not exist." 119 else if ! builtins.hasAttr "updateScript" package then 120 builtins.throw "Package with an attribute name `${path}` does not have a `passthru.updateScript` attribute defined." 121 else 122 { attrPath = path; inherit package; }; 123 124 /* List of packages matched based on the CLI arguments. 125 */ 126 packages = 127 if package != null then 128 [ (packageByName package pkgs) ] 129 else if predicate != null then 130 packagesWithUpdateScriptMatchingPredicate predicate pkgs 131 else if maintainer != null then 132 packagesWithUpdateScriptAndMaintainer maintainer pkgs 133 else if path != null then 134 packagesWithUpdateScript path pkgs 135 else 136 builtins.throw "No arguments provided.\n\n${helpText}"; 137 138 helpText = '' 139 Please run: 140 141 % nix-shell maintainers/scripts/update.nix --argstr maintainer garbas 142 143 to run all update scripts for all packages that lists \`garbas\` as a maintainer 144 and have \`updateScript\` defined, or: 145 146 % nix-shell maintainers/scripts/update.nix --argstr package gnome.nautilus 147 148 to run update script for specific package, or 149 150 % nix-shell maintainers/scripts/update.nix --arg predicate '(path: pkg: builtins.isList pkg.updateScript && builtins.length pkg.updateScript >= 1 && (let script = builtins.head pkg.updateScript; in builtins.isAttrs script && script.name == "gnome-update-script"))' 151 152 to run update script for all packages matching given predicate, or 153 154 % nix-shell maintainers/scripts/update.nix --argstr path gnome 155 156 to run update script for all package under an attribute path. 157 158 You can also add 159 160 --argstr max-workers 8 161 162 to increase the number of jobs in parallel, or 163 164 --argstr keep-going true 165 166 to continue running when a single update fails. 167 168 You can also make the updater automatically commit on your behalf from updateScripts 169 that support it by adding 170 171 --argstr commit true 172 ''; 173 174 /* Transform a matched package into an object for update.py. 175 */ 176 packageData = { package, attrPath }: { 177 name = package.name; 178 pname = lib.getName package; 179 oldVersion = lib.getVersion package; 180 updateScript = map builtins.toString (lib.toList (package.updateScript.command or package.updateScript)); 181 supportedFeatures = package.updateScript.supportedFeatures or []; 182 attrPath = package.updateScript.attrPath or attrPath; 183 }; 184 185 /* JSON file with data for update.py. 186 */ 187 packagesJson = pkgs.writeText "packages.json" (builtins.toJSON (map packageData packages)); 188 189 optionalArgs = 190 lib.optional (max-workers != null) "--max-workers=${max-workers}" 191 ++ lib.optional (keep-going == "true") "--keep-going" 192 ++ lib.optional (commit == "true") "--commit"; 193 194 args = [ packagesJson ] ++ optionalArgs; 195 196in pkgs.stdenv.mkDerivation { 197 name = "nixpkgs-update-script"; 198 buildCommand = '' 199 echo "" 200 echo "----------------------------------------------------------------" 201 echo "" 202 echo "Not possible to update packages using \`nix-build\`" 203 echo "" 204 echo "${helpText}" 205 echo "----------------------------------------------------------------" 206 exit 1 207 ''; 208 shellHook = '' 209 unset shellHook # do not contaminate nested shells 210 exec ${pkgs.python3.interpreter} ${./update.py} ${builtins.concatStringsSep " " args} 211 ''; 212}