Skip to content

Commit 036c2f8

Browse files
committed
added support for native modules and patching tarballs, added node-sass example
1 parent c8d2da4 commit 036c2f8

File tree

13 files changed

+1596
-79
lines changed

13 files changed

+1596
-79
lines changed

buildCache.nix

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,79 @@
1-
{lib,runCommand,coreutils,fetchurl}:
2-
{packageLockPath}:
1+
{lib,runCommand,coreutils,fetchurl,unixtools,writeText}:
2+
{packageLockPath,patches}:
33
let
44
json = builtins.readFile packageLockPath;
5-
dependencies = (builtins.fromJSON json).dependencies;
5+
packageLock = builtins.fromJSON json;
66
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:
834
let
935
parts = lib.strings.splitString "-" spec.integrity;
36+
in rec {
1037
algorithm = builtins.elemAt parts 0;
1138
base64hash = builtins.elemAt parts 1;
1239
hexHash = base64toHex base64hash;
1340
tarball = fetchurl {
1441
url = spec.resolved;
1542
"${algorithm}" = hexHash;
1643
};
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
1850
folder1 = builtins.substring 0 2 hexHash;
1951
folder2 = builtins.substring 2 2 hexHash;
2052
fileName = builtins.substring 4 ((builtins.stringLength hexHash)-4) hexHash;
2153
folderPath = "$out/_cacache/content-v2/${algorithm}/${folder1}/${folder2}";
22-
command = "mkdir -p ${folderPath} && cd ${folderPath} && ln -sf ${tarball} ${fileName}";
2354
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+
}

buildNodeModules.nix

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
1-
{lib,stdenv,runCommand,coreutils,fetchurl,nodejs}:
2-
{directory,buildInputs?[]}:
1+
{lib,stdenv,runCommand,coreutils,fetchurl,nodejs,writeText,unixtools}:
2+
{directory,buildInputs?[],preInstallScript?"",postInstallScript?"",patches?{}}:
33
let
4-
buildCache = import ./buildCache.nix {inherit lib runCommand coreutils fetchurl;};
5-
cache = buildCache {packageLockPath = "${directory}/package-lock.json";};
6-
derivation = stdenv.mkDerivation {
4+
# import the function to build the cache
5+
buildCache = import ./buildCache.nix {inherit lib runCommand coreutils fetchurl writeText unixtools;};
6+
7+
# build the cache
8+
cache = buildCache {packageLockPath = "${directory}/package-lock.json"; inherit patches;};
9+
10+
# get the Node.js sources needed by native modules
11+
nodeSources = runCommand "node-sources" {} ''
12+
tar --no-same-owner --no-same-permissions -xf ${nodejs.src}
13+
mv node-* $out
14+
'';
15+
16+
# build the node_modules directory
17+
derivation = builtins.trace "${cache.packageLock}" stdenv.mkDerivation {
718
pname = "node_modules";
819
version = "";
920
buildInputs=[nodejs coreutils]++buildInputs;
1021
phases = ["buildPhase" "patchPhase" "fixupPhase" ];
11-
buildPhase = ''
12-
cp ${directory}/package-lock.json ./package-lock.json
22+
buildPhase = ''
23+
cp ${cache.packageLock} package-lock.json
1324
cp ${directory}/package.json ./package.json
14-
npm ci --cache ${cache}
25+
${preInstallScript}
26+
npm ci --cache ${cache.cache} --nodedir=${nodeSources}
27+
${postInstallScript}
1528
mkdir $out
1629
mv node_modules $out/node_modules/
1730
'';

examples/express-app/default.nix

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1-
{nodejs,node_modules,runCommand}:
2-
runCommand "express-app" {buildInputs=[nodejs];} ''
3-
mkdir $out
4-
cp ${./index.js} $out/index.js
5-
ln -s "${node_modules}" $out/node_modules
6-
mkdir $out/bin
7-
echo '#!node
8-
require("../index.js")' > $out/bin/express-app
9-
chmod +x $out/bin/express-app
10-
''
1+
{pkgs?import<nixpkgs> {}}: with pkgs;
2+
let
3+
buildNodeModules = callPackage ../../buildNodeModules.nix {};
4+
in rec {
5+
node_modules = buildNodeModules {directory = ./.;};
6+
app = runCommand "express-app" {buildInputs=[nodejs];} ''
7+
mkdir $out
8+
cp ${./index.js} $out/index.js
9+
ln -s "${node_modules}" $out/node_modules
10+
mkdir $out/bin
11+
echo '#!node
12+
require("../index.js")' > $out/bin/express-app
13+
chmod +x $out/bin/express-app
14+
'';
15+
shell = mkShell {
16+
shellHook = ''
17+
ln -sf ${node_modules} node_modules
18+
'';
19+
};
20+
}

examples/express-app/node_modules.nix

Lines changed: 0 additions & 1 deletion
This file was deleted.

examples/express-app/shell.nix

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1 @@
1-
with import <nixpkgs> {};
2-
3-
let
4-
buildNodeModules = callPackage ../../buildNodeModules.nix {};
5-
node_modules = import ./node_modules.nix {inherit buildNodeModules;};
6-
express-app = callPackage ./default.nix {inherit nodejs node_modules;};
7-
in
8-
mkShell {
9-
buildInputs = [
10-
express-app
11-
];
12-
shellHook = ''
13-
ln -s ${node_modules} node_modules
14-
'';
15-
}
1+
(import ./default.nix {}).shell

examples/sass-lib/default.nix

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{pkgs?import<nixpkgs> {}}:with pkgs;
2+
let
3+
buildNodeModules = callPackage ../../buildNodeModules.nix {};
4+
in rec {
5+
node_modules = buildNodeModules {directory = ./.;buildInputs=[python];};
6+
lib = runCommand "sass-lib" {} ''
7+
export PATH=$PATH:${node_modules}/.bin
8+
node-sass ${./src} -o $out
9+
'';
10+
shell = mkShell {
11+
shellHook = ''
12+
ln -sf ${node_modules} node_modules
13+
'';
14+
};
15+
}

0 commit comments

Comments
 (0)