|
1 |
| -{lib,runCommand,coreutils,fetchurl}: |
2 |
| -{packageLockPath}: |
| 1 | +{lib,runCommand,coreutils,fetchurl,unixtools,writeText}: |
| 2 | +{packageLockPath,patches}: |
3 | 3 | let
|
4 | 4 | json = builtins.readFile packageLockPath;
|
5 |
| - dependencies = (builtins.fromJSON json).dependencies; |
| 5 | + packageLock = builtins.fromJSON json; |
6 | 6 | base64toHex = import ./base64toHex.nix;
|
7 |
| - getTarball = (spec: |
| 7 | + |
| 8 | + # take a tarball and create a patched tarball in the same format |
| 9 | + patchTarball = {tarball,patch}: |
| 10 | + let |
| 11 | + derivation = runCommand "patched-package" {buildInputs=[coreutils unixtools.xxd];} '' |
| 12 | + tar -xzf ${tarball.tarball} package |
| 13 | + ${patch} |
| 14 | + mkdir $out |
| 15 | + tar -cf $out/tarball.tgz --xform 's:^\./::' package |
| 16 | + sha512sum $out/tarball.tgz | cut -d " " -f 1 > $out/sha512.hex |
| 17 | + xxd -r -p < $out/sha512.hex | base64 -w 0 > $out/sha512.base64 |
| 18 | + ''; |
| 19 | + in { |
| 20 | + algorithm = builtins.trace "${derivation}" "sha512"; |
| 21 | + base64hash = builtins.readFile "${derivation}/sha512.base64"; |
| 22 | + hexHash = builtins.readFile "${derivation}/sha512.hex"; |
| 23 | + tarball = "${derivation}/tarball.tgz"; |
| 24 | + } |
| 25 | + ; |
| 26 | + |
| 27 | + # get a flat list of all depencency specs |
| 28 | + getFlatSpecs = dependencies: |
| 29 | + builtins.concatLists (lib.attrsets.mapAttrsToList (name: spec: [spec]++(if spec ? dependencies then getFlatSpecs spec.dependencies else [])) dependencies) |
| 30 | + ; |
| 31 | + |
| 32 | + # convert a dependency spec into a tarball with additional information |
| 33 | + getTarballFromSpec = spec: |
8 | 34 | let
|
9 | 35 | parts = lib.strings.splitString "-" spec.integrity;
|
| 36 | + in rec { |
10 | 37 | algorithm = builtins.elemAt parts 0;
|
11 | 38 | base64hash = builtins.elemAt parts 1;
|
12 | 39 | hexHash = base64toHex base64hash;
|
13 | 40 | tarball = fetchurl {
|
14 | 41 | url = spec.resolved;
|
15 | 42 | "${algorithm}" = hexHash;
|
16 | 43 | };
|
17 |
| - dependencies = if spec ? dependencies then getTarballs spec.dependencies else []; |
| 44 | + } |
| 45 | + ; |
| 46 | + |
| 47 | + # get the command to create the cache entry for this tarball |
| 48 | + getCommandFromTarball = {algorithm,hexHash,tarball,...}: |
| 49 | + let |
18 | 50 | folder1 = builtins.substring 0 2 hexHash;
|
19 | 51 | folder2 = builtins.substring 2 2 hexHash;
|
20 | 52 | fileName = builtins.substring 4 ((builtins.stringLength hexHash)-4) hexHash;
|
21 | 53 | folderPath = "$out/_cacache/content-v2/${algorithm}/${folder1}/${folder2}";
|
22 |
| - command = "mkdir -p ${folderPath} && cd ${folderPath} && ln -sf ${tarball} ${fileName}"; |
23 | 54 | in
|
24 |
| - [command]++dependencies |
25 |
| - ); |
26 |
| - getTarballs = (dependencies: |
27 |
| - builtins.concatLists (lib.attrsets.mapAttrsToList (name: spec: getTarball spec) dependencies) |
28 |
| - ); |
29 |
| - tarballs = getTarballs dependencies; |
30 |
| - command = builtins.concatStringsSep "\n" tarballs; |
31 |
| -in |
32 |
| - runCommand "cache" {buildInputs = [coreutils];} '' |
33 |
| - ${command} |
34 |
| - '' |
| 55 | + "mkdir -p ${folderPath} && cd ${folderPath} && ln -sf ${tarball} ${fileName}" |
| 56 | + ; |
| 57 | + |
| 58 | + # flat list of all dependency specs |
| 59 | + dependencies = getFlatSpecs packageLock.dependencies; |
| 60 | + |
| 61 | + # flat list of all tarballs |
| 62 | + tarballs = builtins.map getTarballFromSpec dependencies; |
| 63 | + |
| 64 | + # an index of all tarballs by their base64 hash |
| 65 | + tarballsByBase64Hash = builtins.listToAttrs (builtins.map (tarball: {name = tarball.base64hash; value = tarball;}) tarballs); |
| 66 | + |
| 67 | + # an index of all patched tarballs by their base64 hash |
| 68 | + patchedTarballs = lib.attrsets.mapAttrs (hash: patch: patchTarball {tarball = tarballsByBase64Hash."${hash}"; inherit patch;}) patches; |
| 69 | + |
| 70 | + # all commands to create the cache |
| 71 | + commands = builtins.concatStringsSep "\n" (builtins.map getCommandFromTarball (builtins.map (tarball: if builtins.hasAttr tarball.base64hash patchedTarballs then patchedTarballs."${tarball.base64hash}" else tarball) tarballs)); |
| 72 | +in { |
| 73 | + cache = runCommand "cache" {buildInputs = [coreutils];} '' |
| 74 | + ${commands} |
| 75 | + ''; |
| 76 | + |
| 77 | + # when we patch packages, we also need to return a new package-lock.json because the hashes changed |
| 78 | + packageLock = writeText "package-lock.json" ( builtins.foldl' (json: hash: builtins.replaceStrings [hash] [patchedTarballs."${hash}".base64hash] json) json (builtins.attrNames patches)); |
| 79 | +} |
0 commit comments