From 67b1265fb3d38ead5a57fee838405a2d997777c2 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Tue, 11 Dec 2018 21:18:22 +0000 Subject: [PATCH 1/3] bundlerEnv: ensure dependencies always included Suppose I have a Gemfile like this: source "https://rubygems.org" gem "actioncable" gem "websocket-driver", group: :test The gemset.nix generated by Bundix 2.4.1 will set ActionCable's groups to [ "default" ], and websocket-driver's to [ "test" ]. This means that the generated bundlerEnv wouldn't include websocket-driver unless the test group was included, even though it's required by the default group. This is arguably a bug in Bundix (websocket-driver's groups should probably be [ "default" "test" ] or just [ "default" ]), but there's no reason bundlerEnv should omit dependencies even given such an input -- it won't necessarily come from Bundix, and it would be good for bundlerEnv to do the right thing. To fix this, filterGemset is now a recursive function, that adds dependencies of gems in the group to the filtered gemset until it stabilises on the gems that match the required groups, and all of their recursive dependencies. --- lib/attrsets.nix | 9 +++++++++ lib/default.nix | 4 ++-- lib/fixed-points.nix | 10 ++++++++++ .../ruby-modules/bundled-common/functions.nix | 20 +++++++++++++++++-- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/lib/attrsets.nix b/lib/attrsets.nix index 2a1b866dbc5..d374d229f59 100644 --- a/lib/attrsets.nix +++ b/lib/attrsets.nix @@ -94,6 +94,15 @@ rec { attrValues = builtins.attrValues or (attrs: attrVals (attrNames attrs) attrs); + /* Given a set of attribute names, return the set of the corresponding + attributes from the given set. + + Example: + getAttrs [ "a" "b" ] { a = 1; b = 2; c = 3; } + => { a = 1; b = 2; } + */ + getAttrs = names: attrs: genAttrs names (name: attrs.${name}); + /* Collect each attribute named `attr' from a list of attribute sets. Sets that don't contain the named attribute are ignored. diff --git a/lib/default.nix b/lib/default.nix index d7a05fec833..e4e3e7d325a 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -61,10 +61,10 @@ let boolToString mergeAttrs flip mapNullable inNixShell min max importJSON warn info nixpkgsVersion version mod compare splitByAndCompare functionArgs setFunctionArgs isFunction; - inherit (fixedPoints) fix fix' extends composeExtensions + inherit (fixedPoints) fix fix' converge extends composeExtensions makeExtensible makeExtensibleWithCustomName; inherit (attrsets) attrByPath hasAttrByPath setAttrByPath - getAttrFromPath attrVals attrValues catAttrs filterAttrs + getAttrFromPath attrVals attrValues getAttrs catAttrs filterAttrs filterAttrsRecursive foldAttrs collect nameValuePair mapAttrs mapAttrs' mapAttrsToList mapAttrsRecursive mapAttrsRecursiveCond genAttrs isDerivation toDerivation optionalAttrs diff --git a/lib/fixed-points.nix b/lib/fixed-points.nix index 7169c46fcbb..2f818c88de5 100644 --- a/lib/fixed-points.nix +++ b/lib/fixed-points.nix @@ -24,6 +24,16 @@ rec { # for a concrete example. fix' = f: let x = f x // { __unfix__ = f; }; in x; + # Return the fixpoint that `f` converges to when called recursively, starting + # with the input `x`. + # + # nix-repl> converge (x: x / 2) 16 + # 0 + converge = f: x: + if (f x) == x + then x + else converge f (f x); + # Modify the contents of an explicitly recursive attribute set in a way that # honors `self`-references. This is accomplished with a function # diff --git a/pkgs/development/ruby-modules/bundled-common/functions.nix b/pkgs/development/ruby-modules/bundled-common/functions.nix index b8e666a92f9..81e2fa35329 100644 --- a/pkgs/development/ruby-modules/bundled-common/functions.nix +++ b/pkgs/development/ruby-modules/bundled-common/functions.nix @@ -1,5 +1,9 @@ { lib, gemConfig, ... }: -rec { + +let + inherit (lib) attrValues concatMap converge filterAttrs getAttrs; + +in rec { bundlerFiles = { gemfile ? null , lockfile ? null @@ -22,7 +26,19 @@ rec { else gemset; }; - filterGemset = {ruby, groups,...}: gemset: lib.filterAttrs (name: attrs: platformMatches ruby attrs && groupMatches groups attrs) gemset; + filterGemset = { ruby, groups, ... }: gemset: + let + platformGems = filterAttrs (_: platformMatches ruby) gemset; + directlyMatchingGems = filterAttrs (_: groupMatches groups) platformGems; + + expandDependencies = gems: + let + depNames = concatMap (gem: gem.dependencies or []) (attrValues gems); + deps = getAttrs depNames platformGems; + in + gems // deps; + in + converge expandDependencies directlyMatchingGems; platformMatches = {rubyEngine, version, ...}: attrs: ( !(attrs ? "platforms") || From 83a2d993d401a65d98784df5cc9fb1ea72e2f2a2 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Tue, 11 Dec 2018 21:19:32 +0000 Subject: [PATCH 2/3] bundlerEnv: include all groups by default This wasn't really an issue until the latest minor release of Bundix (2.4), because prior to then Bundix didn't emit group attributes, and so this functionality of bundlerEnv wasn't really used. However, it is now apparent that a better default for bundlerEnv would be to include all gem groups by default, not just the default group. This matches the behavior of Bundler, and makes more sense, because the default group alone isn't necessarily useful for anything -- consider a Rails app with production, development, and test groups. It has the additional benefit of being backwards compatible with how this would have worked before the Bundix update. --- .../ruby-modules/bundled-common/default.nix | 2 +- .../ruby-modules/bundled-common/functions.nix | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/development/ruby-modules/bundled-common/default.nix b/pkgs/development/ruby-modules/bundled-common/default.nix index 1b64456fb20..c31dfb63b3c 100644 --- a/pkgs/development/ruby-modules/bundled-common/default.nix +++ b/pkgs/development/ruby-modules/bundled-common/default.nix @@ -17,7 +17,7 @@ , postBuild ? null , document ? [] , meta ? {} -, groups ? ["default"] +, groups ? null , ignoreCollisions ? false , buildInputs ? [] , ... diff --git a/pkgs/development/ruby-modules/bundled-common/functions.nix b/pkgs/development/ruby-modules/bundled-common/functions.nix index 81e2fa35329..f477b2bee7c 100644 --- a/pkgs/development/ruby-modules/bundled-common/functions.nix +++ b/pkgs/development/ruby-modules/bundled-common/functions.nix @@ -1,7 +1,8 @@ { lib, gemConfig, ... }: let - inherit (lib) attrValues concatMap converge filterAttrs getAttrs; + inherit (lib) attrValues concatMap converge filterAttrs getAttrs + intersectLists; in rec { bundlerFiles = { @@ -49,10 +50,9 @@ in rec { ) attrs.platforms ); - groupMatches = groups: attrs: ( - !(attrs ? "groups") || - builtins.any (gemGroup: builtins.any (group: group == gemGroup) groups) attrs.groups - ); + groupMatches = groups: attrs: + groups == null || !(attrs ? "groups") || + (intersectLists groups attrs.groups) != []; applyGemConfigs = attrs: (if gemConfig ? "${attrs.gemName}" From 26053cae74248f77bf34fb2a3f0efa0ec2af822f Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Tue, 11 Dec 2018 21:22:45 +0000 Subject: [PATCH 3/3] bundlerEnv: always include default gems "default" isn't really a group, it's more the absence of one. With Bundler, this means that a gem should be installed unconditionally, regardless of which groups are specified. It doesn't really make sense to allow these gems to be omitted from a bundlerEnv. --- pkgs/development/ruby-modules/bundled-common/functions.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/development/ruby-modules/bundled-common/functions.nix b/pkgs/development/ruby-modules/bundled-common/functions.nix index f477b2bee7c..6324f27e9fa 100644 --- a/pkgs/development/ruby-modules/bundled-common/functions.nix +++ b/pkgs/development/ruby-modules/bundled-common/functions.nix @@ -52,7 +52,7 @@ in rec { groupMatches = groups: attrs: groups == null || !(attrs ? "groups") || - (intersectLists groups attrs.groups) != []; + (intersectLists (groups ++ [ "default" ]) attrs.groups) != []; applyGemConfigs = attrs: (if gemConfig ? "${attrs.gemName}"