1{
2 lib,
3 runCommand,
4 writeText,
5}:
6
7{
8 beforeDir,
9 afterDir,
10 evalSystem,
11}:
12
13let
14 /*
15 Computes the key difference between two attrs
16
17 {
18 added: [ <keys only in the second object> ],
19 removed: [ <keys only in the first object> ],
20 changed: [ <keys with different values between the two objects> ],
21 rebuilds: [ <keys in the second object with values not present at all in first object> ],
22 }
23 */
24 diff =
25 old: new:
26 let
27 filterKeys = cond: attrs: lib.attrNames (lib.filterAttrs cond attrs);
28 oldOutputs = lib.pipe old [
29 (lib.mapAttrsToList (_: lib.attrValues))
30 lib.concatLists
31 (lib.flip lib.genAttrs (_: true))
32 ];
33 in
34 {
35 added = filterKeys (n: _: !(old ? ${n})) new;
36 removed = filterKeys (n: _: !(new ? ${n})) old;
37 changed = filterKeys (
38 n: v:
39 # Filter out attributes that don't exist anymore
40 (new ? ${n})
41
42 # Filter out attributes that are the same as the new value
43 && (v != (new.${n}))
44 ) old;
45 # A "rebuild" is every attrpath ...
46 rebuilds = filterKeys (
47 _: pkg:
48 # ... that has at least one output ...
49 lib.any (
50 output:
51 # ... which has not been built in "old" already.
52 !(oldOutputs ? ${output})
53 ) (lib.attrValues pkg)
54 ) new;
55 };
56
57 getAttrs =
58 dir:
59 let
60 raw = builtins.readFile "${dir}/${evalSystem}/paths.json";
61 # The file contains Nix paths; we need to ignore them for evaluation purposes,
62 # else there will be a "is not allowed to refer to a store path" error.
63 data = builtins.unsafeDiscardStringContext raw;
64 in
65 builtins.fromJSON data;
66
67 beforeAttrs = getAttrs beforeDir;
68 afterAttrs = getAttrs afterDir;
69 diffAttrs = diff beforeAttrs afterAttrs;
70 diffJson = writeText "diff.json" (builtins.toJSON diffAttrs);
71in
72runCommand "diff" { } ''
73 mkdir -p $out/${evalSystem}
74
75 cp -r ${beforeDir} $out/before
76 cp -r ${afterDir} $out/after
77 cp ${diffJson} $out/${evalSystem}/diff.json
78''