diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index b76ce7f92984..80a88a2cd3ff 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -4924,6 +4924,12 @@ githubId = 1202012; name = "Ignat Loskutov"; }; + lostnet = { + email = "lost.networking@gmail.com"; + github = "lostnet"; + githubId = 1422781; + name = "Will Young"; + }; louisdk1 = { email = "louis@louis.dk"; github = "louisdk1"; diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix index 26fc8fb402e6..1da3a5b27eb1 100644 --- a/nixos/modules/installer/tools/tools.nix +++ b/nixos/modules/installer/tools/tools.nix @@ -22,7 +22,11 @@ let src = ./nixos-install.sh; inherit (pkgs) runtimeShell; nix = config.nix.package.out; - path = makeBinPath [ pkgs.nixUnstable nixos-enter ]; + path = makeBinPath [ + pkgs.nixUnstable + pkgs.jq + nixos-enter + ]; }; nixos-rebuild = diff --git a/nixos/modules/services/databases/couchdb.nix b/nixos/modules/services/databases/couchdb.nix index 53224db1d896..f385331e8782 100644 --- a/nixos/modules/services/databases/couchdb.nix +++ b/nixos/modules/services/databases/couchdb.nix @@ -11,7 +11,13 @@ let database_dir = ${cfg.databaseDir} uri_file = ${cfg.uriFile} view_index_dir = ${cfg.viewIndexDir} - '' + (if useVersion2 then + '' + (if cfg.adminPass != null then + '' + [admins] + ${cfg.adminUser} = ${cfg.adminPass} + '' else + '' + '') + (if useVersion2 then '' [chttpd] '' else @@ -54,6 +60,23 @@ in { ''; }; + adminUser = mkOption { + type = types.str; + default = "admin"; + description = '' + Couchdb (i.e. fauxton) account with permission for all dbs and + tasks. + ''; + }; + + adminPass = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Couchdb (i.e. fauxton) account with permission for all dbs and + tasks. + ''; + }; user = mkOption { type = types.str; diff --git a/nixos/modules/services/databases/victoriametrics.nix b/nixos/modules/services/databases/victoriametrics.nix index cb6bf8508fb6..0af5d2adf372 100644 --- a/nixos/modules/services/databases/victoriametrics.nix +++ b/nixos/modules/services/databases/victoriametrics.nix @@ -49,8 +49,8 @@ let cfg = config.services.victoriametrics; in ExecStart = '' ${cfg.package}/bin/victoria-metrics \ -storageDataPath=/var/lib/victoriametrics \ - -httpListenAddr ${cfg.listenAddress} - -retentionPeriod ${toString cfg.retentionPeriod} + -httpListenAddr ${cfg.listenAddress} \ + -retentionPeriod ${toString cfg.retentionPeriod} \ ${lib.escapeShellArgs cfg.extraOptions} ''; }; diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix index 08c912e0fcd4..7d3c601d6cd5 100644 --- a/nixos/modules/services/network-filesystems/samba.nix +++ b/nixos/modules/services/network-filesystems/samba.nix @@ -248,7 +248,7 @@ in }; security.pam.services.samba = {}; - + environment.systemPackages = [ config.services.samba.package ]; }) ]; diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix index 23ab7f2ae433..eae70a57c781 100644 --- a/nixos/modules/services/x11/display-managers/gdm.nix +++ b/nixos/modules/services/x11/display-managers/gdm.nix @@ -64,13 +64,9 @@ in services.xserver.displayManager.gdm = { - enable = mkEnableOption '' - GDM, the GNOME Display Manager - ''; + enable = mkEnableOption "GDM, the GNOME Display Manager"; - debug = mkEnableOption '' - debugging messages in GDM - ''; + debug = mkEnableOption "debugging messages in GDM"; # Auto login options specific to GDM autoLogin.delay = mkOption { diff --git a/nixos/tests/couchdb.nix b/nixos/tests/couchdb.nix index 10e95701acdb..57b79e29b433 100644 --- a/nixos/tests/couchdb.nix +++ b/nixos/tests/couchdb.nix @@ -1,4 +1,19 @@ -import ./make-test-python.nix ({ pkgs, lib, ...}: +let + + makeNode = couchpkg: user: passwd: + { pkgs, ... } : + + { environment.systemPackages = with pkgs; [ jq ]; + services.couchdb.enable = true; + services.couchdb.package = couchpkg; + services.couchdb.adminUser = user; + services.couchdb.adminPass = passwd; + }; + testuser = "testadmin"; + testpass = "cowabunga"; + testlogin = "${testuser}:${testpass}@"; + +in import ./make-test-python.nix ({ pkgs, lib, ...}: with lib; @@ -9,26 +24,15 @@ with lib; }; nodes = { - couchdb1 = - { pkgs, ... }: - - { environment.systemPackages = with pkgs; [ jq ]; - services.couchdb.enable = true; - }; - - couchdb2 = - { pkgs, ... }: - - { environment.systemPackages = with pkgs; [ jq ]; - services.couchdb.enable = true; - services.couchdb.package = pkgs.couchdb2; - }; + couchdb1 = makeNode pkgs.couchdb testuser testpass; + couchdb2 = makeNode pkgs.couchdb2 testuser testpass; + couchdb3 = makeNode pkgs.couchdb3 testuser testpass; }; testScript = let - curlJqCheck = action: path: jqexpr: result: + curlJqCheck = login: action: path: jqexpr: result: pkgs.writeScript "curl-jq-check-${action}-${path}.sh" '' - RESULT=$(curl -X ${action} http://127.0.0.1:5984/${path} | jq -r '${jqexpr}') + RESULT=$(curl -X ${action} http://${login}127.0.0.1:5984/${path} | jq -r '${jqexpr}') echo $RESULT >&2 if [ "$RESULT" != "${result}" ]; then exit 1 @@ -39,38 +43,56 @@ with lib; couchdb1.wait_for_unit("couchdb.service") couchdb1.wait_until_succeeds( - "${curlJqCheck "GET" "" ".couchdb" "Welcome"}" + "${curlJqCheck "" "GET" "" ".couchdb" "Welcome"}" ) couchdb1.wait_until_succeeds( - "${curlJqCheck "GET" "_all_dbs" ". | length" "2"}" + "${curlJqCheck "" "GET" "_all_dbs" ". | length" "2"}" ) - couchdb1.succeed("${curlJqCheck "PUT" "foo" ".ok" "true"}") + couchdb1.succeed("${curlJqCheck testlogin "PUT" "foo" ".ok" "true"}") couchdb1.succeed( - "${curlJqCheck "GET" "_all_dbs" ". | length" "3"}" + "${curlJqCheck "" "GET" "_all_dbs" ". | length" "3"}" ) couchdb1.succeed( - "${curlJqCheck "DELETE" "foo" ".ok" "true"}" + "${curlJqCheck testlogin "DELETE" "foo" ".ok" "true"}" ) couchdb1.succeed( - "${curlJqCheck "GET" "_all_dbs" ". | length" "2"}" + "${curlJqCheck "" "GET" "_all_dbs" ". | length" "2"}" ) couchdb2.wait_for_unit("couchdb.service") couchdb2.wait_until_succeeds( - "${curlJqCheck "GET" "" ".couchdb" "Welcome"}" + "${curlJqCheck "" "GET" "" ".couchdb" "Welcome"}" ) couchdb2.wait_until_succeeds( - "${curlJqCheck "GET" "_all_dbs" ". | length" "0"}" + "${curlJqCheck "" "GET" "_all_dbs" ". | length" "0"}" ) - couchdb2.succeed("${curlJqCheck "PUT" "foo" ".ok" "true"}") + couchdb2.succeed("${curlJqCheck testlogin "PUT" "foo" ".ok" "true"}") couchdb2.succeed( - "${curlJqCheck "GET" "_all_dbs" ". | length" "1"}" + "${curlJqCheck "" "GET" "_all_dbs" ". | length" "1"}" ) couchdb2.succeed( - "${curlJqCheck "DELETE" "foo" ".ok" "true"}" + "${curlJqCheck testlogin "DELETE" "foo" ".ok" "true"}" ) couchdb2.succeed( - "${curlJqCheck "GET" "_all_dbs" ". | length" "0"}" + "${curlJqCheck "" "GET" "_all_dbs" ". | length" "0"}" + ) + + couchdb3.wait_for_unit("couchdb.service") + couchdb3.wait_until_succeeds( + "${curlJqCheck testlogin "GET" "" ".couchdb" "Welcome"}" + ) + couchdb3.wait_until_succeeds( + "${curlJqCheck testlogin "GET" "_all_dbs" ". | length" "0"}" + ) + couchdb3.succeed("${curlJqCheck testlogin "PUT" "foo" ".ok" "true"}") + couchdb3.succeed( + "${curlJqCheck testlogin "GET" "_all_dbs" ". | length" "1"}" + ) + couchdb3.succeed( + "${curlJqCheck testlogin "DELETE" "foo" ".ok" "true"}" + ) + couchdb3.succeed( + "${curlJqCheck testlogin "GET" "_all_dbs" ". | length" "0"}" ) ''; }) diff --git a/pkgs/applications/audio/jackmix/default.nix b/pkgs/applications/audio/jackmix/default.nix index ad01a3ff76ec..405217675a3e 100644 --- a/pkgs/applications/audio/jackmix/default.nix +++ b/pkgs/applications/audio/jackmix/default.nix @@ -1,4 +1,4 @@ -{ stdenv, fetchurl, pkgconfig, sconsPackages, qt4, lash, libjack2, jack ? libjack2 }: +{ stdenv, fetchurl, pkgconfig, sconsPackages, qt4, lash, libjack2, jack ? libjack2, alsaLib }: stdenv.mkDerivation { name = "jackmix-0.5.2"; @@ -14,6 +14,7 @@ stdenv.mkDerivation { qt4 lash jack + alsaLib ]; installPhase = '' diff --git a/pkgs/applications/version-management/commitizen/default.nix b/pkgs/applications/version-management/commitizen/default.nix new file mode 100644 index 000000000000..c673fd6d0c9a --- /dev/null +++ b/pkgs/applications/version-management/commitizen/default.nix @@ -0,0 +1,17 @@ +{ pkgs, nodejs, stdenv, lib, ... }: + +let + nodePackages = import ./node-composition.nix { + inherit pkgs nodejs; + inherit (stdenv.hostPlatform) system; + }; +in +nodePackages.commitizen.override { + meta = with lib; { + description = "The commitizen command line utility"; + homepage = "https://commitizen.github.io/cz-cli"; + maintainers = with maintainers; [ freezeboy ]; + license = licenses.mit; + platforms = platforms.linux ++ platforms.darwin; + }; +} diff --git a/pkgs/applications/version-management/commitizen/generate-dependencies.sh b/pkgs/applications/version-management/commitizen/generate-dependencies.sh new file mode 100755 index 000000000000..7111289d2909 --- /dev/null +++ b/pkgs/applications/version-management/commitizen/generate-dependencies.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env nix-shell +#! nix-shell -i bash -p nodePackages.node2nix + +node2nix \ + --node-env node-env.nix \ + --development \ + --input package.json \ + --output node-packages.nix \ + --composition node-composition.nix diff --git a/pkgs/applications/version-management/commitizen/node-composition.nix b/pkgs/applications/version-management/commitizen/node-composition.nix new file mode 100644 index 000000000000..c970861a86f0 --- /dev/null +++ b/pkgs/applications/version-management/commitizen/node-composition.nix @@ -0,0 +1,17 @@ +# This file has been generated by node2nix 1.8.0. Do not edit! + +{pkgs ? import { + inherit system; + }, system ? builtins.currentSystem, nodejs ? pkgs."nodejs-12_x"}: + +let + nodeEnv = import ./node-env.nix { + inherit (pkgs) stdenv python2 utillinux runCommand writeTextFile; + inherit nodejs; + libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null; + }; +in +import ./node-packages.nix { + inherit (pkgs) fetchurl fetchgit; + inherit nodeEnv; +} \ No newline at end of file diff --git a/pkgs/applications/version-management/commitizen/node-env.nix b/pkgs/applications/version-management/commitizen/node-env.nix new file mode 100644 index 000000000000..e1abf5304935 --- /dev/null +++ b/pkgs/applications/version-management/commitizen/node-env.nix @@ -0,0 +1,542 @@ +# This file originates from node2nix + +{stdenv, nodejs, python2, utillinux, libtool, runCommand, writeTextFile}: + +let + python = if nodejs ? python then nodejs.python else python2; + + # Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise + tarWrapper = runCommand "tarWrapper" {} '' + mkdir -p $out/bin + + cat > $out/bin/tar <> $out/nix-support/hydra-build-products + ''; + }; + + includeDependencies = {dependencies}: + stdenv.lib.optionalString (dependencies != []) + (stdenv.lib.concatMapStrings (dependency: + '' + # Bundle the dependencies of the package + mkdir -p node_modules + cd node_modules + + # Only include dependencies if they don't exist. They may also be bundled in the package. + if [ ! -e "${dependency.name}" ] + then + ${composePackage dependency} + fi + + cd .. + '' + ) dependencies); + + # Recursively composes the dependencies of a package + composePackage = { name, packageName, src, dependencies ? [], ... }@args: + builtins.addErrorContext "while evaluating node package '${packageName}'" '' + DIR=$(pwd) + cd $TMPDIR + + unpackFile ${src} + + # Make the base dir in which the target dependency resides first + mkdir -p "$(dirname "$DIR/${packageName}")" + + if [ -f "${src}" ] + then + # Figure out what directory has been unpacked + packageDir="$(find . -maxdepth 1 -type d | tail -1)" + + # Restore write permissions to make building work + find "$packageDir" -type d -exec chmod u+x {} \; + chmod -R u+w "$packageDir" + + # Move the extracted tarball into the output folder + mv "$packageDir" "$DIR/${packageName}" + elif [ -d "${src}" ] + then + # Get a stripped name (without hash) of the source directory. + # On old nixpkgs it's already set internally. + if [ -z "$strippedName" ] + then + strippedName="$(stripHash ${src})" + fi + + # Restore write permissions to make building work + chmod -R u+w "$strippedName" + + # Move the extracted directory into the output folder + mv "$strippedName" "$DIR/${packageName}" + fi + + # Unset the stripped name to not confuse the next unpack step + unset strippedName + + # Include the dependencies of the package + cd "$DIR/${packageName}" + ${includeDependencies { inherit dependencies; }} + cd .. + ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + ''; + + pinpointDependencies = {dependencies, production}: + let + pinpointDependenciesFromPackageJSON = writeTextFile { + name = "pinpointDependencies.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + function resolveDependencyVersion(location, name) { + if(location == process.env['NIX_STORE']) { + return null; + } else { + var dependencyPackageJSON = path.join(location, "node_modules", name, "package.json"); + + if(fs.existsSync(dependencyPackageJSON)) { + var dependencyPackageObj = JSON.parse(fs.readFileSync(dependencyPackageJSON)); + + if(dependencyPackageObj.name == name) { + return dependencyPackageObj.version; + } + } else { + return resolveDependencyVersion(path.resolve(location, ".."), name); + } + } + } + + function replaceDependencies(dependencies) { + if(typeof dependencies == "object" && dependencies !== null) { + for(var dependency in dependencies) { + var resolvedVersion = resolveDependencyVersion(process.cwd(), dependency); + + if(resolvedVersion === null) { + process.stderr.write("WARNING: cannot pinpoint dependency: "+dependency+", context: "+process.cwd()+"\n"); + } else { + dependencies[dependency] = resolvedVersion; + } + } + } + } + + /* Read the package.json configuration */ + var packageObj = JSON.parse(fs.readFileSync('./package.json')); + + /* Pinpoint all dependencies */ + replaceDependencies(packageObj.dependencies); + if(process.argv[2] == "development") { + replaceDependencies(packageObj.devDependencies); + } + replaceDependencies(packageObj.optionalDependencies); + + /* Write the fixed package.json file */ + fs.writeFileSync("package.json", JSON.stringify(packageObj, null, 2)); + ''; + }; + in + '' + node ${pinpointDependenciesFromPackageJSON} ${if production then "production" else "development"} + + ${stdenv.lib.optionalString (dependencies != []) + '' + if [ -d node_modules ] + then + cd node_modules + ${stdenv.lib.concatMapStrings (dependency: pinpointDependenciesOfPackage dependency) dependencies} + cd .. + fi + ''} + ''; + + # Recursively traverses all dependencies of a package and pinpoints all + # dependencies in the package.json file to the versions that are actually + # being used. + + pinpointDependenciesOfPackage = { packageName, dependencies ? [], production ? true, ... }@args: + '' + if [ -d "${packageName}" ] + then + cd "${packageName}" + ${pinpointDependencies { inherit dependencies production; }} + cd .. + ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + fi + ''; + + # Extract the Node.js source code which is used to compile packages with + # native bindings + nodeSources = runCommand "node-sources" {} '' + tar --no-same-owner --no-same-permissions -xf ${nodejs.src} + mv node-* $out + ''; + + # Script that adds _integrity fields to all package.json files to prevent NPM from consulting the cache (that is empty) + addIntegrityFieldsScript = writeTextFile { + name = "addintegrityfields.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + function augmentDependencies(baseDir, dependencies) { + for(var dependencyName in dependencies) { + var dependency = dependencies[dependencyName]; + + // Open package.json and augment metadata fields + var packageJSONDir = path.join(baseDir, "node_modules", dependencyName); + var packageJSONPath = path.join(packageJSONDir, "package.json"); + + if(fs.existsSync(packageJSONPath)) { // Only augment packages that exist. Sometimes we may have production installs in which development dependencies can be ignored + console.log("Adding metadata fields to: "+packageJSONPath); + var packageObj = JSON.parse(fs.readFileSync(packageJSONPath)); + + if(dependency.integrity) { + packageObj["_integrity"] = dependency.integrity; + } else { + packageObj["_integrity"] = "sha1-000000000000000000000000000="; // When no _integrity string has been provided (e.g. by Git dependencies), add a dummy one. It does not seem to harm and it bypasses downloads. + } + + if(dependency.resolved) { + packageObj["_resolved"] = dependency.resolved; // Adopt the resolved property if one has been provided + } else { + packageObj["_resolved"] = dependency.version; // Set the resolved version to the version identifier. This prevents NPM from cloning Git repositories. + } + + if(dependency.from !== undefined) { // Adopt from property if one has been provided + packageObj["_from"] = dependency.from; + } + + fs.writeFileSync(packageJSONPath, JSON.stringify(packageObj, null, 2)); + } + + // Augment transitive dependencies + if(dependency.dependencies !== undefined) { + augmentDependencies(packageJSONDir, dependency.dependencies); + } + } + } + + if(fs.existsSync("./package-lock.json")) { + var packageLock = JSON.parse(fs.readFileSync("./package-lock.json")); + + if(packageLock.lockfileVersion !== 1) { + process.stderr.write("Sorry, I only understand lock file version 1!\n"); + process.exit(1); + } + + if(packageLock.dependencies !== undefined) { + augmentDependencies(".", packageLock.dependencies); + } + } + ''; + }; + + # Reconstructs a package-lock file from the node_modules/ folder structure and package.json files with dummy sha1 hashes + reconstructPackageLock = writeTextFile { + name = "addintegrityfields.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + var packageObj = JSON.parse(fs.readFileSync("package.json")); + + var lockObj = { + name: packageObj.name, + version: packageObj.version, + lockfileVersion: 1, + requires: true, + dependencies: {} + }; + + function augmentPackageJSON(filePath, dependencies) { + var packageJSON = path.join(filePath, "package.json"); + if(fs.existsSync(packageJSON)) { + var packageObj = JSON.parse(fs.readFileSync(packageJSON)); + dependencies[packageObj.name] = { + version: packageObj.version, + integrity: "sha1-000000000000000000000000000=", + dependencies: {} + }; + processDependencies(path.join(filePath, "node_modules"), dependencies[packageObj.name].dependencies); + } + } + + function processDependencies(dir, dependencies) { + if(fs.existsSync(dir)) { + var files = fs.readdirSync(dir); + + files.forEach(function(entry) { + var filePath = path.join(dir, entry); + var stats = fs.statSync(filePath); + + if(stats.isDirectory()) { + if(entry.substr(0, 1) == "@") { + // When we encounter a namespace folder, augment all packages belonging to the scope + var pkgFiles = fs.readdirSync(filePath); + + pkgFiles.forEach(function(entry) { + if(stats.isDirectory()) { + var pkgFilePath = path.join(filePath, entry); + augmentPackageJSON(pkgFilePath, dependencies); + } + }); + } else { + augmentPackageJSON(filePath, dependencies); + } + } + }); + } + } + + processDependencies("node_modules", lockObj.dependencies); + + fs.writeFileSync("package-lock.json", JSON.stringify(lockObj, null, 2)); + ''; + }; + + prepareAndInvokeNPM = {packageName, bypassCache, reconstructLock, npmFlags, production}: + let + forceOfflineFlag = if bypassCache then "--offline" else "--registry http://www.example.com"; + in + '' + # Pinpoint the versions of all dependencies to the ones that are actually being used + echo "pinpointing versions of dependencies..." + source $pinpointDependenciesScriptPath + + # Patch the shebangs of the bundled modules to prevent them from + # calling executables outside the Nix store as much as possible + patchShebangs . + + # Deploy the Node.js package by running npm install. Since the + # dependencies have been provided already by ourselves, it should not + # attempt to install them again, which is good, because we want to make + # it Nix's responsibility. If it needs to install any dependencies + # anyway (e.g. because the dependency parameters are + # incomplete/incorrect), it fails. + # + # The other responsibilities of NPM are kept -- version checks, build + # steps, postprocessing etc. + + export HOME=$TMPDIR + cd "${packageName}" + runHook preRebuild + + ${stdenv.lib.optionalString bypassCache '' + ${stdenv.lib.optionalString reconstructLock '' + if [ -f package-lock.json ] + then + echo "WARNING: Reconstruct lock option enabled, but a lock file already exists!" + echo "This will most likely result in version mismatches! We will remove the lock file and regenerate it!" + rm package-lock.json + else + echo "No package-lock.json file found, reconstructing..." + fi + + node ${reconstructPackageLock} + ''} + + node ${addIntegrityFieldsScript} + ''} + + npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} rebuild + + if [ "''${dontNpmInstall-}" != "1" ] + then + # NPM tries to download packages even when they already exist if npm-shrinkwrap is used. + rm -f npm-shrinkwrap.json + + npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} install + fi + ''; + + # Builds and composes an NPM package including all its dependencies + buildNodePackage = + { name + , packageName + , version + , dependencies ? [] + , buildInputs ? [] + , production ? true + , npmFlags ? "" + , dontNpmInstall ? false + , bypassCache ? false + , reconstructLock ? false + , preRebuild ? "" + , dontStrip ? true + , unpackPhase ? "true" + , buildPhase ? "true" + , ... }@args: + + let + extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "preRebuild" "unpackPhase" "buildPhase" ]; + in + stdenv.mkDerivation ({ + name = "node_${name}-${version}"; + buildInputs = [ tarWrapper python nodejs ] + ++ stdenv.lib.optional (stdenv.isLinux) utillinux + ++ stdenv.lib.optional (stdenv.isDarwin) libtool + ++ buildInputs; + + inherit nodejs; + + inherit dontStrip; # Stripping may fail a build for some package deployments + inherit dontNpmInstall preRebuild unpackPhase buildPhase; + + compositionScript = composePackage args; + pinpointDependenciesScript = pinpointDependenciesOfPackage args; + + passAsFile = [ "compositionScript" "pinpointDependenciesScript" ]; + + installPhase = '' + # Create and enter a root node_modules/ folder + mkdir -p $out/lib/node_modules + cd $out/lib/node_modules + + # Compose the package and all its dependencies + source $compositionScriptPath + + ${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }} + + # Create symlink to the deployed executable folder, if applicable + if [ -d "$out/lib/node_modules/.bin" ] + then + ln -s $out/lib/node_modules/.bin $out/bin + fi + + # Create symlinks to the deployed manual page folders, if applicable + if [ -d "$out/lib/node_modules/${packageName}/man" ] + then + mkdir -p $out/share + for dir in "$out/lib/node_modules/${packageName}/man/"* + do + mkdir -p $out/share/man/$(basename "$dir") + for page in "$dir"/* + do + ln -s $page $out/share/man/$(basename "$dir") + done + done + fi + + # Run post install hook, if provided + runHook postInstall + ''; + } // extraArgs); + + # Builds a development shell + buildNodeShell = + { name + , packageName + , version + , src + , dependencies ? [] + , buildInputs ? [] + , production ? true + , npmFlags ? "" + , dontNpmInstall ? false + , bypassCache ? false + , reconstructLock ? false + , dontStrip ? true + , unpackPhase ? "true" + , buildPhase ? "true" + , ... }@args: + + let + extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" ]; + + nodeDependencies = stdenv.mkDerivation ({ + name = "node-dependencies-${name}-${version}"; + + buildInputs = [ tarWrapper python nodejs ] + ++ stdenv.lib.optional (stdenv.isLinux) utillinux + ++ stdenv.lib.optional (stdenv.isDarwin) libtool + ++ buildInputs; + + inherit dontStrip; # Stripping may fail a build for some package deployments + inherit dontNpmInstall unpackPhase buildPhase; + + includeScript = includeDependencies { inherit dependencies; }; + pinpointDependenciesScript = pinpointDependenciesOfPackage args; + + passAsFile = [ "includeScript" "pinpointDependenciesScript" ]; + + installPhase = '' + mkdir -p $out/${packageName} + cd $out/${packageName} + + source $includeScriptPath + + # Create fake package.json to make the npm commands work properly + cp ${src}/package.json . + chmod 644 package.json + ${stdenv.lib.optionalString bypassCache '' + if [ -f ${src}/package-lock.json ] + then + cp ${src}/package-lock.json . + fi + ''} + + # Go to the parent folder to make sure that all packages are pinpointed + cd .. + ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + + ${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }} + + # Expose the executables that were installed + cd .. + ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + + mv ${packageName} lib + ln -s $out/lib/node_modules/.bin $out/bin + ''; + } // extraArgs); + in + stdenv.mkDerivation { + name = "node-shell-${name}-${version}"; + + buildInputs = [ python nodejs ] ++ stdenv.lib.optional (stdenv.isLinux) utillinux ++ buildInputs; + buildCommand = '' + mkdir -p $out/bin + cat > $out/bin/shell < https://github.com/hasura/ci-info-hs.git ci-info = self.callPackage ../misc/haskell/hasura/ci-info {}; diff --git a/pkgs/development/libraries/exiv2/default.nix b/pkgs/development/libraries/exiv2/default.nix index b79415894b3e..a9c66709dba6 100644 --- a/pkgs/development/libraries/exiv2/default.nix +++ b/pkgs/development/libraries/exiv2/default.nix @@ -17,9 +17,7 @@ stdenv.mkDerivation rec { pname = "exiv2"; version = "0.27.3"; - # Disabled since splitting the outputs leads to issues, see - # https://github.com/NixOS/nixpkgs/pull/97161#issuecomment-689426419 - # outputs = [ "out" "dev" "doc" "man" ]; + outputs = [ "out" "dev" "doc" "man" ]; src = fetchFromGitHub { owner = "exiv2"; @@ -39,10 +37,14 @@ stdenv.mkDerivation rec { # Use correct paths with multiple outputs # https://github.com/Exiv2/exiv2/pull/1275 (fetchpatch { - name = "cmake-fix-aarch64.patch"; url = "https://github.com/Exiv2/exiv2/commit/48f2c9dbbacc0ef84c8ebf4cb1a603327f0b8750.patch"; sha256 = "vjB3+Ld4c/2LT7nq6uatYwfHTh+HeU5QFPFXuNLpIPA="; }) + # https://github.com/Exiv2/exiv2/pull/1294 + (fetchpatch { + url = "https://github.com/Exiv2/exiv2/commit/306c8a6fd4ddd70e76043ab255734720829a57e8.patch"; + sha256 = "0D/omxYxBPGUu3uSErlf48dc6Ukwc2cEN9/J3e7a9eU="; + }) ]; nativeBuildInputs = [ diff --git a/pkgs/development/libraries/qtstyleplugins/default.nix b/pkgs/development/libraries/qtstyleplugins/default.nix index ac5755f7c3d9..1970dd183c1c 100644 --- a/pkgs/development/libraries/qtstyleplugins/default.nix +++ b/pkgs/development/libraries/qtstyleplugins/default.nix @@ -10,6 +10,8 @@ mkDerivation { sha256 = "085wyn85nrmzr8nv5zv7fi2kqf8rp1gnd30h72s30j55xvhmxvmy"; }; + patches = [ ./fix-build-against-Qt-5.15.patch ]; + nativeBuildInputs = [ pkgconfig qmake ]; buildInputs = [ gtk2 ]; diff --git a/pkgs/development/libraries/qtstyleplugins/fix-build-against-Qt-5.15.patch b/pkgs/development/libraries/qtstyleplugins/fix-build-against-Qt-5.15.patch new file mode 100644 index 000000000000..7eb744158204 --- /dev/null +++ b/pkgs/development/libraries/qtstyleplugins/fix-build-against-Qt-5.15.patch @@ -0,0 +1,44 @@ +From 335dbece103e2cbf6c7cf819ab6672c2956b17b3 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt +Date: Thu, 28 May 2020 12:35:42 +0200 +Subject: [PATCH] fix build against Qt 5.15 + +With 0a93db4d82c051164923a10e4382b12de9049b45 ("Unify application +palette handling between QGuiApplication and QApplication") +QApplicationPrivate::setSystemPalette is no longer used and necessary. +--- + src/plugins/styles/gtk2/qgtkstyle.cpp | 2 ++ + src/plugins/styles/gtk2/qgtkstyle_p.cpp | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/src/plugins/styles/gtk2/qgtkstyle.cpp b/src/plugins/styles/gtk2/qgtkstyle.cpp +index 36169c9..2544593 100644 +--- a/src/plugins/styles/gtk2/qgtkstyle.cpp ++++ b/src/plugins/styles/gtk2/qgtkstyle.cpp +@@ -440,7 +440,9 @@ void QGtkStyle::polish(QApplication *app) + // not supported as these should be entirely determined by + // current Gtk settings + if (app->desktopSettingsAware() && d->isThemeAvailable()) { ++#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + QApplicationPrivate::setSystemPalette(standardPalette()); ++#endif + QApplicationPrivate::setSystemFont(d->getThemeFont()); + d->applyCustomPaletteHash(); + if (!d->isKDE4Session()) +diff --git a/src/plugins/styles/gtk2/qgtkstyle_p.cpp b/src/plugins/styles/gtk2/qgtkstyle_p.cpp +index e57b3d8..e71beb0 100644 +--- a/src/plugins/styles/gtk2/qgtkstyle_p.cpp ++++ b/src/plugins/styles/gtk2/qgtkstyle_p.cpp +@@ -508,7 +508,9 @@ void QGtkStyleUpdateScheduler::updateTheme() + if (oldTheme != QGtkStylePrivate::getThemeName()) { + oldTheme = QGtkStylePrivate::getThemeName(); + QPalette newPalette = qApp->style()->standardPalette(); ++#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + QApplicationPrivate::setSystemPalette(newPalette); ++#endif + QApplication::setPalette(newPalette); + if (!QGtkStylePrivate::instances.isEmpty()) { + QGtkStylePrivate::instances.last()->initGtkWidgets(); +-- +2.26.2 + diff --git a/pkgs/development/tools/buildah/default.nix b/pkgs/development/tools/buildah/default.nix index 7f1d90291f44..d93ec54b3e3a 100644 --- a/pkgs/development/tools/buildah/default.nix +++ b/pkgs/development/tools/buildah/default.nix @@ -14,13 +14,13 @@ buildGoModule rec { pname = "buildah"; - version = "1.16.0"; + version = "1.16.1"; src = fetchFromGitHub { owner = "containers"; repo = "buildah"; - rev = "V${version}"; - sha256 = "0z9fblxm3pk2jqw7h36clmj8k4k39n6ab536lyh0rp6p7hz5a988"; + rev = "v${version}"; + sha256 = "0nndm936g0i18ly6395y5s4h1f6cfbg602cvlg7c6w007f2j15hq"; }; outputs = [ "out" "man" ]; diff --git a/pkgs/development/tools/haskell/haskell-language-server/default.nix b/pkgs/development/tools/haskell/haskell-language-server/default.nix index c29ed6ce07ef..10cfa57f9147 100644 --- a/pkgs/development/tools/haskell/haskell-language-server/default.nix +++ b/pkgs/development/tools/haskell/haskell-language-server/default.nix @@ -14,8 +14,8 @@ mkDerivation { version = "0.4.0.0"; src = fetchgit { url = "https://github.com/haskell/haskell-language-server.git"; - sha256 = "157bsq6i824bl6krw7znp0byd8ibaqsq7mfwnkl741dmrflsxpa9"; - rev = "cb861b878ae01911b066182ff0d8080050c3b2d6"; + sha256 = "1fh9k9b3880m6ql4i10yn2yanskk9xhrakrrddqvainhcf2ik8hl"; + rev = "c4576992f443a9abe48ffcfa0e2d2b9bce15d7ae"; fetchSubmodules = true; }; isLibrary = true; diff --git a/pkgs/development/tools/haskell/haskell-language-server/hls-brittany.nix b/pkgs/development/tools/haskell/haskell-language-server/hls-brittany.nix new file mode 100644 index 000000000000..0519cf42dd8c --- /dev/null +++ b/pkgs/development/tools/haskell/haskell-language-server/hls-brittany.nix @@ -0,0 +1,36 @@ +{ mkDerivation, aeson, base, butcher, bytestring, cmdargs +, containers, czipwith, data-tree-print, deepseq, directory, extra +, fetchgit, filepath, ghc, ghc-boot-th, ghc-exactprint, ghc-paths +, hspec, monad-memo, mtl, multistate, parsec, pretty, random, safe +, semigroups, stdenv, strict, syb, text, transformers, uniplate +, unsafe, yaml +}: +mkDerivation { + pname = "brittany"; + version = "0.12.1.1"; + src = fetchgit { + url = "https://github.com/bubba/brittany"; + sha256 = "1rkk09f8750qykrmkqfqbh44dbx1p8aq1caznxxlw8zqfvx39cxl"; + rev = "c59655f10d5ad295c2481537fc8abf0a297d9d1c"; + fetchSubmodules = true; + }; + isLibrary = true; + isExecutable = true; + libraryHaskellDepends = [ + aeson base butcher bytestring cmdargs containers czipwith + data-tree-print deepseq directory extra filepath ghc ghc-boot-th + ghc-exactprint ghc-paths monad-memo mtl multistate pretty random + safe semigroups strict syb text transformers uniplate unsafe yaml + ]; + executableHaskellDepends = [ base ]; + testHaskellDepends = [ + aeson base butcher bytestring cmdargs containers czipwith + data-tree-print deepseq directory extra filepath ghc ghc-boot-th + ghc-exactprint ghc-paths hspec monad-memo mtl multistate parsec + pretty safe semigroups strict syb text transformers uniplate unsafe + yaml + ]; + homepage = "https://github.com/lspitzner/brittany/"; + description = "Haskell source code formatter"; + license = stdenv.lib.licenses.agpl3; +} diff --git a/pkgs/development/tools/haskell/haskell-language-server/hls-ghcide.nix b/pkgs/development/tools/haskell/haskell-language-server/hls-ghcide.nix index c6a9b31fb738..af38312a3616 100644 --- a/pkgs/development/tools/haskell/haskell-language-server/hls-ghcide.nix +++ b/pkgs/development/tools/haskell/haskell-language-server/hls-ghcide.nix @@ -5,21 +5,20 @@ , ghc-boot-th, ghc-check, ghc-paths, ghc-typelits-knownnat, gitrev , haddock-library, hashable, haskell-lsp, haskell-lsp-types , hie-bios, hslogger, lens, lsp-test, mtl, network-uri -, opentelemetry, optparse-applicative, prettyprinter -, prettyprinter-ansi-terminal, process, QuickCheck -, quickcheck-instances, regex-tdfa, rope-utf16-splay, safe -, safe-exceptions, shake, sorted-list, stdenv, stm, syb, tasty -, tasty-expected-failure, tasty-hunit, tasty-quickcheck -, tasty-rerun, text, time, transformers, unix, unordered-containers -, utf8-string, yaml +, optparse-applicative, prettyprinter, prettyprinter-ansi-terminal +, process, QuickCheck, quickcheck-instances, regex-tdfa +, rope-utf16-splay, safe, safe-exceptions, shake, sorted-list +, stdenv, stm, syb, tasty, tasty-expected-failure, tasty-hunit +, tasty-quickcheck, tasty-rerun, text, time, transformers, unix +, unordered-containers, utf8-string, yaml }: mkDerivation { pname = "ghcide"; - version = "0.2.0"; + version = "0.3.0"; src = fetchgit { url = "https://github.com/haskell/ghcide"; - sha256 = "1zq7ngaak8il91a309rl51dghzasnk4m2sm3av6d93cyqyra1hfc"; - rev = "078e3d3c0d319f83841ccbcdc60ff5f0e243f6be"; + sha256 = "15v3g3i5v0xbq50lfvl4bv3rx01nixiqx02sddqi5lj2idgmg24g"; + rev = "96cf8c53d0bdc16d3d2cd0559b74962593ce6dc5"; fetchSubmodules = true; }; isLibrary = true; @@ -29,10 +28,10 @@ mkDerivation { containers cryptohash-sha1 data-default deepseq directory extra filepath fuzzy ghc ghc-boot ghc-boot-th ghc-check ghc-paths haddock-library hashable haskell-lsp haskell-lsp-types hie-bios - hslogger mtl network-uri opentelemetry prettyprinter - prettyprinter-ansi-terminal regex-tdfa rope-utf16-splay safe - safe-exceptions shake sorted-list stm syb text time transformers - unix unordered-containers utf8-string + hslogger mtl network-uri prettyprinter prettyprinter-ansi-terminal + regex-tdfa rope-utf16-splay safe safe-exceptions shake sorted-list + stm syb text time transformers unix unordered-containers + utf8-string ]; executableHaskellDepends = [ aeson base bytestring containers data-default directory extra @@ -52,7 +51,7 @@ mkDerivation { aeson base Chart Chart-diagrams diagrams diagrams-svg directory extra filepath shake text yaml ]; - homepage = "https://github.com/digital-asset/ghcide#readme"; + homepage = "https://github.com/haskell/ghcide#readme"; description = "The core of an IDE"; license = stdenv.lib.licenses.asl20; } diff --git a/pkgs/development/tools/haskell/haskell-language-server/update.sh b/pkgs/development/tools/haskell/haskell-language-server/update.sh index 2f2741e9a49a..fd22a80126d9 100755 --- a/pkgs/development/tools/haskell/haskell-language-server/update.sh +++ b/pkgs/development/tools/haskell/haskell-language-server/update.sh @@ -7,6 +7,8 @@ # Note that you should always try building haskell-language-server after updating it here, since # some of the overrides in pkgs/development/haskell/configuration-nix.nix may # need to be updated/changed. +# +# Remember to split out different updates into multiple commits set -eo pipefail @@ -31,6 +33,22 @@ echo "Running cabal2nix and outputting to ${ghcide_derivation_file}..." cabal2nix --revision "$ghcide_new_version" "https://github.com/haskell/ghcide" > "$ghcide_derivation_file" +# =========================== +# HLS maintainer's Brittany fork +# =========================== + +# brittany derivation created with cabal2nix. +brittany_derivation_file="${script_dir}/hls-brittany.nix" + +# This is the current revision of the brittany fork in Nixpkgs. +brittany_old_version="$(sed -En 's/.*\bversion = "(.*?)".*/\1/p' "$brittany_derivation_file")" + +brittany_new_version=$(curl --silent "https://api.github.com/repos/bubba/brittany/commits/ghc-8.10.1" | jq '.sha' --raw-output) + +echo "Updating haskell-language-server's brittany from old version $brittany_old_version to new version $brittany_new_version." +echo "Running cabal2nix and outputting to ${brittany_derivation_file}..." + +cabal2nix --revision "$brittany_new_version" "https://github.com/bubba/brittany" > "$brittany_derivation_file" # =========================== # HLS diff --git a/pkgs/development/web/lucky-cli/default.nix b/pkgs/development/web/lucky-cli/default.nix index 4c983d0931e3..8add66bf998a 100644 --- a/pkgs/development/web/lucky-cli/default.nix +++ b/pkgs/development/web/lucky-cli/default.nix @@ -2,13 +2,13 @@ crystal.buildCrystalPackage rec { pname = "lucky-cli"; - version = "0.23.0"; + version = "0.23.1"; src = fetchFromGitHub { owner = "luckyframework"; repo = "lucky_cli"; rev = "v${version}"; - sha256 = "1qggbczrnrfjba6ipzjkqp6ni4rjc79pxy3vhgd7nq88ipa1sygk"; + sha256 = "0xj7mcmz1rxv3ff530q8c5y1y7hccsmr8azk9nhmrk1q355vnxfw"; }; # the integration tests will try to clone a remote repos diff --git a/pkgs/development/web/newman/default.nix b/pkgs/development/web/newman/default.nix new file mode 100644 index 000000000000..ed4041ab76f0 --- /dev/null +++ b/pkgs/development/web/newman/default.nix @@ -0,0 +1,19 @@ +{ pkgs, nodejs, stdenv, lib, ... }: + +let + + packageName = with lib; concatStrings (map (entry: (concatStrings (mapAttrsToList (key: value: "${key}-${value}") entry))) (importJSON ./package.json)); + + nodePackages = import ./node-composition.nix { + inherit pkgs nodejs; + inherit (stdenv.hostPlatform) system; + }; +in +nodePackages.newman.override { + meta = with lib; { + homepage = "https://www.getpostman.com"; + description = "Newman is a command-line collection runner for Postman"; + maintainers = with maintainers; [ freezeboy ]; + license = licenses.asl20; + }; +} diff --git a/pkgs/development/web/newman/generate-dependencies.sh b/pkgs/development/web/newman/generate-dependencies.sh new file mode 100755 index 000000000000..7111289d2909 --- /dev/null +++ b/pkgs/development/web/newman/generate-dependencies.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env nix-shell +#! nix-shell -i bash -p nodePackages.node2nix + +node2nix \ + --node-env node-env.nix \ + --development \ + --input package.json \ + --output node-packages.nix \ + --composition node-composition.nix diff --git a/pkgs/development/web/newman/node-composition.nix b/pkgs/development/web/newman/node-composition.nix new file mode 100644 index 000000000000..c970861a86f0 --- /dev/null +++ b/pkgs/development/web/newman/node-composition.nix @@ -0,0 +1,17 @@ +# This file has been generated by node2nix 1.8.0. Do not edit! + +{pkgs ? import { + inherit system; + }, system ? builtins.currentSystem, nodejs ? pkgs."nodejs-12_x"}: + +let + nodeEnv = import ./node-env.nix { + inherit (pkgs) stdenv python2 utillinux runCommand writeTextFile; + inherit nodejs; + libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null; + }; +in +import ./node-packages.nix { + inherit (pkgs) fetchurl fetchgit; + inherit nodeEnv; +} \ No newline at end of file diff --git a/pkgs/development/web/newman/node-env.nix b/pkgs/development/web/newman/node-env.nix new file mode 100644 index 000000000000..e1abf5304935 --- /dev/null +++ b/pkgs/development/web/newman/node-env.nix @@ -0,0 +1,542 @@ +# This file originates from node2nix + +{stdenv, nodejs, python2, utillinux, libtool, runCommand, writeTextFile}: + +let + python = if nodejs ? python then nodejs.python else python2; + + # Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise + tarWrapper = runCommand "tarWrapper" {} '' + mkdir -p $out/bin + + cat > $out/bin/tar <> $out/nix-support/hydra-build-products + ''; + }; + + includeDependencies = {dependencies}: + stdenv.lib.optionalString (dependencies != []) + (stdenv.lib.concatMapStrings (dependency: + '' + # Bundle the dependencies of the package + mkdir -p node_modules + cd node_modules + + # Only include dependencies if they don't exist. They may also be bundled in the package. + if [ ! -e "${dependency.name}" ] + then + ${composePackage dependency} + fi + + cd .. + '' + ) dependencies); + + # Recursively composes the dependencies of a package + composePackage = { name, packageName, src, dependencies ? [], ... }@args: + builtins.addErrorContext "while evaluating node package '${packageName}'" '' + DIR=$(pwd) + cd $TMPDIR + + unpackFile ${src} + + # Make the base dir in which the target dependency resides first + mkdir -p "$(dirname "$DIR/${packageName}")" + + if [ -f "${src}" ] + then + # Figure out what directory has been unpacked + packageDir="$(find . -maxdepth 1 -type d | tail -1)" + + # Restore write permissions to make building work + find "$packageDir" -type d -exec chmod u+x {} \; + chmod -R u+w "$packageDir" + + # Move the extracted tarball into the output folder + mv "$packageDir" "$DIR/${packageName}" + elif [ -d "${src}" ] + then + # Get a stripped name (without hash) of the source directory. + # On old nixpkgs it's already set internally. + if [ -z "$strippedName" ] + then + strippedName="$(stripHash ${src})" + fi + + # Restore write permissions to make building work + chmod -R u+w "$strippedName" + + # Move the extracted directory into the output folder + mv "$strippedName" "$DIR/${packageName}" + fi + + # Unset the stripped name to not confuse the next unpack step + unset strippedName + + # Include the dependencies of the package + cd "$DIR/${packageName}" + ${includeDependencies { inherit dependencies; }} + cd .. + ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + ''; + + pinpointDependencies = {dependencies, production}: + let + pinpointDependenciesFromPackageJSON = writeTextFile { + name = "pinpointDependencies.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + function resolveDependencyVersion(location, name) { + if(location == process.env['NIX_STORE']) { + return null; + } else { + var dependencyPackageJSON = path.join(location, "node_modules", name, "package.json"); + + if(fs.existsSync(dependencyPackageJSON)) { + var dependencyPackageObj = JSON.parse(fs.readFileSync(dependencyPackageJSON)); + + if(dependencyPackageObj.name == name) { + return dependencyPackageObj.version; + } + } else { + return resolveDependencyVersion(path.resolve(location, ".."), name); + } + } + } + + function replaceDependencies(dependencies) { + if(typeof dependencies == "object" && dependencies !== null) { + for(var dependency in dependencies) { + var resolvedVersion = resolveDependencyVersion(process.cwd(), dependency); + + if(resolvedVersion === null) { + process.stderr.write("WARNING: cannot pinpoint dependency: "+dependency+", context: "+process.cwd()+"\n"); + } else { + dependencies[dependency] = resolvedVersion; + } + } + } + } + + /* Read the package.json configuration */ + var packageObj = JSON.parse(fs.readFileSync('./package.json')); + + /* Pinpoint all dependencies */ + replaceDependencies(packageObj.dependencies); + if(process.argv[2] == "development") { + replaceDependencies(packageObj.devDependencies); + } + replaceDependencies(packageObj.optionalDependencies); + + /* Write the fixed package.json file */ + fs.writeFileSync("package.json", JSON.stringify(packageObj, null, 2)); + ''; + }; + in + '' + node ${pinpointDependenciesFromPackageJSON} ${if production then "production" else "development"} + + ${stdenv.lib.optionalString (dependencies != []) + '' + if [ -d node_modules ] + then + cd node_modules + ${stdenv.lib.concatMapStrings (dependency: pinpointDependenciesOfPackage dependency) dependencies} + cd .. + fi + ''} + ''; + + # Recursively traverses all dependencies of a package and pinpoints all + # dependencies in the package.json file to the versions that are actually + # being used. + + pinpointDependenciesOfPackage = { packageName, dependencies ? [], production ? true, ... }@args: + '' + if [ -d "${packageName}" ] + then + cd "${packageName}" + ${pinpointDependencies { inherit dependencies production; }} + cd .. + ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + fi + ''; + + # Extract the Node.js source code which is used to compile packages with + # native bindings + nodeSources = runCommand "node-sources" {} '' + tar --no-same-owner --no-same-permissions -xf ${nodejs.src} + mv node-* $out + ''; + + # Script that adds _integrity fields to all package.json files to prevent NPM from consulting the cache (that is empty) + addIntegrityFieldsScript = writeTextFile { + name = "addintegrityfields.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + function augmentDependencies(baseDir, dependencies) { + for(var dependencyName in dependencies) { + var dependency = dependencies[dependencyName]; + + // Open package.json and augment metadata fields + var packageJSONDir = path.join(baseDir, "node_modules", dependencyName); + var packageJSONPath = path.join(packageJSONDir, "package.json"); + + if(fs.existsSync(packageJSONPath)) { // Only augment packages that exist. Sometimes we may have production installs in which development dependencies can be ignored + console.log("Adding metadata fields to: "+packageJSONPath); + var packageObj = JSON.parse(fs.readFileSync(packageJSONPath)); + + if(dependency.integrity) { + packageObj["_integrity"] = dependency.integrity; + } else { + packageObj["_integrity"] = "sha1-000000000000000000000000000="; // When no _integrity string has been provided (e.g. by Git dependencies), add a dummy one. It does not seem to harm and it bypasses downloads. + } + + if(dependency.resolved) { + packageObj["_resolved"] = dependency.resolved; // Adopt the resolved property if one has been provided + } else { + packageObj["_resolved"] = dependency.version; // Set the resolved version to the version identifier. This prevents NPM from cloning Git repositories. + } + + if(dependency.from !== undefined) { // Adopt from property if one has been provided + packageObj["_from"] = dependency.from; + } + + fs.writeFileSync(packageJSONPath, JSON.stringify(packageObj, null, 2)); + } + + // Augment transitive dependencies + if(dependency.dependencies !== undefined) { + augmentDependencies(packageJSONDir, dependency.dependencies); + } + } + } + + if(fs.existsSync("./package-lock.json")) { + var packageLock = JSON.parse(fs.readFileSync("./package-lock.json")); + + if(packageLock.lockfileVersion !== 1) { + process.stderr.write("Sorry, I only understand lock file version 1!\n"); + process.exit(1); + } + + if(packageLock.dependencies !== undefined) { + augmentDependencies(".", packageLock.dependencies); + } + } + ''; + }; + + # Reconstructs a package-lock file from the node_modules/ folder structure and package.json files with dummy sha1 hashes + reconstructPackageLock = writeTextFile { + name = "addintegrityfields.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + var packageObj = JSON.parse(fs.readFileSync("package.json")); + + var lockObj = { + name: packageObj.name, + version: packageObj.version, + lockfileVersion: 1, + requires: true, + dependencies: {} + }; + + function augmentPackageJSON(filePath, dependencies) { + var packageJSON = path.join(filePath, "package.json"); + if(fs.existsSync(packageJSON)) { + var packageObj = JSON.parse(fs.readFileSync(packageJSON)); + dependencies[packageObj.name] = { + version: packageObj.version, + integrity: "sha1-000000000000000000000000000=", + dependencies: {} + }; + processDependencies(path.join(filePath, "node_modules"), dependencies[packageObj.name].dependencies); + } + } + + function processDependencies(dir, dependencies) { + if(fs.existsSync(dir)) { + var files = fs.readdirSync(dir); + + files.forEach(function(entry) { + var filePath = path.join(dir, entry); + var stats = fs.statSync(filePath); + + if(stats.isDirectory()) { + if(entry.substr(0, 1) == "@") { + // When we encounter a namespace folder, augment all packages belonging to the scope + var pkgFiles = fs.readdirSync(filePath); + + pkgFiles.forEach(function(entry) { + if(stats.isDirectory()) { + var pkgFilePath = path.join(filePath, entry); + augmentPackageJSON(pkgFilePath, dependencies); + } + }); + } else { + augmentPackageJSON(filePath, dependencies); + } + } + }); + } + } + + processDependencies("node_modules", lockObj.dependencies); + + fs.writeFileSync("package-lock.json", JSON.stringify(lockObj, null, 2)); + ''; + }; + + prepareAndInvokeNPM = {packageName, bypassCache, reconstructLock, npmFlags, production}: + let + forceOfflineFlag = if bypassCache then "--offline" else "--registry http://www.example.com"; + in + '' + # Pinpoint the versions of all dependencies to the ones that are actually being used + echo "pinpointing versions of dependencies..." + source $pinpointDependenciesScriptPath + + # Patch the shebangs of the bundled modules to prevent them from + # calling executables outside the Nix store as much as possible + patchShebangs . + + # Deploy the Node.js package by running npm install. Since the + # dependencies have been provided already by ourselves, it should not + # attempt to install them again, which is good, because we want to make + # it Nix's responsibility. If it needs to install any dependencies + # anyway (e.g. because the dependency parameters are + # incomplete/incorrect), it fails. + # + # The other responsibilities of NPM are kept -- version checks, build + # steps, postprocessing etc. + + export HOME=$TMPDIR + cd "${packageName}" + runHook preRebuild + + ${stdenv.lib.optionalString bypassCache '' + ${stdenv.lib.optionalString reconstructLock '' + if [ -f package-lock.json ] + then + echo "WARNING: Reconstruct lock option enabled, but a lock file already exists!" + echo "This will most likely result in version mismatches! We will remove the lock file and regenerate it!" + rm package-lock.json + else + echo "No package-lock.json file found, reconstructing..." + fi + + node ${reconstructPackageLock} + ''} + + node ${addIntegrityFieldsScript} + ''} + + npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} rebuild + + if [ "''${dontNpmInstall-}" != "1" ] + then + # NPM tries to download packages even when they already exist if npm-shrinkwrap is used. + rm -f npm-shrinkwrap.json + + npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} install + fi + ''; + + # Builds and composes an NPM package including all its dependencies + buildNodePackage = + { name + , packageName + , version + , dependencies ? [] + , buildInputs ? [] + , production ? true + , npmFlags ? "" + , dontNpmInstall ? false + , bypassCache ? false + , reconstructLock ? false + , preRebuild ? "" + , dontStrip ? true + , unpackPhase ? "true" + , buildPhase ? "true" + , ... }@args: + + let + extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "preRebuild" "unpackPhase" "buildPhase" ]; + in + stdenv.mkDerivation ({ + name = "node_${name}-${version}"; + buildInputs = [ tarWrapper python nodejs ] + ++ stdenv.lib.optional (stdenv.isLinux) utillinux + ++ stdenv.lib.optional (stdenv.isDarwin) libtool + ++ buildInputs; + + inherit nodejs; + + inherit dontStrip; # Stripping may fail a build for some package deployments + inherit dontNpmInstall preRebuild unpackPhase buildPhase; + + compositionScript = composePackage args; + pinpointDependenciesScript = pinpointDependenciesOfPackage args; + + passAsFile = [ "compositionScript" "pinpointDependenciesScript" ]; + + installPhase = '' + # Create and enter a root node_modules/ folder + mkdir -p $out/lib/node_modules + cd $out/lib/node_modules + + # Compose the package and all its dependencies + source $compositionScriptPath + + ${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }} + + # Create symlink to the deployed executable folder, if applicable + if [ -d "$out/lib/node_modules/.bin" ] + then + ln -s $out/lib/node_modules/.bin $out/bin + fi + + # Create symlinks to the deployed manual page folders, if applicable + if [ -d "$out/lib/node_modules/${packageName}/man" ] + then + mkdir -p $out/share + for dir in "$out/lib/node_modules/${packageName}/man/"* + do + mkdir -p $out/share/man/$(basename "$dir") + for page in "$dir"/* + do + ln -s $page $out/share/man/$(basename "$dir") + done + done + fi + + # Run post install hook, if provided + runHook postInstall + ''; + } // extraArgs); + + # Builds a development shell + buildNodeShell = + { name + , packageName + , version + , src + , dependencies ? [] + , buildInputs ? [] + , production ? true + , npmFlags ? "" + , dontNpmInstall ? false + , bypassCache ? false + , reconstructLock ? false + , dontStrip ? true + , unpackPhase ? "true" + , buildPhase ? "true" + , ... }@args: + + let + extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" ]; + + nodeDependencies = stdenv.mkDerivation ({ + name = "node-dependencies-${name}-${version}"; + + buildInputs = [ tarWrapper python nodejs ] + ++ stdenv.lib.optional (stdenv.isLinux) utillinux + ++ stdenv.lib.optional (stdenv.isDarwin) libtool + ++ buildInputs; + + inherit dontStrip; # Stripping may fail a build for some package deployments + inherit dontNpmInstall unpackPhase buildPhase; + + includeScript = includeDependencies { inherit dependencies; }; + pinpointDependenciesScript = pinpointDependenciesOfPackage args; + + passAsFile = [ "includeScript" "pinpointDependenciesScript" ]; + + installPhase = '' + mkdir -p $out/${packageName} + cd $out/${packageName} + + source $includeScriptPath + + # Create fake package.json to make the npm commands work properly + cp ${src}/package.json . + chmod 644 package.json + ${stdenv.lib.optionalString bypassCache '' + if [ -f ${src}/package-lock.json ] + then + cp ${src}/package-lock.json . + fi + ''} + + # Go to the parent folder to make sure that all packages are pinpointed + cd .. + ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + + ${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }} + + # Expose the executables that were installed + cd .. + ${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + + mv ${packageName} lib + ln -s $out/lib/node_modules/.bin $out/bin + ''; + } // extraArgs); + in + stdenv.mkDerivation { + name = "node-shell-${name}-${version}"; + + buildInputs = [ python nodejs ] ++ stdenv.lib.optional (stdenv.isLinux) utillinux ++ buildInputs; + buildCommand = '' + mkdir -p $out/bin + cat > $out/bin/shell <