Skip to content
This repository has been archived by the owner on Jan 15, 2020. It is now read-only.

Review for buildNpmPackage #2

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions pkgs/buildNpmPackage/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{ stdenvNoCC, writeShellScriptBin, writeText, stdenv, fetchurl, makeWrapper, nodejs-10_x }:
with stdenv.lib;
let
inherit (builtins) fromJSON toJSON split;

depsToFetches = deps: concatMap depToFetch (attrValues deps);
depFetchOwn = { resolved, integrity, ... }: let
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't you also getting non-production dependencies here?

ssri = split "-" integrity; # standard subresource integrity
hashType = head ssri;
hash = elemAt ssri 2;
in nameValuePair resolved (fetchurl {
url = resolved;
"${hashType}" = hash;
});

depToFetch = args @ { resolved ? null, dependencies ? {}, ... }:
(optional (resolved != null) (depFetchOwn args)) ++ (depsToFetches dependencies);

npmCacheInput = lock: writeText "npm-cache-input.json" (toJSON (listToAttrs (depToFetch lock)));

patchShebangs = writeShellScriptBin "patchShebangs.sh" ''
set -e
source ${stdenvNoCC}/setup
patchShebangs "$@"
'';

shellWrap = writeShellScriptBin "npm-shell-wrap.sh" ''
set -e
if [ ! -e .shebangs_patched ]; then
${patchShebangs}/bin/patchShebangs.sh .
touch .shebangs_patched
fi
exec bash "$@"
'';
in
args @ { lockfile, src, buildInputs ? [], npmFlags ? [], ... }:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is lockfile set?

let
lock = fromJSON (readFile lockfile);
in
stdenv.mkDerivation ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stdenv.mkDerivation ({ /* ... */ }) -> stdenv.mkDerivation { /* ... */ }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is stdenv.mkDerivation ({ ... } // { ... } // { ... })

inherit (lock) version;
name = "${lock.name}-${lock.version}";

XDG_CONFIG_DIRS = ".";
NO_UPDATE_NOTIFIER = true;
preBuildPhases = [ "npmCachePhase" ];
preInstallPhases = [ "npmPackPhase" ];
installJavascript = true;
npmCachePhase = ''
node ${./mkcache.js} ${npmCacheInput lock}
'';
buildPhase = ''
runHook preBuild
npm ci $npmFlags
runHook postBuild
'';
# make a package .tgz (no way around it)
npmPackPhase = ''
npm prune --production $npmFlags
npm pack --ignore-scripts $npmFlags
'';
# unpack the .tgz into output directory and add npm wrapper
installPhase = ''
mkdir -p $out/bin
tar xzvf ./${lock.name}-${lock.version}.tgz -C $out --strip-components=1
Copy link
Contributor

@obfusk obfusk Oct 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use ${name} (= "${lock.name}-${lock.version}") here.

if [ "$installJavascript" -eq "1" ]; then
cp -R node_modules $out/
makeWrapper ${nodejs-10_x}/bin/npm $out/bin/npm --run "cd $out"
fi
'';
} // args // {
buildInputs = [ nodejs-10_x makeWrapper ] ++ buildInputs;
npmFlags = [ "--cache=./npm-cache" "--offline" "--script-shell=${shellWrap}/bin/npm-shell-wrap.sh" ] ++ npmFlags;
})
38 changes: 38 additions & 0 deletions pkgs/buildNpmPackage/mkcache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const assert = require('assert')
const fs = require('fs')
const path = require('path')
const {promisify} = require('util')

// find pacote from npm dependencies
module.paths.push(path.join(process.argv[0], "../../lib/node_modules/npm/node_modules"))
const pacote = require('pacote')
lukateras marked this conversation as resolved.
Show resolved Hide resolved

function traverseDeps(pkg, fn) {
Object.values(pkg.dependencies).forEach(dep => {
if (dep.resolved && dep.integrity) fn(dep)
if (dep.dependencies) traverseDeps(dep, fn)
})
}

async function main(lockfile, nix, cache) {
var promises = Object.keys(nix).map(async function(url) {
var tar = nix[url]
const manifest = await pacote.manifest(tar, {offline: true, cache})
return [url, manifest._integrity]
})
var hashes = new Map(await Promise.all(promises))
traverseDeps(lockfile, dep => {
if (dep.integrity.startsWith("sha1-")) {
assert(hashes.has(dep.resolved))
dep.integrity = hashes.get(dep.resolved)
obfusk marked this conversation as resolved.
Show resolved Hide resolved
}
else {
assert(dep.integrity == hashes.get(dep.resolved))
}
})
await promisify(fs.writeFile)("./package-lock.json", JSON.stringify(lock, null, 4))
obfusk marked this conversation as resolved.
Show resolved Hide resolved
}
const lock = JSON.parse(fs.readFileSync('./package-lock.json', 'utf8'))
const nix_pkgs = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'))
main(lock, nix_pkgs, './npm-cache/_cacache')

2 changes: 2 additions & 0 deletions pkgs/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ in
rev = "76dc0f06d21f6063cb7b7d2291b8623da24affa9";
}) {};

buildNpmPackage = callPackage ./buildNpmPackage {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nicer if this were a separate repo, I don't think it's right to commit JS code into a closure which is supposed to mostly point to stuff.


buildMacOSApp = callPackage (fetchGit {
url = https://github.com/serokell/nix-macos-app;
rev = "192f3c22b4270be84aef9176fdf52a41d0d85b32";
Expand Down