nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 lib,
3 runCommand,
4 writeText,
5}:
6
7{
8 beforeDir,
9 afterDir,
10 evalSystem,
11}:
12
13let
14 # Usually we expect a derivation, but when evaluating in multiple separate steps, we pass
15 # nix store paths around. These need to be turned into (fake) derivations again to track
16 # dependencies properly.
17 # We use two steps for evaluation, because we compare results from two different checkouts.
18 # CI additionalls spreads evaluation across multiple workers.
19 before = if lib.isDerivation beforeDir then beforeDir else lib.toDerivation beforeDir;
20 after = if lib.isDerivation afterDir then afterDir else lib.toDerivation afterDir;
21
22 /*
23 Computes the key difference between two attrs
24
25 {
26 added: [ <keys only in the second object> ],
27 removed: [ <keys only in the first object> ],
28 changed: [ <keys with different values between the two objects> ],
29 rebuilds: [ <keys in the second object with values not present at all in first object> ],
30 }
31 */
32 diff =
33 old: new:
34 let
35 filterKeys = cond: attrs: lib.attrNames (lib.filterAttrs cond attrs);
36 oldOutputs = lib.pipe old [
37 (lib.mapAttrsToList (_: lib.attrValues))
38 lib.concatLists
39 (lib.flip lib.genAttrs (_: true))
40 ];
41 in
42 {
43 added = filterKeys (n: _: !(old ? ${n})) new;
44 removed = filterKeys (n: _: !(new ? ${n})) old;
45 changed = filterKeys (
46 n: v:
47 # Filter out attributes that don't exist anymore
48 (new ? ${n})
49
50 # Filter out attributes that are the same as the new value
51 && (v != (new.${n}))
52 ) old;
53 # A "rebuild" is every attrpath ...
54 rebuilds = filterKeys (
55 _: pkg:
56 # ... that has at least one output ...
57 lib.any (
58 output:
59 # ... which has not been built in "old" already.
60 !(oldOutputs ? ${output})
61 ) (lib.attrValues pkg)
62 ) new;
63 };
64
65 getAttrs =
66 dir:
67 let
68 raw = builtins.readFile "${dir}/${evalSystem}/paths.json";
69 # The file contains Nix paths; we need to ignore them for evaluation purposes,
70 # else there will be a "is not allowed to refer to a store path" error.
71 data = builtins.unsafeDiscardStringContext raw;
72 in
73 builtins.fromJSON data;
74
75 beforeAttrs = getAttrs before;
76 afterAttrs = getAttrs after;
77 diffAttrs = diff beforeAttrs afterAttrs;
78 diffJson = writeText "diff.json" (builtins.toJSON diffAttrs);
79
80 # The maintainer list is not diffed, but just taken as is, to provide a map
81 # of maintainers on the target branch. A list of GitHub IDs is sufficient for
82 # all our purposes and reduces size massively.
83 meta = lib.importJSON "${after}/${evalSystem}/meta.json";
84 maintainers = lib.pipe meta [
85 (lib.mapAttrsToList (
86 k: v: {
87 # splits off the platform suffix
88 package = lib.pipe k [
89 (lib.splitString ".")
90 lib.init
91 (lib.concatStringsSep ".")
92 ];
93 maintainers = map (m: m.githubId) v.maintainers or [ ];
94 }
95 ))
96 # Some paths don't have a platform suffix, those will appear with an empty package here.
97 (lib.filter ({ package, maintainers }: package != "" && maintainers != [ ]))
98 ];
99 maintainersJson = writeText "maintainers.json" (builtins.toJSON maintainers);
100in
101runCommand "diff" { } ''
102 mkdir -p $out/${evalSystem}
103
104 cp -r --no-preserve=mode ${before} $out/before
105 cp -r --no-preserve=mode ${after} $out/after
106 # JSON files will be processed above explicitly, so avoid copying over
107 # the source files to keep the artifacts smaller.
108 find $out/before $out/after -iname '*.json' -delete
109 cp ${diffJson} $out/${evalSystem}/diff.json
110 cp ${maintainersJson} $out/${evalSystem}/maintainers.json
111''