diff --git a/pkgs/development/interpreters/ruby/bundler-env.nix b/pkgs/development/interpreters/ruby/bundler-env.nix new file mode 100644 index 000000000000..3fb02fa1ef8e --- /dev/null +++ b/pkgs/development/interpreters/ruby/bundler-env.nix @@ -0,0 +1,147 @@ +{ stdenv, runCommand, writeText, writeScriptBin, ruby, lib, callPackage +, gemFixes, fetchurl, fetchgit, buildRubyGem +}@defs: + +# This is a work-in-progress. +# The idea is that his will replace load-ruby-env.nix, +# using (a patched) bundler to handle the entirety of the installation process. + +{ name, gemset, gemfile, lockfile, ruby ? defs.ruby, fixes ? gemFixes }@args: + +let + const = x: y: x; + + fetchers.path = attrs: attrs.src.path; + fetchers.gem = attrs: fetchurl { + url = "${attrs.src.source or "https://rubygems.org"}/downloads/${attrs.name}-${attrs.version}.gem"; + inherit (attrs.src) sha256; + }; + fetchers.git = attrs: fetchgit { + inherit (attrs.src) url rev sha256 fetchSubmodules; + leaveDotGit = true; + }; + + fixSpec = attrs: + attrs // (fixes."${attrs.name}" or (const {})) attrs; + + instantiate = (attrs: + let + withFixes = fixSpec attrs; + withSource = withFixes // + (if (lib.isDerivation withFixes.src || builtins.isString withFixes.src) + then { source = attrs.src; } + else { source = attrs.src; src = (fetchers."${attrs.src.type}" attrs); }); + + in + withSource + ); + + instantiated = lib.flip lib.mapAttrs (import gemset) (name: attrs: + instantiate (attrs // { inherit name; }) + ); + + runRuby = name: env: command: + runCommand name env '' + ${ruby}/bin/ruby ${writeText name command} + ''; + + # TODO: include json_pure, so the version of ruby doesn't matter. + # not all rubies have support for JSON built-in, + # so we'll convert JSON to ruby expressions. + json2rb = writeScriptBin "json2rb" '' + #!${ruby}/bin/ruby + begin + require 'json' + rescue LoadError => ex + require 'json_pure' + end + + puts JSON.parse(STDIN.read).inspect + ''; + + # dump the instantiated gemset as a ruby expression. + serializedGemset = runCommand "gemset.rb" { json = builtins.toJSON instantiated; } '' + printf '%s' "$json" | ${json2rb}/bin/json2rb > $out + ''; + + # this is a mapping from a source type and identifier (uri/path/etc) + # to the pure store path. + # we'll use this from the patched bundler to make fetching sources pure. + sources = runRuby "sources.rb" { gemset = serializedGemset; } '' + out = ENV['out'] + gemset = eval(File.read(ENV['gemset'])) + + sources = { + "git" => { }, + "path" => { }, + "gem" => { }, + "svn" => { } + } + + gemset.each_value do |spec| + type = spec["source"]["type"] + val = spec["src"] + key = + case type + when "gem" + spec["name"] + when "git" + spec["source"]["url"] + when "path" + spec["source"]["originalPath"] + when "svn" + nil # TODO + end + + sources[type][key] = val if key + end + + File.open(out, "wb") do |f| + f.print sources.inspect + end + ''; + + # rewrite PATH sources to point into the nix store. + pureLockfile = runRuby "pureLockfile" { inherit sources; } '' + out = ENV['out'] + paths = eval(File.read(ENV['sources'])) + + lockfile = File.read("${lockfile}") + + paths.each_pair do |impure, pure| + lockfile.gsub!(/^ remote: #{Regexp.escape(impure)}/, " remote: #{pure}") + end + + File.open(out, "wb") do |f| + f.print lockfile + end + ''; + +in + +stdenv.mkDerivation { + inherit name; + outputs = [ + "out" # the installed libs/bins + "bundler" # supporting files for bundler + ]; + phases = [ "installPhase" "fixupPhase" ]; + installPhase = '' + # Copy the Gemfile and Gemfile.lock + mkdir -p $bundler + BUNDLE_GEMFILE=$bundler/Gemfile + cp ${gemfile} $BUNDLE_GEMFILE + cp ${pureLockfile} $BUNDLE_GEMFILE.lock + + export NIX_GEM_SOURCES=${sources} + + export GEM_HOME=$out/${ruby.gemPath} + export GEM_PATH=$GEM_HOME + mkdir -p $GEM_HOME + + bundler install + ''; + passthru = { + inherit ruby; + }; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 9c966b0066ff..a267110a7710 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -4193,6 +4193,7 @@ let gemFixes = callPackage ../development/interpreters/ruby/fixes.nix { }; buildRubyGem = callPackage ../development/interpreters/ruby/gem.nix { }; loadRubyEnv = callPackage ../development/interpreters/ruby/load-ruby-env.nix { }; + bundlerEnv = callPackage ../development/interpreters/ruby/bundler-env.nix { }; ruby_1_8_7 = callPackage ../development/interpreters/ruby/ruby-1.8.7.nix { }; ruby_1_9_3 = callPackage ../development/interpreters/ruby/ruby-1.9.3.nix { };