at master 7.5 kB view raw
1{ 2 callPackage, 3 lib, 4 jq, 5 runCommand, 6 writeText, 7 python3, 8 stdenvNoCC, 9 makeWrapper, 10}: 11let 12 python = python3.withPackages (ps: [ 13 ps.numpy 14 ps.pandas 15 ps.scipy 16 ps.tabulate 17 ]); 18 19 cmp-stats = stdenvNoCC.mkDerivation { 20 pname = "cmp-stats"; 21 version = lib.trivial.release; 22 23 dontUnpack = true; 24 25 nativeBuildInputs = [ makeWrapper ]; 26 27 installPhase = '' 28 runHook preInstall 29 30 mkdir -p $out/share/cmp-stats 31 32 cp ${./cmp-stats.py} "$out/share/cmp-stats/cmp-stats.py" 33 34 makeWrapper ${python.interpreter} "$out/bin/cmp-stats" \ 35 --add-flags "$out/share/cmp-stats/cmp-stats.py" 36 37 runHook postInstall 38 ''; 39 40 meta = { 41 description = "Performance comparison of Nix evaluation statistics"; 42 license = lib.licenses.mit; 43 mainProgram = "cmp-stats"; 44 maintainers = with lib.maintainers; [ philiptaron ]; 45 }; 46 }; 47in 48{ 49 combinedDir, 50 touchedFilesJson, 51 githubAuthorId, 52 byName ? false, 53}: 54let 55 # Usually we expect a derivation, but when evaluating in multiple separate steps, we pass 56 # nix store paths around. These need to be turned into (fake) derivations again to track 57 # dependencies properly. 58 # We use two steps for evaluation, because we compare results from two different checkouts. 59 # CI additionalls spreads evaluation across multiple workers. 60 combined = if lib.isDerivation combinedDir then combinedDir else lib.toDerivation combinedDir; 61 62 /* 63 Derivation that computes which packages are affected (added, changed or removed) between two revisions of nixpkgs. 64 Note: "platforms" are "x86_64-linux", "aarch64-darwin", ... 65 66 --- 67 Inputs: 68 - beforeDir, afterDir: The evaluation result from before and after the change. 69 They can be obtained by running `nix-build -A ci.eval.full` on both revisions. 70 71 --- 72 Outputs: 73 - changed-paths.json: Various information about the changes: 74 { 75 attrdiff: { 76 added: ["package1"], 77 changed: ["package2", "package3"], 78 removed: ["package4"], 79 }, 80 labels: { 81 "10.rebuild-darwin: 1-10": true, 82 "10.rebuild-linux: 1-10": true 83 }, 84 rebuildsByKernel: { 85 darwin: ["package1", "package2"], 86 linux: ["package1", "package2", "package3"] 87 }, 88 rebuildCountByKernel: { 89 darwin: 2, 90 linux: 3, 91 }, 92 rebuildsByPlatform: { 93 aarch64-darwin: ["package1", "package2"], 94 aarch64-linux: ["package1", "package2"], 95 x86_64-linux: ["package1", "package2", "package3"], 96 x86_64-darwin: ["package1"], 97 }, 98 } 99 - step-summary.md: A markdown render of the changes 100 101 --- 102 Implementation details: 103 104 Helper functions can be found in ./utils.nix. 105 Two main "types" are important: 106 107 - `packagePlatformPath`: A string of the form "<PACKAGE_PATH>.<PLATFORM>" 108 Example: "python312Packages.numpy.x86_64-linux" 109 110 - `packagePlatformAttr`: An attrs representation of a packagePlatformPath: 111 Example: { name = "python312Packages.numpy"; platform = "x86_64-linux"; } 112 */ 113 inherit (import ./utils.nix { inherit lib; }) 114 groupByKernel 115 convertToPackagePlatformAttrs 116 groupByPlatform 117 extractPackageNames 118 getLabels 119 ; 120 121 # Attrs 122 # - keys: "added", "changed", "removed" and "rebuilds" 123 # - values: lists of `packagePlatformPath`s 124 diffAttrs = builtins.fromJSON (builtins.readFile "${combined}/combined-diff.json"); 125 126 changedPackagePlatformAttrs = convertToPackagePlatformAttrs diffAttrs.changed; 127 rebuildsPackagePlatformAttrs = convertToPackagePlatformAttrs diffAttrs.rebuilds; 128 removedPackagePlatformAttrs = convertToPackagePlatformAttrs diffAttrs.removed; 129 130 changed-paths = 131 let 132 rebuildsByPlatform = groupByPlatform rebuildsPackagePlatformAttrs; 133 rebuildsByKernel = groupByKernel rebuildsPackagePlatformAttrs; 134 rebuildCountByKernel = lib.mapAttrs ( 135 kernel: kernelRebuilds: lib.length kernelRebuilds 136 ) rebuildsByKernel; 137 in 138 writeText "changed-paths.json" ( 139 builtins.toJSON { 140 attrdiff = lib.mapAttrs (_: extractPackageNames) { inherit (diffAttrs) added changed removed; }; 141 inherit 142 rebuildsByPlatform 143 rebuildsByKernel 144 rebuildCountByKernel 145 ; 146 labels = 147 getLabels rebuildCountByKernel 148 # Sets "10.rebuild-*-stdenv" label to whether the "stdenv" attribute was changed. 149 // lib.mapAttrs' ( 150 kernel: rebuilds: lib.nameValuePair "10.rebuild-${kernel}-stdenv" (lib.elem "stdenv" rebuilds) 151 ) rebuildsByKernel 152 // { 153 "10.rebuild-nixos-tests" = 154 lib.elem "nixosTests.simple" (extractPackageNames diffAttrs.rebuilds) 155 && 156 # Only set this label when no other label with indication for staging has been set. 157 # This avoids confusion whether to target staging or batch this with kernel updates. 158 lib.last (lib.sort lib.lessThan (lib.attrValues rebuildCountByKernel)) <= 500; 159 # Set the "11.by: package-maintainer" label to whether all packages directly 160 # changed are maintained by the PR's author. 161 "11.by: package-maintainer" = 162 maintainers ? ${githubAuthorId} 163 && lib.all (lib.flip lib.elem maintainers.${githubAuthorId}) ( 164 lib.flatten (lib.attrValues maintainers) 165 ); 166 }; 167 } 168 ); 169 170 maintainers = callPackage ./maintainers.nix { } { 171 changedattrs = lib.attrNames (lib.groupBy (a: a.name) changedPackagePlatformAttrs); 172 changedpathsjson = touchedFilesJson; 173 removedattrs = lib.attrNames (lib.groupBy (a: a.name) removedPackagePlatformAttrs); 174 inherit byName; 175 }; 176in 177runCommand "compare" 178 { 179 # Don't depend on -dev outputs to reduce closure size for CI. 180 nativeBuildInputs = map lib.getBin [ 181 jq 182 cmp-stats 183 ]; 184 maintainers = builtins.toJSON maintainers; 185 passAsFile = [ "maintainers" ]; 186 } 187 '' 188 mkdir $out 189 190 cp ${changed-paths} $out/changed-paths.json 191 192 { 193 echo 194 echo "# Packages" 195 echo 196 jq -r -f ${./generate-step-summary.jq} < ${changed-paths} 197 } >> $out/step-summary.md 198 199 if jq -e '(.attrdiff.added | length == 0) and (.attrdiff.removed | length == 0)' "${changed-paths}" > /dev/null; then 200 # Chunks have changed between revisions 201 # We cannot generate a performance comparison 202 { 203 echo 204 echo "# Performance comparison" 205 echo 206 echo "This compares the performance of this branch against its pull request base branch (e.g., 'master')" 207 echo 208 echo "For further help please refer to: [ci/README.md](https://github.com/NixOS/nixpkgs/blob/master/ci/README.md)" 209 echo 210 } >> $out/step-summary.md 211 212 cmp-stats --explain ${combined}/before/stats ${combined}/after/stats >> $out/step-summary.md 213 214 else 215 # Package chunks are the same in both revisions 216 # We can use the to generate a performance comparison 217 { 218 echo 219 echo "# Performance Comparison" 220 echo 221 echo "Performance stats were skipped because the package sets differ between the two revisions." 222 echo 223 echo "For further help please refer to: [ci/README.md](https://github.com/NixOS/nixpkgs/blob/master/ci/README.md)" 224 } >> $out/step-summary.md 225 fi 226 227 cp "$maintainersPath" "$out/maintainers.json" 228 ''