Installing simple ruby gem via nix


#1

My concrete goal is to install twitter/twurl on macOS without letting ruby/gem litter the filesystem, thus want to install it via nix. twurl is a simple command line tool which you’d usually install simply via

gem install twurl

What I’ve got so far: With a small fix, bundix --magic runs successfully within a source checkout of twurl, generating gemset.nix.

Furthermore, bundix -i generates a shell.nix, and nix-shell seems to build the ruby dependencies successfully. However, running ./bin/twurl within nix-shell fails due to failing ruby imports of dependencies. It turns out this is because it uses system ruby; adding ruby to the shell dependencies gives me a working twurl in nix-shell.

Where I’m stuck now is finding a working default.nix that allows installing the twurl executable so that it runs. The closest I’ve come is:

with (import <nixpkgs> {});
let
  env = bundlerEnv {
    name = "twurl-bundler-env";
    inherit ruby;
    gemfile  = ./Gemfile;
    lockfile = ./Gemfile.lock;
    gemset   = ./gemset.nix;
  };
in stdenv.mkDerivation {
  name = "twurl";
  buildInputs = [ env ruby ];
  src = ./.;
  installPhase = ''
    mkdir -p $out
    cp -r $src/* $out
  '';
}

which gives me twurl in the PATH and with a correctly patched ruby interpreter, but it fails because it doesn’t find the library files from src/lib. I’m unclear on whether these should go inside the bundlerEnv or whether I should do some explicit magic to tell the executable about where to look for them. (I’m also unclear on the meaning of gemdir as an argument to bundlerEnv which shows up in examples apparently randomly instead of gemfile/gemset.)

Finally, I’m wondering if there isn’t (shouldn’t be) a more straightforward way to do this? Initially I’d have thought bundix or a similar tool might just generate a working default.nix from the gemspec file. Maybe that exists but bundix is not that tool?


#2

That’s basically what bundlerApp is for.

You start with the default.nix and Gemfile:

default.nix
with import <nixpkgs> {};
bundlerApp {
  pname = "twurl";
  exes = ["twurl"];
  gemdir = ./.;
}
Gemfile
source 'https://rubygems.org' do
  gem 'twurl'
end

After running bundix -l, you should then have these files in the directory:

Gemfile.lock
GEM
  remote: https://rubygems.org/
  specs:
    oauth (0.5.4)
    twurl (0.9.3)
      oauth (~> 0.4)

PLATFORMS
  ruby

DEPENDENCIES
  twurl!

BUNDLED WITH
   1.16.3
gemset.nix
{
  oauth = {
    source = {
      remotes = ["https://rubygems.org"];
      sha256 = "1zszdg8q1b135z7l7crjj234k4j0m347hywp5kj6zsq7q78pw09y";
      type = "gem";
    };
    version = "0.5.4";
  };
  twurl = {
    dependencies = ["oauth"];
    source = {
      remotes = ["https://rubygems.org"];
      sha256 = "10lcdakbgvbwcij8ngxm7vws7b40l004ma4mza9idi54vkxqzaws";
      type = "gem";
    };
    version = "0.9.3";
  };
}

Finally a nix build gives you the result/bin/twurl executable.

The difference is that bundlerEnv is for development with these gems, while bundlerApp simply gives you the executables without need for development dependencies, environment changes, nix-shell, etc…

Hope this helps :smile:


#3

Yes, that helped indeed! It never occurred to me write an extra meta-Gemfile. I’m probably just blind, but might I suggest documenting this a bit more clearly?

As a small amendment: bundix -l failed because of missing Gemfile.lock, but bundix -m did the trick.