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