Fedeback on Ruby setup

Hi!

I would love some feedback on how to cleanup + improve my Nix seutp for use with Ruby / JRuby.

Disclaimer: Although the “nix” thing to do is probably use a shell.nix file to set a specific Ruby version installation; I’m trying to migrate an existing large codebase to Nix and it heavily relies on rbenv

rbenv dynamically switches the ruby version according to a .ruby-version file in a directory

First thing I did was create a very simple rbenv derivation

{ stdenv, fetchFromGitHub, bash }:
stdenv.mkDerivation {
  name = "rbenv";

  # fetchFromGitHub is a build support function that fetches a GitHub
  # repository and extracts into a directory; so we can use it
  # fetchFromGithub is actually a derivation itself :)
  src = fetchFromGitHub {
    owner = "rbenv";
    repo = "rbenv";
    rev = "v1.1.2";
    sha256 = "12i050vs35iiblxga43zrj7xwbaisv3mq55y9ikagkr8pj1vmq53";
  };

  buildPhase = ''
    ${bash}/bin/bash src/configure
    make -C src
  '';

  # This overrides the shell code that is run during the installPhase.
  # By default; this runs `make install`.
  # The install phase will fail if there is no makefile; so it is the
  # best choice to replace with our custom code.
  installPhase = ''
    mkdir -p $out/bin
    mv libexec $out
    mv completions $out
    ln -s $out/libexec/rbenv $out/bin/rbenv
  '';
}

I’ve actually posted this as a pull-request to nixpkgs.

The way in which rbenv works is by finding versions in ~/.rbenv/versions.
My plan is to use home-manager to symlink my ruby/jruby versions in that directory

home.file = {
    ".rbenv/versions/jruby-${jruby_9_2_9_0.version}"  =  {
      source = jruby_9_2_9_0;
      target = ".rbenv/versions/jruby-${jruby_9_2_9_0.version}";
    };
    ".rbenv/versions/jruby-${jruby_9_2_12_0.version}"  =  {
      source = jruby_9_2_12_0;
      target = ".rbenv/versions/jruby-${jruby_9_2_12_0.version}";
    };
  };

This works great so far however it turns out that nixpkg only has a single jruby version; so I had to create my own derivations for each version.

  jruby_9_2_9_0 = super.jruby.overrideAttrs (oldAtrrs: rec {
  	version = "9.2.9.0";
  	src = super.fetchurl {
  		url = "https://s3.amazonaws.com/jruby.org/downloads/${version}/jruby-bin-${version}.tar.gz";
  		sha256 = "04grdf57c1dgragm17yyjk69ak8mwiwfc1vjzskzcaag3fwgplyf";
  	};
  });

  jruby_9_2_12_0 = super.jruby.overrideAttrs (oldAtrrs: rec {
  	version = "9.2.12.0";
  	src = super.fetchurl {
  		url = "https://s3.amazonaws.com/jruby.org/downloads/${version}/jruby-bin-${version}.tar.gz";
  		sha256 = "013c1q1n525y9ghp369z1jayivm9bw8c1x0g5lz7479hqhj62zrh";
  	};
  });

My questions are as follows:

  1. It’s a bit of a PITA to have to override the whole fetchurl just to change the sha256. Is this the only way ?
  2. I wouldn’t mind someone helping me understand the jruby derivation; specifically the use of rubyVersion and why it’s doing some funky override at the bottom.
  3. What’s the nixlang syntax so I can just iterate over an array of versions to create that symlink attr set above rather than manually typing it. Something like:
map [ "9.2.9.0" "9.2.12.0"] (version: {
   # what here!
});

Regarding question 3:

lib.mapAttrs (version: _: { foo = 1;}) (lib.listToAttrs (builtins.map (version: lib.nameValuePair version null) ["9.2.9.0" "9.2.12.0"]))

or

lib.mapAttrs (version: _: { foo = 1;}) {"9.2.9.0" = null; "9.2.12.0" = null; }

@Mic92 thanks for the feedback on the PR + the nixlang code above.

Further question: Using Nix + Ruby (or JRuby); one must always override GEM_HOME?
Otherwise gem will try to install gems in the installation directory which is the nix-store.

Seems like that should be the default in the wrapper script potentially.

Looking at MRI Ruby – Ruby gem home by zimbatm · Pull Request #18792 · NixOS/nixpkgs · GitHub
It patches to change the default GEM_HOME to the user directory

I filled JRuby should apply same Ruby patch to change default GEM_HOME · Issue #92948 · NixOS/nixpkgs · GitHub to document that the same GEM_HOME patch is needed.

For anyone who comes here wanting to get started with JRuby here is a minimal shell.nix

{ pkgs ? import <nixpkgs> { } }:
with pkgs;
with stdenv;
with stdenv.lib;
mkShell {
  name = "jruby-shell";

  buildInputs = [ jruby cacert ];

  # Likely want a CA file; so lets add one.
  SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt";

  # Make UTF8 the default as it's more sane
  LANG = "en_US.UTF-8";
  # https://nixos.org/nixpkgs/manual/#locales
  LOCALE_ARCHIVE =
    optionalString isLinux "${glibcLocales}/lib/locale/locale-archive";
  shellHook = ''
      # JRuby wants to install Gem's in the nix-store which is read-only
      # set GEM_HOME to make it a writable directory
    	export GEM_HOME=$(${jruby}/bin/ruby -e 'puts Gem.user_dir')
      export GEM_PATH=$GEM_HOME
      # Add the GEM binary files to the path
      export PATH=$GEM_HOME/bin:$PATH
      '';
}

I’ve submitted it to nix-environments as a pull-request.

1 Like

Hi @fzakaria, given your PR was merged I was wondering what the steps are for home-manager + working with projects based on .ruby-version files?