1{
2 callPackage,
3 lib,
4 jq,
5 runCommand,
6 writeText,
7 python3,
8}:
9{
10 combinedDir,
11 touchedFilesJson,
12 githubAuthorId,
13 byName ? false,
14}:
15let
16 /*
17 Derivation that computes which packages are affected (added, changed or removed) between two revisions of nixpkgs.
18 Note: "platforms" are "x86_64-linux", "aarch64-darwin", ...
19
20 ---
21 Inputs:
22 - beforeDir, afterDir: The evaluation result from before and after the change.
23 They can be obtained by running `nix-build -A ci.eval.full` on both revisions.
24
25 ---
26 Outputs:
27 - changed-paths.json: Various information about the changes:
28 {
29 attrdiff: {
30 added: ["package1"],
31 changed: ["package2", "package3"],
32 removed: ["package4"],
33 },
34 labels: {
35 "10.rebuild-darwin: 1-10": true,
36 "10.rebuild-linux: 1-10": true
37 },
38 rebuildsByKernel: {
39 darwin: ["package1", "package2"],
40 linux: ["package1", "package2", "package3"]
41 },
42 rebuildCountByKernel: {
43 darwin: 2,
44 linux: 3,
45 },
46 rebuildsByPlatform: {
47 aarch64-darwin: ["package1", "package2"],
48 aarch64-linux: ["package1", "package2"],
49 x86_64-linux: ["package1", "package2", "package3"],
50 x86_64-darwin: ["package1"],
51 },
52 }
53 - step-summary.md: A markdown render of the changes
54
55 ---
56 Implementation details:
57
58 Helper functions can be found in ./utils.nix.
59 Two main "types" are important:
60
61 - `packagePlatformPath`: A string of the form "<PACKAGE_PATH>.<PLATFORM>"
62 Example: "python312Packages.numpy.x86_64-linux"
63
64 - `packagePlatformAttr`: An attrs representation of a packagePlatformPath:
65 Example: { name = "python312Packages.numpy"; platform = "x86_64-linux"; }
66 */
67 inherit (import ./utils.nix { inherit lib; })
68 groupByKernel
69 convertToPackagePlatformAttrs
70 groupByPlatform
71 extractPackageNames
72 getLabels
73 ;
74
75 # Attrs
76 # - keys: "added", "changed", "removed" and "rebuilds"
77 # - values: lists of `packagePlatformPath`s
78 diffAttrs = builtins.fromJSON (builtins.readFile "${combinedDir}/combined-diff.json");
79
80 rebuildsPackagePlatformAttrs = convertToPackagePlatformAttrs diffAttrs.rebuilds;
81
82 changed-paths =
83 let
84 rebuildsByPlatform = groupByPlatform rebuildsPackagePlatformAttrs;
85 rebuildsByKernel = groupByKernel rebuildsPackagePlatformAttrs;
86 rebuildCountByKernel = lib.mapAttrs (
87 kernel: kernelRebuilds: lib.length kernelRebuilds
88 ) rebuildsByKernel;
89 in
90 writeText "changed-paths.json" (
91 builtins.toJSON {
92 attrdiff = lib.mapAttrs (_: extractPackageNames) { inherit (diffAttrs) added changed removed; };
93 inherit
94 rebuildsByPlatform
95 rebuildsByKernel
96 rebuildCountByKernel
97 ;
98 labels =
99 getLabels rebuildCountByKernel
100 # Sets "10.rebuild-*-stdenv" label to whether the "stdenv" attribute was changed.
101 // lib.mapAttrs' (
102 kernel: rebuilds: lib.nameValuePair "10.rebuild-${kernel}-stdenv" (lib.elem "stdenv" rebuilds)
103 ) rebuildsByKernel
104 # Set the "11.by: package-maintainer" label to whether all packages directly
105 # changed are maintained by the PR's author.
106 # (https://github.com/NixOS/ofborg/blob/df400f44502d4a4a80fa283d33f2e55a4e43ee90/ofborg/src/tagger.rs#L83-L88)
107 // {
108 "11.by: package-maintainer" =
109 maintainers ? ${githubAuthorId}
110 && lib.all (lib.flip lib.elem maintainers.${githubAuthorId}) (
111 lib.flatten (lib.attrValues maintainers)
112 );
113 };
114 }
115 );
116
117 maintainers = callPackage ./maintainers.nix { } {
118 changedattrs = lib.attrNames (lib.groupBy (a: a.name) rebuildsPackagePlatformAttrs);
119 changedpathsjson = touchedFilesJson;
120 inherit byName;
121 };
122in
123runCommand "compare"
124 {
125 # Don't depend on -dev outputs to reduce closure size for CI.
126 nativeBuildInputs = map lib.getBin [
127 jq
128 (python3.withPackages (
129 ps: with ps; [
130 numpy
131 pandas
132 scipy
133 ]
134 ))
135
136 ];
137 maintainers = builtins.toJSON maintainers;
138 passAsFile = [ "maintainers" ];
139 env = {
140 BEFORE_DIR = "${combinedDir}/before";
141 AFTER_DIR = "${combinedDir}/after";
142 };
143 }
144 ''
145 mkdir $out
146
147 cp ${changed-paths} $out/changed-paths.json
148
149
150 if jq -e '(.attrdiff.added | length == 0) and (.attrdiff.removed | length == 0)' "${changed-paths}" > /dev/null; then
151 # Chunks have changed between revisions
152 # We cannot generate a performance comparison
153 {
154 echo
155 echo "# Performance comparison"
156 echo
157 echo "This compares the performance of this branch against its pull request base branch (e.g., 'master')"
158 echo
159 echo "For further help please refer to: [ci/README.md](https://github.com/NixOS/nixpkgs/blob/master/ci/README.md)"
160 echo
161 } >> $out/step-summary.md
162
163 python3 ${./cmp-stats.py} >> $out/step-summary.md
164
165 else
166 # Package chunks are the same in both revisions
167 # We can use the to generate a performance comparison
168 {
169 echo
170 echo "# Performance Comparison"
171 echo
172 echo "Performance stats were skipped because the package sets differ between the two revisions."
173 echo
174 echo "For further help please refer to: [ci/README.md](https://github.com/NixOS/nixpkgs/blob/master/ci/README.md)"
175 } >> $out/step-summary.md
176 fi
177
178 {
179 echo
180 echo "# Packages"
181 echo
182 jq -r -f ${./generate-step-summary.jq} < ${changed-paths}
183 } >> $out/step-summary.md
184
185 cp "$maintainersPath" "$out/maintainers.json"
186 ''