Overriding bundler version in bundlerEnv (with a possible fix?)

I’ve been trying to get bundlerEnv to use a newer version of bundler, and have been going down many rabbitholes.

Here’s the bundler.nix that I’d like to use in place of the older bundler version (2.1.4)

{ buildRubyGem, makeWrapper, ruby, coreutils }:
buildRubyGem rec {
  inherit ruby;
  name = "${gemName}-${version}";
  gemName = "bundler";
  version = "2.2.11";
  source.sha256 = "1izx6wsjdm6mnbxazgz1z5qbhwrrisbq0np2nmx4ij6lrqjy18jf";
  dontPatchShebangs = true;

  postFixup = ''
    sed -i -e "s/activate_bin_path/bin_path/g" $out/bin/bundle

    # silence an annoying warning about sudo being needed
    sed -i -e '/if sudo_needed/I,+2 d' $out/lib/ruby/gems/${ruby.version.libDir}/gems/${name}/lib/bundler.rb
  '';
}

One option is to add this as an overlay to override pkgs.bundler, but that seems to cause an awful lot of rebuilds from source for any other packages that happen to use ruby.

So I’ve been trying to override it just for bundlerEnv. It sort of looks like you might just be able to use bundlerEnv.override({ bundler = newBundler }) (https://github.com/NixOS/nixpkgs/blob/11cd34cd592f917bab5f42e2b378ab329dee3bcf/pkgs/development/ruby-modules/bundler-env/default.nix#L1-L3) but as far as I can tell that bundler argument actually does very little. eg if you pass a bad value for bundler it still builds fine:

pkgs.bundlerEnv.override { bundler = 12345; } {
   name = "foo";
   gemset = ./gemset.nix;
}

One possible fix is to change this line: https://github.com/NixOS/nixpkgs/blob/11cd34cd592f917bab5f42e2b378ab329dee3bcf/pkgs/development/ruby-modules/bundler-env/default.nix#L26 as follows:

- basicEnv = (callPackage ../bundled-common {}) (args // { inherit pname name; mainGemName = pname; });
+ basicEnv = (callPackage ../bundled-common { inherit bundler ruby; }) (args // { inherit pname name; mainGemName = pname; });

Can anyone see a good reason that isn’t already the case? I’m basically doing the monkeys-on-typewriters approach to fixing this, I can’t say I’ve got a good understanding on what’s going on with nix/bundlerEnv.

And is there a way of using that newer version of bundler short of getting that fix upstreamed into nixpkgs?

Started a PR for this here: bundlerEnv: Allow overriding bundler by jdelStrother · Pull Request #115582 · NixOS/nixpkgs · GitHub

The following is written chronologically during the hours i spent working on this:

From looking through the code shouldn’t bundlerEnv use the bundler included in the gemset?

I foolishly thought this before trying it out on my own project that uses bundlerEnv and bundix. I have still not gotten it to work that well.

If the gemset contains a specification for bundler bundlerEnv will use that but if you use bundix it will delete that specification the next time you run it, unless you manually add bundler to the Gemfile.lock file, which will be deleted by bundler the next time you run it.

I guess you could just add bundler gemset specification directly to the derivation instead of having it in gemset.nix… success! Testing for updating bundler · griff/whitelist@70201b5 · GitHub

The short answer is that you can get bundlerEnv to use a custom version of bundler like this:

bundlerEnv {
   name = "foo";
   gemset = (import ./gemset.nix) // {
     bundler = {
       groups = ["default"];
       platforms = [];
       source = {
         remotes = ["https://rubygems.org"];
         sha256 = "1izx6wsjdm6mnbxazgz1z5qbhwrrisbq0np2nmx4ij6lrqjy18jf";
         type = "gem";
       };
       version = "2.2.11";
     };
   };
}

Interesting, thanks for the workaround / fix. Do you think merging into the gemset like that is the best approach, or would something like this be easier? bundlerEnv: Allow overriding bundler by jdelStrother · Pull Request #115582 · NixOS/nixpkgs · GitHub .

I confess I’m pretty confused what effect the bundler argument has in the original code, it seems like it’s maybe leftover from a previous version.

After looking some more at the code and commit history for bundlerEnv it is clear that there are some gaps left from when bundlerEnv was split into multiple components. The code that determines the version of bundler used is all over the place.

basicEnv will use the default bundler or if found the bundler in the gemset and bundlerEnv is just basicEnv most of the time. But if pname attribute is set on bundlerEnv then it will always use the bundler it is called with for generating bin stubs.

So that means that if you use pname and want to use a different bundler you have to specify it in two places:

bundlerEnv.override { bundler = newBundler; } {
   name = "foo";
   gemset = (import ./gemset.nix) // {
     bundler = {
       groups = ["default"];
       platforms = [];
       source = {
         remotes = ["https://rubygems.org"];
         sha256 = "1izx6wsjdm6mnbxazgz1z5qbhwrrisbq0np2nmx4ij6lrqjy18jf";
         type = "gem";
       };
       version = "2.2.11";
     };
   };
}

My personal opinion is that either there should be a top level bundlerCommon that bundlerApp and bundlerEnv take as argument or the callPackage ../bundled-common {} should be replaced with import ../bundled-common { inherit stdenv runCommand ruby lib rsync defaultGemConfig buildRubyGem buildEnv makeWrapper bundler; } so that you have the option of overriding all the arguments to ../bundled-common.

As for the gemset bundler I think bundlerEnv should be changed to use the bundler found by basicEnv so that it will use the gemset bundler if someone else uses that functionality.

I will try and put these changes into a PR later today.

3 Likes

I love the idea of an easier-to-change version of bundler. Is there any chance that PR was created and/or merged?

@StevenH bundlerEnv: Allow overriding bundler by jdelStrother · Pull Request #115582 · NixOS/nixpkgs · GitHub is in unstable now, so something like this should work:

  ruby = pkgs.ruby_2_7;
  bundler = pkgs.buildRubyGem rec {
    inherit ruby;
    name = "${gemName}-${version}";
    gemName = "bundler";
    version = "2.2.11";
    source = {
      remotes = ["https://rubygems.org"];
      sha256 = "1izx6wsjdm6mnbxazgz1z5qbhwrrisbq0np2nmx4ij6lrqjy18jf";
      type = "gem";
    };
  };
  gems = pkgs.bundlerEnv.override { inherit bundler; } {
    name = "foo";
    gemdir = ./.;
    inherit ruby;
  };

2 Likes