nixpkgs/pkgs/development/interpreters/ruby/bundler-env.nix
2015-01-18 18:47:28 -05:00

148 lines
3.7 KiB
Nix

{ 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;
};
}