josiah
josiah

Reputation: 1414

Ensure Ruby version in Nix Dev Environment when using latest version

Currently, the default and latest Ruby in Nix is 2.2.2-p0. When I run nix-env -qaP ruby it returns a list, which says that this ruby version is accessed via nixpkgs.ruby. I expect that this Ruby link will change to stay up-to-date with the latest supported ruby version. There is no optional nixpkgs.ruby_2_2_2 for me to use to ensure my ruby version.

Looking at the .nix definition file at https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/interpreters/ruby/ruby-2.2.2.nix, however, I see that they specify the revision in that script.

So I'm wondering, is there some way for me to specify the revision of the Nix package that I want when I'm listing it in the buildInputs of my Nix expression for creating the development environment (which will be accessed via nix-shell .)? Or is there something else that I might do that would enable me to ensure that ruby 2.2.2-p0 is used for the installation, and not just the latest Ruby, which might change?

Current script:

  let
    pkgs = import <nixpkgs> {};
  in with pkgs; {
    rubyEnv = stdenv.mkDerivation rec {
      name = "ruby-env";
      version = "0.1";
      src = ./.;
      buildInputs = [
        stdenv
        ruby
        bundler_HEAD
      ];
    };
  }

I didn't see this covered in the documentation at http://nixos.org/nix/manual/#chap-writing-nix-expressions

Upvotes: 3

Views: 1199

Answers (2)

hraban
hraban

Reputation: 2091

The currently accepted answer (Aug 2023) is obsolete because nixpkgs doesn't carry point-releases for Ruby anymore, only minor versions. So if you need e.g. 3.2.0, but nixpkgs is on 3.2.2, you have to build it yourself or get an old Ruby.

Using mkRuby

Here's how to build 3.2.0 using nix(pkgs), using all the latest toolchains etc:

let
  pkgs = (import <nixpkgs> {});
  ruby_3_2_0 = pkgs.mkRuby {
    version = pkgs.mkRubyVersion "3" "2" "0" "";
    # Leave this empty. Nix will complain, and tell you what
    # value to put here.
    sha256 = "";
  };
in
  ruby_3_2_0

Here's how to use that to e.g. get a dev shell:

nix shell --impure --expr 'let ...'

Or using old style nix:

nix-shell -E 'let ... in pkgs.mkShell { packages = [ ruby_3_2_0 ]; }'

I guess you can install it the same way, using nix-env -i -E 'let ...', although I don't use nix-env locally so I can't try it.

This solution works for as long as nixpkgs carries the right information for you to build ruby locally, such as the necessary patches etc. At some point, your ruby version falls so far behind that nixpkgs won't maintain that anymore.

Using an older nixpkgs revision

You can always go back to an old version of nixpkgs which has the ruby version you want.

Finding the right nixpkgs commit though can be a bit of a dark art. Ruby is a perfect example here because it's so tricky. I'll take you through some examples.

3.2.1

I first use the nixpkgs PR tracker to find the right version: ruby 3.2.1 in:title. If you look closely, you can see a PR labeled "init at 3.2.1", which I'm assuming means they started tracking the 3.2.x minor at 3.2.1, not 3.2.0. Let's take that commit, as it was merged into their staging branch: a239c7d2eeaad81602f83ece87dc7bfa5593ceab.†

Now we can do:

# _3_2, not _3_2_1
nix shell github:NixOS/nixpkgs/a239c7d2eeaad81602f83ece87dc7bfa5593ceab#ruby_3_2 
ruby --version

† See the "Get latest successful Hydra build" chapter for a better strategy on choosing a commit.

3.2.0

I can't find any record of ruby 3.2.0 ever living in nixpkgs, so I have a feeling your only option is to build this yourself.

2.3.0

A different example: 2.3.0. Much older (2016), from a time nixpkgs didn't have flakes (did they even exist at all?).

I use the same GitHub PR search, this time it's an easier find: commit 3d4dff50724030376d7b7c3d4714d375f416f004 into master. But as there were no flakes at that time:

$ nix shell github:NixOS/nixpkgs/3d4dff50724030376d7b7c3d4714d375f416f004#ruby_2_3_0 
error: getting status of '/nix/store/c0csr6c2lxhqhf1s148bq44fz6v5mpp7-source/flake.nix': No such file or directory

So instead I use:

$ nix shell --impure --expr 'let pkgs = (import (builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/3d4dff50724030376d7b7c3d4714d375f416f004.tar.gz") {}); in pkgs.ruby_2_3_0'

But another problem arises:


... long error snip ...


       error: Package ‘ruby-2.3.0-p0’ in ‘/nix/store/c0csr6c2lxhqhf1s148bq44fz6v5mpp7-source/pkgs/development/interpreters/ruby/default.nix:123’ is not supported on ‘aarch64-darwin’, refusing to evaluate.
       a) For `nixos-rebuild` you can set
         { nixpkgs.config.allowBroken = true; }
       in configuration.nix to override this.

       b) For `nix-env`, `nix-build` or any other Nix command you can add
         { allowBroken = true; }
       to ~/.nixpkgs/config.nix.

This is because my laptop is aarch64-darwin (aka Apple Silicon), and that's not supported by Ruby in 2016 (makes sense). If I fire up a linux x86 virtual machine, though, and use the same line, it works.

Get latest successful Hydra build

A merge commit into nixpkgs' stable branch is probably not an ideal revision, because the big nixpkgs CI (called Hydra) doesn't build every staging version. You will end up rebuilding the entire world yourself, including compilers and everything up to ruby itself. This does work in a pinch, but it's painful. It's a lot worse than the solution at the top using mkRuby, because that uses existing, pre-built compilers, and only needs to compile Ruby itself. Not the entire toolchain.

The better solution is to find "what was the first revision that Hydra built that contains my target revision?". As far as I know there is no clean solution to this. My best bet for now is the unofficial https://channels.nix.gsc.io, e.g. picking my desired channel nixpkgs-unstable: "https://channels.nix.gsc.io/nixpkgs-unstable/history-v2". Unfortunately I see its last update was May 2023, and it's Aug by now, so I fear it's out of date. Still, I know my commit is from March 2023 so I can demonstrate:

  1. clone nixpkgs. This takes heaps of disk & network.
    git clone https://github.com/NixOS/nixpkgs && cd nixpkgs
    
  2. Get the unix timestamp of my desired commit, a239c7d2eeaad81602f83ece87dc7bfa5593ceab:
    targettime=$(git show -s --format=%ct a239c7d2eeaad81602f83ece87dc7bfa5593ceab)
    
  3. Find the first successful Hydra build for that commit:
    curl -sSL --fail https://channels.nix.gsc.io/nixpkgs-unstable/history-v2 | \
      while read rev time rest ; do
        if [[ "$time" -lt "$targettime" ]]; then
          continue
        fi
        if git merge-base --is-ancestor a239c7d2eeaad81602f83ece87dc7bfa5593ceab "$rev"; then
          echo "$rev"
          break
        fi
      done
    

I got 98f3b08f58ff125ef02d55cd52a83f44f245f2ea. Plugging that into the same nix shell .. line above gives me a result without building anything, just downloading ruby 3.2.1 from Hydra.

Upvotes: 0

Eric
Eric

Reputation: 2884

There is no optional nixpkgs.ruby_2_2_2 for me to use to ensure my ruby version.

Actually there is a ruby_2_2_2 in nixpkgs:

$ nix-env -qaP ruby
nixos.ruby_1_8      ruby-1.8.7-p374
nixos.ruby_1_9      ruby-1.9.3-p551
nixos.ruby_2_0      ruby-2.0.0-p645
nixos.ruby_2_1_0    ruby-2.1.0-p0
nixos.ruby_2_1_1    ruby-2.1.1-p0
nixos.ruby_2_1_2    ruby-2.1.2-p353
nixos.ruby_2_1_3    ruby-2.1.3-p0
nixos.ruby_2_1      ruby-2.1.6-p0
nixos.ruby_2_2_0    ruby-2.2.0-p0
nixos.ruby          ruby-2.2.2-p0
nixos.bundler_HEAD  ruby-2.2.2-p0-bundler-2015-01-11

By looking at the definition of ruby package in the index, you can see that the current default ruby is just an alias to ruby 2.2:

ruby = ruby_2_2;

that is in turn an alias to ruby 2.2.2:

ruby_2_2 = ruby_2_2_2; 

To override the ruby package to a specific ruby version in a nix expression, overridePackages can be used:

let
  nixpkgs = import <nixpkgs> {};
  pkgs = nixpkgs.overridePackages (self: super: {
    ruby = nixpkgs.ruby_2_2_2;
  });
in with pkgs; 
{
  rubyEnv = stdenv.mkDerivation rec {
    name = "ruby-env";
    version = "0.1";
    src = ./.;
    buildInputs = [
      stdenv
      ruby
      bundler
    ];
  };
}

Upvotes: 3

Related Questions