7fc3b002e6
properly handle the case where a gemspec is missing
247 lines
6.5 KiB
Ruby
247 lines
6.5 KiB
Ruby
require 'bundler'
|
|
|
|
# Undo the RUBYOPT trickery.
|
|
opt = ENV['RUBYOPT'].dup
|
|
opt.gsub!(/-rmonkey_patches.rb -I [^ ]*/, '')
|
|
ENV['RUBYOPT'] = opt
|
|
|
|
Bundler.module_eval do
|
|
class << self
|
|
# mappings from original uris to store paths.
|
|
def nix_gem_sources
|
|
@nix_gem_sources ||=
|
|
begin
|
|
src = ENV['NIX_GEM_SOURCES']
|
|
eval(Bundler.read_file(src))
|
|
end
|
|
end
|
|
|
|
# extract the gemspecs from the gems pulled from Rubygems.
|
|
def nix_gemspecs
|
|
@nix_gemspecs ||= Dir.glob("gems/*.gem").map do |path|
|
|
Bundler.rubygems.spec_from_gem(path)
|
|
end
|
|
end
|
|
|
|
# swap out ENV
|
|
def nix_with_env(env, &block)
|
|
if env
|
|
old_env = ENV.to_hash
|
|
begin
|
|
ENV.replace(env)
|
|
block.call
|
|
ensure
|
|
ENV.replace(old_env)
|
|
end
|
|
else
|
|
block.call
|
|
end
|
|
end
|
|
|
|
# map a git uri to a fetchgit store path.
|
|
def nix_git(uri)
|
|
Pathname.new(nix_gem_sources["git"][uri])
|
|
end
|
|
end
|
|
end
|
|
|
|
Bundler::Source::Git::GitProxy.class_eval do
|
|
def checkout
|
|
unless path.exist?
|
|
FileUtils.mkdir_p(path.dirname)
|
|
FileUtils.cp_r(Bundler.nix_git(@uri).join(".git"), path)
|
|
system("chmod -R +w #{path}")
|
|
end
|
|
end
|
|
|
|
def copy_to(destination, submodules=false)
|
|
unless File.exist?(destination.join(".git"))
|
|
FileUtils.mkdir_p(destination.dirname)
|
|
FileUtils.cp_r(Bundler.nix_git(@uri), destination)
|
|
system("chmod -R +w #{destination}")
|
|
end
|
|
end
|
|
end
|
|
|
|
Bundler::Fetcher.class_eval do
|
|
def use_api
|
|
true
|
|
end
|
|
|
|
def fetch_dependency_remote_specs(gem_names)
|
|
Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}"
|
|
deps_list = []
|
|
|
|
spec_list = gem_names.map do |name|
|
|
spec = Bundler.nix_gemspecs.detect {|spec| spec.name == name }
|
|
if spec.nil?
|
|
msg = "WARNING: Could not find gemspec for '#{name}'"
|
|
Bundler.ui.warn msg
|
|
nil
|
|
else
|
|
dependencies = spec.dependencies.
|
|
select {|dep| dep.type != :development}.
|
|
map do |dep|
|
|
deps_list << dep.name
|
|
dep
|
|
end
|
|
|
|
[spec.name, spec.version, spec.platform, dependencies]
|
|
end
|
|
end
|
|
|
|
spec_list.compact!
|
|
|
|
[spec_list, deps_list.uniq]
|
|
end
|
|
end
|
|
|
|
Bundler::Source::Rubygems.class_eval do
|
|
# We copy all gems into $PWD/gems, and this allows RubyGems to find those
|
|
# gems during installation.
|
|
def fetchers
|
|
@fetchers ||= [
|
|
Bundler::Fetcher.new(URI.parse("file://#{File.expand_path(Dir.pwd)}"))
|
|
]
|
|
end
|
|
|
|
# Look-up gems that were originally from RubyGems.
|
|
def remote_specs
|
|
@remote_specs ||=
|
|
begin
|
|
lockfile = Bundler::LockfileParser.new(Bundler.read_file(Bundler.default_lockfile))
|
|
gem_names = lockfile.specs.
|
|
select {|spec| spec.source.is_a?(Bundler::Source::Rubygems)}.
|
|
map {|spec| spec.name}
|
|
idx = Bundler::Index.new
|
|
api_fetchers.each do |f|
|
|
Bundler.ui.info "Fetching source index from #{f.uri}"
|
|
idx.use f.specs(gem_names, self)
|
|
end
|
|
idx
|
|
end
|
|
end
|
|
end
|
|
|
|
Bundler::Installer.class_eval do
|
|
|
|
# WHY:
|
|
# This allows us to provide a typical Nix experience, where
|
|
# `buildInputs` and/or `preInstall` may set up the $PATH and other env-vars
|
|
# as needed. By swapping out the environment per install, we can have finer
|
|
# grained control than we would have otherwise.
|
|
#
|
|
# HOW:
|
|
# This is a wrapper around the original `install_gem_from_spec`.
|
|
# We expect that a "pre-installer" might exist at `pre-installers/<gem-name>`,
|
|
# and if it does, we execute it.
|
|
# The pre-installer is expected to dump its environment variables as a Ruby
|
|
# hash to `env/<gem-name>`.
|
|
# We then swap out the environment for the duration of the install,
|
|
# and then set it back to what it was originally.
|
|
alias original_install_gem_from_spec install_gem_from_spec
|
|
def install_gem_from_spec(spec, standalone = false, worker = 0)
|
|
env_dump = "env/#{spec.name}"
|
|
if File.exist?(env_dump)
|
|
env = eval(Bundler.read_file(env_dump))
|
|
unless env
|
|
Bundler.ui.error "The environment variables for #{spec.name} could not be loaded!"
|
|
exit 1
|
|
end
|
|
Bundler.nix_with_env(env) do
|
|
original_install_gem_from_spec(spec, standalone, worker)
|
|
end
|
|
else
|
|
original_install_gem_from_spec(spec, standalone, worker)
|
|
end
|
|
end
|
|
|
|
def generate_bundler_executable_stubs(spec, options = {})
|
|
return if spec.executables.empty?
|
|
|
|
out = ENV['out']
|
|
|
|
spec.executables.each do |executable|
|
|
next if executable == "bundle" || executable == "bundler"
|
|
|
|
binstub_path = "#{out}/bin/#{executable}"
|
|
|
|
File.open(binstub_path, 'w', 0777 & ~File.umask) do |f|
|
|
f.print <<-TEXT
|
|
#!#{RbConfig.ruby}
|
|
|
|
old_gemfile = ENV["BUNDLE_GEMFILE"]
|
|
old_gem_home = ENV["GEM_HOME"]
|
|
old_gem_path = ENV["GEM_PATH"]
|
|
|
|
ENV["BUNDLE_GEMFILE"] =
|
|
"#{ENV["BUNDLE_GEMFILE"]}"
|
|
ENV["GEM_HOME"] =
|
|
"#{ENV["GEM_HOME"]}"
|
|
ENV["GEM_PATH"] =
|
|
"#{ENV["NIX_BUNDLER_GEMPATH"]}:\#{ENV["GEM_HOME"]}\#{old_gem_path ? ":\#{old_gem_path}" : ""}}"
|
|
|
|
require 'rubygems'
|
|
require 'bundler/setup'
|
|
|
|
ENV["BUNDLE_GEMFILE"] = old_gemfile
|
|
ENV["GEM_HOME"] = old_gem_home
|
|
ENV["GEM_PATH"] = old_gem_path
|
|
|
|
load Gem.bin_path('#{spec.name}', '#{executable}')
|
|
TEXT
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
Gem::Installer.class_eval do
|
|
# Make the wrappers automagically use bundler.
|
|
#
|
|
# Stage 1.
|
|
# Set $BUNDLE_GEMFILE so bundler knows what gems to load.
|
|
# Set $GEM_HOME to the installed gems, because bundler looks there for
|
|
# non-Rubygems installed gems (e.g. git/svn/path sources).
|
|
# Set $GEM_PATH to include both bundler and installed gems.
|
|
#
|
|
# Stage 2.
|
|
# Setup bundler, locking down the gem versions.
|
|
#
|
|
# Stage 3.
|
|
# Reset $BUNDLE_GEMFILE, $GEM_HOME, $GEM_PATH.
|
|
#
|
|
# Stage 4.
|
|
# Run the actual executable.
|
|
def app_script_text(bin_file_name)
|
|
return <<-TEXT
|
|
#!#{RbConfig.ruby}
|
|
#
|
|
# This file was generated by Nix's RubyGems.
|
|
#
|
|
# The application '#{spec.name}' is installed as part of a gem, and
|
|
# this file is here to facilitate running it.
|
|
#
|
|
|
|
old_gemfile = ENV["BUNDLE_GEMFILE"]
|
|
old_gem_home = ENV["GEM_HOME"]
|
|
old_gem_path = ENV["GEM_PATH"]
|
|
|
|
ENV["BUNDLE_GEMFILE"] =
|
|
"#{ENV["BUNDLE_GEMFILE"]}"
|
|
ENV["GEM_HOME"] =
|
|
"#{ENV["GEM_HOME"]}"
|
|
ENV["GEM_PATH"] =
|
|
"#{ENV["NIX_BUNDLER_GEMPATH"]}:\#{ENV["GEM_HOME"]}\#{old_gem_path ? ":\#{old_gem_path}" : ""}}"
|
|
|
|
require 'rubygems'
|
|
require 'bundler/setup'
|
|
|
|
ENV["BUNDLE_GEMFILE"] = old_gemfile
|
|
ENV["GEM_HOME"] = old_gem_home
|
|
ENV["GEM_PATH"] = old_gem_path
|
|
|
|
load Gem.bin_path('#{spec.name}', '#{bin_file_name}')
|
|
TEXT
|
|
end
|
|
end
|