original version
This commit is contained in:
1 parent
6cfe2d1bf4
commit
7bee686fe5
275 files changed
No files matched your search
+428
@@ -0,0 +1,428 @@
|
||||
require 'fileutils'
|
||||
require 'pathname'
|
||||
require 'rbconfig'
|
||||
require 'bundler/gem_path_manipulation'
|
||||
require 'bundler/rubygems_ext'
|
||||
require 'bundler/rubygems_integration'
|
||||
require 'bundler/version'
|
||||
require 'bundler/constants'
|
||||
require 'bundler/current_ruby'
|
||||
|
||||
module Bundler
|
||||
preserve_gem_path
|
||||
ORIGINAL_ENV = ENV.to_hash
|
||||
|
||||
autoload :Definition, 'bundler/definition'
|
||||
autoload :Dependency, 'bundler/dependency'
|
||||
autoload :DepProxy, 'bundler/dep_proxy'
|
||||
autoload :Deprecate, 'bundler/deprecate'
|
||||
autoload :Dsl, 'bundler/dsl'
|
||||
autoload :EndpointSpecification, 'bundler/endpoint_specification'
|
||||
autoload :Environment, 'bundler/environment'
|
||||
autoload :Env, 'bundler/env'
|
||||
autoload :Fetcher, 'bundler/fetcher'
|
||||
autoload :GemHelper, 'bundler/gem_helper'
|
||||
autoload :GemHelpers, 'bundler/gem_helpers'
|
||||
autoload :GemInstaller, 'bundler/gem_installer'
|
||||
autoload :Graph, 'bundler/graph'
|
||||
autoload :Index, 'bundler/index'
|
||||
autoload :Installer, 'bundler/installer'
|
||||
autoload :Injector, 'bundler/injector'
|
||||
autoload :LazySpecification, 'bundler/lazy_specification'
|
||||
autoload :LockfileParser, 'bundler/lockfile_parser'
|
||||
autoload :MatchPlatform, 'bundler/match_platform'
|
||||
autoload :RemoteSpecification, 'bundler/remote_specification'
|
||||
autoload :Resolver, 'bundler/resolver'
|
||||
autoload :Retry, 'bundler/retry'
|
||||
autoload :RubyVersion, 'bundler/ruby_version'
|
||||
autoload :RubyDsl, 'bundler/ruby_dsl'
|
||||
autoload :Runtime, 'bundler/runtime'
|
||||
autoload :Settings, 'bundler/settings'
|
||||
autoload :SharedHelpers, 'bundler/shared_helpers'
|
||||
autoload :SpecSet, 'bundler/spec_set'
|
||||
autoload :Source, 'bundler/source'
|
||||
autoload :SourceList, 'bundler/source_list'
|
||||
autoload :Specification, 'bundler/shared_helpers'
|
||||
autoload :SystemRubyVersion, 'bundler/ruby_version'
|
||||
autoload :UI, 'bundler/ui'
|
||||
|
||||
class BundlerError < StandardError
|
||||
def self.status_code(code)
|
||||
define_method(:status_code) { code }
|
||||
end
|
||||
end
|
||||
|
||||
class GemfileNotFound < BundlerError; status_code(10) ; end
|
||||
class GemNotFound < BundlerError; status_code(7) ; end
|
||||
class GemfileError < BundlerError; status_code(4) ; end
|
||||
class InstallError < BundlerError; status_code(5) ; end
|
||||
class InstallHookError < BundlerError; status_code(8) ; end
|
||||
class PathError < BundlerError; status_code(13) ; end
|
||||
class GitError < BundlerError; status_code(11) ; end
|
||||
class DeprecatedError < BundlerError; status_code(12) ; end
|
||||
class GemspecError < BundlerError; status_code(14) ; end
|
||||
class InvalidOption < BundlerError; status_code(15) ; end
|
||||
class ProductionError < BundlerError; status_code(16) ; end
|
||||
class HTTPError < BundlerError; status_code(17) ; end
|
||||
class RubyVersionMismatch < BundlerError; status_code(18) ; end
|
||||
class SecurityError < BundlerError; status_code(19) ; end
|
||||
class LockfileError < BundlerError; status_code(20) ; end
|
||||
class CyclicDependencyError < BundlerError; status_code(21) ; end
|
||||
class GemfileLockNotFound < BundlerError; status_code(22) ; end
|
||||
|
||||
# Internal errors, should be rescued
|
||||
class VersionConflict < BundlerError
|
||||
attr_reader :conflicts
|
||||
|
||||
def initialize(conflicts, msg = nil)
|
||||
super(msg)
|
||||
@conflicts = conflicts
|
||||
end
|
||||
|
||||
status_code(6)
|
||||
end
|
||||
|
||||
class MarshalError < StandardError; end
|
||||
|
||||
class << self
|
||||
attr_writer :ui, :bundle_path
|
||||
|
||||
def configure
|
||||
@configured ||= configure_gem_home_and_path
|
||||
end
|
||||
|
||||
def ui
|
||||
@ui ||= UI::Silent.new
|
||||
end
|
||||
|
||||
# Returns absolute path of where gems are installed on the filesystem.
|
||||
def bundle_path
|
||||
@bundle_path ||= Pathname.new(settings.path).expand_path(root)
|
||||
end
|
||||
|
||||
# Returns absolute location of where binstubs are installed to.
|
||||
def bin_path
|
||||
@bin_path ||= begin
|
||||
path = settings[:bin] || "bin"
|
||||
path = Pathname.new(path).expand_path(root).expand_path
|
||||
FileUtils.mkdir_p(path)
|
||||
path
|
||||
end
|
||||
end
|
||||
|
||||
def setup(*groups)
|
||||
# Just return if all groups are already loaded
|
||||
return @setup if defined?(@setup)
|
||||
|
||||
definition.validate_ruby!
|
||||
|
||||
if groups.empty?
|
||||
# Load all groups, but only once
|
||||
@setup = load.setup
|
||||
else
|
||||
@completed_groups ||= []
|
||||
# Figure out which groups haven't been loaded yet
|
||||
unloaded = groups - @completed_groups
|
||||
# Record groups that are now loaded
|
||||
@completed_groups = groups
|
||||
unloaded.any? ? load.setup(*groups) : load
|
||||
end
|
||||
end
|
||||
|
||||
def require(*groups)
|
||||
setup(*groups).require(*groups)
|
||||
end
|
||||
|
||||
def load
|
||||
@load ||= Runtime.new(root, definition)
|
||||
end
|
||||
|
||||
def environment
|
||||
Bundler::Environment.new(root, definition)
|
||||
end
|
||||
|
||||
# Returns an instance of Bundler::Definition for given Gemfile and lockfile
|
||||
#
|
||||
# @param unlock [Hash, Boolean, nil] Gems that have been requested
|
||||
# to be updated or true if all gems should be updated
|
||||
# @return [Bundler::Definition]
|
||||
def definition(unlock = nil)
|
||||
@definition = nil if unlock
|
||||
@definition ||= begin
|
||||
configure
|
||||
upgrade_lockfile
|
||||
Definition.build(default_gemfile, default_lockfile, unlock)
|
||||
end
|
||||
end
|
||||
|
||||
def locked_gems
|
||||
return @locked_gems if defined?(@locked_gems)
|
||||
if Bundler.default_lockfile.exist?
|
||||
lock = Bundler.read_file(Bundler.default_lockfile)
|
||||
@locked_gems = LockfileParser.new(lock)
|
||||
else
|
||||
@locked_gems = nil
|
||||
end
|
||||
end
|
||||
|
||||
def ruby_scope
|
||||
"#{Bundler.rubygems.ruby_engine}/#{Bundler.rubygems.config_map[:ruby_version]}"
|
||||
end
|
||||
|
||||
def user_bundle_path
|
||||
Pathname.new(Bundler.rubygems.user_home).join(".bundler")
|
||||
end
|
||||
|
||||
def home
|
||||
bundle_path.join("bundler")
|
||||
end
|
||||
|
||||
def install_path
|
||||
home.join("gems")
|
||||
end
|
||||
|
||||
def specs_path
|
||||
bundle_path.join("specifications")
|
||||
end
|
||||
|
||||
def cache
|
||||
bundle_path.join("cache/bundler")
|
||||
end
|
||||
|
||||
def root
|
||||
@root ||= default_gemfile.dirname.expand_path
|
||||
end
|
||||
|
||||
def app_config_path
|
||||
ENV['BUNDLE_APP_CONFIG'] ?
|
||||
Pathname.new(ENV['BUNDLE_APP_CONFIG']).expand_path(root) :
|
||||
root.join('.bundle')
|
||||
end
|
||||
|
||||
def app_cache(custom_path = nil)
|
||||
path = custom_path || root
|
||||
path.join("vendor/cache")
|
||||
end
|
||||
|
||||
def tmp(name = Process.pid.to_s)
|
||||
@tmp ||= Pathname.new Dir.mktmpdir("bundler")
|
||||
@tmp.join(name)
|
||||
end
|
||||
|
||||
def settings
|
||||
@settings ||= begin
|
||||
Settings.new(app_config_path)
|
||||
rescue GemfileNotFound
|
||||
Settings.new
|
||||
end
|
||||
end
|
||||
|
||||
def with_original_env
|
||||
bundled_env = ENV.to_hash
|
||||
ENV.replace(ORIGINAL_ENV)
|
||||
yield
|
||||
ensure
|
||||
ENV.replace(bundled_env.to_hash)
|
||||
end
|
||||
|
||||
def with_clean_env
|
||||
with_original_env do
|
||||
ENV['MANPATH'] = ENV['BUNDLE_ORIG_MANPATH']
|
||||
ENV.delete_if { |k,_| k[0,7] == 'BUNDLE_' }
|
||||
if ENV.has_key? 'RUBYOPT'
|
||||
ENV['RUBYOPT'] = ENV['RUBYOPT'].sub '-rbundler/setup', ''
|
||||
ENV['RUBYOPT'] = ENV['RUBYOPT'].sub "-I#{File.expand_path('..', __FILE__)}", ''
|
||||
end
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def clean_system(*args)
|
||||
with_clean_env { Kernel.system(*args) }
|
||||
end
|
||||
|
||||
def clean_exec(*args)
|
||||
with_clean_env { Kernel.exec(*args) }
|
||||
end
|
||||
|
||||
def default_gemfile
|
||||
SharedHelpers.default_gemfile
|
||||
end
|
||||
|
||||
def default_lockfile
|
||||
SharedHelpers.default_lockfile
|
||||
end
|
||||
|
||||
def system_bindir
|
||||
# Gem.bindir doesn't always return the location that Rubygems will install
|
||||
# system binaries. If you put '-n foo' in your .gemrc, Rubygems will
|
||||
# install binstubs there instead. Unfortunately, Rubygems doesn't expose
|
||||
# that directory at all, so rather than parse .gemrc ourselves, we allow
|
||||
# the directory to be set as well, via `bundle config bindir foo`.
|
||||
Bundler.settings[:system_bindir] || Bundler.rubygems.gem_bindir
|
||||
end
|
||||
|
||||
def requires_sudo?
|
||||
return @requires_sudo if defined?(@requires_sudo_ran)
|
||||
|
||||
if settings.allow_sudo?
|
||||
sudo_present = which "sudo"
|
||||
end
|
||||
|
||||
if sudo_present
|
||||
# the bundle path and subdirectories need to be writable for Rubygems
|
||||
# to be able to unpack and install gems without exploding
|
||||
path = bundle_path
|
||||
path = path.parent until path.exist?
|
||||
|
||||
# bins are written to a different location on OS X
|
||||
bin_dir = Pathname.new(Bundler.system_bindir)
|
||||
bin_dir = bin_dir.parent until bin_dir.exist?
|
||||
|
||||
# if any directory is not writable, we need sudo
|
||||
files = [path, bin_dir] | Dir[path.join('build_info/*').to_s] | Dir[path.join('*').to_s]
|
||||
sudo_needed = files.any?{|f| !File.writable?(f) }
|
||||
end
|
||||
|
||||
@requires_sudo_ran = true
|
||||
@requires_sudo = settings.allow_sudo? && sudo_present && sudo_needed
|
||||
end
|
||||
|
||||
def mkdir_p(path)
|
||||
if requires_sudo?
|
||||
sudo "mkdir -p '#{path}'" unless File.exist?(path)
|
||||
else
|
||||
FileUtils.mkdir_p(path)
|
||||
end
|
||||
end
|
||||
|
||||
def which(executable)
|
||||
if File.file?(executable) && File.executable?(executable)
|
||||
executable
|
||||
elsif ENV['PATH']
|
||||
path = ENV['PATH'].split(File::PATH_SEPARATOR).find do |p|
|
||||
File.executable?(File.join(p, executable))
|
||||
end
|
||||
path && File.expand_path(executable, path)
|
||||
end
|
||||
end
|
||||
|
||||
def sudo(str)
|
||||
prompt = "\n\n" + <<-PROMPT.gsub(/^ {6}/, '').strip + " "
|
||||
Your user account isn't allowed to install to the system Rubygems.
|
||||
You can cancel this installation and run:
|
||||
|
||||
bundle install --path vendor/bundle
|
||||
|
||||
to install the gems into ./vendor/bundle/, or you can enter your password
|
||||
and install the bundled gems to Rubygems using sudo.
|
||||
|
||||
Password:
|
||||
PROMPT
|
||||
|
||||
`sudo -p "#{prompt}" #{str}`
|
||||
end
|
||||
|
||||
def read_file(file)
|
||||
File.open(file, "rb") { |f| f.read }
|
||||
end
|
||||
|
||||
def load_marshal(data)
|
||||
Marshal.load(data)
|
||||
rescue => e
|
||||
raise MarshalError, "#{e.class}: #{e.message}"
|
||||
end
|
||||
|
||||
def load_gemspec(file)
|
||||
@gemspec_cache ||= {}
|
||||
key = File.expand_path(file)
|
||||
spec = ( @gemspec_cache[key] ||= load_gemspec_uncached(file) )
|
||||
# Protect against caching side-effected gemspecs by returning a
|
||||
# new instance each time.
|
||||
spec.dup if spec
|
||||
end
|
||||
|
||||
def load_gemspec_uncached(file)
|
||||
path = Pathname.new(file)
|
||||
# Eval the gemspec from its parent directory, because some gemspecs
|
||||
# depend on "./" relative paths.
|
||||
SharedHelpers.chdir(path.dirname.to_s) do
|
||||
contents = path.read
|
||||
if contents[0..2] == "---" # YAML header
|
||||
eval_yaml_gemspec(path, contents)
|
||||
else
|
||||
eval_gemspec(path, contents)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def clear_gemspec_cache
|
||||
@gemspec_cache = {}
|
||||
end
|
||||
|
||||
def git_present?
|
||||
return @git_present if defined?(@git_present)
|
||||
@git_present = Bundler.which("git") || Bundler.which("git.exe")
|
||||
end
|
||||
|
||||
def ruby_version
|
||||
@ruby_version ||= SystemRubyVersion.new
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def eval_yaml_gemspec(path, contents)
|
||||
# If the YAML is invalid, Syck raises an ArgumentError, and Psych
|
||||
# raises a Psych::SyntaxError. See psyched_yaml.rb for more info.
|
||||
Gem::Specification.from_yaml(contents)
|
||||
rescue YamlSyntaxError, ArgumentError, Gem::EndOfYAMLException, Gem::Exception
|
||||
eval_gemspec(path, contents)
|
||||
end
|
||||
|
||||
def eval_gemspec(path, contents)
|
||||
eval(contents, TOPLEVEL_BINDING, path.expand_path.to_s)
|
||||
rescue ScriptError, StandardError => e
|
||||
original_line = e.backtrace.find { |line| line.include?(path.to_s) }
|
||||
msg = "There was a #{e.class} while loading #{path.basename}: \n#{e.message}"
|
||||
msg << " from\n #{original_line}" if original_line
|
||||
msg << "\n"
|
||||
|
||||
if e.is_a?(LoadError) && RUBY_VERSION >= "1.9"
|
||||
msg << "\nDoes it try to require a relative path? That's been removed in Ruby 1.9."
|
||||
end
|
||||
|
||||
raise GemspecError, msg
|
||||
end
|
||||
|
||||
def configure_gem_home_and_path
|
||||
blank_home = ENV['GEM_HOME'].nil? || ENV['GEM_HOME'].empty?
|
||||
if settings[:disable_shared_gems]
|
||||
ENV['GEM_PATH'] = ''
|
||||
elsif blank_home || Bundler.rubygems.gem_dir != bundle_path.to_s
|
||||
possibles = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path]
|
||||
paths = possibles.flatten.compact.uniq.reject { |p| p.empty? }
|
||||
ENV["GEM_PATH"] = paths.join(File::PATH_SEPARATOR)
|
||||
end
|
||||
|
||||
configure_gem_home
|
||||
bundle_path
|
||||
end
|
||||
|
||||
def configure_gem_home
|
||||
# TODO: This mkdir_p is only needed for JRuby <= 1.5 and should go away (GH #602)
|
||||
FileUtils.mkdir_p bundle_path.to_s rescue nil
|
||||
|
||||
ENV['GEM_HOME'] = File.expand_path(bundle_path, root)
|
||||
Bundler.rubygems.clear_paths
|
||||
end
|
||||
|
||||
def upgrade_lockfile
|
||||
lockfile = default_lockfile
|
||||
if lockfile.exist? && lockfile.read(3) == "---"
|
||||
Bundler.ui.warn "Detected Gemfile.lock generated by 0.9, deleting..."
|
||||
lockfile.rmtree
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
# Capistrano task for Bundler.
|
||||
#
|
||||
# Just add "require 'bundler/capistrano'" in your Capistrano deploy.rb, and
|
||||
# Bundler will be activated after each new deployment.
|
||||
require 'bundler/deployment'
|
||||
require 'capistrano/version'
|
||||
|
||||
if defined?(Capistrano::Version) && Gem::Version.new(Capistrano::Version).release >= Gem::Version.new("3.0")
|
||||
raise "For Capistrano 3.x integration, please use http://github.com/capistrano/bundler"
|
||||
end
|
||||
|
||||
Capistrano::Configuration.instance(:must_exist).load do
|
||||
before "deploy:finalize_update", "bundle:install"
|
||||
Bundler::Deployment.define_task(self, :task, :except => { :no_release => true })
|
||||
set :rake, lambda { "#{fetch(:bundle_cmd, "bundle")} exec rake" }
|
||||
end
|
||||
@@ -0,0 +1,372 @@
|
||||
require 'bundler'
|
||||
require 'bundler/vendored_thor'
|
||||
|
||||
module Bundler
|
||||
class CLI < Thor
|
||||
include Thor::Actions
|
||||
|
||||
def self.start(*)
|
||||
super
|
||||
rescue Exception => e
|
||||
Bundler.ui = UI::Shell.new
|
||||
raise e
|
||||
end
|
||||
|
||||
def initialize(*)
|
||||
super
|
||||
ENV['BUNDLE_GEMFILE'] = File.expand_path(options[:gemfile]) if options[:gemfile]
|
||||
Bundler::Retry.attempts = options[:retry] || Bundler.settings[:retry] || Bundler::Retry::DEFAULT_ATTEMPTS
|
||||
Bundler.rubygems.ui = UI::RGProxy.new(Bundler.ui)
|
||||
rescue UnknownArgumentError => e
|
||||
raise InvalidOption, e.message
|
||||
ensure
|
||||
self.options ||= {}
|
||||
Bundler.ui = UI::Shell.new(options)
|
||||
Bundler.ui.level = "debug" if options["verbose"]
|
||||
end
|
||||
|
||||
check_unknown_options!(:except => [:config, :exec])
|
||||
stop_on_unknown_option! :exec
|
||||
|
||||
default_task :install
|
||||
class_option "no-color", :type => :boolean, :banner => "Disable colorization in output"
|
||||
class_option "verbose", :type => :boolean, :banner => "Enable verbose output mode", :aliases => "-V"
|
||||
class_option "retry", :type => :numeric, :aliases => "-r", :banner =>
|
||||
"Specify the number of times you wish to attempt network commands"
|
||||
|
||||
def help(cli = nil)
|
||||
case cli
|
||||
when "gemfile" then command = "gemfile.5"
|
||||
when nil then command = "bundle"
|
||||
else command = "bundle-#{cli}"
|
||||
end
|
||||
|
||||
manpages = %w(
|
||||
bundle
|
||||
bundle-config
|
||||
bundle-exec
|
||||
bundle-install
|
||||
bundle-package
|
||||
bundle-update
|
||||
bundle-platform
|
||||
gemfile.5)
|
||||
|
||||
if manpages.include?(command)
|
||||
root = File.expand_path("../man", __FILE__)
|
||||
|
||||
if Bundler.which("man") && root !~ %r{^file:/.+!/META-INF/jruby.home/.+}
|
||||
Kernel.exec "man #{root}/#{command}"
|
||||
else
|
||||
puts File.read("#{root}/#{command}.txt")
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
desc "init [OPTIONS]", "Generates a Gemfile into the current working directory"
|
||||
long_desc <<-D
|
||||
Init generates a default Gemfile in the current working directory. When adding a
|
||||
Gemfile to a gem with a gemspec, the --gemspec option will automatically add each
|
||||
dependency listed in the gemspec file to the newly created Gemfile.
|
||||
D
|
||||
method_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile"
|
||||
def init
|
||||
require 'bundler/cli/init'
|
||||
Init.new(options.dup).run
|
||||
end
|
||||
|
||||
desc "check [OPTIONS]", "Checks if the dependencies listed in Gemfile are satisfied by currently installed gems"
|
||||
long_desc <<-D
|
||||
Check searches the local machine for each of the gems requested in the Gemfile. If
|
||||
all gems are found, Bundler prints a success message and exits with a status of 0.
|
||||
If not, the first missing gem is listed and Bundler exits status 1.
|
||||
D
|
||||
method_option "gemfile", :type => :string, :banner =>
|
||||
"Use the specified gemfile instead of Gemfile"
|
||||
method_option "path", :type => :string, :banner =>
|
||||
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine"
|
||||
method_option "dry-run", :type => :boolean, :default => false, :banner =>
|
||||
"Lock the Gemfile"
|
||||
def check
|
||||
require 'bundler/cli/check'
|
||||
Check.new(options).run
|
||||
end
|
||||
|
||||
desc "install [OPTIONS]", "Install the current environment to the system"
|
||||
long_desc <<-D
|
||||
Install will install all of the gems in the current bundle, making them available
|
||||
for use. In a freshly checked out repository, this command will give you the same
|
||||
gem versions as the last person who updated the Gemfile and ran `bundle update`.
|
||||
|
||||
Passing [DIR] to install (e.g. vendor) will cause the unpacked gems to be installed
|
||||
into the [DIR] directory rather than into system gems.
|
||||
|
||||
If the bundle has already been installed, bundler will tell you so and then exit.
|
||||
D
|
||||
method_option "without", :type => :array, :banner =>
|
||||
"Exclude gems that are part of the specified named group."
|
||||
method_option "gemfile", :type => :string, :banner =>
|
||||
"Use the specified gemfile instead of Gemfile"
|
||||
method_option "no-prune", :type => :boolean, :banner =>
|
||||
"Don't remove stale gems from the cache."
|
||||
method_option "no-cache", :type => :boolean, :banner =>
|
||||
"Don't update the existing gem cache."
|
||||
method_option "quiet", :type => :boolean, :banner =>
|
||||
"Only output warnings and errors."
|
||||
method_option "local", :type => :boolean, :banner =>
|
||||
"Do not attempt to fetch gems remotely and use the gem cache instead"
|
||||
method_option "binstubs", :type => :string, :lazy_default => "bin", :banner =>
|
||||
"Generate bin stubs for bundled gems to ./bin"
|
||||
method_option "shebang", :type => :string, :banner =>
|
||||
"Specify a different shebang executable name than the default (usually 'ruby')"
|
||||
method_option "path", :type => :string, :banner =>
|
||||
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine"
|
||||
method_option "system", :type => :boolean, :banner =>
|
||||
"Install to the system location ($BUNDLE_PATH or $GEM_HOME) even if the bundle was previously installed somewhere else for this application"
|
||||
method_option "frozen", :type => :boolean, :banner =>
|
||||
"Do not allow the Gemfile.lock to be updated after this install"
|
||||
method_option "deployment", :type => :boolean, :banner =>
|
||||
"Install using defaults tuned for deployment environments"
|
||||
method_option "standalone", :type => :array, :lazy_default => [], :banner =>
|
||||
"Make a bundle that can work without the Bundler runtime"
|
||||
method_option "full-index", :type => :boolean, :banner =>
|
||||
"Use the rubygems modern index instead of the API endpoint"
|
||||
method_option "clean", :type => :boolean, :banner =>
|
||||
"Run bundle clean automatically after install"
|
||||
method_option "trust-policy", :alias => "P", :type => :string, :banner =>
|
||||
"Gem trust policy (like gem install -P). Must be one of " +
|
||||
Bundler.rubygems.security_policy_keys.join('|')
|
||||
method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
|
||||
"Specify the number of jobs to run in parallel"
|
||||
|
||||
def install
|
||||
require 'bundler/cli/install'
|
||||
Install.new(options.dup).run
|
||||
end
|
||||
|
||||
desc "update [OPTIONS]", "update the current environment"
|
||||
long_desc <<-D
|
||||
Update will install the newest versions of the gems listed in the Gemfile. Use
|
||||
update when you have changed the Gemfile, or if you want to get the newest
|
||||
possible versions of the gems in the bundle.
|
||||
D
|
||||
method_option "source", :type => :array, :banner => "Update a specific source (and all gems associated with it)"
|
||||
method_option "local", :type => :boolean, :banner =>
|
||||
"Do not attempt to fetch gems remotely and use the gem cache instead"
|
||||
method_option "quiet", :type => :boolean, :banner =>
|
||||
"Only output warnings and errors."
|
||||
method_option "full-index", :type => :boolean, :banner =>
|
||||
"Use the rubygems modern index instead of the API endpoint"
|
||||
method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
|
||||
"Specify the number of jobs to run in parallel"
|
||||
method_option "group", :aliases => "-g", :type => :array, :banner =>
|
||||
"Update a specific group"
|
||||
def update(*gems)
|
||||
require 'bundler/cli/update'
|
||||
Update.new(options, gems).run
|
||||
end
|
||||
|
||||
desc "show GEM [OPTIONS]", "Shows all gems that are part of the bundle, or the path to a given gem"
|
||||
long_desc <<-D
|
||||
Show lists the names and versions of all gems that are required by your Gemfile.
|
||||
Calling show with [GEM] will list the exact location of that gem on your machine.
|
||||
D
|
||||
method_option "paths", :type => :boolean,
|
||||
:banner => "List the paths of all gems that are required by your Gemfile."
|
||||
def show(gem_name = nil)
|
||||
require 'bundler/cli/show'
|
||||
Show.new(options, gem_name).run
|
||||
end
|
||||
map %w(list) => "show"
|
||||
|
||||
desc "binstubs GEM [OPTIONS]", "install the binstubs of the listed gem"
|
||||
long_desc <<-D
|
||||
Generate binstubs for executables in [GEM]. Binstubs are put into bin,
|
||||
or the --binstubs directory if one has been set.
|
||||
D
|
||||
method_option "path", :type => :string, :lazy_default => "bin", :banner =>
|
||||
"binstub destination directory (default bin)"
|
||||
method_option "force", :type => :boolean, :default => false, :banner =>
|
||||
"overwrite existing binstubs if they exist"
|
||||
def binstubs(*gems)
|
||||
require 'bundler/cli/binstubs'
|
||||
Binstubs.new(options, gems).run
|
||||
end
|
||||
|
||||
desc "outdated GEM [OPTIONS]", "list installed gems with newer versions available"
|
||||
long_desc <<-D
|
||||
Outdated lists the names and versions of gems that have a newer version available
|
||||
in the given source. Calling outdated with [GEM [GEM]] will only check for newer
|
||||
versions of the given gems. Prerelease gems are ignored by default. If your gems
|
||||
are up to date, Bundler will exit with a status of 0. Otherwise, it will exit 1.
|
||||
D
|
||||
method_option "pre", :type => :boolean, :banner => "Check for newer pre-release gems"
|
||||
method_option "source", :type => :array, :banner => "Check against a specific source"
|
||||
method_option "local", :type => :boolean, :banner =>
|
||||
"Do not attempt to fetch gems remotely and use the gem cache instead"
|
||||
method_option "strict", :type => :boolean, :banner =>
|
||||
"Only list newer versions allowed by your Gemfile requirements"
|
||||
def outdated(*gems)
|
||||
require 'bundler/cli/outdated'
|
||||
Outdated.new(options, gems).run
|
||||
end
|
||||
|
||||
desc "cache [OPTIONS]", "Cache all the gems to vendor/cache", :hide => true
|
||||
method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
|
||||
method_option "all", :type => :boolean, :banner => "Include all sources (including path and git)."
|
||||
def cache
|
||||
require 'bundler/cli/cache'
|
||||
Cache.new(options).run
|
||||
end
|
||||
|
||||
desc "package [OPTIONS]", "Locks and then caches all of the gems into vendor/cache"
|
||||
method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
|
||||
method_option "all", :type => :boolean, :banner => "Include all sources (including path and git)."
|
||||
method_option "quiet", :type => :boolean, :banner => "Only output warnings and errors."
|
||||
method_option "path", :type => :string, :banner =>
|
||||
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine"
|
||||
method_option "gemfile", :type => :string, :banner => "Use the specified gemfile instead of Gemfile"
|
||||
long_desc <<-D
|
||||
The package command will copy the .gem files for every gem in the bundle into the
|
||||
directory ./vendor/cache. If you then check that directory into your source
|
||||
control repository, others who check out your source will be able to install the
|
||||
bundle without having to download any additional gems.
|
||||
D
|
||||
def package
|
||||
require 'bundler/cli/package'
|
||||
Package.new(options).run
|
||||
end
|
||||
map %w(pack) => :package
|
||||
|
||||
desc "exec [OPTIONS]", "Run the command in context of the bundle"
|
||||
method_option :keep_file_descriptors, :type => :boolean, :default => false
|
||||
long_desc <<-D
|
||||
Exec runs a command, providing it access to the gems in the bundle. While using
|
||||
bundle exec you can require and call the bundled gems as if they were installed
|
||||
into the system wide Rubygems repository.
|
||||
D
|
||||
def exec(*args)
|
||||
require 'bundler/cli/exec'
|
||||
Exec.new(options, args).run
|
||||
end
|
||||
|
||||
desc "config NAME [VALUE]", "retrieve or set a configuration value"
|
||||
long_desc <<-D
|
||||
Retrieves or sets a configuration value. If only one parameter is provided, retrieve the value. If two parameters are provided, replace the
|
||||
existing value with the newly provided one.
|
||||
|
||||
By default, setting a configuration value sets it for all projects
|
||||
on the machine.
|
||||
|
||||
If a global setting is superceded by local configuration, this command
|
||||
will show the current value, as well as any superceded values and
|
||||
where they were specified.
|
||||
D
|
||||
def config(*args)
|
||||
require 'bundler/cli/config'
|
||||
Config.new(options, args, self).run
|
||||
end
|
||||
|
||||
desc "open GEM", "Opens the source directory of the given bundled gem"
|
||||
def open(name)
|
||||
require 'bundler/cli/open'
|
||||
Open.new(options, name).run
|
||||
end
|
||||
|
||||
CONSOLES = {
|
||||
'pry' => :Pry,
|
||||
'ripl' => :Ripl,
|
||||
'irb' => :IRB,
|
||||
}
|
||||
|
||||
desc "console [GROUP]", "Opens an IRB session with the bundle pre-loaded"
|
||||
def console(group = nil)
|
||||
require 'bundler/cli/console'
|
||||
Console.new(options, group, CONSOLES).run
|
||||
end
|
||||
|
||||
desc "version", "Prints the bundler's version information"
|
||||
def version
|
||||
Bundler.ui.info "Bundler version #{Bundler::VERSION}"
|
||||
end
|
||||
map %w(-v --version) => :version
|
||||
|
||||
desc "licenses", "Prints the license of all gems in the bundle"
|
||||
def licenses
|
||||
Bundler.load.specs.sort_by { |s| s.license.to_s }.reverse.each do |s|
|
||||
gem_name = s.name
|
||||
license = s.license || s.licenses
|
||||
|
||||
if license.empty?
|
||||
Bundler.ui.warn "#{gem_name}: Unknown"
|
||||
else
|
||||
Bundler.ui.info "#{gem_name}: #{license}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc 'viz [OPTIONS]', "Generates a visual dependency graph"
|
||||
long_desc <<-D
|
||||
Viz generates a PNG file of the current Gemfile as a dependency graph.
|
||||
Viz requires the ruby-graphviz gem (and its dependencies).
|
||||
The associated gems must also be installed via 'bundle install'.
|
||||
D
|
||||
method_option :file, :type => :string, :default => 'gem_graph', :aliases => '-f', :banner => "The name to use for the generated file. see format option"
|
||||
method_option :version, :type => :boolean, :default => false, :aliases => '-v', :banner => "Set to show each gem version."
|
||||
method_option :requirements, :type => :boolean, :default => false, :aliases => '-r', :banner => "Set to show the version of each required dependency."
|
||||
method_option :format, :type => :string, :default => "png", :aliases => '-F', :banner => "This is output format option. Supported format is png, jpg, svg, dot ..."
|
||||
def viz
|
||||
require 'bundler/cli/viz'
|
||||
Viz.new(options).run
|
||||
end
|
||||
|
||||
desc "gem GEM [OPTIONS]", "Creates a skeleton for creating a rubygem"
|
||||
method_option :bin, :type => :boolean, :default => false, :aliases => '-b', :banner => "Generate a binary for your library."
|
||||
method_option :test, :type => :string, :lazy_default => 'rspec', :aliases => '-t', :banner => "Generate a test directory for your library: 'rspec' is the default, but 'minitest' is also supported."
|
||||
method_option :edit, :type => :string, :aliases => "-e",
|
||||
:lazy_default => [ENV['BUNDLER_EDITOR'], ENV['VISUAL'], ENV['EDITOR']].find{|e| !e.nil? && !e.empty? },
|
||||
:required => false, :banner => "/path/to/your/editor",
|
||||
:desc => "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)"
|
||||
method_option :ext, :type => :boolean, :detailt => false, :banner => "Generate the boilerplate for C extension code"
|
||||
|
||||
def gem(name)
|
||||
require 'bundler/cli/gem'
|
||||
Gem.new(options, name, self).run
|
||||
end
|
||||
|
||||
def self.source_root
|
||||
File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
|
||||
end
|
||||
|
||||
desc "clean [OPTIONS]", "Cleans up unused gems in your bundler directory"
|
||||
method_option "dry-run", :type => :boolean, :default => false, :banner =>
|
||||
"only print out changes, do not actually clean gems"
|
||||
method_option "force", :type => :boolean, :default => false, :banner =>
|
||||
"forces clean even if --path is not set"
|
||||
def clean
|
||||
require 'bundler/cli/clean'
|
||||
Clean.new(options.dup).run
|
||||
end
|
||||
|
||||
desc "platform [OPTIONS]", "Displays platform compatibility information"
|
||||
method_option "ruby", :type => :boolean, :default => false, :banner =>
|
||||
"only display ruby related platform information"
|
||||
def platform
|
||||
require 'bundler/cli/platform'
|
||||
Platform.new(options).run
|
||||
end
|
||||
|
||||
desc "inject GEM VERSION ...", "Add the named gem(s), with version requirements, to the resolved Gemfile"
|
||||
def inject(name, version, *gems)
|
||||
require 'bundler/cli/inject'
|
||||
Inject.new(options, name, version, gems).run
|
||||
end
|
||||
|
||||
desc "env", "Print information about the environment Bundler is running under"
|
||||
def env
|
||||
Env.new.write($stdout)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
require "bundler/cli/common"
|
||||
|
||||
module Bundler
|
||||
class CLI::Binstubs
|
||||
attr_reader :options, :gems
|
||||
def initialize(options, gems)
|
||||
@options = options
|
||||
@gems = gems
|
||||
end
|
||||
|
||||
def run
|
||||
Bundler.definition.validate_ruby!
|
||||
Bundler.settings[:bin] = options["path"] if options["path"]
|
||||
Bundler.settings[:bin] = nil if options["path"] && options["path"].empty?
|
||||
installer = Installer.new(Bundler.root, Bundler.definition)
|
||||
|
||||
if gems.empty?
|
||||
Bundler.ui.error "`bundle binstubs` needs at least one gem to run."
|
||||
exit 1
|
||||
end
|
||||
|
||||
gems.each do |gem_name|
|
||||
spec = installer.specs.find{|s| s.name == gem_name }
|
||||
unless spec
|
||||
raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(
|
||||
gem_name, Bundler.definition.specs)
|
||||
end
|
||||
|
||||
if spec.name == "bundler"
|
||||
Bundler.ui.warn "Sorry, Bundler can only be run via Rubygems."
|
||||
else
|
||||
installer.generate_bundler_executable_stubs(spec, :force => options[:force], :binstubs_cmd => true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,34 @@
|
||||
module Bundler
|
||||
class CLI::Cache
|
||||
attr_reader :options
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
Bundler.definition.validate_ruby!
|
||||
Bundler.definition.resolve_with_cache!
|
||||
setup_cache_all
|
||||
Bundler.load.cache
|
||||
Bundler.settings[:no_prune] = true if options["no-prune"]
|
||||
Bundler.load.lock
|
||||
rescue GemNotFound => e
|
||||
Bundler.ui.error(e.message)
|
||||
Bundler.ui.warn "Run `bundle install` to install missing gems."
|
||||
exit 1
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def setup_cache_all
|
||||
Bundler.settings[:cache_all] = options[:all] if options.key?("all")
|
||||
|
||||
if Bundler.definition.has_local_dependencies? && !Bundler.settings[:cache_all]
|
||||
Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \
|
||||
"to package them as well, please pass the --all flag. This will be the default " \
|
||||
"on Bundler 2.0."
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
module Bundler
|
||||
class CLI::Check
|
||||
attr_reader :options
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
Bundler.settings[:path] = File.expand_path(options[:path]) if options[:path]
|
||||
begin
|
||||
definition = Bundler.definition
|
||||
definition.validate_ruby!
|
||||
not_installed = definition.missing_specs
|
||||
rescue GemNotFound, VersionConflict
|
||||
Bundler.ui.error "Bundler can't satisfy your Gemfile's dependencies."
|
||||
Bundler.ui.warn "Install missing gems with `bundle install`."
|
||||
exit 1
|
||||
end
|
||||
|
||||
if not_installed.any?
|
||||
Bundler.ui.error "The following gems are missing"
|
||||
not_installed.each { |s| Bundler.ui.error " * #{s.name} (#{s.version})" }
|
||||
Bundler.ui.warn "Install missing gems with `bundle install`"
|
||||
exit 1
|
||||
elsif !Bundler.default_lockfile.exist? && Bundler.settings[:frozen]
|
||||
Bundler.ui.error "This bundle has been frozen, but there is no Gemfile.lock present"
|
||||
exit 1
|
||||
else
|
||||
Bundler.load.lock unless options[:"dry-run"]
|
||||
Bundler.ui.info "The Gemfile's dependencies are satisfied"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
module Bundler
|
||||
class CLI::Clean
|
||||
attr_reader :options
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
if Bundler.settings[:path] || options[:force]
|
||||
Bundler.load.clean(options[:"dry-run"])
|
||||
else
|
||||
Bundler.ui.error "Can only use bundle clean when --path is set or --force is set"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,54 @@
|
||||
module Bundler
|
||||
module CLI::Common
|
||||
def self.without_groups_message
|
||||
groups = Bundler.settings.without
|
||||
group_list = [groups[0...-1].join(", "), groups[-1..-1]].
|
||||
reject{|s| s.to_s.empty? }.join(" and ")
|
||||
group_str = (groups.size == 1) ? "group" : "groups"
|
||||
"Gems in the #{group_str} #{group_list} were not installed."
|
||||
end
|
||||
|
||||
def self.select_spec(name, regex_match = nil)
|
||||
specs = []
|
||||
regexp = Regexp.new(name) if regex_match
|
||||
|
||||
Bundler.definition.specs.each do |spec|
|
||||
return spec if spec.name == name
|
||||
specs << spec if regexp && spec.name =~ regexp
|
||||
end
|
||||
|
||||
case specs.count
|
||||
when 0
|
||||
raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
|
||||
when 1
|
||||
specs.first
|
||||
else
|
||||
ask_for_spec_from(specs)
|
||||
end
|
||||
end
|
||||
|
||||
def self.ask_for_spec_from(specs)
|
||||
if !$stdout.tty? && ENV['BUNDLE_SPEC_RUN'].nil?
|
||||
raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
|
||||
end
|
||||
|
||||
specs.each_with_index do |spec, index|
|
||||
Bundler.ui.info "#{index.succ} : #{spec.name}", true
|
||||
end
|
||||
Bundler.ui.info '0 : - exit -', true
|
||||
|
||||
num = Bundler.ui.ask('> ').to_i
|
||||
num > 0 ? specs[num - 1] : nil
|
||||
end
|
||||
|
||||
def self.gem_not_found_message(missing_gem_name, alternatives)
|
||||
require 'bundler/similarity_detector'
|
||||
message = "Could not find gem '#{missing_gem_name}'."
|
||||
alternate_names = alternatives.map { |a| a.respond_to?(:name) ? a.name : a }
|
||||
suggestions = SimilarityDetector.new(alternate_names).similar_word_list(missing_gem_name)
|
||||
message += "\nDid you mean #{suggestions}?" if suggestions
|
||||
message
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,84 @@
|
||||
module Bundler
|
||||
class CLI::Config
|
||||
attr_reader :options, :thor
|
||||
attr_accessor :args
|
||||
|
||||
def initialize(options, args, thor)
|
||||
@options = options
|
||||
@args = args
|
||||
@thor = thor
|
||||
end
|
||||
|
||||
def run
|
||||
peek = args.shift
|
||||
|
||||
if peek && peek =~ /^\-\-/
|
||||
name, scope = args.shift, $'
|
||||
else
|
||||
name, scope = peek, "global"
|
||||
end
|
||||
|
||||
unless name
|
||||
Bundler.ui.confirm "Settings are listed in order of priority. The top value will be used.\n"
|
||||
|
||||
Bundler.settings.all.each do |setting|
|
||||
Bundler.ui.confirm "#{setting}"
|
||||
thor.with_padding do
|
||||
Bundler.settings.pretty_values_for(setting).each do |line|
|
||||
Bundler.ui.info line
|
||||
end
|
||||
end
|
||||
Bundler.ui.confirm ""
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
case scope
|
||||
when "delete"
|
||||
Bundler.settings.set_local(name, nil)
|
||||
Bundler.settings.set_global(name, nil)
|
||||
when "local", "global"
|
||||
if args.empty?
|
||||
Bundler.ui.confirm "Settings for `#{name}` in order of priority. The top value will be used"
|
||||
thor.with_padding do
|
||||
Bundler.settings.pretty_values_for(name).each { |line| Bundler.ui.info line }
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
locations = Bundler.settings.locations(name)
|
||||
|
||||
if scope == "global"
|
||||
if local = locations[:local]
|
||||
Bundler.ui.info "Your application has set #{name} to #{local.inspect}. This will override the " \
|
||||
"global value you are currently setting"
|
||||
end
|
||||
|
||||
if env = locations[:env]
|
||||
Bundler.ui.info "You have a bundler environment variable for #{name} set to #{env.inspect}. " \
|
||||
"This will take precedence over the global value you are setting"
|
||||
end
|
||||
|
||||
if global = locations[:global]
|
||||
Bundler.ui.info "You are replacing the current global value of #{name}, which is currently #{global.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
if scope == "local" && local = locations[:local]
|
||||
Bundler.ui.info "You are replacing the current local value of #{name}, which is currently #{local.inspect}"
|
||||
end
|
||||
|
||||
if name.match(/\Alocal\./)
|
||||
pathname = Pathname.new(args.join(" "))
|
||||
self.args = [pathname.expand_path.to_s] if pathname.directory?
|
||||
end
|
||||
|
||||
Bundler.settings.send("set_#{scope}", name, args.join(" "))
|
||||
else
|
||||
Bundler.ui.error "Invalid scope --#{scope} given. Please use --local or --global."
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,42 @@
|
||||
module Bundler
|
||||
class CLI::Console
|
||||
attr_reader :options, :group, :consoles
|
||||
def initialize(options, group, consoles)
|
||||
@options = options
|
||||
@group = group
|
||||
@consoles = consoles
|
||||
end
|
||||
|
||||
def run
|
||||
group ? Bundler.require(:default, *(group.split.map! {|g| g.to_sym })) : Bundler.require
|
||||
ARGV.clear
|
||||
|
||||
preferred = Bundler.settings[:console] || 'irb'
|
||||
|
||||
# See if console is available
|
||||
begin
|
||||
require preferred || true
|
||||
rescue LoadError
|
||||
# Is it in Gemfile?
|
||||
Bundler.ui.error "Could not load the #{preferred} console"
|
||||
Bundler.ui.info "Falling back on IRB..."
|
||||
|
||||
require 'irb'
|
||||
preferred = 'irb'
|
||||
end
|
||||
|
||||
constant = consoles[preferred]
|
||||
|
||||
console = begin
|
||||
Object.const_get(constant)
|
||||
rescue NameError => e
|
||||
Bundler.ui.error e.inspect
|
||||
Bundler.ui.error "Could not load the #{constant} console"
|
||||
return
|
||||
end
|
||||
|
||||
console.start
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
module Bundler
|
||||
class CLI::Exec
|
||||
attr_reader :options, :args
|
||||
|
||||
def initialize(options, args)
|
||||
@options = options
|
||||
@args = args
|
||||
end
|
||||
|
||||
def run
|
||||
Bundler.definition.validate_ruby!
|
||||
Bundler.load.setup_environment
|
||||
|
||||
begin
|
||||
if RUBY_VERSION >= "2.0"
|
||||
@args << { :close_others => !options.keep_file_descriptors? }
|
||||
elsif options.keep_file_descriptors?
|
||||
Bundler.ui.warn "Ruby version #{RUBY_VERSION} defaults to keeping non-standard file descriptors on Kernel#exec."
|
||||
end
|
||||
|
||||
# Run
|
||||
Kernel.exec(*args)
|
||||
rescue Errno::EACCES
|
||||
Bundler.ui.error "bundler: not executable: #{args.first}"
|
||||
exit 126
|
||||
rescue Errno::ENOENT
|
||||
Bundler.ui.error "bundler: command not found: #{args.first}"
|
||||
Bundler.ui.warn "Install missing gem executables with `bundle install`"
|
||||
exit 127
|
||||
rescue ArgumentError
|
||||
Bundler.ui.error "bundler: exec needs a command to run"
|
||||
exit 128
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,77 @@
|
||||
module Bundler
|
||||
class CLI::Gem
|
||||
attr_reader :options, :gem_name, :thor
|
||||
def initialize(options, gem_name, thor)
|
||||
@options = options
|
||||
@gem_name = gem_name
|
||||
@thor = thor
|
||||
end
|
||||
|
||||
def run
|
||||
if options[:ext] && gem_name.index('-')
|
||||
Bundler.ui.error "You have specified a gem name which does not conform to the \n" \
|
||||
"naming guidelines for C extensions. For more information, \n" \
|
||||
"see the 'Extension Naming' section at the following URL:\n" \
|
||||
"http://guides.rubygems.org/gems-with-extensions/\n"
|
||||
exit 1
|
||||
end
|
||||
|
||||
name = gem_name.chomp("/") # remove trailing slash if present
|
||||
underscored_name = name.tr('-', '_')
|
||||
namespaced_path = name.tr('-', '/')
|
||||
target = File.join(Dir.pwd, name)
|
||||
constant_name = name.split('_').map{|p| p[0..0].upcase + p[1..-1] }.join
|
||||
constant_name = constant_name.split('-').map{|q| q[0..0].upcase + q[1..-1] }.join('::') if constant_name =~ /-/
|
||||
constant_array = constant_name.split('::')
|
||||
git_user_name = `git config user.name`.chomp
|
||||
git_user_email = `git config user.email`.chomp
|
||||
opts = {
|
||||
:name => name,
|
||||
:underscored_name => underscored_name,
|
||||
:namespaced_path => namespaced_path,
|
||||
:constant_name => constant_name,
|
||||
:constant_array => constant_array,
|
||||
:author => git_user_name.empty? ? "TODO: Write your name" : git_user_name,
|
||||
:email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
|
||||
:test => options[:test],
|
||||
:ext => options[:ext]
|
||||
}
|
||||
gemspec_dest = File.join(target, "#{name}.gemspec")
|
||||
thor.template(File.join("newgem/Gemfile.tt"), File.join(target, "Gemfile"), opts)
|
||||
thor.template(File.join("newgem/Rakefile.tt"), File.join(target, "Rakefile"), opts)
|
||||
thor.template(File.join("newgem/LICENSE.txt.tt"), File.join(target, "LICENSE.txt"), opts)
|
||||
thor.template(File.join("newgem/README.md.tt"), File.join(target, "README.md"), opts)
|
||||
thor.template(File.join("newgem/gitignore.tt"), File.join(target, ".gitignore"), opts)
|
||||
thor.template(File.join("newgem/newgem.gemspec.tt"), gemspec_dest, opts)
|
||||
thor.template(File.join("newgem/lib/newgem.rb.tt"), File.join(target, "lib/#{namespaced_path}.rb"), opts)
|
||||
thor.template(File.join("newgem/lib/newgem/version.rb.tt"), File.join(target, "lib/#{namespaced_path}/version.rb"), opts)
|
||||
if options[:bin]
|
||||
thor.template(File.join("newgem/bin/newgem.tt"), File.join(target, 'bin', name), opts)
|
||||
end
|
||||
case options[:test]
|
||||
when 'rspec'
|
||||
thor.template(File.join("newgem/rspec.tt"), File.join(target, ".rspec"), opts)
|
||||
thor.template(File.join("newgem/spec/spec_helper.rb.tt"), File.join(target, "spec/spec_helper.rb"), opts)
|
||||
thor.template(File.join("newgem/spec/newgem_spec.rb.tt"), File.join(target, "spec/#{namespaced_path}_spec.rb"), opts)
|
||||
when 'minitest'
|
||||
thor.template(File.join("newgem/test/minitest_helper.rb.tt"), File.join(target, "test/minitest_helper.rb"), opts)
|
||||
thor.template(File.join("newgem/test/test_newgem.rb.tt"), File.join(target, "test/test_#{namespaced_path}.rb"), opts)
|
||||
end
|
||||
if options[:test]
|
||||
thor.template(File.join("newgem/.travis.yml.tt"), File.join(target, ".travis.yml"), opts)
|
||||
end
|
||||
if options[:ext]
|
||||
thor.template(File.join("newgem/ext/newgem/extconf.rb.tt"), File.join(target, "ext/#{name}/extconf.rb"), opts)
|
||||
thor.template(File.join("newgem/ext/newgem/newgem.h.tt"), File.join(target, "ext/#{name}/#{underscored_name}.h"), opts)
|
||||
thor.template(File.join("newgem/ext/newgem/newgem.c.tt"), File.join(target, "ext/#{name}/#{underscored_name}.c"), opts)
|
||||
end
|
||||
Bundler.ui.info "Initializing git repo in #{target}"
|
||||
Dir.chdir(target) { `git init`; `git add .` }
|
||||
|
||||
if options[:edit]
|
||||
thor.run("#{options["edit"]} \"#{gemspec_dest}\"") # Open gemspec in editor
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
module Bundler
|
||||
class CLI::Init
|
||||
attr_reader :options
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
if File.exist?("Gemfile")
|
||||
Bundler.ui.error "Gemfile already exists at #{Dir.pwd}/Gemfile"
|
||||
exit 1
|
||||
end
|
||||
|
||||
if options[:gemspec]
|
||||
gemspec = File.expand_path(options[:gemspec])
|
||||
unless File.exist?(gemspec)
|
||||
Bundler.ui.error "Gem specification #{gemspec} doesn't exist"
|
||||
exit 1
|
||||
end
|
||||
spec = Gem::Specification.load(gemspec)
|
||||
puts "Writing new Gemfile to #{Dir.pwd}/Gemfile"
|
||||
File.open('Gemfile', 'wb') do |file|
|
||||
file << "# Generated from #{gemspec}\n"
|
||||
file << spec.to_gemfile
|
||||
end
|
||||
else
|
||||
puts "Writing new Gemfile to #{Dir.pwd}/Gemfile"
|
||||
FileUtils.cp(File.expand_path('../../templates/Gemfile', __FILE__), 'Gemfile')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
module Bundler
|
||||
class CLI::Inject
|
||||
attr_reader :options, :name, :version, :gems
|
||||
def initialize(options, name, version, gems)
|
||||
@options = options
|
||||
@name = name
|
||||
@version = version
|
||||
@gems = gems
|
||||
end
|
||||
|
||||
def run
|
||||
# The required arguments allow Thor to give useful feedback when the arguments
|
||||
# are incorrect. This adds those first two arguments onto the list as a whole.
|
||||
gems.unshift(version).unshift(name)
|
||||
|
||||
# Build an array of Dependency objects out of the arguments
|
||||
deps = []
|
||||
gems.each_slice(2) do |gem_name, gem_version|
|
||||
deps << Bundler::Dependency.new(gem_name, gem_version)
|
||||
end
|
||||
|
||||
added = Injector.inject(deps)
|
||||
|
||||
if added.any?
|
||||
Bundler.ui.confirm "Added to Gemfile:"
|
||||
Bundler.ui.confirm added.map{ |g| " #{g}" }.join("\n")
|
||||
else
|
||||
Bundler.ui.confirm "All injected gems were already present in the Gemfile"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,141 @@
|
||||
module Bundler
|
||||
class CLI::Install
|
||||
attr_reader :options
|
||||
def initialize(options)
|
||||
@options = options.dup
|
||||
end
|
||||
|
||||
def run
|
||||
warn_if_root
|
||||
|
||||
if options[:without]
|
||||
options[:without] = options[:without].map{|g| g.tr(' ', ':') }
|
||||
end
|
||||
|
||||
ENV['RB_USER_INSTALL'] = '1' if Bundler::FREEBSD
|
||||
|
||||
# Just disable color in deployment mode
|
||||
Bundler.ui.shell = Thor::Shell::Basic.new if options[:deployment]
|
||||
|
||||
if (options[:path] || options[:deployment]) && options[:system]
|
||||
Bundler.ui.error "You have specified both a path to install your gems to, \n" \
|
||||
"as well as --system. Please choose."
|
||||
exit 1
|
||||
end
|
||||
|
||||
if (options["trust-policy"])
|
||||
unless (Bundler.rubygems.security_policies.keys.include?(options["trust-policy"]))
|
||||
Bundler.ui.error "Rubygems doesn't know about trust policy '#{options["trust-policy"]}'. " \
|
||||
"The known policies are: #{Bundler.rubygems.security_policies.keys.join(', ')}."
|
||||
exit 1
|
||||
end
|
||||
Bundler.settings["trust-policy"] = options["trust-policy"]
|
||||
else
|
||||
Bundler.settings["trust-policy"] = nil if Bundler.settings["trust-policy"]
|
||||
end
|
||||
|
||||
if options[:deployment] || options[:frozen]
|
||||
unless Bundler.default_lockfile.exist?
|
||||
flag = options[:deployment] ? '--deployment' : '--frozen'
|
||||
raise ProductionError, "The #{flag} flag requires a Gemfile.lock. Please make " \
|
||||
"sure you have checked your Gemfile.lock into version control " \
|
||||
"before deploying."
|
||||
end
|
||||
|
||||
if Bundler.root.join("vendor/cache").exist?
|
||||
options[:local] = true
|
||||
end
|
||||
|
||||
Bundler.settings[:frozen] = '1'
|
||||
end
|
||||
|
||||
# When install is called with --no-deployment, disable deployment mode
|
||||
if options[:deployment] == false
|
||||
Bundler.settings.delete(:frozen)
|
||||
options[:system] = true
|
||||
end
|
||||
|
||||
Bundler.settings[:path] = nil if options[:system]
|
||||
Bundler.settings[:path] = "vendor/bundle" if options[:deployment]
|
||||
Bundler.settings[:path] = options["path"] if options["path"]
|
||||
Bundler.settings[:path] ||= "bundle" if options["standalone"]
|
||||
Bundler.settings[:bin] = options["binstubs"] if options["binstubs"]
|
||||
Bundler.settings[:bin] = nil if options["binstubs"] && options["binstubs"].empty?
|
||||
Bundler.settings[:shebang] = options["shebang"] if options["shebang"]
|
||||
Bundler.settings[:jobs] = options["jobs"] if options["jobs"]
|
||||
Bundler.settings[:no_prune] = true if options["no-prune"]
|
||||
Bundler.settings[:clean] = options["clean"] if options["clean"]
|
||||
Bundler.settings.without = options[:without]
|
||||
Bundler.ui.level = "warn" if options[:quiet]
|
||||
Bundler::Fetcher.disable_endpoint = options["full-index"]
|
||||
Bundler.settings[:disable_shared_gems] = Bundler.settings[:path] ? '1' : nil
|
||||
|
||||
# rubygems plugins sometimes hook into the gem install process
|
||||
Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins)
|
||||
|
||||
definition = Bundler.definition
|
||||
definition.validate_ruby!
|
||||
Installer.install(Bundler.root, definition, options)
|
||||
Bundler.load.cache if Bundler.root.join("vendor/cache").exist? && !options["no-cache"] && !Bundler.settings[:frozen]
|
||||
|
||||
if Bundler.settings[:path]
|
||||
absolute_path = File.expand_path(Bundler.settings[:path])
|
||||
relative_path = absolute_path.sub(File.expand_path('.'), '.')
|
||||
Bundler.ui.confirm "Your bundle is complete!"
|
||||
without_groups_messages
|
||||
Bundler.ui.confirm "It was installed into #{relative_path}"
|
||||
else
|
||||
Bundler.ui.confirm "Your bundle is complete!"
|
||||
without_groups_messages
|
||||
Bundler.ui.confirm "Use `bundle show [gemname]` to see where a bundled gem is installed."
|
||||
end
|
||||
Installer.post_install_messages.to_a.each do |name, msg|
|
||||
Bundler.ui.confirm "Post-install message from #{name}:"
|
||||
Bundler.ui.info msg
|
||||
end
|
||||
Installer.ambiguous_gems.to_a.each do |name, installed_from_uri, *also_found_in_uris|
|
||||
Bundler.ui.error "Warning: the gem '#{name}' was found in multiple sources."
|
||||
Bundler.ui.error "Installed from: #{installed_from_uri}"
|
||||
Bundler.ui.error "Also found in:"
|
||||
also_found_in_uris.each { |uri| Bundler.ui.error " * #{uri}" }
|
||||
Bundler.ui.error "You should add a source requirement to restrict this gem to your preferred source."
|
||||
Bundler.ui.error "For example:"
|
||||
Bundler.ui.error " gem '#{name}', :source => '#{installed_from_uri}'"
|
||||
end
|
||||
|
||||
if Bundler.settings[:clean] && Bundler.settings[:path]
|
||||
require "bundler/cli/clean"
|
||||
Bundler::CLI::Clean.new(options).run
|
||||
end
|
||||
rescue GemNotFound, VersionConflict => e
|
||||
if options[:local] && Bundler.app_cache.exist?
|
||||
Bundler.ui.warn "Some gems seem to be missing from your vendor/cache directory."
|
||||
end
|
||||
|
||||
unless Bundler.definition.has_rubygems_remotes?
|
||||
Bundler.ui.warn <<-WARN, :wrap => true
|
||||
Your Gemfile has no gem server sources. If you need gems that are \
|
||||
not already on your machine, add a line like this to your Gemfile:
|
||||
source 'https://rubygems.org'
|
||||
WARN
|
||||
end
|
||||
raise e
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def warn_if_root
|
||||
return if Bundler::WINDOWS || !Process.uid.zero?
|
||||
Bundler.ui.warn "Don't run Bundler as root. Bundler can ask for sudo " \
|
||||
"if it is needed, and installing your bundle as root will break this " \
|
||||
"application for all non-root users on this machine.", :wrap => true
|
||||
end
|
||||
|
||||
def without_groups_messages
|
||||
if Bundler.settings.without.any?
|
||||
require "bundler/cli/common"
|
||||
Bundler.ui.confirm Bundler::CLI::Common.without_groups_message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
require 'bundler/cli/common'
|
||||
require 'shellwords'
|
||||
|
||||
module Bundler
|
||||
class CLI::Open
|
||||
attr_reader :options, :name
|
||||
def initialize(options, name)
|
||||
@options = options
|
||||
@name = name
|
||||
end
|
||||
|
||||
def run
|
||||
editor = [ENV['BUNDLER_EDITOR'], ENV['VISUAL'], ENV['EDITOR']].find{|e| !e.nil? && !e.empty? }
|
||||
return Bundler.ui.info("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR") unless editor
|
||||
path = Bundler::CLI::Common.select_spec(name, :regex_match).full_gem_path
|
||||
Dir.chdir(path) do
|
||||
command = Shellwords.split(editor) + [path]
|
||||
system(*command) || Bundler.ui.info("Could not run '#{command.join(' ')}'")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,80 @@
|
||||
require 'bundler/cli/common'
|
||||
|
||||
module Bundler
|
||||
class CLI::Outdated
|
||||
attr_reader :options, :gems
|
||||
def initialize(options, gems)
|
||||
@options = options
|
||||
@gems = gems
|
||||
end
|
||||
|
||||
def run
|
||||
sources = Array(options[:source])
|
||||
|
||||
gems.each do |gem_name|
|
||||
Bundler::CLI::Common.select_spec(gem_name)
|
||||
end
|
||||
|
||||
Bundler.definition.validate_ruby!
|
||||
current_specs = Bundler.ui.silence { Bundler.load.specs }
|
||||
current_dependencies = {}
|
||||
Bundler.ui.silence { Bundler.load.dependencies.each { |dep| current_dependencies[dep.name] = dep } }
|
||||
|
||||
if gems.empty? && sources.empty?
|
||||
# We're doing a full update
|
||||
definition = Bundler.definition(true)
|
||||
else
|
||||
definition = Bundler.definition(:gems => gems, :sources => sources)
|
||||
end
|
||||
options["local"] ? definition.resolve_with_cache! : definition.resolve_remotely!
|
||||
|
||||
Bundler.ui.info ""
|
||||
|
||||
out_count = 0
|
||||
# Loop through the current specs
|
||||
gemfile_specs, dependency_specs = current_specs.partition { |spec| current_dependencies.has_key? spec.name }
|
||||
[gemfile_specs.sort_by(&:name), dependency_specs.sort_by(&:name)].flatten.each do |current_spec|
|
||||
next if !gems.empty? && !gems.include?(current_spec.name)
|
||||
|
||||
dependency = current_dependencies[current_spec.name]
|
||||
|
||||
if options["strict"]
|
||||
active_spec = definition.specs.detect { |spec| spec.name == current_spec.name }
|
||||
else
|
||||
active_spec = definition.index[current_spec.name].sort_by { |b| b.version }
|
||||
if !current_spec.version.prerelease? && !options[:pre] && active_spec.size > 1
|
||||
active_spec = active_spec.delete_if { |b| b.respond_to?(:version) && b.version.prerelease? }
|
||||
end
|
||||
active_spec = active_spec.last
|
||||
end
|
||||
next if active_spec.nil?
|
||||
|
||||
gem_outdated = Gem::Version.new(active_spec.version) > Gem::Version.new(current_spec.version)
|
||||
git_outdated = current_spec.git_version != active_spec.git_version
|
||||
if gem_outdated || git_outdated
|
||||
if out_count == 0
|
||||
if options["pre"]
|
||||
Bundler.ui.info "Outdated gems included in the bundle (including pre-releases):"
|
||||
else
|
||||
Bundler.ui.info "Outdated gems included in the bundle:"
|
||||
end
|
||||
end
|
||||
|
||||
spec_version = "#{active_spec.version}#{active_spec.git_version}"
|
||||
current_version = "#{current_spec.version}#{current_spec.git_version}"
|
||||
dependency_version = %|Gemfile specifies "#{dependency.requirement}"| if dependency && dependency.specific?
|
||||
Bundler.ui.info " * #{active_spec.name} (#{spec_version} > #{current_version}) #{dependency_version}".rstrip
|
||||
out_count += 1
|
||||
end
|
||||
Bundler.ui.debug "from #{active_spec.loaded_from}"
|
||||
end
|
||||
|
||||
if out_count.zero?
|
||||
Bundler.ui.info "Your bundle is up to date!\n"
|
||||
else
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,36 @@
|
||||
module Bundler
|
||||
class CLI::Package
|
||||
attr_reader :options
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
Bundler.ui.level = "warn" if options[:quiet]
|
||||
Bundler.settings[:path] = File.expand_path(options[:path]) if options[:path]
|
||||
setup_cache_all
|
||||
install
|
||||
# TODO: move cache contents here now that all bundles are locked
|
||||
custom_path = Pathname.new(options[:path]) if options[:path]
|
||||
Bundler.load.cache(custom_path)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def install
|
||||
require 'bundler/cli/install'
|
||||
Bundler::CLI::Install.new(options.dup).run
|
||||
end
|
||||
|
||||
def setup_cache_all
|
||||
Bundler.settings[:cache_all] = options[:all] if options.key?("all")
|
||||
|
||||
if Bundler.definition.has_local_dependencies? && !Bundler.settings[:cache_all]
|
||||
Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \
|
||||
"to package them as well, please pass the --all flag. This will be the default " \
|
||||
"on Bundler 2.0."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
module Bundler
|
||||
class CLI::Platform
|
||||
attr_reader :options
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
platforms, ruby_version = Bundler.ui.silence do
|
||||
[ Bundler.definition.platforms.map {|p| "* #{p}" },
|
||||
Bundler.definition.ruby_version ]
|
||||
end
|
||||
output = []
|
||||
|
||||
if options[:ruby]
|
||||
if ruby_version
|
||||
output << ruby_version
|
||||
else
|
||||
output << "No ruby version specified"
|
||||
end
|
||||
else
|
||||
output << "Your platform is: #{RUBY_PLATFORM}"
|
||||
output << "Your app has gems that work on these platforms:\n#{platforms.join("\n")}"
|
||||
|
||||
if ruby_version
|
||||
output << "Your Gemfile specifies a Ruby version requirement:\n* #{ruby_version}"
|
||||
|
||||
begin
|
||||
Bundler.definition.validate_ruby!
|
||||
output << "Your current platform satisfies the Ruby version requirement."
|
||||
rescue RubyVersionMismatch => e
|
||||
output << e.message
|
||||
end
|
||||
else
|
||||
output << "Your Gemfile does not specify a Ruby version requirement."
|
||||
end
|
||||
end
|
||||
|
||||
Bundler.ui.info output.join("\n\n")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,48 @@
|
||||
require 'bundler/cli/common'
|
||||
|
||||
module Bundler
|
||||
class CLI::Show
|
||||
attr_reader :options, :gem_name
|
||||
def initialize(options, gem_name)
|
||||
@options = options
|
||||
@gem_name = gem_name
|
||||
end
|
||||
|
||||
def run
|
||||
Bundler.ui.silence do
|
||||
Bundler.definition.validate_ruby!
|
||||
Bundler.load.lock
|
||||
end
|
||||
|
||||
if gem_name
|
||||
if gem_name == "bundler"
|
||||
path = File.expand_path("../../../..", __FILE__)
|
||||
else
|
||||
spec = Bundler::CLI::Common.select_spec(gem_name, :regex_match)
|
||||
return unless spec
|
||||
path = spec.full_gem_path
|
||||
if !File.directory?(path)
|
||||
Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at:"
|
||||
end
|
||||
end
|
||||
return Bundler.ui.info(path)
|
||||
end
|
||||
|
||||
if options[:paths]
|
||||
Bundler.load.specs.sort_by { |s| s.name }.map do |s|
|
||||
Bundler.ui.info s.full_gem_path
|
||||
end
|
||||
else
|
||||
Bundler.ui.info "Gems included by the bundle:"
|
||||
Bundler.load.specs.sort_by { |s| s.name }.each do |s|
|
||||
desc = " * #{s.name} (#{s.version}#{s.git_version})"
|
||||
if @options[:verbose]
|
||||
Bundler.ui.info "#{desc} - #{s.summary || 'No description available.'}"
|
||||
else
|
||||
Bundler.ui.info desc
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,73 @@
|
||||
module Bundler
|
||||
class CLI::Update
|
||||
attr_reader :options, :gems
|
||||
def initialize(options, gems)
|
||||
@options = options
|
||||
@gems = gems
|
||||
end
|
||||
|
||||
def run
|
||||
|
||||
sources = Array(options[:source])
|
||||
groups = Array(options[:group]).map(&:to_sym)
|
||||
Bundler.ui.level = "warn" if options[:quiet]
|
||||
|
||||
if gems.empty? && sources.empty? && groups.empty?
|
||||
# We're doing a full update
|
||||
Bundler.definition(true)
|
||||
else
|
||||
unless Bundler.default_lockfile.exist?
|
||||
raise GemfileLockNotFound, "This Bundle hasn't been installed yet. " \
|
||||
"Run `bundle install` to update and install the bundled gems."
|
||||
end
|
||||
# cycle through the requested gems, just to make sure they exist
|
||||
names = Bundler.locked_gems.specs.map{ |s| s.name }
|
||||
gems.each do |g|
|
||||
next if names.include?(g)
|
||||
require "bundler/cli/common"
|
||||
raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(g, names)
|
||||
end
|
||||
|
||||
if groups.any?
|
||||
specs = Bundler.definition.specs_for groups
|
||||
sources.concat(specs.map(&:name))
|
||||
end
|
||||
|
||||
Bundler.definition(:gems => gems, :sources => sources)
|
||||
end
|
||||
|
||||
Bundler::Fetcher.disable_endpoint = options["full-index"]
|
||||
|
||||
opts = options.dup
|
||||
opts["update"] = true
|
||||
opts["local"] = options[:local]
|
||||
|
||||
Bundler.settings[:jobs] = opts["jobs"] if opts["jobs"]
|
||||
|
||||
# rubygems plugins sometimes hook into the gem install process
|
||||
Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins)
|
||||
|
||||
Bundler.definition.validate_ruby!
|
||||
Installer.install Bundler.root, Bundler.definition, opts
|
||||
Bundler.load.cache if Bundler.root.join("vendor/cache").exist?
|
||||
|
||||
if Bundler.settings[:clean] && Bundler.settings[:path]
|
||||
require "bundler/cli/clean"
|
||||
Bundler::CLI::Clean.new(options).run
|
||||
end
|
||||
|
||||
Bundler.ui.confirm "Your bundle is updated!"
|
||||
without_groups_messages
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def without_groups_messages
|
||||
if Bundler.settings.without.any?
|
||||
require "bundler/cli/common"
|
||||
Bundler.ui.confirm Bundler::CLI::Common.without_groups_message
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
module Bundler
|
||||
class CLI::Viz
|
||||
attr_reader :options, :gem_name
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def run
|
||||
require 'graphviz'
|
||||
output_file = File.expand_path(options[:file])
|
||||
graph = Graph.new(Bundler.load, output_file, options[:version], options[:requirements], options[:format])
|
||||
graph.viz
|
||||
rescue LoadError => e
|
||||
Bundler.ui.error e.inspect
|
||||
Bundler.ui.warn "Make sure you have the graphviz ruby gem. You can install it with:"
|
||||
Bundler.ui.warn "`gem install ruby-graphviz`"
|
||||
rescue StandardError => e
|
||||
if e.message =~ /GraphViz not installed or dot not in PATH/
|
||||
Bundler.ui.error e.message
|
||||
Bundler.ui.warn "Please install GraphViz. On a Mac with homebrew, you can run `brew install graphviz`."
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
module Bundler
|
||||
WINDOWS = RbConfig::CONFIG["host_os"] =~ %r!(msdos|mswin|djgpp|mingw)!
|
||||
FREEBSD = RbConfig::CONFIG["host_os"] =~ /bsd/
|
||||
NULL = WINDOWS ? "NUL" : "/dev/null"
|
||||
end
|
||||
@@ -0,0 +1,123 @@
|
||||
module Bundler
|
||||
# Returns current version of Ruby
|
||||
#
|
||||
# @return [CurrentRuby] Current version of Ruby
|
||||
def self.current_ruby
|
||||
@current_ruby ||= CurrentRuby.new
|
||||
end
|
||||
|
||||
class CurrentRuby
|
||||
def on_18?
|
||||
RUBY_VERSION =~ /^1\.8/
|
||||
end
|
||||
|
||||
def on_19?
|
||||
RUBY_VERSION =~ /^1\.9/
|
||||
end
|
||||
|
||||
def on_20?
|
||||
RUBY_VERSION =~ /^2\.0/
|
||||
end
|
||||
|
||||
def on_21?
|
||||
RUBY_VERSION =~ /^2\.1/
|
||||
end
|
||||
|
||||
def ruby?
|
||||
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev")
|
||||
end
|
||||
|
||||
def ruby_18?
|
||||
ruby? && on_18?
|
||||
end
|
||||
|
||||
def ruby_19?
|
||||
ruby? && on_19?
|
||||
end
|
||||
|
||||
def ruby_20?
|
||||
ruby? && on_20?
|
||||
end
|
||||
|
||||
def ruby_21?
|
||||
ruby? && on_21?
|
||||
end
|
||||
|
||||
def mri?
|
||||
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby")
|
||||
end
|
||||
|
||||
def mri_18?
|
||||
mri? && on_18?
|
||||
end
|
||||
|
||||
def mri_19?
|
||||
mri? && on_19?
|
||||
end
|
||||
|
||||
def mri_20?
|
||||
mri? && on_20?
|
||||
end
|
||||
|
||||
def mri_21?
|
||||
mri? && on_21?
|
||||
end
|
||||
|
||||
def rbx?
|
||||
ruby? && defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx"
|
||||
end
|
||||
|
||||
def jruby?
|
||||
defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
||||
end
|
||||
|
||||
def jruby_18?
|
||||
jruby? && on_18?
|
||||
end
|
||||
|
||||
def jruby_19?
|
||||
jruby? && on_19?
|
||||
end
|
||||
|
||||
def maglev?
|
||||
defined?(RUBY_ENGINE) && RUBY_ENGINE == "maglev"
|
||||
end
|
||||
|
||||
def mswin?
|
||||
Bundler::WINDOWS
|
||||
end
|
||||
|
||||
def mingw?
|
||||
Bundler::WINDOWS && Gem::Platform.local.os == "mingw32" && Gem::Platform.local.cpu != 'x64'
|
||||
end
|
||||
|
||||
def mingw_18?
|
||||
mingw? && on_18?
|
||||
end
|
||||
|
||||
def mingw_19?
|
||||
mingw? && on_19?
|
||||
end
|
||||
|
||||
def mingw_20?
|
||||
mingw? && on_20?
|
||||
end
|
||||
|
||||
def mingw_21?
|
||||
mingw? && on_21?
|
||||
end
|
||||
|
||||
def x64_mingw?
|
||||
Bundler::WINDOWS && Gem::Platform.local.os == "mingw32" && Gem::Platform.local.cpu == 'x64'
|
||||
end
|
||||
|
||||
def x64_mingw_20?
|
||||
x64_mingw? && on_20?
|
||||
end
|
||||
|
||||
def x64_mingw_21?
|
||||
x64_mingw? && on_21?
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,610 @@
|
||||
require "digest/sha1"
|
||||
require "set"
|
||||
|
||||
module Bundler
|
||||
class Definition
|
||||
include GemHelpers
|
||||
|
||||
attr_reader :dependencies, :platforms, :ruby_version, :locked_deps
|
||||
|
||||
# Given a gemfile and lockfile creates a Bundler definition
|
||||
#
|
||||
# @param gemfile [Pathname] Path to Gemfile
|
||||
# @param lockfile [Pathname,nil] Path to Gemfile.lock
|
||||
# @param unlock [Hash, Boolean, nil] Gems that have been requested
|
||||
# to be updated or true if all gems should be updated
|
||||
# @return [Bundler::Definition]
|
||||
def self.build(gemfile, lockfile, unlock)
|
||||
unlock ||= {}
|
||||
gemfile = Pathname.new(gemfile).expand_path
|
||||
|
||||
unless gemfile.file?
|
||||
raise GemfileNotFound, "#{gemfile} not found"
|
||||
end
|
||||
|
||||
Dsl.evaluate(gemfile, lockfile, unlock)
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# How does the new system work?
|
||||
#
|
||||
# * Load information from Gemfile and Lockfile
|
||||
# * Invalidate stale locked specs
|
||||
# * All specs from stale source are stale
|
||||
# * All specs that are reachable only through a stale
|
||||
# dependency are stale.
|
||||
# * If all fresh dependencies are satisfied by the locked
|
||||
# specs, then we can try to resolve locally.
|
||||
#
|
||||
# @param lockfile [Pathname] Path to Gemfile.lock
|
||||
# @param dependencies [Array(Bundler::Dependency)] array of dependencies from Gemfile
|
||||
# @param sources [Bundler::SourceList]
|
||||
# @param unlock [Hash, Boolean, nil] Gems that have been requested
|
||||
# to be updated or true if all gems should be updated
|
||||
# @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
|
||||
def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil)
|
||||
@unlocking = unlock == true || !unlock.empty?
|
||||
|
||||
@dependencies, @sources, @unlock = dependencies, sources, unlock
|
||||
@remote = false
|
||||
@specs = nil
|
||||
@lockfile_contents = ""
|
||||
@ruby_version = ruby_version
|
||||
|
||||
if lockfile && File.exist?(lockfile)
|
||||
@lockfile_contents = Bundler.read_file(lockfile)
|
||||
locked = LockfileParser.new(@lockfile_contents)
|
||||
@platforms = locked.platforms
|
||||
|
||||
if unlock != true
|
||||
@locked_deps = locked.dependencies
|
||||
@locked_specs = SpecSet.new(locked.specs)
|
||||
@locked_sources = locked.sources
|
||||
else
|
||||
@unlock = {}
|
||||
@locked_deps = []
|
||||
@locked_specs = SpecSet.new([])
|
||||
@locked_sources = []
|
||||
end
|
||||
else
|
||||
@unlock = {}
|
||||
@platforms = []
|
||||
@locked_deps = []
|
||||
@locked_specs = SpecSet.new([])
|
||||
@locked_sources = []
|
||||
end
|
||||
|
||||
@unlock[:gems] ||= []
|
||||
@unlock[:sources] ||= []
|
||||
|
||||
current_platform = Bundler.rubygems.platforms.map { |p| generic(p) }.compact.last
|
||||
@new_platform = !@platforms.include?(current_platform)
|
||||
@platforms |= [current_platform]
|
||||
|
||||
@path_changes = converge_paths
|
||||
eager_unlock = expand_dependencies(@unlock[:gems])
|
||||
@unlock[:gems] = @locked_specs.for(eager_unlock).map { |s| s.name }
|
||||
|
||||
@source_changes = converge_sources
|
||||
@dependency_changes = converge_dependencies
|
||||
@local_changes = converge_locals
|
||||
|
||||
fixup_dependency_types!
|
||||
end
|
||||
|
||||
def fixup_dependency_types!
|
||||
# XXX This is a temporary workaround for a bug when using rubygems 1.8.15
|
||||
# where Gem::Dependency#== matches Gem::Dependency#type. As the lockfile
|
||||
# doesn't carry a notion of the dependency type, if you use
|
||||
# add_development_dependency in a gemspec that's loaded with the gemspec
|
||||
# directive, the lockfile dependencies and resolved dependencies end up
|
||||
# with a mismatch on #type.
|
||||
# Test coverage to catch a regression on this is in gemspec_spec.rb
|
||||
@dependencies.each do |d|
|
||||
if ld = @locked_deps.find { |l| l.name == d.name }
|
||||
ld.instance_variable_set(:@type, d.type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_with_cache!
|
||||
raise "Specs already loaded" if @specs
|
||||
sources.cached!
|
||||
specs
|
||||
end
|
||||
|
||||
def resolve_remotely!
|
||||
raise "Specs already loaded" if @specs
|
||||
@remote = true
|
||||
sources.remote!
|
||||
specs
|
||||
end
|
||||
|
||||
# For given dependency list returns a SpecSet with Gemspec of all the required
|
||||
# dependencies.
|
||||
# 1. The method first resolves the dependencies specified in Gemfile
|
||||
# 2. After that it tries and fetches gemspec of resolved dependencies
|
||||
#
|
||||
# @return [Bundler::SpecSet]
|
||||
def specs
|
||||
@specs ||= begin
|
||||
specs = resolve.materialize(requested_dependencies)
|
||||
|
||||
unless specs["bundler"].any?
|
||||
local = Bundler.settings[:frozen] ? rubygems_index : index
|
||||
bundler = local.search(Gem::Dependency.new('bundler', VERSION)).last
|
||||
specs["bundler"] = bundler if bundler
|
||||
end
|
||||
|
||||
specs
|
||||
end
|
||||
end
|
||||
|
||||
def new_specs
|
||||
specs - @locked_specs
|
||||
end
|
||||
|
||||
def removed_specs
|
||||
@locked_specs - specs
|
||||
end
|
||||
|
||||
def new_platform?
|
||||
@new_platform
|
||||
end
|
||||
|
||||
def missing_specs
|
||||
missing = []
|
||||
resolve.materialize(requested_dependencies, missing)
|
||||
missing
|
||||
end
|
||||
|
||||
def requested_specs
|
||||
@requested_specs ||= begin
|
||||
groups = self.groups - Bundler.settings.without
|
||||
groups.map! { |g| g.to_sym }
|
||||
specs_for(groups)
|
||||
end
|
||||
end
|
||||
|
||||
def current_dependencies
|
||||
dependencies.reject { |d| !d.should_include? }
|
||||
end
|
||||
|
||||
def specs_for(groups)
|
||||
deps = dependencies.select { |d| (d.groups & groups).any? }
|
||||
deps.delete_if { |d| !d.should_include? }
|
||||
specs.for(expand_dependencies(deps))
|
||||
end
|
||||
|
||||
# Resolve all the dependencies specified in Gemfile. It ensures that
|
||||
# dependencies that have been already resolved via locked file and are fresh
|
||||
# are reused when resolving dependencies
|
||||
#
|
||||
# @return [SpecSet] resolved dependencies
|
||||
def resolve
|
||||
@resolve ||= begin
|
||||
if Bundler.settings[:frozen] || (!@unlocking && nothing_changed?)
|
||||
@locked_specs
|
||||
else
|
||||
last_resolve = converge_locked_specs
|
||||
|
||||
# Run a resolve against the locally available gems
|
||||
last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def index
|
||||
@index ||= Index.build do |idx|
|
||||
dependency_names = @dependencies.dup || []
|
||||
dependency_names.map! {|d| d.name }
|
||||
|
||||
sources.all_sources.each do |s|
|
||||
s.dependency_names = dependency_names
|
||||
idx.add_source s.specs
|
||||
dependency_names.push(*s.unmet_deps).uniq!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# used when frozen is enabled so we can find the bundler
|
||||
# spec, even if (say) a git gem is not checked out.
|
||||
def rubygems_index
|
||||
@rubygems_index ||= Index.build do |idx|
|
||||
sources.rubygems_sources.each do |rubygems|
|
||||
idx.add_source rubygems.specs
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def has_rubygems_remotes?
|
||||
sources.rubygems_sources.any? {|s| s.remotes.any? }
|
||||
end
|
||||
|
||||
def has_local_dependencies?
|
||||
!sources.path_sources.empty? || !sources.git_sources.empty?
|
||||
end
|
||||
|
||||
def spec_git_paths
|
||||
sources.git_sources.map {|s| s.path.to_s }
|
||||
end
|
||||
|
||||
def groups
|
||||
dependencies.map { |d| d.groups }.flatten.uniq
|
||||
end
|
||||
|
||||
def lock(file)
|
||||
contents = to_lock
|
||||
|
||||
# Convert to \r\n if the existing lock has them
|
||||
# i.e., Windows with `git config core.autocrlf=true`
|
||||
contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match("\r\n")
|
||||
|
||||
return if @lockfile_contents == contents
|
||||
|
||||
if Bundler.settings[:frozen]
|
||||
Bundler.ui.error "Cannot write a changed lockfile while frozen."
|
||||
return
|
||||
end
|
||||
|
||||
File.open(file, 'wb'){|f| f.puts(contents) }
|
||||
rescue Errno::EACCES
|
||||
raise Bundler::InstallError,
|
||||
"There was an error while trying to write to Gemfile.lock. It is likely that \n" \
|
||||
"you need to allow write permissions for the file at path: \n" \
|
||||
"#{File.expand_path(file)}"
|
||||
end
|
||||
|
||||
def to_lock
|
||||
out = ""
|
||||
|
||||
sources.lock_sources.each do |source|
|
||||
# Add the source header
|
||||
out << source.to_lock
|
||||
# Find all specs for this source
|
||||
resolve.
|
||||
select { |s| source.can_lock?(s) }.
|
||||
# This needs to be sorted by full name so that
|
||||
# gems with the same name, but different platform
|
||||
# are ordered consistently
|
||||
sort_by { |s| s.full_name }.
|
||||
each do |spec|
|
||||
next if spec.name == 'bundler'
|
||||
out << spec.to_lock
|
||||
end
|
||||
out << "\n"
|
||||
end
|
||||
|
||||
out << "PLATFORMS\n"
|
||||
|
||||
platforms.map { |p| p.to_s }.sort.each do |p|
|
||||
out << " #{p}\n"
|
||||
end
|
||||
|
||||
out << "\n"
|
||||
out << "DEPENDENCIES\n"
|
||||
|
||||
handled = []
|
||||
dependencies.
|
||||
sort_by { |d| d.to_s }.
|
||||
each do |dep|
|
||||
next if handled.include?(dep.name)
|
||||
out << dep.to_lock
|
||||
handled << dep.name
|
||||
end
|
||||
|
||||
out
|
||||
end
|
||||
|
||||
def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)
|
||||
changes = false
|
||||
|
||||
msg = "You are trying to install in deployment mode after changing\n" \
|
||||
"your Gemfile. Run `bundle install` elsewhere and add the\n" \
|
||||
"updated Gemfile.lock to version control."
|
||||
|
||||
unless explicit_flag
|
||||
msg += "\n\nIf this is a development machine, remove the Gemfile " \
|
||||
"freeze \nby running `bundle install --no-deployment`."
|
||||
end
|
||||
|
||||
added = []
|
||||
deleted = []
|
||||
changed = []
|
||||
|
||||
gemfile_sources = sources.all_sources
|
||||
if @locked_sources != gemfile_sources
|
||||
new_sources = gemfile_sources - @locked_sources
|
||||
deleted_sources = @locked_sources - gemfile_sources
|
||||
|
||||
if new_sources.any?
|
||||
added.concat new_sources.map { |source| "* source: #{source}" }
|
||||
end
|
||||
|
||||
if deleted_sources.any?
|
||||
deleted.concat deleted_sources.map { |source| "* source: #{source}" }
|
||||
end
|
||||
|
||||
changes = true
|
||||
end
|
||||
|
||||
both_sources = Hash.new { |h,k| h[k] = ["no specified source", "no specified source"] }
|
||||
@dependencies.each { |d| both_sources[d.name][0] = d.source if d.source }
|
||||
@locked_deps.each { |d| both_sources[d.name][1] = d.source if d.source }
|
||||
both_sources.delete_if { |k,v| v[0] == v[1] }
|
||||
|
||||
if @dependencies != @locked_deps
|
||||
new_deps = @dependencies - @locked_deps
|
||||
deleted_deps = @locked_deps - @dependencies
|
||||
|
||||
if new_deps.any?
|
||||
added.concat new_deps.map { |d| "* #{pretty_dep(d)}" }
|
||||
end
|
||||
|
||||
if deleted_deps.any?
|
||||
deleted.concat deleted_deps.map { |d| "* #{pretty_dep(d)}" }
|
||||
end
|
||||
|
||||
both_sources.each do |name, sources|
|
||||
changed << "* #{name} from `#{sources[0]}` to `#{sources[1]}`"
|
||||
end
|
||||
|
||||
changes = true
|
||||
end
|
||||
|
||||
msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
|
||||
msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
|
||||
msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
|
||||
msg << "\n"
|
||||
|
||||
raise ProductionError, msg if added.any? || deleted.any? || changed.any?
|
||||
end
|
||||
|
||||
def validate_ruby!
|
||||
return unless ruby_version
|
||||
|
||||
if diff = ruby_version.diff(Bundler.ruby_version)
|
||||
problem, expected, actual = diff
|
||||
|
||||
msg = case problem
|
||||
when :engine
|
||||
"Your Ruby engine is #{actual}, but your Gemfile specified #{expected}"
|
||||
when :version
|
||||
"Your Ruby version is #{actual}, but your Gemfile specified #{expected}"
|
||||
when :engine_version
|
||||
"Your #{Bundler.ruby_version.engine} version is #{actual}, but your Gemfile specified #{ruby_version.engine} #{expected}"
|
||||
when :patchlevel
|
||||
if !expected.is_a?(String)
|
||||
"The Ruby patchlevel in your Gemfile must be a string"
|
||||
else
|
||||
"Your Ruby patchlevel is #{actual}, but your Gemfile specified #{expected}"
|
||||
end
|
||||
end
|
||||
|
||||
raise RubyVersionMismatch, msg
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :sources
|
||||
|
||||
def nothing_changed?
|
||||
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes
|
||||
end
|
||||
|
||||
def pretty_dep(dep, source = false)
|
||||
msg = "#{dep.name}"
|
||||
msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default
|
||||
msg << " from the `#{dep.source}` source" if source && dep.source
|
||||
msg
|
||||
end
|
||||
|
||||
# Check if the specs of the given source changed
|
||||
# according to the locked source. A block should be
|
||||
# in order to specify how the locked version of
|
||||
# the source should be found.
|
||||
def specs_changed?(source, &block)
|
||||
locked = @locked_sources.find(&block)
|
||||
|
||||
if locked
|
||||
unlocking = @locked_specs.any? do |locked_spec|
|
||||
locked_spec.source != locked
|
||||
end
|
||||
end
|
||||
|
||||
!locked || unlocking || source.specs != locked.specs
|
||||
end
|
||||
|
||||
# Get all locals and override their matching sources.
|
||||
# Return true if any of the locals changed (for example,
|
||||
# they point to a new revision) or depend on new specs.
|
||||
def converge_locals
|
||||
locals = []
|
||||
|
||||
Bundler.settings.local_overrides.map do |k,v|
|
||||
spec = @dependencies.find { |s| s.name == k }
|
||||
source = spec && spec.source
|
||||
if source && source.respond_to?(:local_override!)
|
||||
source.unlock! if @unlock[:gems].include?(spec.name)
|
||||
locals << [ source, source.local_override!(v) ]
|
||||
end
|
||||
end
|
||||
|
||||
locals.any? do |source, changed|
|
||||
changed || specs_changed?(source) { |o| source.class == o.class && source.uri == o.uri }
|
||||
end
|
||||
end
|
||||
|
||||
def converge_paths
|
||||
sources.path_sources.any? do |source|
|
||||
specs_changed?(source) do |ls|
|
||||
ls.class == source.class && ls.path == source.path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def converge_sources
|
||||
changes = false
|
||||
|
||||
# Get the Rubygems sources from the Gemfile.lock
|
||||
locked_gem_sources = @locked_sources.select { |s| s.kind_of?(Source::Rubygems) }
|
||||
# Get the Rubygems sources from the Gemfile
|
||||
actual_gem_sources = @sources.rubygems_sources
|
||||
|
||||
# If there is a Rubygems source in both
|
||||
unless locked_gem_sources.empty? && actual_gem_sources.empty?
|
||||
actual_remotes = actual_gem_sources.map(&:remotes).flatten.uniq
|
||||
locked_gem_sources.each do |locked_gem|
|
||||
# Merge the remotes from the Gemfile into the Gemfile.lock
|
||||
changes = changes | locked_gem.replace_remotes(actual_remotes)
|
||||
end
|
||||
end
|
||||
|
||||
# Replace the sources from the Gemfile with the sources from the Gemfile.lock,
|
||||
# if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
|
||||
# source in the Gemfile.lock, use the one from the Gemfile.
|
||||
sources.replace_sources!(@locked_sources)
|
||||
gemfile_sources = sources.all_sources
|
||||
changes = changes | (Set.new(gemfile_sources) != Set.new(@locked_sources))
|
||||
|
||||
gemfile_sources.each do |source|
|
||||
# If the source is unlockable and the current command allows an unlock of
|
||||
# the source (for example, you are doing a `bundle update <foo>` of a git-pinned
|
||||
# gem), unlock it. For git sources, this means to unlock the revision, which
|
||||
# will cause the `ref` used to be the most recent for the branch (or master) if
|
||||
# an explicit `ref` is not used.
|
||||
if source.respond_to?(:unlock!) && @unlock[:sources].include?(source.name)
|
||||
source.unlock!
|
||||
changes = true
|
||||
end
|
||||
end
|
||||
|
||||
changes
|
||||
end
|
||||
|
||||
def converge_dependencies
|
||||
(@dependencies + @locked_deps).each do |dep|
|
||||
if dep.source
|
||||
dep.source = sources.get(dep.source)
|
||||
end
|
||||
end
|
||||
Set.new(@dependencies) != Set.new(@locked_deps)
|
||||
end
|
||||
|
||||
# Remove elements from the locked specs that are expired. This will most
|
||||
# commonly happen if the Gemfile has changed since the lockfile was last
|
||||
# generated
|
||||
def converge_locked_specs
|
||||
deps = []
|
||||
|
||||
# Build a list of dependencies that are the same in the Gemfile
|
||||
# and Gemfile.lock. If the Gemfile modified a dependency, but
|
||||
# the gem in the Gemfile.lock still satisfies it, this is fine
|
||||
# too.
|
||||
locked_deps_hash = @locked_deps.inject({}) { |hsh, dep| hsh[dep] = dep; hsh }
|
||||
@dependencies.each do |dep|
|
||||
locked_dep = locked_deps_hash[dep]
|
||||
|
||||
if in_locked_deps?(dep, locked_dep) || satisfies_locked_spec?(dep)
|
||||
deps << dep
|
||||
elsif dep.source.is_a?(Source::Path) && dep.current_platform? && (!locked_dep || dep.source != locked_dep.source)
|
||||
@locked_specs.each do |s|
|
||||
@unlock[:gems] << s.name if s.source == dep.source
|
||||
end
|
||||
|
||||
dep.source.unlock! if dep.source.respond_to?(:unlock!)
|
||||
dep.source.specs.each { |s| @unlock[:gems] << s.name }
|
||||
end
|
||||
end
|
||||
|
||||
converged = []
|
||||
@locked_specs.each do |s|
|
||||
s.source = sources.get(s.source)
|
||||
|
||||
# Don't add a spec to the list if its source is expired. For example,
|
||||
# if you change a Git gem to Rubygems.
|
||||
next if s.source.nil? || @unlock[:sources].include?(s.name)
|
||||
# If the spec is from a path source and it doesn't exist anymore
|
||||
# then we just unlock it.
|
||||
|
||||
# Path sources have special logic
|
||||
if s.source.instance_of?(Source::Path)
|
||||
other = s.source.specs[s].first
|
||||
|
||||
# If the spec is no longer in the path source, unlock it. This
|
||||
# commonly happens if the version changed in the gemspec
|
||||
next unless other
|
||||
|
||||
deps2 = other.dependencies.select { |d| d.type != :development }
|
||||
# If the dependencies of the path source have changed, unlock it
|
||||
next unless s.dependencies.sort == deps2.sort
|
||||
end
|
||||
|
||||
converged << s
|
||||
end
|
||||
|
||||
resolve = SpecSet.new(converged)
|
||||
resolve = resolve.for(expand_dependencies(deps, true), @unlock[:gems])
|
||||
diff = @locked_specs.to_a - resolve.to_a
|
||||
|
||||
# Now, we unlock any sources that do not have anymore gems pinned to it
|
||||
sources.all_sources.each do |source|
|
||||
next unless source.respond_to?(:unlock!)
|
||||
|
||||
unless resolve.any? { |s| s.source == source }
|
||||
source.unlock! if !diff.empty? && diff.any? { |s| s.source == source }
|
||||
end
|
||||
end
|
||||
|
||||
resolve
|
||||
end
|
||||
|
||||
def in_locked_deps?(dep, d)
|
||||
d && dep.source == d.source
|
||||
end
|
||||
|
||||
def satisfies_locked_spec?(dep)
|
||||
@locked_specs.any? { |s| s.satisfies?(dep) && (!dep.source || s.source == dep.source) }
|
||||
end
|
||||
|
||||
def expanded_dependencies
|
||||
@expanded_dependencies ||= expand_dependencies(dependencies, @remote)
|
||||
end
|
||||
|
||||
def expand_dependencies(dependencies, remote = false)
|
||||
deps = []
|
||||
dependencies.each do |dep|
|
||||
dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name)
|
||||
next unless remote || dep.current_platform?
|
||||
dep.gem_platforms(@platforms).each do |p|
|
||||
deps << DepProxy.new(dep, p) if remote || p == generic(Gem::Platform.local)
|
||||
end
|
||||
end
|
||||
deps
|
||||
end
|
||||
|
||||
def requested_dependencies
|
||||
groups = self.groups - Bundler.settings.without
|
||||
groups.map! { |g| g.to_sym }
|
||||
dependencies.reject { |d| !d.should_include? || (d.groups & groups).empty? }
|
||||
end
|
||||
|
||||
def source_requirements
|
||||
# Load all specs from remote sources
|
||||
index
|
||||
|
||||
# Record the specs available in each gem's source, so that those
|
||||
# specs will be available later when the resolver knows where to
|
||||
# look for that gemspec (or its dependencies)
|
||||
source_requirements = {}
|
||||
dependencies.each do |dep|
|
||||
next unless dep.source
|
||||
source_requirements[dep.name] = dep.source.specs
|
||||
end
|
||||
source_requirements
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
module Bundler
|
||||
class DepProxy
|
||||
|
||||
attr_reader :required_by, :__platform, :dep
|
||||
|
||||
def initialize(dep, platform)
|
||||
@dep, @__platform, @required_by = dep, platform, []
|
||||
end
|
||||
|
||||
def hash
|
||||
@hash ||= dep.hash
|
||||
end
|
||||
|
||||
def ==(o)
|
||||
dep == o.dep && __platform == o.__platform
|
||||
end
|
||||
|
||||
alias eql? ==
|
||||
|
||||
def type
|
||||
@dep.type
|
||||
end
|
||||
|
||||
def name
|
||||
@dep.name
|
||||
end
|
||||
|
||||
def requirement
|
||||
@dep.requirement
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{name} (#{requirement}) #{__platform}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def method_missing(*args)
|
||||
@dep.send(*args)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,99 @@
|
||||
require 'rubygems/dependency'
|
||||
require 'bundler/shared_helpers'
|
||||
require 'bundler/rubygems_ext'
|
||||
|
||||
module Bundler
|
||||
class Dependency < Gem::Dependency
|
||||
attr_reader :autorequire
|
||||
attr_reader :groups
|
||||
attr_reader :platforms
|
||||
|
||||
PLATFORM_MAP = {
|
||||
:ruby => Gem::Platform::RUBY,
|
||||
:ruby_18 => Gem::Platform::RUBY,
|
||||
:ruby_19 => Gem::Platform::RUBY,
|
||||
:ruby_20 => Gem::Platform::RUBY,
|
||||
:ruby_21 => Gem::Platform::RUBY,
|
||||
:mri => Gem::Platform::RUBY,
|
||||
:mri_18 => Gem::Platform::RUBY,
|
||||
:mri_19 => Gem::Platform::RUBY,
|
||||
:mri_20 => Gem::Platform::RUBY,
|
||||
:mri_21 => Gem::Platform::RUBY,
|
||||
:rbx => Gem::Platform::RUBY,
|
||||
:jruby => Gem::Platform::JAVA,
|
||||
:jruby_18 => Gem::Platform::JAVA,
|
||||
:jruby_19 => Gem::Platform::JAVA,
|
||||
:mswin => Gem::Platform::MSWIN,
|
||||
:mingw => Gem::Platform::MINGW,
|
||||
:mingw_18 => Gem::Platform::MINGW,
|
||||
:mingw_19 => Gem::Platform::MINGW,
|
||||
:mingw_20 => Gem::Platform::MINGW,
|
||||
:mingw_21 => Gem::Platform::MINGW,
|
||||
:x64_mingw => Gem::Platform::X64_MINGW,
|
||||
:x64_mingw_20 => Gem::Platform::X64_MINGW,
|
||||
:x64_mingw_21 => Gem::Platform::X64_MINGW
|
||||
}.freeze
|
||||
|
||||
def initialize(name, version, options = {}, &blk)
|
||||
type = options["type"] || :runtime
|
||||
super(name, version, type)
|
||||
|
||||
@autorequire = nil
|
||||
@groups = Array(options["group"] || :default).map { |g| g.to_sym }
|
||||
@source = options["source"]
|
||||
@platforms = Array(options["platforms"])
|
||||
@env = options["env"]
|
||||
|
||||
if options.key?('require')
|
||||
@autorequire = Array(options['require'] || [])
|
||||
end
|
||||
end
|
||||
|
||||
def gem_platforms(valid_platforms)
|
||||
return valid_platforms if @platforms.empty?
|
||||
|
||||
platforms = []
|
||||
@platforms.each do |p|
|
||||
platform = PLATFORM_MAP[p]
|
||||
next unless valid_platforms.include?(platform)
|
||||
platforms |= [platform]
|
||||
end
|
||||
platforms
|
||||
end
|
||||
|
||||
def should_include?
|
||||
current_env? && current_platform?
|
||||
end
|
||||
|
||||
def current_env?
|
||||
return true unless @env
|
||||
if @env.is_a?(Hash)
|
||||
@env.all? do |key, val|
|
||||
ENV[key.to_s] && (val.is_a?(String) ? ENV[key.to_s] == val : ENV[key.to_s] =~ val)
|
||||
end
|
||||
else
|
||||
ENV[@env.to_s]
|
||||
end
|
||||
end
|
||||
|
||||
def current_platform?
|
||||
return true if @platforms.empty?
|
||||
@platforms.any? { |p|
|
||||
Bundler.current_ruby.send("#{p}?")
|
||||
}
|
||||
end
|
||||
|
||||
def to_lock
|
||||
out = super
|
||||
out << '!' if source
|
||||
out << "\n"
|
||||
end
|
||||
|
||||
|
||||
def specific?
|
||||
super
|
||||
rescue NoMethodError
|
||||
requirement != ">= 0"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,59 @@
|
||||
module Bundler
|
||||
class Deployment
|
||||
def self.define_task(context, task_method = :task, opts = {})
|
||||
if defined?(Capistrano) && context.is_a?(Capistrano::Configuration)
|
||||
context_name = "capistrano"
|
||||
role_default = "{:except => {:no_release => true}}"
|
||||
error_type = ::Capistrano::CommandError
|
||||
else
|
||||
context_name = "vlad"
|
||||
role_default = "[:app]"
|
||||
error_type = ::Rake::CommandFailedError
|
||||
end
|
||||
|
||||
roles = context.fetch(:bundle_roles, false)
|
||||
opts[:roles] = roles if roles
|
||||
|
||||
context.send :namespace, :bundle do
|
||||
send :desc, <<-DESC
|
||||
Install the current Bundler environment. By default, gems will be \
|
||||
installed to the shared/bundle path. Gems in the development and \
|
||||
test group will not be installed. The install command is executed \
|
||||
with the --deployment and --quiet flags. If the bundle cmd cannot \
|
||||
be found then you can override the bundle_cmd variable to specify \
|
||||
which one it should use. The base path to the app is fetched from \
|
||||
the :latest_release variable. Set it for custom deploy layouts.
|
||||
|
||||
You can override any of these defaults by setting the variables shown below.
|
||||
|
||||
N.B. bundle_roles must be defined before you require 'bundler/#{context_name}' \
|
||||
in your deploy.rb file.
|
||||
|
||||
set :bundle_gemfile, "Gemfile"
|
||||
set :bundle_dir, File.join(fetch(:shared_path), 'bundle')
|
||||
set :bundle_flags, "--deployment --quiet"
|
||||
set :bundle_without, [:development, :test]
|
||||
set :bundle_cmd, "bundle" # e.g. "/opt/ruby/bin/bundle"
|
||||
set :bundle_roles, #{role_default} # e.g. [:app, :batch]
|
||||
DESC
|
||||
send task_method, :install, opts do
|
||||
bundle_cmd = context.fetch(:bundle_cmd, "bundle")
|
||||
bundle_flags = context.fetch(:bundle_flags, "--deployment --quiet")
|
||||
bundle_dir = context.fetch(:bundle_dir, File.join(context.fetch(:shared_path), 'bundle'))
|
||||
bundle_gemfile = context.fetch(:bundle_gemfile, "Gemfile")
|
||||
bundle_without = [*context.fetch(:bundle_without, [:development, :test])].compact
|
||||
app_path = context.fetch(:latest_release)
|
||||
if app_path.to_s.empty?
|
||||
raise error_type.new("Cannot detect current release path - make sure you have deployed at least once.")
|
||||
end
|
||||
args = ["--gemfile #{File.join(app_path, bundle_gemfile)}"]
|
||||
args << "--path #{bundle_dir}" unless bundle_dir.to_s.empty?
|
||||
args << bundle_flags.to_s
|
||||
args << "--without #{bundle_without.join(" ")}" unless bundle_without.empty?
|
||||
|
||||
run "cd #{app_path} && #{bundle_cmd} install #{args.join(' ')}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
module Bundler
|
||||
|
||||
if defined? ::Deprecate
|
||||
Deprecate = ::Deprecate
|
||||
elsif defined? Gem::Deprecate
|
||||
Deprecate = Gem::Deprecate
|
||||
else
|
||||
class Deprecate; end
|
||||
end
|
||||
|
||||
unless Deprecate.respond_to?(:skip_during)
|
||||
def Deprecate.skip_during; yield; end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,294 @@
|
||||
require 'bundler/dependency'
|
||||
require 'bundler/ruby_dsl'
|
||||
|
||||
module Bundler
|
||||
class Dsl
|
||||
include RubyDsl
|
||||
|
||||
def self.evaluate(gemfile, lockfile, unlock)
|
||||
builder = new
|
||||
builder.eval_gemfile(gemfile)
|
||||
builder.to_definition(lockfile, unlock)
|
||||
end
|
||||
|
||||
VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze
|
||||
|
||||
attr_accessor :dependencies
|
||||
|
||||
def initialize
|
||||
@source = nil
|
||||
@sources = SourceList.new
|
||||
@git_sources = {}
|
||||
@dependencies = []
|
||||
@groups = []
|
||||
@platforms = []
|
||||
@env = nil
|
||||
@ruby_version = nil
|
||||
add_github_sources
|
||||
end
|
||||
|
||||
def eval_gemfile(gemfile, contents = nil)
|
||||
contents ||= Bundler.read_file(gemfile.to_s)
|
||||
instance_eval(contents, gemfile.to_s, 1)
|
||||
rescue SyntaxError => e
|
||||
syntax_msg = e.message.gsub("#{gemfile.to_s}:", 'on line ')
|
||||
raise GemfileError, "Gemfile syntax error #{syntax_msg}"
|
||||
rescue ScriptError, RegexpError, NameError, ArgumentError => e
|
||||
e.backtrace[0] = "#{e.backtrace[0]}: #{e.message} (#{e.class})"
|
||||
Bundler.ui.warn e.backtrace.join("\n ")
|
||||
raise GemfileError, "There was an error in your Gemfile," \
|
||||
" and Bundler cannot continue."
|
||||
end
|
||||
|
||||
def gemspec(opts = nil)
|
||||
path = opts && opts[:path] || '.'
|
||||
name = opts && opts[:name] || '{,*}'
|
||||
development_group = opts && opts[:development_group] || :development
|
||||
expanded_path = File.expand_path(path, Bundler.default_gemfile.dirname)
|
||||
|
||||
gemspecs = Dir[File.join(expanded_path, "#{name}.gemspec")]
|
||||
|
||||
case gemspecs.size
|
||||
when 1
|
||||
spec = Bundler.load_gemspec(gemspecs.first)
|
||||
raise InvalidOption, "There was an error loading the gemspec at #{gemspecs.first}." unless spec
|
||||
gem spec.name, :path => path
|
||||
group(development_group) do
|
||||
spec.development_dependencies.each do |dep|
|
||||
gem dep.name, *(dep.requirement.as_list + [:type => :development])
|
||||
end
|
||||
end
|
||||
when 0
|
||||
raise InvalidOption, "There are no gemspecs at #{expanded_path}."
|
||||
else
|
||||
raise InvalidOption, "There are multiple gemspecs at #{expanded_path}. Please use the :name option to specify which one."
|
||||
end
|
||||
end
|
||||
|
||||
def gem(name, *args)
|
||||
options = args.last.is_a?(Hash) ? args.pop.dup : {}
|
||||
version = args || [">= 0"]
|
||||
|
||||
normalize_options(name, version, options)
|
||||
|
||||
dep = Dependency.new(name, version, options)
|
||||
|
||||
# if there's already a dependency with this name we try to prefer one
|
||||
if current = @dependencies.find { |d| d.name == dep.name }
|
||||
if current.requirement != dep.requirement
|
||||
if current.type == :development
|
||||
@dependencies.delete current
|
||||
elsif dep.type == :development
|
||||
return
|
||||
else
|
||||
raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
|
||||
"You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})"
|
||||
end
|
||||
|
||||
else
|
||||
Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
|
||||
"You should probably keep only one of them.\n" \
|
||||
"While it's not a problem now, it could cause errors if you change the version of just one of them later."
|
||||
end
|
||||
|
||||
if current.source != dep.source
|
||||
if current.type == :development
|
||||
@dependencies.delete current
|
||||
elsif dep.type == :development
|
||||
return
|
||||
else
|
||||
raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
|
||||
"You specified that #{dep.name} (#{dep.requirement}) should come from " \
|
||||
"#{current.source || 'an unspecified source'} and #{dep.source}\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@dependencies << dep
|
||||
end
|
||||
|
||||
def source(source, &blk)
|
||||
source = normalize_source(source)
|
||||
if block_given?
|
||||
with_source(@sources.add_rubygems_source("remotes" => source), &blk)
|
||||
else
|
||||
@sources.add_rubygems_remote(source)
|
||||
end
|
||||
end
|
||||
|
||||
def git_source(name, &block)
|
||||
unless block_given?
|
||||
raise InvalidOption, "You need to pass a block to #git_source"
|
||||
end
|
||||
|
||||
if valid_keys.include?(name.to_s)
|
||||
raise InvalidOption, "You cannot use #{name} as a git source. It " \
|
||||
"is a reserved key. Reserved keys are: #{valid_keys.join(", ")}"
|
||||
end
|
||||
|
||||
@git_sources[name.to_s] = block
|
||||
end
|
||||
|
||||
def path(path, options = {}, &blk)
|
||||
with_source(@sources.add_path_source(normalize_hash(options).merge("path" => Pathname.new(path))), &blk)
|
||||
end
|
||||
|
||||
def git(uri, options = {}, &blk)
|
||||
unless block_given?
|
||||
msg = "You can no longer specify a git source by itself. Instead, \n" \
|
||||
"either use the :git option on a gem, or specify the gems that \n" \
|
||||
"bundler should find in the git source by passing a block to \n" \
|
||||
"the git method, like: \n\n" \
|
||||
" git 'git://github.com/rails/rails.git' do\n" \
|
||||
" gem 'rails'\n" \
|
||||
" end"
|
||||
raise DeprecatedError, msg
|
||||
end
|
||||
|
||||
with_source(@sources.add_git_source(normalize_hash(options).merge("uri" => uri)), &blk)
|
||||
end
|
||||
|
||||
def to_definition(lockfile, unlock)
|
||||
Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version)
|
||||
end
|
||||
|
||||
def group(*args, &blk)
|
||||
@groups.concat args
|
||||
yield
|
||||
ensure
|
||||
args.each { @groups.pop }
|
||||
end
|
||||
|
||||
def platforms(*platforms)
|
||||
@platforms.concat platforms
|
||||
yield
|
||||
ensure
|
||||
platforms.each { @platforms.pop }
|
||||
end
|
||||
alias_method :platform, :platforms
|
||||
|
||||
def env(name)
|
||||
@env, old = name, @env
|
||||
yield
|
||||
ensure
|
||||
@env = old
|
||||
end
|
||||
|
||||
def method_missing(name, *args)
|
||||
location = caller[0].split(':')[0..1].join(':')
|
||||
raise GemfileError, "Undefined local variable or method `#{name}' for Gemfile\n" \
|
||||
" from #{location}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_github_sources
|
||||
git_source(:github) do |repo_name|
|
||||
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
|
||||
"git://github.com/#{repo_name}.git"
|
||||
end
|
||||
|
||||
git_source(:gist){ |repo_name| "https://gist.github.com/#{repo_name}.git" }
|
||||
end
|
||||
|
||||
def with_source(source)
|
||||
if block_given?
|
||||
@source = source
|
||||
yield
|
||||
end
|
||||
source
|
||||
ensure
|
||||
@source = nil
|
||||
end
|
||||
|
||||
def normalize_hash(opts)
|
||||
opts.keys.each do |k|
|
||||
opts[k.to_s] = opts.delete(k) unless k.is_a?(String)
|
||||
end
|
||||
opts
|
||||
end
|
||||
|
||||
def valid_keys
|
||||
@valid_keys ||= %w(group groups git path name branch ref tag require submodules platform platforms type source)
|
||||
end
|
||||
|
||||
def normalize_options(name, version, opts)
|
||||
if name.is_a?(Symbol)
|
||||
raise GemfileError, %{You need to specify gem names as Strings. Use 'gem "#{name.to_s}"' instead.}
|
||||
end
|
||||
|
||||
normalize_hash(opts)
|
||||
|
||||
git_names = @git_sources.keys.map(&:to_s)
|
||||
|
||||
invalid_keys = opts.keys - (valid_keys + git_names)
|
||||
if invalid_keys.any?
|
||||
message = "You passed #{invalid_keys.map{|k| ':'+k }.join(", ")} "
|
||||
message << if invalid_keys.size > 1
|
||||
"as options for gem '#{name}', but they are invalid."
|
||||
else
|
||||
"as an option for gem '#{name}', but it is invalid."
|
||||
end
|
||||
|
||||
message << " Valid options are: #{valid_keys.join(", ")}"
|
||||
raise InvalidOption, message
|
||||
end
|
||||
|
||||
groups = @groups.dup
|
||||
opts["group"] = opts.delete("groups") || opts["group"]
|
||||
groups.concat Array(opts.delete("group"))
|
||||
groups = [:default] if groups.empty?
|
||||
|
||||
platforms = @platforms.dup
|
||||
opts["platforms"] = opts["platform"] || opts["platforms"]
|
||||
platforms.concat Array(opts.delete("platforms"))
|
||||
platforms.map! { |p| p.to_sym }
|
||||
platforms.each do |p|
|
||||
next if VALID_PLATFORMS.include?(p)
|
||||
raise GemfileError, "`#{p}` is not a valid platform. The available options are: #{VALID_PLATFORMS.inspect}"
|
||||
end
|
||||
|
||||
# Save sources passed in a key
|
||||
if opts.has_key?("source")
|
||||
source = normalize_source(opts["source"])
|
||||
opts["source"] = @sources.add_rubygems_source("remotes" => source)
|
||||
end
|
||||
|
||||
git_name = (git_names & opts.keys).last
|
||||
if @git_sources[git_name]
|
||||
opts["git"] = @git_sources[git_name].call(opts[git_name])
|
||||
end
|
||||
|
||||
["git", "path"].each do |type|
|
||||
if param = opts[type]
|
||||
if version.first && version.first =~ /^\s*=?\s*(\d[^\s]*)\s*$/
|
||||
options = opts.merge("name" => name, "version" => $1)
|
||||
else
|
||||
options = opts.dup
|
||||
end
|
||||
source = send(type, param, options) {}
|
||||
opts["source"] = source
|
||||
end
|
||||
end
|
||||
|
||||
opts["source"] ||= @source
|
||||
opts["env"] ||= @env
|
||||
opts["platforms"] = platforms.dup
|
||||
opts["group"] = groups
|
||||
end
|
||||
|
||||
def normalize_source(source)
|
||||
case source
|
||||
when :gemcutter, :rubygems, :rubyforge
|
||||
Bundler.ui.warn "The source :#{source} is deprecated because HTTP " \
|
||||
"requests are insecure.\nPlease change your source to 'https://" \
|
||||
"rubygems.org' if possible, or 'http://rubygems.org' if not."
|
||||
"http://rubygems.org"
|
||||
when String
|
||||
source
|
||||
else
|
||||
raise GemfileError, "Unknown source '#{source}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,76 @@
|
||||
module Bundler
|
||||
# used for Creating Specifications from the Gemcutter Endpoint
|
||||
class EndpointSpecification < Gem::Specification
|
||||
include MatchPlatform
|
||||
|
||||
attr_reader :name, :version, :platform, :dependencies
|
||||
attr_accessor :source, :source_uri
|
||||
|
||||
def initialize(name, version, platform, dependencies)
|
||||
@name = name
|
||||
@version = version
|
||||
@platform = platform
|
||||
@dependencies = dependencies
|
||||
end
|
||||
|
||||
def fetch_platform
|
||||
@platform
|
||||
end
|
||||
|
||||
# needed for standalone, load required_paths from local gemspec
|
||||
# after the gem in installed
|
||||
def require_paths
|
||||
if @remote_specification
|
||||
@remote_specification.require_paths
|
||||
elsif _local_specification
|
||||
_local_specification.require_paths
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
# needed for binstubs
|
||||
def executables
|
||||
if @remote_specification
|
||||
@remote_specification.executables
|
||||
elsif _local_specification
|
||||
_local_specification.executables
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
# needed for bundle clean
|
||||
def bindir
|
||||
if @remote_specification
|
||||
@remote_specification.bindir
|
||||
elsif _local_specification
|
||||
_local_specification.bindir
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
# needed for post_install_messages during install
|
||||
def post_install_message
|
||||
if @remote_specification
|
||||
@remote_specification.post_install_message
|
||||
elsif _local_specification
|
||||
_local_specification.post_install_message
|
||||
end
|
||||
end
|
||||
|
||||
def _local_specification
|
||||
eval(File.read(local_specification_path)) if @loaded_from && File.exist?(local_specification_path)
|
||||
end
|
||||
|
||||
def __swap__(spec)
|
||||
@remote_specification = spec
|
||||
end
|
||||
|
||||
private
|
||||
def local_specification_path
|
||||
"#{base_dir}/specifications/#{full_name}.gemspec"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,56 @@
|
||||
module Bundler
|
||||
class Env
|
||||
|
||||
def write(io)
|
||||
io.write(report)
|
||||
end
|
||||
|
||||
def report
|
||||
out = "Bundler #{Bundler::VERSION}\n"
|
||||
|
||||
out << "Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
|
||||
out << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
|
||||
out << ") [#{RUBY_PLATFORM}]\n"
|
||||
|
||||
out << "Rubygems #{Gem::VERSION}\n"
|
||||
|
||||
out << "rvm #{ENV['rvm_version']}\n" if ENV['rvm_version']
|
||||
|
||||
out << "GEM_HOME #{ENV['GEM_HOME']}\n"
|
||||
|
||||
out << "GEM_PATH #{ENV['GEM_PATH']}\n" unless ENV['GEM_PATH'] == ENV['GEM_HOME']
|
||||
|
||||
%w(rubygems-bundler open_gem).each do |name|
|
||||
specs = Gem::Specification.find_all{|s| s.name == name }
|
||||
out << "#{name} (#{specs.map(&:version).join(',')})\n" unless specs.empty?
|
||||
end
|
||||
|
||||
out << "\nBundler settings\n" unless Bundler.settings.all.empty?
|
||||
Bundler.settings.all.each do |setting|
|
||||
out << " #{setting}\n"
|
||||
Bundler.settings.pretty_values_for(setting).each do |line|
|
||||
out << " " << line << "\n"
|
||||
end
|
||||
end
|
||||
|
||||
out << "\n\n" << "Gemfile\n"
|
||||
out << read_file("Gemfile") << "\n"
|
||||
|
||||
out << "\n\n" << "Gemfile.lock\n"
|
||||
out << read_file("Gemfile.lock") << "\n"
|
||||
|
||||
out
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def read_file(filename)
|
||||
File.read(filename).strip
|
||||
rescue Errno::ENOENT
|
||||
"<No #{filename} found>"
|
||||
rescue => e
|
||||
"#{e.class}: #{e.message}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,42 @@
|
||||
module Bundler
|
||||
class Environment
|
||||
attr_reader :root
|
||||
|
||||
def initialize(root, definition)
|
||||
@root = root
|
||||
@definition = definition
|
||||
|
||||
env_file = Bundler.app_config_path.join('environment.rb')
|
||||
env_file.rmtree if env_file.exist?
|
||||
end
|
||||
|
||||
def inspect
|
||||
@definition.to_lock.inspect
|
||||
end
|
||||
|
||||
def requested_specs
|
||||
@definition.requested_specs
|
||||
end
|
||||
|
||||
def specs
|
||||
@definition.specs
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@definition.dependencies
|
||||
end
|
||||
|
||||
def current_dependencies
|
||||
@definition.current_dependencies
|
||||
end
|
||||
|
||||
def lock
|
||||
@definition.lock(Bundler.default_lockfile)
|
||||
end
|
||||
|
||||
def update(*gems)
|
||||
# Nothing
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,399 @@
|
||||
require 'bundler/vendored_persistent'
|
||||
require 'securerandom'
|
||||
require 'cgi'
|
||||
|
||||
module Bundler
|
||||
|
||||
# Handles all the fetching with the rubygems server
|
||||
class Fetcher
|
||||
# This error is raised if the API returns a 413 (only printed in verbose)
|
||||
class FallbackError < HTTPError; end
|
||||
# This is the error raised if OpenSSL fails the cert verification
|
||||
class CertificateFailureError < HTTPError
|
||||
def initialize(remote_uri)
|
||||
super "Could not verify the SSL certificate for #{remote_uri}.\nThere" \
|
||||
" is a chance you are experiencing a man-in-the-middle attack, but" \
|
||||
" most likely your system doesn't have the CA certificates needed" \
|
||||
" for verification. For information about OpenSSL certificates, see" \
|
||||
" bit.ly/ruby-ssl. To connect without using SSL, edit your Gemfile" \
|
||||
" sources and change 'https' to 'http'."
|
||||
end
|
||||
end
|
||||
# This is the error raised when a source is HTTPS and OpenSSL didn't load
|
||||
class SSLError < HTTPError
|
||||
def initialize(msg = nil)
|
||||
super msg || "Could not load OpenSSL.\n" \
|
||||
"You must recompile Ruby with OpenSSL support or change the sources in your " \
|
||||
"Gemfile from 'https' to 'http'. Instructions for compiling with OpenSSL " \
|
||||
"using RVM are available at rvm.io/packages/openssl."
|
||||
end
|
||||
end
|
||||
# This error is raised if HTTP authentication is required, but not provided.
|
||||
class AuthenticationRequiredError < HTTPError
|
||||
def initialize(remote_uri)
|
||||
super "Authentication is required for #{remote_uri}.\n" \
|
||||
"Please supply credentials for this source. You can do this by running:\n" \
|
||||
" bundle config #{remote_uri} username:password"
|
||||
end
|
||||
end
|
||||
# This error is raised if HTTP authentication is provided, but incorrect.
|
||||
class BadAuthenticationError < HTTPError
|
||||
def initialize(remote_uri)
|
||||
super "Bad username or password for #{remote_uri}.\n" \
|
||||
"Please double-check your credentials and correct them."
|
||||
end
|
||||
end
|
||||
|
||||
# Exceptions classes that should bypass retry attempts. If your password didn't work the
|
||||
# first time, it's not going to the third time.
|
||||
AUTH_ERRORS = [AuthenticationRequiredError, BadAuthenticationError]
|
||||
|
||||
class << self
|
||||
attr_accessor :disable_endpoint, :api_timeout, :redirect_limit, :max_retries
|
||||
|
||||
def download_gem_from_uri(spec, uri)
|
||||
spec.fetch_platform
|
||||
|
||||
download_path = Bundler.requires_sudo? ? Bundler.tmp(spec.full_name) : Bundler.rubygems.gem_dir
|
||||
gem_path = "#{Bundler.rubygems.gem_dir}/cache/#{spec.full_name}.gem"
|
||||
|
||||
FileUtils.mkdir_p("#{download_path}/cache")
|
||||
Bundler.rubygems.download_gem(spec, uri, download_path)
|
||||
|
||||
if Bundler.requires_sudo?
|
||||
Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/cache"
|
||||
Bundler.sudo "mv #{Bundler.tmp(spec.full_name)}/cache/#{spec.full_name}.gem #{gem_path}"
|
||||
end
|
||||
|
||||
gem_path
|
||||
end
|
||||
|
||||
def user_agent
|
||||
@user_agent ||= begin
|
||||
ruby = Bundler.ruby_version
|
||||
|
||||
agent = "bundler/#{Bundler::VERSION}"
|
||||
agent += " rubygems/#{Gem::VERSION}"
|
||||
agent += " ruby/#{ruby.version}"
|
||||
agent += " (#{ruby.host})"
|
||||
agent += " command/#{ARGV.first}"
|
||||
|
||||
if ruby.engine != "ruby"
|
||||
# engine_version raises on unknown engines
|
||||
engine_version = ruby.engine_version rescue "???"
|
||||
agent += " #{ruby.engine}/#{engine_version}"
|
||||
end
|
||||
# add a random ID so we can consolidate runs server-side
|
||||
agent << " " << SecureRandom.hex(8)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def initialize(remote_uri)
|
||||
@redirect_limit = 5 # How many redirects to allow in one request
|
||||
@api_timeout = 10 # How long to wait for each API call
|
||||
@max_retries = 3 # How many retries for the API call
|
||||
|
||||
@remote_uri = Bundler::Source.mirror_for(remote_uri)
|
||||
@public_uri = @remote_uri.dup
|
||||
@public_uri.user, @public_uri.password = nil, nil # don't print these
|
||||
|
||||
Socket.do_not_reverse_lookup = true
|
||||
connection # create persistent connection
|
||||
end
|
||||
|
||||
def connection
|
||||
@connection ||= begin
|
||||
needs_ssl = @remote_uri.scheme == "https" ||
|
||||
Bundler.settings[:ssl_verify_mode] ||
|
||||
Bundler.settings[:ssl_client_cert]
|
||||
raise SSLError if needs_ssl && !defined?(OpenSSL::SSL)
|
||||
|
||||
con = Net::HTTP::Persistent.new 'bundler', :ENV
|
||||
|
||||
if @remote_uri.scheme == "https"
|
||||
con.verify_mode = (Bundler.settings[:ssl_verify_mode] ||
|
||||
OpenSSL::SSL::VERIFY_PEER)
|
||||
con.cert_store = bundler_cert_store
|
||||
end
|
||||
|
||||
if Bundler.settings[:ssl_client_cert]
|
||||
pem = File.read(Bundler.settings[:ssl_client_cert])
|
||||
con.cert = OpenSSL::X509::Certificate.new(pem)
|
||||
con.key = OpenSSL::PKey::RSA.new(pem)
|
||||
end
|
||||
|
||||
con.read_timeout = @api_timeout
|
||||
con.override_headers["User-Agent"] = self.class.user_agent
|
||||
con
|
||||
end
|
||||
end
|
||||
|
||||
def uri
|
||||
@public_uri
|
||||
end
|
||||
|
||||
# fetch a gem specification
|
||||
def fetch_spec(spec)
|
||||
spec = spec - [nil, 'ruby', '']
|
||||
spec_file_name = "#{spec.join '-'}.gemspec"
|
||||
|
||||
uri = URI.parse("#{@remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
|
||||
if uri.scheme == 'file'
|
||||
Bundler.load_marshal Gem.inflate(Gem.read_binary(uri.path))
|
||||
elsif cached_spec_path = gemspec_cached_path(spec_file_name)
|
||||
Bundler.load_gemspec(cached_spec_path)
|
||||
else
|
||||
Bundler.load_marshal Gem.inflate(fetch(uri))
|
||||
end
|
||||
rescue MarshalError
|
||||
raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \
|
||||
"Your network or your gem server is probably having issues right now."
|
||||
end
|
||||
|
||||
# cached gem specification path, if one exists
|
||||
def gemspec_cached_path spec_file_name
|
||||
paths = Bundler.rubygems.spec_cache_dirs.map { |dir| File.join(dir, spec_file_name) }
|
||||
paths = paths.select {|path| File.file? path }
|
||||
paths.first
|
||||
end
|
||||
|
||||
# return the specs in the bundler format as an index
|
||||
def specs(gem_names, source)
|
||||
old = Bundler.rubygems.sources
|
||||
index = Index.new
|
||||
|
||||
if gem_names && use_api
|
||||
specs = fetch_remote_specs(gem_names)
|
||||
end
|
||||
|
||||
if specs.nil?
|
||||
# API errors mean we should treat this as a non-API source
|
||||
@use_api = false
|
||||
|
||||
specs = Bundler::Retry.new("source fetch", AUTH_ERRORS).attempts do
|
||||
fetch_all_remote_specs
|
||||
end
|
||||
end
|
||||
|
||||
specs[@remote_uri].each do |name, version, platform, dependencies|
|
||||
next if name == 'bundler'
|
||||
spec = nil
|
||||
if dependencies
|
||||
spec = EndpointSpecification.new(name, version, platform, dependencies)
|
||||
else
|
||||
spec = RemoteSpecification.new(name, version, platform, self)
|
||||
end
|
||||
spec.source = source
|
||||
spec.source_uri = @remote_uri
|
||||
index << spec
|
||||
end
|
||||
|
||||
index
|
||||
rescue CertificateFailureError => e
|
||||
Bundler.ui.info "" if gem_names && use_api # newline after dots
|
||||
raise e
|
||||
ensure
|
||||
Bundler.rubygems.sources = old
|
||||
end
|
||||
|
||||
# fetch index
|
||||
def fetch_remote_specs(gem_names, full_dependency_list = [], last_spec_list = [])
|
||||
query_list = gem_names - full_dependency_list
|
||||
|
||||
# only display the message on the first run
|
||||
if Bundler.ui.debug?
|
||||
Bundler.ui.debug "Query List: #{query_list.inspect}"
|
||||
else
|
||||
Bundler.ui.info ".", false
|
||||
end
|
||||
|
||||
return {@remote_uri => last_spec_list} if query_list.empty?
|
||||
|
||||
remote_specs = Bundler::Retry.new("dependency api", AUTH_ERRORS).attempts do
|
||||
fetch_dependency_remote_specs(query_list)
|
||||
end
|
||||
|
||||
spec_list, deps_list = remote_specs
|
||||
returned_gems = spec_list.map {|spec| spec.first }.uniq
|
||||
fetch_remote_specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list)
|
||||
rescue HTTPError, MarshalError, GemspecError
|
||||
Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
|
||||
Bundler.ui.debug "could not fetch from the dependency API, trying the full index"
|
||||
@use_api = false
|
||||
return nil
|
||||
end
|
||||
|
||||
def use_api
|
||||
return @use_api if defined?(@use_api)
|
||||
|
||||
if @remote_uri.scheme == "file" || Bundler::Fetcher.disable_endpoint
|
||||
@use_api = false
|
||||
elsif fetch(dependency_api_uri)
|
||||
@use_api = true
|
||||
end
|
||||
rescue HTTPError
|
||||
@use_api = false
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class}:0x#{object_id} uri=#{uri}>"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
HTTP_ERRORS = [
|
||||
Timeout::Error, EOFError, SocketError, Errno::ENETDOWN,
|
||||
Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
|
||||
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
|
||||
Net::HTTP::Persistent::Error
|
||||
]
|
||||
|
||||
def fetch(uri, counter = 0)
|
||||
raise HTTPError, "Too many redirects" if counter >= @redirect_limit
|
||||
|
||||
response = request(uri)
|
||||
Bundler.ui.debug("HTTP #{response.code} #{response.message}")
|
||||
|
||||
case response
|
||||
when Net::HTTPRedirection
|
||||
new_uri = URI.parse(response["location"])
|
||||
if new_uri.host == uri.host
|
||||
new_uri.user = uri.user
|
||||
new_uri.password = uri.password
|
||||
end
|
||||
fetch(new_uri, counter + 1)
|
||||
when Net::HTTPSuccess
|
||||
response.body
|
||||
when Net::HTTPRequestEntityTooLarge
|
||||
raise FallbackError, response.body
|
||||
else
|
||||
raise HTTPError, "#{response.class}: #{response.body}"
|
||||
end
|
||||
end
|
||||
|
||||
def request(uri)
|
||||
Bundler.ui.debug "HTTP GET #{uri}"
|
||||
req = Net::HTTP::Get.new uri.request_uri
|
||||
if uri.user
|
||||
user = CGI.unescape(uri.user)
|
||||
password = uri.password ? CGI.unescape(uri.password) : nil
|
||||
req.basic_auth(user, password)
|
||||
end
|
||||
connection.request(uri, req)
|
||||
rescue Net::HTTPUnauthorized, Net::HTTPForbidden
|
||||
retry_with_auth { request(uri) }
|
||||
rescue OpenSSL::SSL::SSLError
|
||||
raise CertificateFailureError.new(uri)
|
||||
rescue *HTTP_ERRORS => e
|
||||
Bundler.ui.trace e
|
||||
raise HTTPError, "Network error while fetching #{uri}"
|
||||
end
|
||||
|
||||
def dependency_api_uri(gem_names = [])
|
||||
uri = fetch_uri + "api/v1/dependencies"
|
||||
uri.query = "gems=#{URI.encode(gem_names.join(","))}" if gem_names.any?
|
||||
uri
|
||||
end
|
||||
|
||||
# fetch from Gemcutter Dependency Endpoint API
|
||||
def fetch_dependency_remote_specs(gem_names)
|
||||
Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}"
|
||||
marshalled_deps = fetch dependency_api_uri(gem_names)
|
||||
gem_list = Bundler.load_marshal(marshalled_deps)
|
||||
deps_list = []
|
||||
|
||||
spec_list = gem_list.map do |s|
|
||||
dependencies = s[:dependencies].map do |name, requirement|
|
||||
dep = well_formed_dependency(name, requirement.split(", "))
|
||||
deps_list << dep.name
|
||||
dep
|
||||
end
|
||||
|
||||
[s[:name], Gem::Version.new(s[:number]), s[:platform], dependencies]
|
||||
end
|
||||
|
||||
[spec_list, deps_list.uniq]
|
||||
end
|
||||
|
||||
# fetch from modern index: specs.4.8.gz
|
||||
def fetch_all_remote_specs
|
||||
old_sources = Bundler.rubygems.sources
|
||||
Bundler.rubygems.sources = [@remote_uri.to_s]
|
||||
Bundler.rubygems.fetch_all_remote_specs
|
||||
rescue Gem::RemoteFetcher::FetchError, OpenSSL::SSL::SSLError => e
|
||||
case e.message
|
||||
when /certificate verify failed/
|
||||
raise CertificateFailureError.new(uri)
|
||||
when /401|403/
|
||||
# Gemfury uses a 403 for unauthenticated requests instead of a 401, so retry auth
|
||||
# on both.
|
||||
retry_with_auth { fetch_all_remote_specs }
|
||||
else
|
||||
Bundler.ui.trace e
|
||||
raise HTTPError, "Could not fetch specs from #{uri}"
|
||||
end
|
||||
ensure
|
||||
Bundler.rubygems.sources = old_sources
|
||||
end
|
||||
|
||||
def well_formed_dependency(name, *requirements)
|
||||
Gem::Dependency.new(name, *requirements)
|
||||
rescue ArgumentError => e
|
||||
illformed = 'Ill-formed requirement ["#<YAML::Syck::DefaultKey'
|
||||
raise e unless e.message.include?(illformed)
|
||||
puts # we shouldn't print the error message on the "fetching info" status line
|
||||
raise GemspecError,
|
||||
"Unfortunately, the gem #{s[:name]} (#{s[:number]}) has an invalid " \
|
||||
"gemspec. \nPlease ask the gem author to yank the bad version to fix " \
|
||||
"this issue. For more information, see http://bit.ly/syck-defaultkey."
|
||||
end
|
||||
|
||||
def bundler_cert_store
|
||||
store = OpenSSL::X509::Store.new
|
||||
if Bundler.settings[:ssl_ca_cert]
|
||||
if File.directory? Bundler.settings[:ssl_ca_cert]
|
||||
store.add_path Bundler.settings[:ssl_ca_cert]
|
||||
else
|
||||
store.add_file Bundler.settings[:ssl_ca_cert]
|
||||
end
|
||||
else
|
||||
store.set_default_paths
|
||||
certs = File.expand_path("../ssl_certs/*.pem", __FILE__)
|
||||
Dir.glob(certs).each { |c| store.add_file c }
|
||||
end
|
||||
store
|
||||
end
|
||||
|
||||
# Attempt to retry with HTTP authentication, if it's appropriate to do so. Yields to a block;
|
||||
# the caller should use this to re-attempt the failing request with the altered `@remote_uri`.
|
||||
def retry_with_auth
|
||||
# Authentication has already been attempted and failed.
|
||||
raise BadAuthenticationError.new(uri) if @remote_uri.user
|
||||
|
||||
auth = Bundler.settings[@remote_uri.to_s]
|
||||
|
||||
# Authentication isn't provided at all, by "bundle config" or in the URI.
|
||||
raise AuthenticationRequiredError.new(uri) if auth.nil?
|
||||
|
||||
@remote_uri.user, @remote_uri.password = *auth.split(":", 2)
|
||||
yield
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_uri
|
||||
@fetch_uri ||= begin
|
||||
if @remote_uri.host == "rubygems.org"
|
||||
uri = @remote_uri.dup
|
||||
uri.host = "bundler.rubygems.org"
|
||||
uri
|
||||
else
|
||||
@remote_uri
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,42 @@
|
||||
require "bundler/vendored_thor"
|
||||
|
||||
module Bundler
|
||||
def self.with_friendly_errors
|
||||
yield
|
||||
rescue Bundler::BundlerError => e
|
||||
Bundler.ui.error e.message, :wrap => true
|
||||
Bundler.ui.trace e
|
||||
exit e.status_code
|
||||
rescue Thor::AmbiguousTaskError => e
|
||||
Bundler.ui.error e.message
|
||||
exit 15
|
||||
rescue Thor::UndefinedTaskError => e
|
||||
Bundler.ui.error e.message
|
||||
exit 15
|
||||
rescue Thor::Error => e
|
||||
Bundler.ui.error e.message
|
||||
exit 1
|
||||
rescue LoadError => e
|
||||
raise e unless e.message =~ /cannot load such file -- openssl|openssl.so|libcrypto.so/
|
||||
Bundler.ui.error "\nCould not load OpenSSL."
|
||||
Bundler.ui.warn <<-WARN, :wrap => true
|
||||
You must recompile Ruby with OpenSSL support or change the sources in your \
|
||||
Gemfile from 'https' to 'http'. Instructions for compiling with OpenSSL \
|
||||
using RVM are available at http://rvm.io/packages/openssl.
|
||||
WARN
|
||||
Bundler.ui.trace e
|
||||
exit 1
|
||||
rescue Interrupt => e
|
||||
Bundler.ui.error "\nQuitting..."
|
||||
Bundler.ui.trace e
|
||||
exit 1
|
||||
rescue SystemExit => e
|
||||
exit e.status
|
||||
rescue Exception => e
|
||||
Bundler.ui.error <<-ERR, :wrap => true
|
||||
Unfortunately, a fatal error has occurred. Please see the Bundler \
|
||||
troubleshooting documentation at http://bit.ly/bundler-issues. Thanks!
|
||||
ERR
|
||||
raise e
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,169 @@
|
||||
require 'bundler/vendored_thor' unless defined?(Thor)
|
||||
require 'bundler'
|
||||
|
||||
module Bundler
|
||||
class GemHelper
|
||||
include Rake::DSL if defined? Rake::DSL
|
||||
|
||||
class << self
|
||||
# set when install'd.
|
||||
attr_accessor :instance
|
||||
|
||||
def install_tasks(opts = {})
|
||||
new(opts[:dir], opts[:name]).install
|
||||
end
|
||||
|
||||
def gemspec(&block)
|
||||
gemspec = instance.gemspec
|
||||
block.call(gemspec) if block
|
||||
gemspec
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :spec_path, :base, :gemspec
|
||||
|
||||
def initialize(base = nil, name = nil)
|
||||
Bundler.ui = UI::Shell.new
|
||||
@base = (base ||= SharedHelpers.pwd)
|
||||
gemspecs = name ? [File.join(base, "#{name}.gemspec")] : Dir[File.join(base, "{,*}.gemspec")]
|
||||
raise "Unable to determine name from existing gemspec. Use :name => 'gemname' in #install_tasks to manually set it." unless gemspecs.size == 1
|
||||
@spec_path = gemspecs.first
|
||||
@gemspec = Bundler.load_gemspec(@spec_path)
|
||||
end
|
||||
|
||||
def install
|
||||
built_gem_path = nil
|
||||
|
||||
desc "Build #{name}-#{version}.gem into the pkg directory."
|
||||
task 'build' do
|
||||
built_gem_path = build_gem
|
||||
end
|
||||
|
||||
desc "Build and install #{name}-#{version}.gem into system gems."
|
||||
task 'install' => 'build' do
|
||||
install_gem(built_gem_path)
|
||||
end
|
||||
|
||||
desc "Create tag #{version_tag} and build and push #{name}-#{version}.gem to Rubygems"
|
||||
task 'release' => 'build' do
|
||||
release_gem(built_gem_path)
|
||||
end
|
||||
|
||||
GemHelper.instance = self
|
||||
end
|
||||
|
||||
def build_gem
|
||||
file_name = nil
|
||||
sh("gem build -V '#{spec_path}'") { |out, code|
|
||||
file_name = File.basename(built_gem_path)
|
||||
FileUtils.mkdir_p(File.join(base, 'pkg'))
|
||||
FileUtils.mv(built_gem_path, 'pkg')
|
||||
Bundler.ui.confirm "#{name} #{version} built to pkg/#{file_name}."
|
||||
}
|
||||
File.join(base, 'pkg', file_name)
|
||||
end
|
||||
|
||||
def install_gem(built_gem_path=nil)
|
||||
built_gem_path ||= build_gem
|
||||
out, _ = sh_with_code("gem install '#{built_gem_path}' --local")
|
||||
raise "Couldn't install gem, run `gem install #{built_gem_path}' for more detailed output" unless out[/Successfully installed/]
|
||||
Bundler.ui.confirm "#{name} (#{version}) installed."
|
||||
end
|
||||
|
||||
def release_gem(built_gem_path=nil)
|
||||
guard_clean
|
||||
built_gem_path ||= build_gem
|
||||
tag_version { git_push } unless already_tagged?
|
||||
rubygem_push(built_gem_path) if gem_push?
|
||||
end
|
||||
|
||||
protected
|
||||
def rubygem_push(path)
|
||||
if Pathname.new("~/.gem/credentials").expand_path.exist?
|
||||
sh("gem push '#{path}'")
|
||||
Bundler.ui.confirm "Pushed #{name} #{version} to rubygems.org."
|
||||
else
|
||||
raise "Your rubygems.org credentials aren't set. Run `gem push` to set them."
|
||||
end
|
||||
end
|
||||
|
||||
def built_gem_path
|
||||
Dir[File.join(base, "#{name}-*.gem")].sort_by{|f| File.mtime(f)}.last
|
||||
end
|
||||
|
||||
def git_push
|
||||
perform_git_push
|
||||
perform_git_push ' --tags'
|
||||
Bundler.ui.confirm "Pushed git commits and tags."
|
||||
end
|
||||
|
||||
def perform_git_push(options = '')
|
||||
cmd = "git push #{options}"
|
||||
out, code = sh_with_code(cmd)
|
||||
raise "Couldn't git push. `#{cmd}' failed with the following output:\n\n#{out}\n" unless code == 0
|
||||
end
|
||||
|
||||
def already_tagged?
|
||||
if sh('git tag').split(/\n/).include?(version_tag)
|
||||
Bundler.ui.confirm "Tag #{version_tag} has already been created."
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def guard_clean
|
||||
clean? && committed? or raise("There are files that need to be committed first.")
|
||||
end
|
||||
|
||||
def clean?
|
||||
sh_with_code("git diff --exit-code")[1] == 0
|
||||
end
|
||||
|
||||
def committed?
|
||||
sh_with_code("git diff-index --quiet --cached HEAD")[1] == 0
|
||||
end
|
||||
|
||||
def tag_version
|
||||
sh "git tag -a -m \"Version #{version}\" #{version_tag}"
|
||||
Bundler.ui.confirm "Tagged #{version_tag}."
|
||||
yield if block_given?
|
||||
rescue
|
||||
Bundler.ui.error "Untagging #{version_tag} due to error."
|
||||
sh_with_code "git tag -d #{version_tag}"
|
||||
raise
|
||||
end
|
||||
|
||||
def version
|
||||
gemspec.version
|
||||
end
|
||||
|
||||
def version_tag
|
||||
"v#{version}"
|
||||
end
|
||||
|
||||
def name
|
||||
gemspec.name
|
||||
end
|
||||
|
||||
def sh(cmd, &block)
|
||||
out, code = sh_with_code(cmd, &block)
|
||||
code == 0 ? out : raise(out.empty? ? "Running `#{cmd}' failed. Run this command directly for more detailed output." : out)
|
||||
end
|
||||
|
||||
def sh_with_code(cmd, &block)
|
||||
cmd << " 2>&1"
|
||||
outbuf = ''
|
||||
Bundler.ui.debug(cmd)
|
||||
SharedHelpers.chdir(base) {
|
||||
outbuf = `#{cmd}`
|
||||
if $? == 0
|
||||
block.call(outbuf) if block
|
||||
end
|
||||
}
|
||||
[outbuf, $?]
|
||||
end
|
||||
|
||||
def gem_push?
|
||||
! %w{n no nil false off 0}.include?(ENV['gem_push'].to_s.downcase)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
module Bundler
|
||||
module GemHelpers
|
||||
|
||||
GENERIC_CACHE = {}
|
||||
GENERICS = [
|
||||
[Gem::Platform.new('java'), Gem::Platform.new('java')],
|
||||
[Gem::Platform.new('mswin32'), Gem::Platform.new('mswin32')],
|
||||
[Gem::Platform.new('x64-mingw32'), Gem::Platform.new('x64-mingw32')],
|
||||
[Gem::Platform.new('x86_64-mingw32'), Gem::Platform.new('x64-mingw32')],
|
||||
[Gem::Platform.new('mingw32'), Gem::Platform.new('x86-mingw32')]
|
||||
]
|
||||
|
||||
def generic(p)
|
||||
return p if p == Gem::Platform::RUBY
|
||||
|
||||
GENERIC_CACHE[p] ||= begin
|
||||
_, found = GENERICS.find do |match, _generic|
|
||||
p.os == match.os && (!match.cpu || p.cpu == match.cpu)
|
||||
end
|
||||
found || Gem::Platform::RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
require 'rubygems/installer'
|
||||
|
||||
module Bundler
|
||||
class GemInstaller < Gem::Installer
|
||||
def check_executable_overwrite(filename)
|
||||
# Bundler needs to install gems regardless of binstub overwriting
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
module Bundler
|
||||
def self.preserve_gem_path
|
||||
original_gem_path = ENV["_ORIGINAL_GEM_PATH"]
|
||||
gem_path = ENV["GEM_PATH"]
|
||||
ENV["_ORIGINAL_GEM_PATH"] = gem_path if original_gem_path.nil? || original_gem_path == ""
|
||||
ENV["GEM_PATH"] = original_gem_path if gem_path.nil? || gem_path == ""
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,2 @@
|
||||
require 'bundler/gem_helper'
|
||||
Bundler::GemHelper.install_tasks
|
||||
@@ -0,0 +1,166 @@
|
||||
require 'set'
|
||||
module Bundler
|
||||
class Graph
|
||||
GRAPH_NAME = :Gemfile
|
||||
|
||||
def initialize(env, output_file, show_version = false, show_requirements = false, output_format = "png")
|
||||
@env = env
|
||||
@output_file = output_file
|
||||
@show_version = show_version
|
||||
@show_requirements = show_requirements
|
||||
@output_format = output_format
|
||||
|
||||
@groups = []
|
||||
@relations = Hash.new {|h, k| h[k] = Set.new}
|
||||
@node_options = {}
|
||||
@edge_options = {}
|
||||
|
||||
_patching_gem_dependency_class
|
||||
_populate_relations
|
||||
end
|
||||
|
||||
attr_reader :groups, :relations, :node_options, :edge_options, :output_file, :output_format
|
||||
|
||||
def viz
|
||||
GraphVizClient.new(self).run
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _populate_relations
|
||||
parent_dependencies = _groups.values.to_set.flatten
|
||||
while true
|
||||
if parent_dependencies.empty?
|
||||
break
|
||||
else
|
||||
tmp = Set.new
|
||||
parent_dependencies.each do |dependency|
|
||||
child_dependencies = dependency.to_spec.runtime_dependencies.to_set
|
||||
@relations[dependency.name] += child_dependencies.map(&:name).to_set
|
||||
tmp += child_dependencies
|
||||
|
||||
@node_options[dependency.name] = _make_label(dependency, :node)
|
||||
child_dependencies.each do |c_dependency|
|
||||
@edge_options["#{dependency.name}_#{c_dependency.name}"] = _make_label(c_dependency, :edge)
|
||||
end
|
||||
end
|
||||
parent_dependencies = tmp
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def _groups
|
||||
relations = Hash.new {|h, k| h[k] = Set.new}
|
||||
@env.current_dependencies.each do |dependency|
|
||||
dependency.groups.each do |group|
|
||||
relations[group.to_s].add(dependency)
|
||||
@relations[group.to_s].add(dependency.name)
|
||||
|
||||
@node_options[group.to_s] ||= _make_label(group, :node)
|
||||
@edge_options["#{group}_#{dependency.name}"] = _make_label(dependency, :edge)
|
||||
end
|
||||
end
|
||||
@groups = relations.keys
|
||||
relations
|
||||
end
|
||||
|
||||
def _make_label(symbol_or_string_or_dependency, element_type)
|
||||
case element_type.to_sym
|
||||
when :node
|
||||
if symbol_or_string_or_dependency.is_a?(Gem::Dependency)
|
||||
label = symbol_or_string_or_dependency.name.dup
|
||||
label << "\n#{symbol_or_string_or_dependency.to_spec.version.to_s}" if @show_version
|
||||
else
|
||||
label = symbol_or_string_or_dependency.to_s
|
||||
end
|
||||
when :edge
|
||||
label = nil
|
||||
if symbol_or_string_or_dependency.respond_to?(:requirements_list) && @show_requirements
|
||||
tmp = symbol_or_string_or_dependency.requirements_list.join(", ")
|
||||
label = tmp if tmp != ">= 0"
|
||||
end
|
||||
else
|
||||
raise ArgumentError, "2nd argument is invalid"
|
||||
end
|
||||
label.nil? ? {} : { :label => label }
|
||||
end
|
||||
|
||||
def _patching_gem_dependency_class
|
||||
# method borrow from rubygems/dependency.rb
|
||||
# redefinition of matching_specs will also redefine to_spec and to_specs
|
||||
Gem::Dependency.class_eval do
|
||||
def matching_specs platform_only = false
|
||||
matches = Bundler.load.specs.select { |spec|
|
||||
self.name == spec.name and
|
||||
requirement.satisfied_by? spec.version
|
||||
}
|
||||
|
||||
if platform_only
|
||||
matches.reject! { |spec|
|
||||
not Gem::Platform.match spec.platform
|
||||
}
|
||||
end
|
||||
|
||||
matches = matches.sort_by { |s| s.sort_obj } # HACK: shouldn't be needed
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class GraphVizClient
|
||||
def initialize(graph_instance)
|
||||
@graph_name = graph_instance.class::GRAPH_NAME
|
||||
@groups = graph_instance.groups
|
||||
@relations = graph_instance.relations
|
||||
@node_options = graph_instance.node_options
|
||||
@edge_options = graph_instance.edge_options
|
||||
@output_file = graph_instance.output_file
|
||||
@output_format = graph_instance.output_format
|
||||
end
|
||||
|
||||
def g
|
||||
@g ||= ::GraphViz.digraph(@graph_name, {:concentrate => true, :normalize => true, :nodesep => 0.55}) do |g|
|
||||
g.edge[:weight] = 2
|
||||
g.edge[:fontname] = g.node[:fontname] = 'Arial, Helvetica, SansSerif'
|
||||
g.edge[:fontsize] = 12
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
@groups.each do |group|
|
||||
g.add_nodes(
|
||||
group,
|
||||
{:style => 'filled',
|
||||
:fillcolor => '#B9B9D5',
|
||||
:shape => "box3d",
|
||||
:fontsize => 16}.merge(@node_options[group])
|
||||
)
|
||||
end
|
||||
|
||||
@relations.each do |parent, children|
|
||||
children.each do |child|
|
||||
if @groups.include?(parent)
|
||||
g.add_nodes(child, {:style => 'filled', :fillcolor => '#B9B9D5'}.merge(@node_options[child]))
|
||||
g.add_edges(parent, child, {:constraint => false}.merge(@edge_options["#{parent}_#{child}"]))
|
||||
else
|
||||
g.add_nodes(child, @node_options[child])
|
||||
g.add_edges(parent, child, @edge_options["#{parent}_#{child}"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if @output_format.to_s == "debug"
|
||||
$stdout.puts g.output :none => String
|
||||
Bundler.ui.info "debugging bundle viz..."
|
||||
else
|
||||
begin
|
||||
g.output @output_format.to_sym => "#{@output_file}.#{@output_format}"
|
||||
Bundler.ui.info "#{@output_file}.#{@output_format}"
|
||||
rescue ArgumentError => e
|
||||
$stderr.puts "Unsupported output format. See Ruby-Graphviz/lib/graphviz/constants.rb"
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,203 @@
|
||||
require "set"
|
||||
|
||||
module Bundler
|
||||
class Index
|
||||
include Enumerable
|
||||
|
||||
def self.build
|
||||
i = new
|
||||
yield i
|
||||
i
|
||||
end
|
||||
|
||||
attr_reader :specs, :all_specs, :sources
|
||||
protected :specs, :all_specs
|
||||
|
||||
def initialize
|
||||
@sources = []
|
||||
@cache = {}
|
||||
@specs = Hash.new { |h,k| h[k] = [] }
|
||||
@all_specs = Hash.new { |h,k| h[k] = [] }
|
||||
end
|
||||
|
||||
def initialize_copy(o)
|
||||
super
|
||||
@sources = @sources.dup
|
||||
@cache = {}
|
||||
@specs = Hash.new { |h,k| h[k] = [] }
|
||||
@all_specs = Hash.new { |h,k| h[k] = [] }
|
||||
|
||||
o.specs.each do |name, array|
|
||||
@specs[name] = array.dup
|
||||
end
|
||||
o.all_specs.each do |name, array|
|
||||
@all_specs[name] = array.dup
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class}:0x#{object_id} sources=#{sources.map{|s| s.inspect}} specs.size=#{specs.size}>"
|
||||
end
|
||||
|
||||
def empty?
|
||||
each { return false }
|
||||
true
|
||||
end
|
||||
|
||||
def search_all(name)
|
||||
all_matches = @all_specs[name] + local_search(name)
|
||||
@sources.each do |source|
|
||||
all_matches.concat(source.search_all(name))
|
||||
end
|
||||
all_matches
|
||||
end
|
||||
|
||||
# Search this index's specs, and any source indexes that this index knows
|
||||
# about, returning all of the results.
|
||||
def search(query, base = nil)
|
||||
results = local_search(query, base)
|
||||
seen = Set.new(results.map { |spec| [spec.name, spec.version, spec.platform] })
|
||||
|
||||
@sources.each do |source|
|
||||
source.search(query, base).each do |spec|
|
||||
lookup = [spec.name, spec.version, spec.platform]
|
||||
unless seen.include?(lookup)
|
||||
results << spec
|
||||
seen << lookup
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
results.sort_by {|s| [s.version, s.platform.to_s == 'ruby' ? "\0" : s.platform.to_s] }
|
||||
end
|
||||
|
||||
def local_search(query, base = nil)
|
||||
case query
|
||||
when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query)
|
||||
when String then specs_by_name(query)
|
||||
when Gem::Dependency then search_by_dependency(query, base)
|
||||
else
|
||||
raise "You can't search for a #{query.inspect}."
|
||||
end
|
||||
end
|
||||
|
||||
def source_types
|
||||
sources.map{|s| s.class }.uniq
|
||||
end
|
||||
|
||||
alias [] search
|
||||
|
||||
def <<(spec)
|
||||
arr = specs_by_name(spec.name)
|
||||
|
||||
arr.delete_if do |s|
|
||||
same_version?(s.version, spec.version) && s.platform == spec.platform
|
||||
end
|
||||
|
||||
arr << spec
|
||||
spec
|
||||
end
|
||||
|
||||
def each(&blk)
|
||||
specs.values.each do |specs|
|
||||
specs.each(&blk)
|
||||
end
|
||||
end
|
||||
|
||||
# returns a list of the dependencies
|
||||
def unmet_dependency_names
|
||||
names = []
|
||||
each{|s| names.push *s.dependencies.map{|d| d.name } }
|
||||
names.uniq!
|
||||
names.delete_if{|n| n == "bundler" }
|
||||
names.select{|n| search(n).empty? }
|
||||
end
|
||||
|
||||
def use(other, override_dupes = false)
|
||||
return unless other
|
||||
other.each do |s|
|
||||
if (dupes = search_by_spec(s)) && dupes.any?
|
||||
@all_specs[s.name] = [s] + dupes
|
||||
next unless override_dupes
|
||||
@specs[s.name] -= dupes
|
||||
end
|
||||
@specs[s.name] << s
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def size
|
||||
@sources.inject(@specs.size) do |size, source|
|
||||
size += source.size
|
||||
end
|
||||
end
|
||||
|
||||
def ==(o)
|
||||
all? do |spec|
|
||||
other_spec = o[spec].first
|
||||
(spec.dependencies & other_spec.dependencies).empty? && spec.source == other_spec.source
|
||||
end
|
||||
end
|
||||
|
||||
def add_source(index)
|
||||
if index.is_a?(Index)
|
||||
@sources << index
|
||||
@sources.uniq! # need to use uniq! here instead of checking for the item before adding
|
||||
else
|
||||
raise ArgumentError, "Source must be an index, not #{index.class}"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def specs_by_name(name)
|
||||
@specs[name]
|
||||
end
|
||||
|
||||
def search_by_dependency(dependency, base = nil)
|
||||
@cache[base || false] ||= {}
|
||||
@cache[base || false][dependency] ||= begin
|
||||
specs = specs_by_name(dependency.name) + (base || [])
|
||||
found = specs.select do |spec|
|
||||
if base # allow all platforms when searching from a lockfile
|
||||
dependency.matches_spec?(spec)
|
||||
else
|
||||
dependency.matches_spec?(spec) && Gem::Platform.match(spec.platform)
|
||||
end
|
||||
end
|
||||
|
||||
wants_prerelease = dependency.requirement.prerelease?
|
||||
only_prerelease = specs.all? {|spec| spec.version.prerelease? }
|
||||
|
||||
unless wants_prerelease || only_prerelease
|
||||
found.reject! { |spec| spec.version.prerelease? }
|
||||
end
|
||||
|
||||
found
|
||||
end
|
||||
end
|
||||
|
||||
def search_by_spec(spec)
|
||||
specs_by_name(spec.name).select do |s|
|
||||
same_version?(s.version, spec.version) && Gem::Platform.new(s.platform) == Gem::Platform.new(spec.platform)
|
||||
end
|
||||
end
|
||||
|
||||
if RUBY_VERSION < '1.9'
|
||||
def same_version?(a, b)
|
||||
regex = /^(.*?)(?:\.0)*$/
|
||||
a.to_s[regex, 1] == b.to_s[regex, 1]
|
||||
end
|
||||
else
|
||||
def same_version?(a, b)
|
||||
a == b
|
||||
end
|
||||
end
|
||||
|
||||
def spec_satisfies_dependency?(spec, dep)
|
||||
return false unless dep.name == spec.name
|
||||
dep.requirement.satisfied_by?(spec.version)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,64 @@
|
||||
module Bundler
|
||||
class Injector
|
||||
def self.inject(new_deps)
|
||||
injector = new(new_deps)
|
||||
injector.inject(Bundler.default_gemfile, Bundler.default_lockfile)
|
||||
end
|
||||
|
||||
def initialize(new_deps)
|
||||
@new_deps = new_deps
|
||||
end
|
||||
|
||||
def inject(gemfile_path, lockfile_path)
|
||||
if Bundler.settings[:frozen]
|
||||
# ensure the lock and Gemfile are synced
|
||||
Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true)
|
||||
# temporarily remove frozen while we inject
|
||||
frozen = Bundler.settings.delete(:frozen)
|
||||
end
|
||||
|
||||
# evaluate the Gemfile we have now
|
||||
builder = Dsl.new
|
||||
builder.eval_gemfile(gemfile_path)
|
||||
|
||||
# don't inject any gems that are already in the Gemfile
|
||||
@new_deps -= builder.dependencies
|
||||
|
||||
# add new deps to the end of the in-memory Gemfile
|
||||
builder.eval_gemfile("injected gems", new_gem_lines) if @new_deps.any?
|
||||
|
||||
# resolve to see if the new deps broke anything
|
||||
definition = builder.to_definition(lockfile_path, {})
|
||||
definition.resolve_remotely!
|
||||
|
||||
# since nothing broke, we can add those gems to the gemfile
|
||||
append_to(gemfile_path) if @new_deps.any?
|
||||
|
||||
# since we resolved successfully, write out the lockfile
|
||||
definition.lock(Bundler.default_lockfile)
|
||||
|
||||
# return an array of the deps that we added
|
||||
return @new_deps
|
||||
ensure
|
||||
Bundler.settings[:frozen] = '1' if frozen
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def new_gem_lines
|
||||
@new_deps.map do |d|
|
||||
%|gem '#{d.name}', '#{d.requirement}'|
|
||||
end.join("\n")
|
||||
end
|
||||
|
||||
def append_to(gemfile_path)
|
||||
gemfile_path.open("a") do |f|
|
||||
f.puts
|
||||
f.puts "# Added at #{Time.now} by #{`whoami`.chomp}:"
|
||||
f.puts new_gem_lines
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,334 @@
|
||||
require 'erb'
|
||||
require 'rubygems/dependency_installer'
|
||||
require 'bundler/parallel_workers'
|
||||
|
||||
module Bundler
|
||||
class Installer < Environment
|
||||
class << self
|
||||
attr_accessor :post_install_messages, :ambiguous_gems
|
||||
|
||||
Installer.post_install_messages = {}
|
||||
Installer.ambiguous_gems = []
|
||||
end
|
||||
|
||||
# Begins the installation process for Bundler.
|
||||
# For more information see the #run method on this class.
|
||||
def self.install(root, definition, options = {})
|
||||
installer = new(root, definition)
|
||||
installer.run(options)
|
||||
installer
|
||||
end
|
||||
|
||||
# Runs the install procedures for a specific Gemfile.
|
||||
#
|
||||
# Firstly, this method will check to see if Bundler.bundle_path exists
|
||||
# and if not then will create it. This is usually the location of gems
|
||||
# on the system, be it RVM or at a system path.
|
||||
#
|
||||
# Secondly, it checks if Bundler has been configured to be "frozen"
|
||||
# Frozen ensures that the Gemfile and the Gemfile.lock file are matching.
|
||||
# This stops a situation where a developer may update the Gemfile but may not run
|
||||
# `bundle install`, which leads to the Gemfile.lock file not being correctly updated.
|
||||
# If this file is not correctly updated then any other developer running
|
||||
# `bundle install` will potentially not install the correct gems.
|
||||
#
|
||||
# Thirdly, Bundler checks if there are any dependencies specified in the Gemfile using
|
||||
# Bundler::Environment#dependencies. If there are no dependencies specified then
|
||||
# Bundler returns a warning message stating so and this method returns.
|
||||
#
|
||||
# Fourthly, Bundler checks if the default lockfile (Gemfile.lock) exists, and if so
|
||||
# then proceeds to set up a defintion based on the default gemfile (Gemfile) and the
|
||||
# default lock file (Gemfile.lock). However, this is not the case if the platform is different
|
||||
# to that which is specified in Gemfile.lock, or if there are any missing specs for the gems.
|
||||
#
|
||||
# Fifthly, Bundler resolves the dependencies either through a cache of gems or by remote.
|
||||
# This then leads into the gems being installed, along with stubs for their executables,
|
||||
# but only if the --binstubs option has been passed or Bundler.options[:bin] has been set
|
||||
# earlier.
|
||||
#
|
||||
# Sixthly, a new Gemfile.lock is created from the installed gems to ensure that the next time
|
||||
# that a user runs `bundle install` they will receive any updates from this process.
|
||||
#
|
||||
# Finally: TODO add documentation for how the standalone process works.
|
||||
def run(options)
|
||||
create_bundle_path
|
||||
|
||||
if Bundler.settings[:frozen]
|
||||
@definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment])
|
||||
end
|
||||
|
||||
if dependencies.empty?
|
||||
Bundler.ui.warn "The Gemfile specifies no dependencies"
|
||||
lock
|
||||
return
|
||||
end
|
||||
|
||||
if Bundler.default_lockfile.exist? && !options["update"]
|
||||
local = Bundler.ui.silence do
|
||||
begin
|
||||
tmpdef = Definition.build(Bundler.default_gemfile, Bundler.default_lockfile, nil)
|
||||
true unless tmpdef.new_platform? || tmpdef.missing_specs.any?
|
||||
rescue BundlerError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Since we are installing, we can resolve the definition
|
||||
# using remote specs
|
||||
unless local
|
||||
options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely!
|
||||
end
|
||||
|
||||
# the order that the resolver provides is significant, since
|
||||
# dependencies might actually affect the installation of a gem.
|
||||
# that said, it's a rare situation (other than rake), and parallel
|
||||
# installation is just SO MUCH FASTER. so we let people opt in.
|
||||
jobs = [Bundler.settings[:jobs].to_i-1, 1].max
|
||||
if jobs > 1 && can_install_parallely?
|
||||
install_in_parallel jobs, options[:standalone]
|
||||
else
|
||||
install_sequentially options[:standalone]
|
||||
end
|
||||
|
||||
lock unless Bundler.settings[:frozen]
|
||||
generate_standalone(options[:standalone]) if options[:standalone]
|
||||
end
|
||||
|
||||
def install_gem_from_spec(spec, standalone = false, worker = 0)
|
||||
# Fetch the build settings, if there are any
|
||||
settings = Bundler.settings["build.#{spec.name}"]
|
||||
install_message = nil
|
||||
post_install_message = nil
|
||||
debug_message = nil
|
||||
Bundler.rubygems.with_build_args [settings] do
|
||||
install_message, post_install_message, debug_message = spec.source.install(spec)
|
||||
if install_message.include? 'Installing'
|
||||
Bundler.ui.confirm install_message
|
||||
else
|
||||
Bundler.ui.info install_message
|
||||
end
|
||||
Bundler.ui.debug debug_message if debug_message
|
||||
Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}"
|
||||
end
|
||||
|
||||
if Bundler.settings[:bin] && standalone
|
||||
generate_standalone_bundler_executable_stubs(spec)
|
||||
elsif Bundler.settings[:bin]
|
||||
generate_bundler_executable_stubs(spec, :force => true)
|
||||
end
|
||||
|
||||
post_install_message
|
||||
rescue Errno::ENOSPC
|
||||
raise Bundler::InstallError, "Your disk is out of space. Free some " \
|
||||
"space to be able to install your bundle."
|
||||
rescue Exception => e
|
||||
# if install hook failed or gem signature is bad, just die
|
||||
raise e if e.is_a?(Bundler::InstallHookError) || e.is_a?(Bundler::SecurityError)
|
||||
|
||||
# other failure, likely a native extension build failure
|
||||
Bundler.ui.info ""
|
||||
Bundler.ui.warn "#{e.class}: #{e.message}"
|
||||
msg = "An error occurred while installing #{spec.name} (#{spec.version}),"
|
||||
msg << " and Bundler cannot continue."
|
||||
|
||||
unless spec.source.options["git"]
|
||||
msg << "\nMake sure that `gem install"
|
||||
msg << " #{spec.name} -v '#{spec.version}'` succeeds before bundling."
|
||||
end
|
||||
Bundler.ui.debug e.backtrace.join("\n")
|
||||
raise Bundler::InstallError, msg
|
||||
end
|
||||
|
||||
def generate_bundler_executable_stubs(spec, options = {})
|
||||
if options[:binstubs_cmd] && spec.executables.empty?
|
||||
options = {}
|
||||
spec.runtime_dependencies.each do |dep|
|
||||
bins = @definition.specs[dep].first.executables
|
||||
options[dep.name] = bins unless bins.empty?
|
||||
end
|
||||
if options.any?
|
||||
Bundler.ui.warn "#{spec.name} has no executables, but you may want " +
|
||||
"one from a gem it depends on."
|
||||
options.each{|name,bins| Bundler.ui.warn " #{name} has: #{bins.join(', ')}" }
|
||||
else
|
||||
Bundler.ui.warn "There are no executables for the gem #{spec.name}."
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
# double-assignment to avoid warnings about variables that will be used by ERB
|
||||
bin_path = bin_path = Bundler.bin_path
|
||||
template = template = File.read(File.expand_path('../templates/Executable', __FILE__))
|
||||
relative_gemfile_path = relative_gemfile_path = Bundler.default_gemfile.relative_path_from(bin_path)
|
||||
ruby_command = ruby_command = Thor::Util.ruby_command
|
||||
|
||||
exists = []
|
||||
spec.executables.each do |executable|
|
||||
next if executable == "bundle"
|
||||
|
||||
binstub_path = "#{bin_path}/#{executable}"
|
||||
if File.exist?(binstub_path) && !options[:force]
|
||||
exists << executable
|
||||
next
|
||||
end
|
||||
|
||||
File.open(binstub_path, 'w', 0777 & ~File.umask) do |f|
|
||||
f.puts ERB.new(template, nil, '-').result(binding)
|
||||
end
|
||||
end
|
||||
|
||||
if options[:binstubs_cmd] && exists.any?
|
||||
case exists.size
|
||||
when 1
|
||||
Bundler.ui.warn "Skipped #{exists[0]} since it already exists."
|
||||
when 2
|
||||
Bundler.ui.warn "Skipped #{exists.join(' and ')} since they already exist."
|
||||
else
|
||||
items = exists[0...-1].empty? ? nil : exists[0...-1].join(', ')
|
||||
skipped = [items, exists[-1]].compact.join(' and ')
|
||||
Bundler.ui.warn "Skipped #{skipped} since they already exist."
|
||||
end
|
||||
Bundler.ui.warn "If you want to overwrite skipped stubs, use --force."
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_install_parallely?
|
||||
min_rubygems = "2.0.7"
|
||||
if Bundler.current_ruby.mri? || Bundler.rubygems.provides?(">= #{min_rubygems}")
|
||||
true
|
||||
else
|
||||
Bundler.ui.warn "Rubygems #{Gem::VERSION} is not threadsafe, so your "\
|
||||
"gems must be installed one at a time. Upgrade to Rubygems " \
|
||||
"#{min_rubygems} or higher to enable parallel gem installation."
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def generate_standalone_bundler_executable_stubs(spec)
|
||||
# double-assignment to avoid warnings about variables that will be used by ERB
|
||||
bin_path = Bundler.bin_path
|
||||
template = File.read(File.expand_path('../templates/Executable.standalone', __FILE__))
|
||||
ruby_command = ruby_command = Thor::Util.ruby_command
|
||||
|
||||
spec.executables.each do |executable|
|
||||
next if executable == "bundle"
|
||||
standalone_path = standalone_path = Pathname(Bundler.settings[:path]).expand_path.relative_path_from(bin_path)
|
||||
executable_path = executable_path = Pathname(spec.full_gem_path).join(spec.bindir, executable).relative_path_from(bin_path)
|
||||
File.open "#{bin_path}/#{executable}", 'w', 0755 do |f|
|
||||
f.puts ERB.new(template, nil, '-').result(binding)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def generate_standalone(groups)
|
||||
standalone_path = Bundler.settings[:path]
|
||||
bundler_path = File.join(standalone_path, "bundler")
|
||||
FileUtils.mkdir_p(bundler_path)
|
||||
|
||||
paths = []
|
||||
|
||||
if groups.empty?
|
||||
specs = @definition.requested_specs
|
||||
else
|
||||
specs = @definition.specs_for groups.map { |g| g.to_sym }
|
||||
end
|
||||
|
||||
specs.each do |spec|
|
||||
next if spec.name == "bundler"
|
||||
next if spec.require_paths.nil? # builtin gems
|
||||
|
||||
spec.require_paths.each do |path|
|
||||
full_path = File.join(spec.full_gem_path, path)
|
||||
gem_path = Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path))
|
||||
paths << gem_path.to_s.sub("#{Bundler.ruby_version.engine}/#{RbConfig::CONFIG['ruby_version']}", '#{ruby_engine}/#{ruby_version}')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
File.open File.join(bundler_path, "setup.rb"), "w" do |file|
|
||||
file.puts "require 'rbconfig'"
|
||||
file.puts "# ruby 1.8.7 doesn't define RUBY_ENGINE"
|
||||
file.puts "ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'"
|
||||
file.puts "ruby_version = RbConfig::CONFIG[\"ruby_version\"]"
|
||||
file.puts "path = File.expand_path('..', __FILE__)"
|
||||
paths.each do |path|
|
||||
file.puts %{$:.unshift File.expand_path("\#{path}/#{path}")}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def install_sequentially(standalone)
|
||||
specs.each do |spec|
|
||||
message = install_gem_from_spec spec, standalone, 0
|
||||
if message
|
||||
Installer.post_install_messages[spec.name] = message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def install_in_parallel(size, standalone)
|
||||
name2spec = {}
|
||||
remains = {}
|
||||
enqueued = {}
|
||||
specs.each do |spec|
|
||||
name2spec[spec.name] = spec
|
||||
remains[spec.name] = true
|
||||
end
|
||||
|
||||
worker_pool = ParallelWorkers.worker_pool size, lambda { |name, worker|
|
||||
spec = name2spec[name]
|
||||
message = install_gem_from_spec spec, standalone, worker
|
||||
{ :name => spec.name, :post_install => message }
|
||||
}
|
||||
|
||||
# Keys in the remains hash represent uninstalled gems specs.
|
||||
# We enqueue all gem specs that do not have any dependencies.
|
||||
# Later we call this lambda again to install specs that depended on
|
||||
# previously installed specifications. We continue until all specs
|
||||
# are installed.
|
||||
enqueue_remaining_specs = lambda do
|
||||
remains.keys.each do |name|
|
||||
next if enqueued[name]
|
||||
spec = name2spec[name]
|
||||
if ready_to_install?(spec, remains)
|
||||
worker_pool.enq name
|
||||
enqueued[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
enqueue_remaining_specs.call
|
||||
|
||||
until remains.empty?
|
||||
message = worker_pool.deq
|
||||
remains.delete message[:name]
|
||||
if message[:post_install]
|
||||
Installer.post_install_messages[message[:name]] = message[:post_install]
|
||||
end
|
||||
enqueue_remaining_specs.call
|
||||
end
|
||||
message
|
||||
ensure
|
||||
worker_pool && worker_pool.stop
|
||||
end
|
||||
|
||||
# We only want to install a gem spec if all its dependencies are met.
|
||||
# If the dependency is no longer in the `remains` hash then it has been met.
|
||||
# If a dependency is only development or is self referential it can be ignored.
|
||||
def ready_to_install?(spec, remains)
|
||||
spec.dependencies.none? do |dep|
|
||||
next if dep.type == :development || dep.name == spec.name
|
||||
remains[dep.name]
|
||||
end
|
||||
end
|
||||
|
||||
def create_bundle_path
|
||||
Bundler.mkdir_p(Bundler.bundle_path.to_s) unless Bundler.bundle_path.exist?
|
||||
rescue Errno::EEXIST
|
||||
raise PathError, "Could not install to path `#{Bundler.settings[:path]}` " +
|
||||
"because of an invalid symlink. Remove the symlink so the directory can be created."
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,83 @@
|
||||
require "uri"
|
||||
require "rubygems/spec_fetcher"
|
||||
require "bundler/match_platform"
|
||||
|
||||
module Bundler
|
||||
class LazySpecification
|
||||
include MatchPlatform
|
||||
|
||||
attr_reader :name, :version, :dependencies, :platform
|
||||
attr_accessor :source, :source_uri
|
||||
|
||||
def initialize(name, version, platform, source = nil)
|
||||
@name = name
|
||||
@version = version
|
||||
@dependencies = []
|
||||
@platform = platform
|
||||
@source = source
|
||||
@specification = nil
|
||||
end
|
||||
|
||||
def full_name
|
||||
if platform == Gem::Platform::RUBY or platform.nil? then
|
||||
"#{@name}-#{@version}"
|
||||
else
|
||||
"#{@name}-#{@version}-#{platform}"
|
||||
end
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
identifier == other.identifier
|
||||
end
|
||||
|
||||
def satisfies?(dependency)
|
||||
@name == dependency.name && dependency.requirement.satisfied_by?(Gem::Version.new(@version))
|
||||
end
|
||||
|
||||
def to_lock
|
||||
if platform == Gem::Platform::RUBY or platform.nil?
|
||||
out = " #{name} (#{version})\n"
|
||||
else
|
||||
out = " #{name} (#{version}-#{platform})\n"
|
||||
end
|
||||
|
||||
dependencies.sort_by {|d| d.to_s }.each do |dep|
|
||||
next if dep.type == :development
|
||||
out << " #{dep.to_lock}\n"
|
||||
end
|
||||
|
||||
out
|
||||
end
|
||||
|
||||
def __materialize__
|
||||
@specification = source.specs.search(Gem::Dependency.new(name, version)).last
|
||||
end
|
||||
|
||||
def respond_to?(*args)
|
||||
super || @specification.respond_to?(*args)
|
||||
end
|
||||
|
||||
def to_s
|
||||
@__to_s ||= "#{name} (#{version})"
|
||||
end
|
||||
|
||||
def identifier
|
||||
@__identifier ||= [name, version, source, platform, dependencies].hash
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def to_ary
|
||||
nil
|
||||
end
|
||||
|
||||
def method_missing(method, *args, &blk)
|
||||
raise "LazySpecification has not been materialized yet (calling :#{method} #{args.inspect})" unless @specification
|
||||
|
||||
return super unless respond_to?(method)
|
||||
|
||||
@specification.send(method, *args, &blk)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,157 @@
|
||||
require "strscan"
|
||||
|
||||
# Some versions of the Bundler 1.1 RC series introduced corrupted
|
||||
# lockfiles. There were two major problems:
|
||||
#
|
||||
# * multiple copies of the same GIT section appeared in the lockfile
|
||||
# * when this happened, those sections got multiple copies of gems
|
||||
# in those sections.
|
||||
#
|
||||
# As a result, Bundler 1.1 contains code that fixes the earlier
|
||||
# corruption. We will remove this fix-up code in Bundler 1.2.
|
||||
|
||||
module Bundler
|
||||
class LockfileParser
|
||||
attr_reader :sources, :dependencies, :specs, :platforms
|
||||
|
||||
DEPENDENCIES = "DEPENDENCIES"
|
||||
PLATFORMS = "PLATFORMS"
|
||||
GIT = "GIT"
|
||||
GEM = "GEM"
|
||||
PATH = "PATH"
|
||||
SPECS = " specs:"
|
||||
OPTIONS = /^ ([a-z]+): (.*)$/i
|
||||
|
||||
def initialize(lockfile)
|
||||
@platforms = []
|
||||
@sources = []
|
||||
@dependencies = []
|
||||
@state = :source
|
||||
@specs = {}
|
||||
|
||||
@rubygems_aggregate = Source::Rubygems.new
|
||||
|
||||
if lockfile.match(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/)
|
||||
raise LockfileError, "Your Gemfile.lock contains merge conflicts.\n" \
|
||||
"Run `git checkout HEAD -- Gemfile.lock` first to get a clean lock."
|
||||
end
|
||||
|
||||
lockfile.split(/(?:\r?\n)+/).each do |line|
|
||||
if line == DEPENDENCIES
|
||||
@state = :dependency
|
||||
elsif line == PLATFORMS
|
||||
@state = :platform
|
||||
else
|
||||
send("parse_#{@state}", line)
|
||||
end
|
||||
end
|
||||
@sources << @rubygems_aggregate
|
||||
@specs = @specs.values
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
TYPES = {
|
||||
"GIT" => Bundler::Source::Git,
|
||||
"GEM" => Bundler::Source::Rubygems,
|
||||
"PATH" => Bundler::Source::Path
|
||||
}
|
||||
|
||||
def parse_source(line)
|
||||
case line
|
||||
when GIT, GEM, PATH
|
||||
@current_source = nil
|
||||
@opts, @type = {}, line
|
||||
when SPECS
|
||||
case @type
|
||||
when "PATH"
|
||||
@current_source = TYPES[@type].from_lock(@opts)
|
||||
@sources << @current_source
|
||||
when "GIT"
|
||||
@current_source = TYPES[@type].from_lock(@opts)
|
||||
# Strip out duplicate GIT sections
|
||||
if @type == "GIT" && @sources.include?(@current_source)
|
||||
@current_source = @sources.find { |s| s == @current_source }
|
||||
else
|
||||
@sources << @current_source
|
||||
end
|
||||
when "GEM"
|
||||
Array(@opts["remote"]).each do |url|
|
||||
@rubygems_aggregate.add_remote(url)
|
||||
end
|
||||
@current_source = @rubygems_aggregate
|
||||
end
|
||||
when OPTIONS
|
||||
value = $2
|
||||
value = true if value == "true"
|
||||
value = false if value == "false"
|
||||
|
||||
key = $1
|
||||
|
||||
if @opts[key]
|
||||
@opts[key] = Array(@opts[key])
|
||||
@opts[key] << value
|
||||
else
|
||||
@opts[key] = value
|
||||
end
|
||||
else
|
||||
parse_spec(line)
|
||||
end
|
||||
end
|
||||
|
||||
NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'
|
||||
NAME_VERSION_2 = %r{^ {2}#{NAME_VERSION}(!)?$}
|
||||
NAME_VERSION_4 = %r{^ {4}#{NAME_VERSION}$}
|
||||
NAME_VERSION_6 = %r{^ {6}#{NAME_VERSION}$}
|
||||
|
||||
def parse_dependency(line)
|
||||
if line =~ NAME_VERSION_2
|
||||
name, version, pinned = $1, $2, $4
|
||||
version = version.split(",").map { |d| d.strip } if version
|
||||
|
||||
dep = Bundler::Dependency.new(name, version)
|
||||
|
||||
if pinned && dep.name != 'bundler'
|
||||
spec = @specs.find {|k, v| v.name == dep.name }
|
||||
dep.source = spec.last.source if spec
|
||||
|
||||
# Path sources need to know what the default name / version
|
||||
# to use in the case that there are no gemspecs present. A fake
|
||||
# gemspec is created based on the version set on the dependency
|
||||
# TODO: Use the version from the spec instead of from the dependency
|
||||
if version && version.size == 1 && version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path)
|
||||
dep.source.name = name
|
||||
dep.source.version = $1
|
||||
end
|
||||
end
|
||||
|
||||
@dependencies << dep
|
||||
end
|
||||
end
|
||||
|
||||
def parse_spec(line)
|
||||
if line =~ NAME_VERSION_4
|
||||
name, version = $1, Gem::Version.new($2)
|
||||
platform = $3 ? Gem::Platform.new($3) : Gem::Platform::RUBY
|
||||
@current_spec = LazySpecification.new(name, version, platform)
|
||||
@current_spec.source = @current_source
|
||||
|
||||
# Avoid introducing multiple copies of the same spec (caused by
|
||||
# duplicate GIT sections)
|
||||
@specs[@current_spec.identifier] ||= @current_spec
|
||||
elsif line =~ NAME_VERSION_6
|
||||
name, version = $1, $2
|
||||
version = version.split(',').map { |d| d.strip } if version
|
||||
dep = Gem::Dependency.new(name, version)
|
||||
@current_spec.dependencies << dep
|
||||
end
|
||||
end
|
||||
|
||||
def parse_platform(line)
|
||||
if line =~ /^ (.*)$/
|
||||
@platforms << Gem::Platform.new($1)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,13 @@
|
||||
require 'bundler/gem_helpers'
|
||||
|
||||
module Bundler
|
||||
module MatchPlatform
|
||||
include GemHelpers
|
||||
|
||||
def match_platform(p)
|
||||
Gem::Platform::RUBY == platform or
|
||||
platform.nil? or p == platform or
|
||||
generic(Gem::Platform.new(platform)) == p
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
require 'thread'
|
||||
|
||||
require "bundler/parallel_workers/worker"
|
||||
|
||||
module Bundler
|
||||
module ParallelWorkers
|
||||
autoload :UnixWorker, "bundler/parallel_workers/unix_worker"
|
||||
autoload :ThreadWorker, "bundler/parallel_workers/thread_worker"
|
||||
|
||||
def self.worker_pool(size, job)
|
||||
if Bundler.current_ruby.mswin? || Bundler.current_ruby.jruby? || Bundler.current_ruby.rbx?
|
||||
ThreadWorker.new(size, job)
|
||||
else
|
||||
UnixWorker.new(size, job)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
module Bundler
|
||||
module ParallelWorkers
|
||||
class ThreadWorker < Worker
|
||||
|
||||
private
|
||||
|
||||
# On platforms where fork is not available
|
||||
# use Threads for parallely downloading gems
|
||||
#
|
||||
# @param size [Integer] Size of thread worker pool
|
||||
# @param func [Proc] Job to be run inside thread worker pool
|
||||
def prepare_workers(size, func)
|
||||
@threads = size.times.map do |i|
|
||||
Thread.start do
|
||||
loop do
|
||||
obj = @request_queue.deq
|
||||
break if obj.equal? POISON
|
||||
begin
|
||||
@response_queue.enq func.call(obj, i)
|
||||
rescue Exception => e
|
||||
@response_queue.enq(WrappedException.new(e))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,101 @@
|
||||
module Bundler
|
||||
module ParallelWorkers
|
||||
# UnixWorker is used only on platforms where fork is available. The way
|
||||
# this code works is, it forks a preconfigured number of workers and then
|
||||
# It starts preconfigured number of threads that write to the connected pipe.
|
||||
class UnixWorker < Worker
|
||||
|
||||
class JobHandler < Struct.new(:pid, :io_r, :io_w)
|
||||
def work(obj)
|
||||
Marshal.dump obj, io_w
|
||||
Marshal.load io_r
|
||||
rescue IOError, Errno::EPIPE
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(size, job)
|
||||
# Close the persistent connections for the main thread before forking
|
||||
Net::HTTP::Persistent.new('bundler', :ENV).shutdown
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Start forked workers for downloading gems. This version of worker
|
||||
# is only used on platforms where fork is available.
|
||||
#
|
||||
# @param size [Integer] Size of worker pool
|
||||
# @param func [Proc] Job that should be executed in the worker
|
||||
def prepare_workers(size, func)
|
||||
@workers = size.times.map do |num|
|
||||
child_read, parent_write = IO.pipe
|
||||
parent_read, child_write = IO.pipe
|
||||
|
||||
pid = Process.fork do
|
||||
begin
|
||||
parent_read.close
|
||||
parent_write.close
|
||||
|
||||
while !child_read.eof?
|
||||
obj = Marshal.load child_read
|
||||
Marshal.dump func.call(obj, num), child_write
|
||||
end
|
||||
rescue Exception => e
|
||||
begin
|
||||
Marshal.dump WrappedException.new(e), child_write
|
||||
rescue Errno::EPIPE
|
||||
nil
|
||||
end
|
||||
ensure
|
||||
child_read.close
|
||||
child_write.close
|
||||
end
|
||||
end
|
||||
|
||||
child_read.close
|
||||
child_write.close
|
||||
JobHandler.new pid, parent_read, parent_write
|
||||
end
|
||||
end
|
||||
|
||||
# Start the threads whose job is basically to wait for incoming messages
|
||||
# on request queue and write that message to the connected pipe. Also retrieve
|
||||
# messages from child worker via connected pipe and write the message to response queue
|
||||
#
|
||||
# @param size [Integer] Number of threads to be started
|
||||
def prepare_threads(size)
|
||||
@threads = size.times.map do |i|
|
||||
Thread.start do
|
||||
worker = @workers[i]
|
||||
loop do
|
||||
obj = @request_queue.deq
|
||||
break if obj.equal? POISON
|
||||
@response_queue.enq worker.work(obj)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Kill the forked workers by sending SIGINT to them
|
||||
def stop_workers
|
||||
@workers.each do |worker|
|
||||
worker.io_r.close unless worker.io_r.closed?
|
||||
worker.io_w.close unless worker.io_w.closed?
|
||||
begin
|
||||
Process.kill :INT, worker.pid
|
||||
rescue Errno::ESRCH
|
||||
nil
|
||||
end
|
||||
end
|
||||
@workers.each do |worker|
|
||||
begin
|
||||
Process.waitpid worker.pid
|
||||
rescue Errno::ECHILD
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,69 @@
|
||||
module Bundler
|
||||
module ParallelWorkers
|
||||
class Worker
|
||||
POISON = Object.new
|
||||
|
||||
class WrappedException < StandardError
|
||||
attr_reader :exception
|
||||
def initialize(exn)
|
||||
@exception = exn
|
||||
end
|
||||
end
|
||||
|
||||
# Creates a worker pool of specified size
|
||||
#
|
||||
# @param size [Integer] Size of pool
|
||||
# @param func [Proc] job to run in inside the worker pool
|
||||
def initialize(size, func)
|
||||
@request_queue = Queue.new
|
||||
@response_queue = Queue.new
|
||||
prepare_workers size, func
|
||||
prepare_threads size
|
||||
trap("INT") { @threads.each {|i| i.exit }; stop_workers; exit 1 }
|
||||
end
|
||||
|
||||
# Enqueue a request to be executed in the worker pool
|
||||
#
|
||||
# @param obj [String] mostly it is name of spec that should be downloaded
|
||||
def enq(obj)
|
||||
@request_queue.enq obj
|
||||
end
|
||||
|
||||
# Retrieves results of job function being executed in worker pool
|
||||
def deq
|
||||
result = @response_queue.deq
|
||||
if result.is_a?(WrappedException)
|
||||
raise result.exception
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
# Stop the forked workers and started threads
|
||||
def stop
|
||||
stop_threads
|
||||
stop_workers
|
||||
end
|
||||
|
||||
private
|
||||
# Stop the worker threads by sending a poison object down the request queue
|
||||
# so as worker threads after retrieving it, shut themselves down
|
||||
def stop_threads
|
||||
@threads.each do
|
||||
@request_queue.enq POISON
|
||||
end
|
||||
@threads.each do |thread|
|
||||
thread.join
|
||||
end
|
||||
end
|
||||
|
||||
# To be overridden by child classes
|
||||
def prepare_threads(size)
|
||||
end
|
||||
|
||||
# To be overridden by child classes
|
||||
def stop_workers
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
# Psych could be a gem, so try to ask for it
|
||||
begin
|
||||
gem 'psych'
|
||||
rescue LoadError
|
||||
end if defined?(gem)
|
||||
|
||||
# Psych could just be in the stdlib
|
||||
# but it's too late if Syck is already loaded
|
||||
begin
|
||||
require 'psych' unless defined?(Syck)
|
||||
rescue LoadError
|
||||
# Apparently Psych wasn't available. Oh well.
|
||||
end
|
||||
|
||||
# At least load the YAML stdlib, whatever that may be
|
||||
require 'yaml' unless defined?(YAML.dump)
|
||||
|
||||
module Bundler
|
||||
# On encountering invalid YAML,
|
||||
# Psych raises Psych::SyntaxError
|
||||
if defined?(::Psych::SyntaxError)
|
||||
YamlSyntaxError = ::Psych::SyntaxError
|
||||
else # Syck raises ArgumentError
|
||||
YamlSyntaxError = ::ArgumentError
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,57 @@
|
||||
require "uri"
|
||||
require "rubygems/spec_fetcher"
|
||||
|
||||
module Bundler
|
||||
# Represents a lazily loaded gem specification, where the full specification
|
||||
# is on the source server in rubygems' "quick" index. The proxy object is to
|
||||
# be seeded with what we're given from the source's abbreviated index - the
|
||||
# full specification will only be fetched when necessary.
|
||||
class RemoteSpecification
|
||||
include MatchPlatform
|
||||
|
||||
attr_reader :name, :version, :platform
|
||||
attr_accessor :source, :source_uri
|
||||
|
||||
def initialize(name, version, platform, spec_fetcher)
|
||||
@name = name
|
||||
@version = version
|
||||
@platform = platform
|
||||
@spec_fetcher = spec_fetcher
|
||||
end
|
||||
|
||||
# Needed before installs, since the arch matters then and quick
|
||||
# specs don't bother to include the arch in the platform string
|
||||
def fetch_platform
|
||||
@platform = _remote_specification.platform
|
||||
end
|
||||
|
||||
def full_name
|
||||
if platform == Gem::Platform::RUBY or platform.nil? then
|
||||
"#{@name}-#{@version}"
|
||||
else
|
||||
"#{@name}-#{@version}-#{platform}"
|
||||
end
|
||||
end
|
||||
|
||||
# Because Rubyforge cannot be trusted to provide valid specifications
|
||||
# once the remote gem is downloaded, the backend specification will
|
||||
# be swapped out.
|
||||
def __swap__(spec)
|
||||
@specification = spec
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _remote_specification
|
||||
@specification ||= @spec_fetcher.fetch_spec([@name, @version, @platform])
|
||||
end
|
||||
|
||||
def method_missing(method, *args, &blk)
|
||||
if Gem::Specification.new.respond_to?(method)
|
||||
_remote_specification.send(method, *args, &blk)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,534 @@
|
||||
require 'set'
|
||||
# This is the latest iteration of the gem dependency resolving algorithm. As of now,
|
||||
# it can resolve (as a success or failure) any set of gem dependencies we throw at it
|
||||
# in a reasonable amount of time. The most iterations I've seen it take is about 150.
|
||||
# The actual implementation of the algorithm is not as good as it could be yet, but that
|
||||
# can come later.
|
||||
|
||||
# Extending Gem classes to add necessary tracking information
|
||||
module Gem
|
||||
class Specification
|
||||
def required_by
|
||||
@required_by ||= []
|
||||
end
|
||||
end
|
||||
class Dependency
|
||||
def required_by
|
||||
@required_by ||= []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Bundler
|
||||
class Resolver
|
||||
|
||||
ALL = Bundler::Dependency::PLATFORM_MAP.values.uniq.freeze
|
||||
|
||||
class SpecGroup < Array
|
||||
include GemHelpers
|
||||
|
||||
attr_reader :activated, :required_by
|
||||
|
||||
def initialize(a)
|
||||
super
|
||||
@required_by = []
|
||||
@activated = []
|
||||
@dependencies = nil
|
||||
@specs = {}
|
||||
|
||||
ALL.each do |p|
|
||||
@specs[p] = reverse.find { |s| s.match_platform(p) }
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_copy(o)
|
||||
super
|
||||
@required_by = o.required_by.dup
|
||||
@activated = o.activated.dup
|
||||
end
|
||||
|
||||
def to_specs
|
||||
specs = {}
|
||||
|
||||
@activated.each do |p|
|
||||
if s = @specs[p]
|
||||
platform = generic(Gem::Platform.new(s.platform))
|
||||
next if specs[platform]
|
||||
|
||||
lazy_spec = LazySpecification.new(name, version, platform, source)
|
||||
lazy_spec.dependencies.replace s.dependencies
|
||||
specs[platform] = lazy_spec
|
||||
end
|
||||
end
|
||||
specs.values
|
||||
end
|
||||
|
||||
def activate_platform(platform)
|
||||
unless @activated.include?(platform)
|
||||
@activated << platform
|
||||
return __dependencies[platform] || []
|
||||
end
|
||||
[]
|
||||
end
|
||||
|
||||
def name
|
||||
@name ||= first.name
|
||||
end
|
||||
|
||||
def version
|
||||
@version ||= first.version
|
||||
end
|
||||
|
||||
def source
|
||||
@source ||= first.source
|
||||
end
|
||||
|
||||
def for?(platform)
|
||||
@specs[platform]
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{name} (#{version})"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def __dependencies
|
||||
@dependencies ||= begin
|
||||
dependencies = {}
|
||||
ALL.each do |p|
|
||||
if spec = @specs[p]
|
||||
dependencies[p] = []
|
||||
spec.dependencies.each do |dep|
|
||||
next if dep.type == :development
|
||||
dependencies[p] << DepProxy.new(dep, p)
|
||||
end
|
||||
end
|
||||
end
|
||||
dependencies
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :errors, :started_at, :iteration_rate, :iteration_counter
|
||||
|
||||
# Figures out the best possible configuration of gems that satisfies
|
||||
# the list of passed dependencies and any child dependencies without
|
||||
# causing any gem activation errors.
|
||||
#
|
||||
# ==== Parameters
|
||||
# *dependencies<Gem::Dependency>:: The list of dependencies to resolve
|
||||
#
|
||||
# ==== Returns
|
||||
# <GemBundle>,nil:: If the list of dependencies can be resolved, a
|
||||
# collection of gemspecs is returned. Otherwise, nil is returned.
|
||||
def self.resolve(requirements, index, source_requirements = {}, base = [])
|
||||
Bundler.ui.info "Resolving dependencies...", false
|
||||
base = SpecSet.new(base) unless base.is_a?(SpecSet)
|
||||
resolver = new(index, source_requirements, base)
|
||||
result = resolver.start(requirements)
|
||||
Bundler.ui.info "" # new line now that dots are done
|
||||
SpecSet.new(result)
|
||||
rescue => e
|
||||
Bundler.ui.info "" # new line before the error
|
||||
raise e
|
||||
end
|
||||
|
||||
def initialize(index, source_requirements, base)
|
||||
@errors = {}
|
||||
@base = base
|
||||
@index = index
|
||||
@deps_for = {}
|
||||
@missing_gems = Hash.new(0)
|
||||
@source_requirements = source_requirements
|
||||
@iteration_counter = 0
|
||||
@started_at = Time.now
|
||||
end
|
||||
|
||||
def debug
|
||||
if ENV['DEBUG_RESOLVER']
|
||||
debug_info = yield
|
||||
debug_info = debug_info.inspect unless debug_info.is_a?(String)
|
||||
$stderr.puts debug_info
|
||||
end
|
||||
end
|
||||
|
||||
def successify(activated)
|
||||
activated.values.map { |s| s.to_specs }.flatten.compact
|
||||
end
|
||||
|
||||
def start(reqs)
|
||||
activated = {}
|
||||
@gems_size = Hash[reqs.map { |r| [r, gems_size(r)] }]
|
||||
|
||||
resolve(reqs, activated)
|
||||
end
|
||||
|
||||
class State < Struct.new(:reqs, :activated, :requirement, :possibles, :depth, :conflicts)
|
||||
def name
|
||||
requirement.name
|
||||
end
|
||||
end
|
||||
|
||||
def handle_conflict(current, states, existing=nil)
|
||||
until current.nil? && existing.nil?
|
||||
current_state = find_state(current, states)
|
||||
existing_state = find_state(existing, states)
|
||||
return current if state_any?(current_state)
|
||||
return existing if state_any?(existing_state)
|
||||
existing = existing.required_by.last if existing
|
||||
current = current.required_by.last if current
|
||||
end
|
||||
end
|
||||
|
||||
def state_any?(state)
|
||||
state && state.possibles.any?
|
||||
end
|
||||
|
||||
def find_state(current, states)
|
||||
states.detect { |i| current && current.name == i.name }
|
||||
end
|
||||
|
||||
def other_possible?(conflict, states)
|
||||
return unless conflict
|
||||
state = states.detect { |i| i.name == conflict.name }
|
||||
state && state.possibles.any?
|
||||
end
|
||||
|
||||
def find_conflict_state(conflict, states)
|
||||
return unless conflict
|
||||
until states.empty? do
|
||||
state = states.pop
|
||||
return state if conflict.name == state.name
|
||||
end
|
||||
end
|
||||
|
||||
def activate_gem(reqs, activated, requirement, current)
|
||||
requirement.required_by.replace current.required_by
|
||||
requirement.required_by << current
|
||||
activated[requirement.name] = requirement
|
||||
|
||||
debug { " Activating: #{requirement.name} (#{requirement.version})" }
|
||||
debug { requirement.required_by.map { |d| " * #{d.name} (#{d.requirement})" }.join("\n") }
|
||||
|
||||
dependencies = requirement.activate_platform(current.__platform)
|
||||
|
||||
debug { " Dependencies"}
|
||||
dependencies.each do |dep|
|
||||
next if dep.type == :development
|
||||
dep.required_by.replace(current.required_by)
|
||||
dep.required_by << current
|
||||
@gems_size[dep] ||= gems_size(dep)
|
||||
reqs << dep
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_for_conflict(state)
|
||||
raise version_conflict if state.nil? || state.possibles.empty?
|
||||
reqs, activated, depth, conflicts = state.reqs.dup, state.activated.dup, state.depth, state.conflicts.dup
|
||||
requirement = state.requirement
|
||||
possible = state.possibles.pop
|
||||
|
||||
activate_gem(reqs, activated, possible, requirement)
|
||||
|
||||
return reqs, activated, depth, conflicts
|
||||
end
|
||||
|
||||
def resolve_conflict(current, states)
|
||||
# Find the state where the conflict has occurred
|
||||
state = find_conflict_state(current, states)
|
||||
|
||||
debug { " -> Going to: #{current.name} state" } if current
|
||||
|
||||
# Resolve the conflicts by rewinding the state
|
||||
# when the conflicted gem was activated
|
||||
reqs, activated, depth, conflicts = resolve_for_conflict(state)
|
||||
|
||||
# Keep the state around if it still has other possibilities
|
||||
states << state unless state.possibles.empty?
|
||||
clear_search_cache
|
||||
|
||||
return reqs, activated, depth, conflicts
|
||||
end
|
||||
|
||||
def resolve(reqs, activated)
|
||||
states = []
|
||||
depth = 0
|
||||
conflicts = Set.new
|
||||
|
||||
until reqs.empty?
|
||||
|
||||
indicate_progress
|
||||
|
||||
debug { print "\e[2J\e[f" ; "==== Iterating ====\n\n" }
|
||||
|
||||
reqs = reqs.sort_by do |a|
|
||||
[ activated[a.name] ? 0 : 1,
|
||||
a.requirement.prerelease? ? 0 : 1,
|
||||
@errors[a.name] ? 0 : 1,
|
||||
activated[a.name] ? 0 : @gems_size[a] ]
|
||||
end
|
||||
|
||||
debug { "Activated:\n" + activated.values.map {|a| " #{a}" }.join("\n") }
|
||||
debug { "Requirements:\n" + reqs.map {|r| " #{r}"}.join("\n") }
|
||||
|
||||
current = reqs.shift
|
||||
|
||||
$stderr.puts "#{' ' * depth}#{current}" if ENV['DEBUG_RESOLVER_TREE']
|
||||
|
||||
debug { "Attempting:\n #{current}"}
|
||||
|
||||
existing = activated[current.name]
|
||||
|
||||
|
||||
if existing || current.name == 'bundler'
|
||||
# Force the current
|
||||
if current.name == 'bundler' && !existing
|
||||
existing = search(DepProxy.new(Gem::Dependency.new('bundler', VERSION), Gem::Platform::RUBY)).first
|
||||
raise GemNotFound, %Q{Bundler could not find gem "bundler" (#{VERSION})} unless existing
|
||||
existing.required_by << existing
|
||||
activated['bundler'] = existing
|
||||
end
|
||||
|
||||
if current.requirement.satisfied_by?(existing.version)
|
||||
debug { " * [SUCCESS] Already activated" }
|
||||
@errors.delete(existing.name)
|
||||
dependencies = existing.activate_platform(current.__platform)
|
||||
reqs.concat dependencies
|
||||
|
||||
dependencies.each do |dep|
|
||||
next if dep.type == :development
|
||||
@gems_size[dep] ||= gems_size(dep)
|
||||
end
|
||||
|
||||
depth += 1
|
||||
next
|
||||
else
|
||||
debug { " * [FAIL] Already activated" }
|
||||
@errors[existing.name] = [existing, current]
|
||||
|
||||
conflicts << current.name
|
||||
|
||||
parent = current.required_by.last
|
||||
if existing.respond_to?(:required_by)
|
||||
parent = handle_conflict(current, states, existing.required_by[-2]) unless other_possible?(parent, states)
|
||||
else
|
||||
parent = handle_conflict(current, states) unless other_possible?(parent, states)
|
||||
end
|
||||
|
||||
if parent.nil? && !conflicts.empty?
|
||||
parent = states.reverse.detect { |i| conflicts.include?(i.name) && state_any?(i)}
|
||||
end
|
||||
|
||||
raise version_conflict if parent.nil? || parent.name == 'bundler'
|
||||
|
||||
reqs, activated, depth, conflicts = resolve_conflict(parent, states)
|
||||
end
|
||||
else
|
||||
matching_versions = search(current)
|
||||
|
||||
# If we found no versions that match the current requirement
|
||||
if matching_versions.empty?
|
||||
# If this is a top-level Gemfile requirement
|
||||
if current.required_by.empty?
|
||||
if base = @base[current.name] and !base.empty?
|
||||
version = base.first.version
|
||||
message = "You have requested:\n" \
|
||||
" #{current.name} #{current.requirement}\n\n" \
|
||||
"The bundle currently has #{current.name} locked at #{version}.\n" \
|
||||
"Try running `bundle update #{current.name}`"
|
||||
elsif current.source
|
||||
name = current.name
|
||||
versions = @source_requirements[name][name].map { |s| s.version }
|
||||
message = "Could not find gem '#{current}' in #{current.source}.\n"
|
||||
if versions.any?
|
||||
message << "Source contains '#{name}' at: #{versions.join(', ')}"
|
||||
else
|
||||
message << "Source does not contain any versions of '#{current}'"
|
||||
end
|
||||
else
|
||||
message = "Could not find gem '#{current}' "
|
||||
if @index.source_types.include?(Bundler::Source::Rubygems)
|
||||
message << "in any of the gem sources listed in your Gemfile."
|
||||
else
|
||||
message << "in the gems available on this machine."
|
||||
end
|
||||
end
|
||||
raise GemNotFound, message
|
||||
# This is not a top-level Gemfile requirement
|
||||
else
|
||||
@errors[current.name] = [nil, current]
|
||||
parent = handle_conflict(current, states)
|
||||
reqs, activated, depth = resolve_conflict(parent, states)
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
state = State.new(reqs.dup, activated.dup, current, matching_versions, depth, conflicts)
|
||||
states << state
|
||||
requirement = state.possibles.pop
|
||||
activate_gem(reqs, activated, requirement, current)
|
||||
end
|
||||
end
|
||||
successify(activated)
|
||||
end
|
||||
|
||||
def gems_size(dep)
|
||||
search(dep).size
|
||||
end
|
||||
|
||||
def clear_search_cache
|
||||
@deps_for = {}
|
||||
end
|
||||
|
||||
def search(dep)
|
||||
if base = @base[dep.name] and base.any?
|
||||
reqs = [dep.requirement.as_list, base.first.version.to_s].flatten.compact
|
||||
d = Gem::Dependency.new(base.first.name, *reqs)
|
||||
else
|
||||
d = dep.dep
|
||||
end
|
||||
|
||||
@deps_for[d.hash] ||= begin
|
||||
index = @source_requirements[d.name] || @index
|
||||
results = index.search(d, @base[d.name])
|
||||
|
||||
if results.any?
|
||||
version = results.first.version
|
||||
nested = [[]]
|
||||
results.each do |spec|
|
||||
if spec.version != version
|
||||
nested << []
|
||||
version = spec.version
|
||||
end
|
||||
nested.last << spec
|
||||
end
|
||||
deps = nested.map{|a| SpecGroup.new(a) }.select{|sg| sg.for?(dep.__platform) }
|
||||
else
|
||||
deps = []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def clean_req(req)
|
||||
if req.to_s.include?(">= 0")
|
||||
req.to_s.gsub(/ \(.*?\)$/, '')
|
||||
else
|
||||
req.to_s.gsub(/\, (runtime|development)\)$/, ')')
|
||||
end
|
||||
end
|
||||
|
||||
def version_conflict
|
||||
VersionConflict.new(errors.keys, error_message)
|
||||
end
|
||||
|
||||
# For a given conflicted requirement, print out what exactly went wrong
|
||||
def gem_message(requirement, required_by=[])
|
||||
m = ""
|
||||
|
||||
# A requirement that is required by itself is actually in the Gemfile, and does
|
||||
# not "depend on" itself
|
||||
if requirement.required_by.first && requirement.required_by.first.name != requirement.name
|
||||
dependency_tree(m, required_by)
|
||||
m << "#{clean_req(requirement)}\n"
|
||||
else
|
||||
m << " #{clean_req(requirement)}\n"
|
||||
end
|
||||
m << "\n"
|
||||
end
|
||||
|
||||
def dependency_tree(m, requirements)
|
||||
requirements.each_with_index do |i, j|
|
||||
m << " " << (" " * j)
|
||||
m << "#{clean_req(i)}"
|
||||
m << " depends on\n"
|
||||
end
|
||||
m << " " << (" " * requirements.size)
|
||||
end
|
||||
|
||||
def error_message
|
||||
errors.inject("") do |o, (conflict, (origin, requirement))|
|
||||
|
||||
# origin is the SpecSet of specs from the Gemfile that is conflicted with
|
||||
if origin
|
||||
|
||||
o << %{Bundler could not find compatible versions for gem "#{origin.name}":\n}
|
||||
o << " In Gemfile:\n"
|
||||
|
||||
required_by = requirement.required_by
|
||||
o << gem_message(requirement, required_by)
|
||||
|
||||
# If the origin is "bundler", the conflict is us
|
||||
if origin.name == "bundler"
|
||||
o << " Current Bundler version:\n"
|
||||
other_bundler_required = !requirement.requirement.satisfied_by?(origin.version)
|
||||
# If the origin is a LockfileParser, it does not respond_to :required_by
|
||||
elsif !origin.respond_to?(:required_by) || !(origin.required_by.first)
|
||||
o << " In snapshot (Gemfile.lock):\n"
|
||||
end
|
||||
|
||||
required_by = origin.required_by[0..-2]
|
||||
o << gem_message(origin, required_by)
|
||||
|
||||
# If the bundle wants a newer bundler than the running bundler, explain
|
||||
if origin.name == "bundler" && other_bundler_required
|
||||
o << "This Gemfile requires a different version of Bundler.\n"
|
||||
o << "Perhaps you need to update Bundler by running `gem install bundler`?"
|
||||
end
|
||||
|
||||
# origin is nil if the required gem and version cannot be found in any of
|
||||
# the specified sources
|
||||
else
|
||||
|
||||
# if the gem cannot be found because of a version conflict between lockfile and gemfile,
|
||||
# print a useful error that suggests running `bundle update`, which may fix things
|
||||
#
|
||||
# @base is a SpecSet of the gems in the lockfile
|
||||
# conflict is the name of the gem that could not be found
|
||||
if locked = @base[conflict].first
|
||||
o << "Bundler could not find compatible versions for gem #{conflict.inspect}:\n"
|
||||
o << " In snapshot (Gemfile.lock):\n"
|
||||
o << " #{clean_req(locked)}\n\n"
|
||||
|
||||
o << " In Gemfile:\n"
|
||||
|
||||
required_by = requirement.required_by
|
||||
o << gem_message(requirement, required_by)
|
||||
o << "Running `bundle update` will rebuild your snapshot from scratch, using only\n"
|
||||
o << "the gems in your Gemfile, which may resolve the conflict.\n"
|
||||
|
||||
# the rest of the time, the gem cannot be found because it does not exist in the known sources
|
||||
else
|
||||
if requirement.required_by.first
|
||||
o << "Could not find gem '#{clean_req(requirement)}', which is required by "
|
||||
o << "gem '#{clean_req(requirement.required_by.first)}', in any of the sources."
|
||||
else
|
||||
o << "Could not find gem '#{clean_req(requirement)} in any of the sources\n"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
o
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Indicates progress by writing a '.' every iteration_rate time which is
|
||||
# approximately every second. iteration_rate is calculated in the first
|
||||
# second of resolve running.
|
||||
def indicate_progress
|
||||
@iteration_counter += 1
|
||||
|
||||
if iteration_rate.nil?
|
||||
if ((Time.now - started_at) % 3600).round >= 1
|
||||
@iteration_rate = iteration_counter
|
||||
end
|
||||
else
|
||||
if ((iteration_counter % iteration_rate) == 0)
|
||||
Bundler.ui.info ".", false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,60 @@
|
||||
module Bundler
|
||||
# General purpose class for retrying code that may fail
|
||||
class Retry
|
||||
DEFAULT_ATTEMPTS = 2
|
||||
attr_accessor :name, :total_runs, :current_run
|
||||
|
||||
class << self
|
||||
attr_accessor :attempts
|
||||
end
|
||||
|
||||
def initialize(name, exceptions = nil, attempts = nil)
|
||||
@name = name
|
||||
attempts ||= default_attempts
|
||||
@exceptions = Array(exceptions) || []
|
||||
@total_runs = attempts.next # will run once, then upto attempts.times
|
||||
end
|
||||
|
||||
def default_attempts
|
||||
return Integer(self.class.attempts) if self.class.attempts
|
||||
DEFAULT_ATTEMPTS
|
||||
end
|
||||
|
||||
def attempt(&block)
|
||||
@current_run = 0
|
||||
@failed = false
|
||||
@error = nil
|
||||
while keep_trying? do
|
||||
run(&block)
|
||||
end
|
||||
@result
|
||||
end
|
||||
alias :attempts :attempt
|
||||
|
||||
private
|
||||
def run(&block)
|
||||
@failed = false
|
||||
@current_run += 1
|
||||
@result = block.call
|
||||
rescue => e
|
||||
fail(e)
|
||||
end
|
||||
|
||||
def fail(e)
|
||||
@failed = true
|
||||
raise e if last_attempt? || @exceptions.any?{ |k| e.is_a?(k) }
|
||||
return true unless name
|
||||
Bundler.ui.warn "Retrying#{" #{name}" if name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}"
|
||||
end
|
||||
|
||||
def keep_trying?
|
||||
return true if current_run.zero?
|
||||
return false if last_attempt?
|
||||
return true if @failed
|
||||
end
|
||||
|
||||
def last_attempt?
|
||||
current_run >= total_runs
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,11 @@
|
||||
module Bundler
|
||||
module RubyDsl
|
||||
def ruby(ruby_version, options = {})
|
||||
raise GemfileError, "Please define :engine_version" if options[:engine] && options[:engine_version].nil?
|
||||
raise GemfileError, "Please define :engine" if options[:engine_version] && options[:engine].nil?
|
||||
|
||||
raise GemfileError, "ruby_version must match the :engine_version for MRI" if options[:engine] == "ruby" && options[:engine_version] && ruby_version != options[:engine_version]
|
||||
@ruby_version = RubyVersion.new(ruby_version, options[:patchlevel], options[:engine], options[:engine_version])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,117 @@
|
||||
module Bundler
|
||||
class RubyVersion
|
||||
attr_reader :version, :patchlevel, :engine, :engine_version
|
||||
|
||||
def initialize(version, patchlevel, engine, engine_version)
|
||||
# The parameters to this method must satisfy the
|
||||
# following constraints, which are verified in
|
||||
# the DSL:
|
||||
#
|
||||
# * If an engine is specified, an engine version
|
||||
# must also be specified
|
||||
# * If an engine version is specified, an engine
|
||||
# must also be specified
|
||||
# * If the engine is "ruby", the engine version
|
||||
# must not be specified, or the engine version
|
||||
# specified must match the version.
|
||||
|
||||
@version = version
|
||||
@engine = engine || "ruby"
|
||||
# keep track of the engine specified by the user
|
||||
@input_engine = engine
|
||||
@engine_version = engine_version || version
|
||||
@patchlevel = patchlevel
|
||||
end
|
||||
|
||||
def to_s
|
||||
output = "ruby #{version}"
|
||||
output << "p#{patchlevel}" if patchlevel
|
||||
output << " (#{engine} #{engine_version})" unless engine == "ruby"
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
version == other.version &&
|
||||
engine == other.engine &&
|
||||
engine_version == other.engine_version &&
|
||||
patchlevel == other.patchlevel
|
||||
end
|
||||
|
||||
# Returns a tuple of thsee things:
|
||||
# [diff, this, other]
|
||||
# The priority of attributes are
|
||||
# 1. engine
|
||||
# 2. ruby_version
|
||||
# 3. engine_version
|
||||
def diff(other)
|
||||
if engine != other.engine && @input_engine
|
||||
[ :engine, engine, other.engine ]
|
||||
elsif version != other.version
|
||||
[ :version, version, other.version ]
|
||||
elsif engine_version != other.engine_version && @input_engine
|
||||
[ :engine_version, engine_version, other.engine_version ]
|
||||
elsif patchlevel != other.patchlevel && @patchlevel
|
||||
[ :patchlevel, patchlevel, other.patchlevel ]
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def host
|
||||
@host ||= [
|
||||
RbConfig::CONFIG["host_cpu"],
|
||||
RbConfig::CONFIG["host_vendor"],
|
||||
RbConfig::CONFIG["host_os"]
|
||||
].join("-")
|
||||
end
|
||||
end
|
||||
|
||||
# A subclass of RubyVersion that implements version,
|
||||
# engine and engine_version based upon the current
|
||||
# information in the system. It can be used anywhere
|
||||
# a RubyVersion object is expected, and can be
|
||||
# compared with a RubyVersion object.
|
||||
class SystemRubyVersion < RubyVersion
|
||||
def initialize(*)
|
||||
# override the default initialize, because
|
||||
# we will implement version, engine and
|
||||
# engine_version dynamically
|
||||
end
|
||||
|
||||
def version
|
||||
RUBY_VERSION.dup
|
||||
end
|
||||
|
||||
def gem_version
|
||||
@gem_version ||= Gem::Version.new(version)
|
||||
end
|
||||
|
||||
def engine
|
||||
if defined?(RUBY_ENGINE)
|
||||
RUBY_ENGINE.dup
|
||||
else
|
||||
# not defined in ruby 1.8.7
|
||||
"ruby"
|
||||
end
|
||||
end
|
||||
|
||||
def engine_version
|
||||
case engine
|
||||
when "ruby"
|
||||
RUBY_VERSION.dup
|
||||
when "rbx"
|
||||
Rubinius::VERSION.dup
|
||||
when "jruby"
|
||||
JRUBY_VERSION.dup
|
||||
else
|
||||
raise BundlerError, "RUBY_ENGINE value #{RUBY_ENGINE} is not recognized"
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def patchlevel
|
||||
RUBY_PATCHLEVEL.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,167 @@
|
||||
require 'pathname'
|
||||
|
||||
if defined?(Gem::QuickLoader)
|
||||
# Gem Prelude makes me a sad panda :'(
|
||||
Gem::QuickLoader.load_full_rubygems_library
|
||||
end
|
||||
|
||||
require 'rubygems'
|
||||
require 'rubygems/specification'
|
||||
require 'bundler/match_platform'
|
||||
|
||||
module Gem
|
||||
@loaded_stacks = Hash.new { |h,k| h[k] = [] }
|
||||
|
||||
class Specification
|
||||
attr_accessor :source_uri, :location, :relative_loaded_from
|
||||
|
||||
remove_method :source if instance_methods(false).include?(:source)
|
||||
attr_accessor :source
|
||||
|
||||
alias_method :rg_full_gem_path, :full_gem_path
|
||||
alias_method :rg_loaded_from, :loaded_from
|
||||
|
||||
def full_gem_path
|
||||
source.respond_to?(:path) ?
|
||||
Pathname.new(loaded_from).dirname.expand_path(Bundler.root).to_s :
|
||||
rg_full_gem_path
|
||||
end
|
||||
|
||||
def loaded_from
|
||||
relative_loaded_from ?
|
||||
source.path.join(relative_loaded_from).to_s :
|
||||
rg_loaded_from
|
||||
end
|
||||
|
||||
def load_paths
|
||||
return full_require_paths if respond_to?(:full_require_paths)
|
||||
|
||||
require_paths.map do |require_path|
|
||||
if require_path.include?(full_gem_path)
|
||||
require_path
|
||||
else
|
||||
File.join(full_gem_path, require_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if method_defined?(:extension_dir)
|
||||
alias_method :rg_extension_dir, :extension_dir
|
||||
def extension_dir
|
||||
@extension_dir ||= source.respond_to?(:extension_dir_name) ?
|
||||
File.expand_path(File.join(extensions_dir, source.extension_dir_name)) :
|
||||
rg_extension_dir
|
||||
end
|
||||
end
|
||||
|
||||
# RubyGems 1.8+ used only.
|
||||
remove_method :gem_dir if instance_methods(false).include?(:gem_dir)
|
||||
def gem_dir
|
||||
full_gem_path
|
||||
end
|
||||
|
||||
def groups
|
||||
@groups ||= []
|
||||
end
|
||||
|
||||
def git_version
|
||||
return unless loaded_from && source.is_a?(Bundler::Source::Git)
|
||||
" #{source.revision[0..6]}"
|
||||
end
|
||||
|
||||
def to_gemfile(path = nil)
|
||||
gemfile = "source :gemcutter\n"
|
||||
gemfile << dependencies_to_gemfile(nondevelopment_dependencies)
|
||||
unless development_dependencies.empty?
|
||||
gemfile << "\n"
|
||||
gemfile << dependencies_to_gemfile(development_dependencies, :development)
|
||||
end
|
||||
gemfile
|
||||
end
|
||||
|
||||
def nondevelopment_dependencies
|
||||
dependencies - development_dependencies
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dependencies_to_gemfile(dependencies, group = nil)
|
||||
gemfile = ''
|
||||
if dependencies.any?
|
||||
gemfile << "group :#{group} do\n" if group
|
||||
dependencies.each do |dependency|
|
||||
gemfile << ' ' if group
|
||||
gemfile << %|gem "#{dependency.name}"|
|
||||
req = dependency.requirements_list.first
|
||||
gemfile << %|, "#{req}"| if req
|
||||
gemfile << "\n"
|
||||
end
|
||||
gemfile << "end\n" if group
|
||||
end
|
||||
gemfile
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Dependency
|
||||
attr_accessor :source, :groups
|
||||
|
||||
alias eql? ==
|
||||
|
||||
def encode_with(coder)
|
||||
to_yaml_properties.each do |ivar|
|
||||
coder[ivar.to_s.sub(/^@/, '')] = instance_variable_get(ivar)
|
||||
end
|
||||
end
|
||||
|
||||
def to_yaml_properties
|
||||
instance_variables.reject { |p| ["@source", "@groups"].include?(p.to_s) }
|
||||
end
|
||||
|
||||
def to_lock
|
||||
out = " #{name}"
|
||||
unless requirement == Gem::Requirement.default
|
||||
reqs = requirement.requirements.map{|o,v| "#{o} #{v}" }.sort.reverse
|
||||
out << " (#{reqs.join(', ')})"
|
||||
end
|
||||
out
|
||||
end
|
||||
|
||||
# Backport of performance enhancement added to Rubygems 1.4
|
||||
def matches_spec?(spec)
|
||||
# name can be a Regexp, so use ===
|
||||
return false unless name === spec.name
|
||||
return true if requirement.none?
|
||||
|
||||
requirement.satisfied_by?(spec.version)
|
||||
end unless allocate.respond_to?(:matches_spec?)
|
||||
end
|
||||
|
||||
class Requirement
|
||||
# Backport of performance enhancement added to Rubygems 1.4
|
||||
def none?
|
||||
@none ||= (to_s == ">= 0")
|
||||
end unless allocate.respond_to?(:none?)
|
||||
end
|
||||
|
||||
class Platform
|
||||
JAVA = Gem::Platform.new('java') unless defined?(JAVA)
|
||||
MSWIN = Gem::Platform.new('mswin32') unless defined?(MSWIN)
|
||||
MINGW = Gem::Platform.new('x86-mingw32') unless defined?(MINGW)
|
||||
X64_MINGW = Gem::Platform.new('x64-mingw32') unless defined?(X64_MINGW)
|
||||
|
||||
undef_method :hash if method_defined? :hash
|
||||
def hash
|
||||
@cpu.hash ^ @os.hash ^ @version.hash
|
||||
end
|
||||
|
||||
undef_method :eql? if method_defined? :eql?
|
||||
alias eql? ==
|
||||
end
|
||||
end
|
||||
|
||||
module Gem
|
||||
class Specification
|
||||
include ::Bundler::MatchPlatform
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,578 @@
|
||||
require 'rubygems'
|
||||
require 'rubygems/config_file'
|
||||
|
||||
module Bundler
|
||||
class RubygemsIntegration
|
||||
|
||||
def self.version
|
||||
@version ||= Gem::Version.new(Gem::VERSION)
|
||||
end
|
||||
|
||||
def self.provides?(req_str)
|
||||
Gem::Requirement.new(req_str).satisfied_by?(version)
|
||||
end
|
||||
|
||||
def version
|
||||
self.class.version
|
||||
end
|
||||
|
||||
def provides?(req_str)
|
||||
self.class.provides?(req_str)
|
||||
end
|
||||
|
||||
def build_args
|
||||
Gem::Command.build_args
|
||||
end
|
||||
|
||||
def build_args=(args)
|
||||
Gem::Command.build_args = args
|
||||
end
|
||||
|
||||
def loaded_specs(name)
|
||||
Gem.loaded_specs[name]
|
||||
end
|
||||
|
||||
def mark_loaded(spec)
|
||||
Gem.loaded_specs[spec.name] = spec
|
||||
end
|
||||
|
||||
def path(obj)
|
||||
obj.to_s
|
||||
end
|
||||
|
||||
def platforms
|
||||
Gem.platforms
|
||||
end
|
||||
|
||||
def configuration
|
||||
Gem.configuration
|
||||
rescue Gem::SystemExitException => e
|
||||
Bundler.ui.error "#{e.class}: #{e.message}"
|
||||
Bundler.ui.trace e
|
||||
raise Gem::SystemExitException
|
||||
end
|
||||
|
||||
def ruby_engine
|
||||
Gem.ruby_engine
|
||||
end
|
||||
|
||||
def read_binary(path)
|
||||
Gem.read_binary(path)
|
||||
end
|
||||
|
||||
def inflate(obj)
|
||||
Gem.inflate(obj)
|
||||
end
|
||||
|
||||
def sources=(val)
|
||||
# Gem.configuration creates a new Gem::ConfigFile, which by default will read ~/.gemrc
|
||||
# If that file exists, its settings (including sources) will overwrite the values we
|
||||
# are about to set here. In order to avoid that, we force memoizing the config file now.
|
||||
configuration
|
||||
|
||||
Gem.sources = val
|
||||
end
|
||||
|
||||
def sources
|
||||
Gem.sources
|
||||
end
|
||||
|
||||
def gem_dir
|
||||
Gem.dir
|
||||
end
|
||||
|
||||
def gem_bindir
|
||||
Gem.bindir
|
||||
end
|
||||
|
||||
def user_home
|
||||
Gem.user_home
|
||||
end
|
||||
|
||||
def gem_path
|
||||
Gem.path
|
||||
end
|
||||
|
||||
def gem_cache
|
||||
gem_path.map{|p| File.expand_path("cache", p) }
|
||||
end
|
||||
|
||||
def spec_cache_dirs
|
||||
@spec_cache_dirs ||= begin
|
||||
dirs = gem_path.map {|dir| File.join(dir, 'specifications')}
|
||||
dirs << Gem.spec_cache_dir if Gem.respond_to?(:spec_cache_dir) # Not in Rubygems 2.0.3 or earlier
|
||||
dirs.uniq.select {|dir| File.directory? dir}
|
||||
end
|
||||
end
|
||||
|
||||
def marshal_spec_dir
|
||||
Gem::MARSHAL_SPEC_DIR
|
||||
end
|
||||
|
||||
def config_map
|
||||
Gem::ConfigMap
|
||||
end
|
||||
|
||||
def repository_subdirectories
|
||||
%w[cache doc gems specifications]
|
||||
end
|
||||
|
||||
def clear_paths
|
||||
Gem.clear_paths
|
||||
end
|
||||
|
||||
def bin_path(gem, bin, ver)
|
||||
Gem.bin_path(gem, bin, ver)
|
||||
end
|
||||
|
||||
def preserve_paths
|
||||
# this is a no-op outside of Rubygems 1.8
|
||||
yield
|
||||
end
|
||||
|
||||
def ui=(obj)
|
||||
Gem::DefaultUserInteraction.ui = obj
|
||||
end
|
||||
|
||||
def fetch_specs(all, pre, &blk)
|
||||
specs = Gem::SpecFetcher.new.list(all, pre)
|
||||
specs.each { yield } if block_given?
|
||||
specs
|
||||
end
|
||||
|
||||
def fetch_prerelease_specs
|
||||
fetch_specs(false, true)
|
||||
rescue Gem::RemoteFetcher::FetchError
|
||||
[] # if we can't download them, there aren't any
|
||||
end
|
||||
|
||||
def fetch_all_remote_specs
|
||||
# Fetch all specs, minus prerelease specs
|
||||
spec_list = fetch_specs(true, false)
|
||||
# Then fetch the prerelease specs
|
||||
fetch_prerelease_specs.each {|k, v| spec_list[k] += v }
|
||||
|
||||
return spec_list
|
||||
end
|
||||
|
||||
def with_build_args(args)
|
||||
old_args = self.build_args
|
||||
begin
|
||||
self.build_args = args
|
||||
yield
|
||||
ensure
|
||||
self.build_args = old_args
|
||||
end
|
||||
end
|
||||
|
||||
def gem_from_path(path, policy = nil)
|
||||
require 'rubygems/format'
|
||||
Gem::Format.from_file_by_path(path, policy)
|
||||
end
|
||||
|
||||
def spec_from_gem(path, policy = nil)
|
||||
require 'rubygems/security'
|
||||
gem_from_path(path, security_policies[policy]).spec
|
||||
rescue Gem::Package::FormatError
|
||||
raise GemspecError, "Could not read gem at #{path}. It may be corrupted."
|
||||
rescue Exception, Gem::Exception, Gem::Security::Exception => e
|
||||
if e.is_a?(Gem::Security::Exception) ||
|
||||
e.message =~ /unknown trust policy|unsigned gem/i ||
|
||||
e.message =~ /couldn't verify (meta)?data signature/i
|
||||
raise SecurityError,
|
||||
"The gem #{File.basename(path, '.gem')} can't be installed because " \
|
||||
"the security policy didn't allow it, with the message: #{e.message}"
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
def build(spec, skip_validation = false)
|
||||
require 'rubygems/builder'
|
||||
Gem::Builder.new(spec).build
|
||||
end
|
||||
|
||||
def build_gem(gem_dir, spec)
|
||||
SharedHelpers.chdir(gem_dir) { build(spec) }
|
||||
end
|
||||
|
||||
def download_gem(spec, uri, path)
|
||||
uri = Bundler::Source.mirror_for(uri)
|
||||
fetcher = Gem::RemoteFetcher.new(configuration[:http_proxy])
|
||||
fetcher.download(spec, uri, path)
|
||||
end
|
||||
|
||||
def security_policy_keys
|
||||
%w{High Medium Low AlmostNo No}.map { |level| "#{level}Security" }
|
||||
end
|
||||
|
||||
def security_policies
|
||||
@security_policies ||= begin
|
||||
require 'rubygems/security'
|
||||
Gem::Security::Policies
|
||||
rescue LoadError, NameError
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
def reverse_rubygems_kernel_mixin
|
||||
# Disable rubygems' gem activation system
|
||||
::Kernel.class_eval do
|
||||
if private_method_defined?(:gem_original_require)
|
||||
alias rubygems_require require
|
||||
alias require gem_original_require
|
||||
end
|
||||
|
||||
undef gem
|
||||
end
|
||||
end
|
||||
|
||||
def replace_gem(specs)
|
||||
reverse_rubygems_kernel_mixin
|
||||
|
||||
executables = specs.map { |s| s.executables }.flatten
|
||||
|
||||
::Kernel.send(:define_method, :gem) do |dep, *reqs|
|
||||
if executables.include? File.basename(caller.first.split(':').first)
|
||||
return
|
||||
end
|
||||
reqs.pop if reqs.last.is_a?(Hash)
|
||||
|
||||
unless dep.respond_to?(:name) && dep.respond_to?(:requirement)
|
||||
dep = Gem::Dependency.new(dep, reqs)
|
||||
end
|
||||
|
||||
spec = specs.find { |s| s.name == dep.name }
|
||||
|
||||
if spec.nil?
|
||||
|
||||
e = Gem::LoadError.new "#{dep.name} is not part of the bundle. Add it to Gemfile."
|
||||
e.name = dep.name
|
||||
if e.respond_to?(:requirement=)
|
||||
e.requirement = dep.requirement
|
||||
else
|
||||
e.version_requirement = dep.requirement
|
||||
end
|
||||
raise e
|
||||
elsif dep !~ spec
|
||||
e = Gem::LoadError.new "can't activate #{dep}, already activated #{spec.full_name}. " \
|
||||
"Make sure all dependencies are added to Gemfile."
|
||||
e.name = dep.name
|
||||
if e.respond_to?(:requirement=)
|
||||
e.requirement = dep.requirement
|
||||
else
|
||||
e.version_requirement = dep.requirement
|
||||
end
|
||||
raise e
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def stub_source_index(specs)
|
||||
Gem::SourceIndex.send(:alias_method, :old_initialize, :initialize)
|
||||
redefine_method(Gem::SourceIndex, :initialize) do |*args|
|
||||
@gems = {}
|
||||
# You're looking at this thinking: Oh! This is how I make those
|
||||
# rubygems deprecations go away!
|
||||
#
|
||||
# You'd be correct BUT using of this method in production code
|
||||
# must be approved by the rubygems team itself!
|
||||
#
|
||||
# This is your warning. If you use this and don't have approval
|
||||
# we can't protect you.
|
||||
#
|
||||
Deprecate.skip_during do
|
||||
self.spec_dirs = *args
|
||||
add_specs(*specs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Used to make bin stubs that are not created by bundler work
|
||||
# under bundler. The new Gem.bin_path only considers gems in
|
||||
# +specs+
|
||||
def replace_bin_path(specs)
|
||||
gem_class = (class << Gem ; self ; end)
|
||||
redefine_method(gem_class, :bin_path) do |name, *args|
|
||||
exec_name = args.first
|
||||
|
||||
if exec_name == 'bundle'
|
||||
return ENV['BUNDLE_BIN_PATH']
|
||||
end
|
||||
|
||||
spec = nil
|
||||
|
||||
if exec_name
|
||||
spec = specs.find { |s| s.executables.include?(exec_name) }
|
||||
unless spec.name == name
|
||||
warn "Bundler is using a binstub that was created for a different gem.\n" \
|
||||
"This is deprecated, in future versions you may need to `bundle binstub #{name}` " \
|
||||
"to work around a system/bundle conflict."
|
||||
end
|
||||
spec or raise Gem::Exception, "can't find executable #{exec_name}"
|
||||
else
|
||||
spec = specs.find { |s| s.name == name }
|
||||
exec_name = spec.default_executable or raise Gem::Exception, "no default executable for #{spec.full_name}"
|
||||
end
|
||||
|
||||
gem_bin = File.join(spec.full_gem_path, spec.bindir, exec_name)
|
||||
gem_from_path_bin = File.join(File.dirname(spec.loaded_from), spec.bindir, exec_name)
|
||||
File.exist?(gem_bin) ? gem_bin : gem_from_path_bin
|
||||
end
|
||||
end
|
||||
|
||||
# Because Bundler has a static view of what specs are available,
|
||||
# we don't #refresh, so stub it out.
|
||||
def replace_refresh
|
||||
gem_class = (class << Gem ; self ; end)
|
||||
redefine_method(gem_class, :refresh) { }
|
||||
end
|
||||
|
||||
# Replace or hook into Rubygems to provide a bundlerized view
|
||||
# of the world.
|
||||
def replace_entrypoints(specs)
|
||||
replace_gem(specs)
|
||||
|
||||
stub_rubygems(specs)
|
||||
|
||||
replace_bin_path(specs)
|
||||
replace_refresh
|
||||
|
||||
Gem.clear_paths
|
||||
end
|
||||
|
||||
# This backports the correct segment generation code from Rubygems 1.4+
|
||||
# by monkeypatching it into the method in Rubygems 1.3.6 and 1.3.7.
|
||||
def backport_segment_generation
|
||||
redefine_method(Gem::Version, :segments) do
|
||||
@segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
|
||||
/^\d+$/ =~ s ? s.to_i : s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This backport fixes the marshaling of @segments.
|
||||
def backport_yaml_initialize
|
||||
redefine_method(Gem::Version, :yaml_initialize) do |tag, map|
|
||||
@version = map['version']
|
||||
@segments = nil
|
||||
@hash = nil
|
||||
end
|
||||
end
|
||||
|
||||
# This backports base_dir which replaces installation path
|
||||
# Rubygems 1.8+
|
||||
def backport_base_dir
|
||||
redefine_method(Gem::Specification, :base_dir) do
|
||||
return Gem.dir unless loaded_from
|
||||
File.dirname File.dirname loaded_from
|
||||
end
|
||||
end
|
||||
|
||||
def backport_cache_file
|
||||
redefine_method(Gem::Specification, :cache_dir) do
|
||||
@cache_dir ||= File.join base_dir, "cache"
|
||||
end
|
||||
|
||||
redefine_method(Gem::Specification, :cache_file) do
|
||||
@cache_file ||= File.join cache_dir, "#{full_name}.gem"
|
||||
end
|
||||
end
|
||||
|
||||
def backport_spec_file
|
||||
redefine_method(Gem::Specification, :spec_dir) do
|
||||
@spec_dir ||= File.join base_dir, "specifications"
|
||||
end
|
||||
|
||||
redefine_method(Gem::Specification, :spec_file) do
|
||||
@spec_file ||= File.join spec_dir, "#{full_name}.gemspec"
|
||||
end
|
||||
end
|
||||
|
||||
def redefine_method(klass, method, &block)
|
||||
if klass.instance_methods(false).include?(method)
|
||||
klass.send(:remove_method, method)
|
||||
end
|
||||
klass.send(:define_method, method, &block)
|
||||
end
|
||||
|
||||
# Rubygems 1.4 through 1.6
|
||||
class Legacy < RubygemsIntegration
|
||||
def initialize
|
||||
super
|
||||
backport_base_dir
|
||||
backport_cache_file
|
||||
backport_spec_file
|
||||
backport_yaml_initialize
|
||||
end
|
||||
|
||||
def stub_rubygems(specs)
|
||||
# Rubygems versions lower than 1.7 use SourceIndex#from_gems_in
|
||||
source_index_class = (class << Gem::SourceIndex ; self ; end)
|
||||
source_index_class.send(:define_method, :from_gems_in) do |*args|
|
||||
source_index = Gem::SourceIndex.new
|
||||
source_index.spec_dirs = *args
|
||||
source_index.add_specs(*specs)
|
||||
source_index
|
||||
end
|
||||
end
|
||||
|
||||
def all_specs
|
||||
Gem.source_index.gems.values
|
||||
end
|
||||
|
||||
def find_name(name)
|
||||
Gem.source_index.find_name(name)
|
||||
end
|
||||
end
|
||||
|
||||
# Rubygems versions 1.3.6 and 1.3.7
|
||||
class Ancient < Legacy
|
||||
def initialize
|
||||
super
|
||||
backport_segment_generation
|
||||
end
|
||||
end
|
||||
|
||||
# Rubygems 1.7
|
||||
class Transitional < Legacy
|
||||
def stub_rubygems(specs)
|
||||
stub_source_index(specs)
|
||||
end
|
||||
end
|
||||
|
||||
# Rubygems 1.8.5-1.8.19
|
||||
class Modern < RubygemsIntegration
|
||||
def stub_rubygems(specs)
|
||||
Gem::Specification.all = specs
|
||||
|
||||
Gem.post_reset {
|
||||
Gem::Specification.all = specs
|
||||
}
|
||||
|
||||
stub_source_index(specs)
|
||||
end
|
||||
|
||||
def all_specs
|
||||
Gem::Specification.to_a
|
||||
end
|
||||
|
||||
def find_name(name)
|
||||
Gem::Specification.find_all_by_name name
|
||||
end
|
||||
end
|
||||
|
||||
# Rubygems 1.8.0 to 1.8.4
|
||||
class AlmostModern < Modern
|
||||
# Rubygems [>= 1.8.0, < 1.8.5] has a bug that changes Gem.dir whenever
|
||||
# you call Gem::Installer#install with an :install_dir set. We have to
|
||||
# change it back for our sudo mode to work.
|
||||
def preserve_paths
|
||||
old_dir, old_path = gem_dir, gem_path
|
||||
yield
|
||||
Gem.use_paths(old_dir, old_path)
|
||||
end
|
||||
end
|
||||
|
||||
# Rubygems 1.8.20+
|
||||
class MoreModern < Modern
|
||||
# Rubygems 1.8.20 and adds the skip_validation parameter, so that's
|
||||
# when we start passing it through.
|
||||
def build(spec, skip_validation = false)
|
||||
require 'rubygems/builder'
|
||||
Gem::Builder.new(spec).build(skip_validation)
|
||||
end
|
||||
end
|
||||
|
||||
# Rubygems 2.0
|
||||
class Future < RubygemsIntegration
|
||||
def stub_rubygems(specs)
|
||||
Gem::Specification.all = specs
|
||||
|
||||
Gem.post_reset do
|
||||
Gem::Specification.all = specs
|
||||
end
|
||||
end
|
||||
|
||||
def all_specs
|
||||
Gem::Specification.to_a
|
||||
end
|
||||
|
||||
def find_name(name)
|
||||
Gem::Specification.find_all_by_name name
|
||||
end
|
||||
|
||||
def fetch_specs(source, name)
|
||||
path = source + "#{name}.#{Gem.marshal_version}.gz"
|
||||
string = Gem::RemoteFetcher.fetcher.fetch_path(path)
|
||||
Bundler.load_marshal(string)
|
||||
rescue Gem::RemoteFetcher::FetchError => e
|
||||
# it's okay for prerelease to fail
|
||||
raise e unless name == "prerelease_specs"
|
||||
end
|
||||
|
||||
def fetch_all_remote_specs
|
||||
# Since SpecFetcher now returns NameTuples, we just fetch directly
|
||||
# and unmarshal the array ourselves.
|
||||
hash = {}
|
||||
|
||||
Gem.sources.each do |source|
|
||||
source = URI.parse(source.to_s) unless source.is_a?(URI)
|
||||
hash[source] = fetch_specs(source, "specs")
|
||||
|
||||
pres = fetch_specs(source, "prerelease_specs")
|
||||
hash[source].push(*pres) if pres && !pres.empty?
|
||||
end
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
def download_gem(spec, uri, path)
|
||||
require 'resolv'
|
||||
uri = Bundler::Source.mirror_for(uri)
|
||||
proxy, dns = configuration[:http_proxy], Resolv::DNS.new
|
||||
fetcher = Gem::RemoteFetcher.new(proxy, dns)
|
||||
fetcher.download(spec, uri, path)
|
||||
end
|
||||
|
||||
def gem_from_path(path, policy = nil)
|
||||
require 'rubygems/package'
|
||||
p = Gem::Package.new(path)
|
||||
p.security_policy = policy if policy
|
||||
return p
|
||||
end
|
||||
|
||||
def build(spec, skip_validation = false)
|
||||
require 'rubygems/package'
|
||||
Gem::Package.build(spec, skip_validation)
|
||||
end
|
||||
|
||||
def repository_subdirectories
|
||||
Gem::REPOSITORY_SUBDIRECTORIES
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if RubygemsIntegration.provides?(">= 1.99.99")
|
||||
@rubygems = RubygemsIntegration::Future.new
|
||||
elsif RubygemsIntegration.provides?('>= 1.8.20')
|
||||
@rubygems = RubygemsIntegration::MoreModern.new
|
||||
elsif RubygemsIntegration.provides?('>= 1.8.5')
|
||||
@rubygems = RubygemsIntegration::Modern.new
|
||||
elsif RubygemsIntegration.provides?('>= 1.8.0')
|
||||
@rubygems = RubygemsIntegration::AlmostModern.new
|
||||
elsif RubygemsIntegration.provides?('>= 1.7.0')
|
||||
@rubygems = RubygemsIntegration::Transitional.new
|
||||
elsif RubygemsIntegration.provides?('>= 1.4.0')
|
||||
@rubygems = RubygemsIntegration::Legacy.new
|
||||
else # Rubygems 1.3.6 and 1.3.7
|
||||
@rubygems = RubygemsIntegration::Ancient.new
|
||||
end
|
||||
|
||||
class << self
|
||||
attr_reader :rubygems
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,310 @@
|
||||
require "digest/sha1"
|
||||
|
||||
module Bundler
|
||||
class Runtime < Environment
|
||||
include SharedHelpers
|
||||
|
||||
def setup(*groups)
|
||||
groups.map! { |g| g.to_sym }
|
||||
|
||||
# Has to happen first
|
||||
clean_load_path
|
||||
|
||||
specs = groups.any? ? @definition.specs_for(groups) : requested_specs
|
||||
|
||||
setup_environment
|
||||
Bundler.rubygems.replace_entrypoints(specs)
|
||||
|
||||
# Activate the specs
|
||||
specs.each do |spec|
|
||||
unless spec.loaded_from
|
||||
raise GemNotFound, "#{spec.full_name} is missing. Run `bundle` to get it."
|
||||
end
|
||||
|
||||
if activated_spec = Bundler.rubygems.loaded_specs(spec.name) and activated_spec.version != spec.version
|
||||
e = Gem::LoadError.new "You have already activated #{activated_spec.name} #{activated_spec.version}, " \
|
||||
"but your Gemfile requires #{spec.name} #{spec.version}. Prepending " \
|
||||
"`bundle exec` to your command may solve this."
|
||||
e.name = spec.name
|
||||
if e.respond_to?(:requirement=)
|
||||
e.requirement = Gem::Requirement.new(spec.version.to_s)
|
||||
else
|
||||
e.version_requirement = Gem::Requirement.new(spec.version.to_s)
|
||||
end
|
||||
raise e
|
||||
end
|
||||
|
||||
Bundler.rubygems.mark_loaded(spec)
|
||||
load_paths = spec.load_paths.reject {|path| $LOAD_PATH.include?(path)}
|
||||
$LOAD_PATH.unshift(*load_paths)
|
||||
end
|
||||
|
||||
setup_manpath
|
||||
|
||||
lock
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
REGEXPS = [
|
||||
/^no such file to load -- (.+)$/i,
|
||||
/^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
|
||||
/^Missing API definition file in (.+)$/i,
|
||||
/^cannot load such file -- (.+)$/i,
|
||||
/^dlopen\([^)]*\): Library not loaded: (.+)$/i,
|
||||
]
|
||||
|
||||
def require(*groups)
|
||||
groups.map! { |g| g.to_sym }
|
||||
groups = [:default] if groups.empty?
|
||||
|
||||
@definition.dependencies.each do |dep|
|
||||
# Skip the dependency if it is not in any of the requested
|
||||
# groups
|
||||
next unless ((dep.groups & groups).any? && dep.current_platform?)
|
||||
|
||||
required_file = nil
|
||||
|
||||
begin
|
||||
# Loop through all the specified autorequires for the
|
||||
# dependency. If there are none, use the dependency's name
|
||||
# as the autorequire.
|
||||
Array(dep.autorequire || dep.name).each do |file|
|
||||
# Allow `require: true` as an alias for `require: <name>`
|
||||
file = dep.name if file == true
|
||||
required_file = file
|
||||
Kernel.require file
|
||||
end
|
||||
rescue LoadError => e
|
||||
REGEXPS.find { |r| r =~ e.message }
|
||||
raise if dep.autorequire || $1 != required_file
|
||||
|
||||
if dep.autorequire.nil? && dep.name.include?('-')
|
||||
begin
|
||||
namespaced_file = dep.name.gsub('-', '/')
|
||||
Kernel.require namespaced_file
|
||||
rescue LoadError
|
||||
REGEXPS.find { |r| r =~ e.message }
|
||||
regex_name = $1
|
||||
raise e if dep.autorequire || (regex_name && regex_name.gsub('-', '/') != namespaced_file)
|
||||
raise e if regex_name.nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def dependencies_for(*groups)
|
||||
if groups.empty?
|
||||
dependencies
|
||||
else
|
||||
dependencies.select { |d| (groups & d.groups).any? }
|
||||
end
|
||||
end
|
||||
|
||||
alias gems specs
|
||||
|
||||
def cache(custom_path = nil)
|
||||
cache_path = cache_path(custom_path)
|
||||
FileUtils.mkdir_p(cache_path) unless File.exist?(cache_path)
|
||||
|
||||
Bundler.ui.info "Updating files in vendor/cache"
|
||||
specs.each do |spec|
|
||||
next if spec.name == 'bundler'
|
||||
spec.source.cache(spec, custom_path) if spec.source.respond_to?(:cache)
|
||||
end
|
||||
|
||||
Dir[cache_path.join("*/.git")].each do |git_dir|
|
||||
FileUtils.rm_rf(git_dir)
|
||||
FileUtils.touch(File.expand_path("../.bundlecache", git_dir))
|
||||
end
|
||||
|
||||
prune_cache(custom_path) unless Bundler.settings[:no_prune]
|
||||
end
|
||||
|
||||
def prune_cache(custom_path)
|
||||
cache_path = cache_path(custom_path)
|
||||
FileUtils.mkdir_p(cache_path) unless File.exist?(cache_path)
|
||||
resolve = @definition.resolve
|
||||
prune_gem_cache(resolve, custom_path)
|
||||
prune_git_and_path_cache(resolve, custom_path)
|
||||
end
|
||||
|
||||
def clean(dry_run = false)
|
||||
gem_bins = Dir["#{Gem.dir}/bin/*"]
|
||||
git_dirs = Dir["#{Gem.dir}/bundler/gems/*"]
|
||||
git_cache_dirs = Dir["#{Gem.dir}/cache/bundler/git/*"]
|
||||
gem_dirs = Dir["#{Gem.dir}/gems/*"]
|
||||
gem_files = Dir["#{Gem.dir}/cache/*.gem"]
|
||||
gemspec_files = Dir["#{Gem.dir}/specifications/*.gemspec"]
|
||||
spec_gem_paths = []
|
||||
# need to keep git sources around
|
||||
spec_git_paths = @definition.spec_git_paths
|
||||
spec_git_cache_dirs = []
|
||||
spec_gem_executables = []
|
||||
spec_cache_paths = []
|
||||
spec_gemspec_paths = []
|
||||
specs.each do |spec|
|
||||
spec_gem_paths << spec.full_gem_path
|
||||
# need to check here in case gems are nested like for the rails git repo
|
||||
md = %r{(.+bundler/gems/.+-[a-f0-9]{7,12})}.match(spec.full_gem_path)
|
||||
spec_git_paths << md[1] if md
|
||||
spec_gem_executables << spec.executables.collect do |executable|
|
||||
e = "#{Bundler.rubygems.gem_bindir}/#{executable}"
|
||||
[e, "#{e}.bat"]
|
||||
end
|
||||
spec_cache_paths << spec.cache_file
|
||||
spec_gemspec_paths << spec.spec_file
|
||||
spec_git_cache_dirs << spec.source.cache_path.to_s if spec.source.is_a?(Bundler::Source::Git)
|
||||
end
|
||||
spec_gem_paths.uniq!
|
||||
spec_gem_executables.flatten!
|
||||
|
||||
stale_gem_bins = gem_bins - spec_gem_executables
|
||||
stale_git_dirs = git_dirs - spec_git_paths
|
||||
stale_git_cache_dirs = git_cache_dirs - spec_git_cache_dirs
|
||||
stale_gem_dirs = gem_dirs - spec_gem_paths
|
||||
stale_gem_files = gem_files - spec_cache_paths
|
||||
stale_gemspec_files = gemspec_files - spec_gemspec_paths
|
||||
|
||||
output = stale_gem_dirs.collect do |gem_dir|
|
||||
full_name = Pathname.new(gem_dir).basename.to_s
|
||||
|
||||
parts = full_name.split('-')
|
||||
name = parts[0..-2].join('-')
|
||||
version = parts.last
|
||||
output = "#{name} (#{version})"
|
||||
|
||||
if dry_run
|
||||
Bundler.ui.info "Would have removed #{output}"
|
||||
else
|
||||
Bundler.ui.info "Removing #{output}"
|
||||
FileUtils.rm_rf(gem_dir)
|
||||
end
|
||||
|
||||
output
|
||||
end + stale_git_dirs.collect do |gem_dir|
|
||||
full_name = Pathname.new(gem_dir).basename.to_s
|
||||
|
||||
parts = full_name.split('-')
|
||||
name = parts[0..-2].join('-')
|
||||
revision = parts[-1]
|
||||
output = "#{name} (#{revision})"
|
||||
|
||||
if dry_run
|
||||
Bundler.ui.info "Would have removed #{output}"
|
||||
else
|
||||
Bundler.ui.info "Removing #{output}"
|
||||
FileUtils.rm_rf(gem_dir)
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
unless dry_run
|
||||
stale_gem_bins.each { |bin| FileUtils.rm(bin) if File.exist?(bin) }
|
||||
stale_gem_files.each { |file| FileUtils.rm(file) if File.exist?(file) }
|
||||
stale_gemspec_files.each { |file| FileUtils.rm(file) if File.exist?(file) }
|
||||
stale_git_cache_dirs.each { |dir| FileUtils.rm_rf(dir) if File.exist?(dir) }
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
def setup_environment
|
||||
begin
|
||||
ENV["BUNDLE_BIN_PATH"] = Bundler.rubygems.bin_path("bundler", "bundle", VERSION)
|
||||
rescue Gem::GemNotFoundException
|
||||
ENV["BUNDLE_BIN_PATH"] = File.expand_path("../../../bin/bundle", __FILE__)
|
||||
end
|
||||
|
||||
# Set PATH
|
||||
paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
|
||||
paths.unshift "#{Bundler.bundle_path}/bin"
|
||||
ENV["PATH"] = paths.uniq.join(File::PATH_SEPARATOR)
|
||||
|
||||
# Set BUNDLE_GEMFILE
|
||||
ENV["BUNDLE_GEMFILE"] = default_gemfile.to_s
|
||||
|
||||
# Set RUBYOPT
|
||||
rubyopt = [ENV["RUBYOPT"]].compact
|
||||
if rubyopt.empty? || rubyopt.first !~ /-rbundler\/setup/
|
||||
rubyopt.unshift %|-rbundler/setup|
|
||||
ENV["RUBYOPT"] = rubyopt.join(' ')
|
||||
end
|
||||
|
||||
# Set RUBYLIB
|
||||
rubylib = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR)
|
||||
rubylib.unshift File.expand_path('../..', __FILE__)
|
||||
ENV["RUBYLIB"] = rubylib.uniq.join(File::PATH_SEPARATOR)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prune_gem_cache(resolve, custom_path)
|
||||
cached = Dir["#{cache_path(custom_path)}/*.gem"]
|
||||
|
||||
cached = cached.delete_if do |path|
|
||||
spec = Bundler.rubygems.spec_from_gem path
|
||||
|
||||
resolve.any? do |s|
|
||||
s.name == spec.name && s.version == spec.version && !s.source.is_a?(Bundler::Source::Git)
|
||||
end
|
||||
end
|
||||
|
||||
if cached.any?
|
||||
Bundler.ui.info "Removing outdated .gem files from vendor/cache"
|
||||
|
||||
cached.each do |path|
|
||||
Bundler.ui.info " * #{File.basename(path)}"
|
||||
File.delete(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def prune_git_and_path_cache(resolve, custom_path)
|
||||
cached = Dir["#{cache_path(custom_path)}/*/.bundlecache"]
|
||||
|
||||
cached = cached.delete_if do |path|
|
||||
name = File.basename(File.dirname(path))
|
||||
|
||||
resolve.any? do |s|
|
||||
source = s.source
|
||||
source.respond_to?(:app_cache_dirname) && source.app_cache_dirname == name
|
||||
end
|
||||
end
|
||||
|
||||
if cached.any?
|
||||
Bundler.ui.info "Removing outdated git and path gems from vendor/cache"
|
||||
|
||||
cached.each do |path|
|
||||
path = File.dirname(path)
|
||||
Bundler.ui.info " * #{File.basename(path)}"
|
||||
FileUtils.rm_rf(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def setup_manpath
|
||||
# Store original MANPATH for restoration later in with_clean_env()
|
||||
ENV['BUNDLE_ORIG_MANPATH'] = ENV['MANPATH']
|
||||
|
||||
# Add man/ subdirectories from activated bundles to MANPATH for man(1)
|
||||
manuals = $LOAD_PATH.map do |path|
|
||||
man_subdir = path.sub(/lib$/, 'man')
|
||||
man_subdir unless Dir[man_subdir + '/man?/'].empty?
|
||||
end.compact
|
||||
|
||||
unless manuals.empty?
|
||||
ENV['MANPATH'] = manuals.concat(
|
||||
ENV['MANPATH'].to_s.split(File::PATH_SEPARATOR)
|
||||
).uniq.join(File::PATH_SEPARATOR)
|
||||
end
|
||||
end
|
||||
|
||||
def cache_path(custom_path = nil)
|
||||
path = custom_path || root
|
||||
path.join("vendor/cache")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,176 @@
|
||||
module Bundler
|
||||
class Settings
|
||||
def initialize(root = nil)
|
||||
@root = root
|
||||
@local_config = load_config(local_config_file)
|
||||
@global_config = load_config(global_config_file)
|
||||
end
|
||||
|
||||
def [](key)
|
||||
the_key = key_for(key)
|
||||
value = (@local_config[the_key] || ENV[the_key] || @global_config[the_key])
|
||||
is_bool(key) ? to_bool(value) : value
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
local_config_file or raise GemfileNotFound, "Could not locate Gemfile"
|
||||
set_key(key, value, @local_config, local_config_file)
|
||||
end
|
||||
|
||||
alias :set_local :[]=
|
||||
|
||||
def delete(key)
|
||||
@local_config.delete(key_for(key))
|
||||
end
|
||||
|
||||
def set_global(key, value)
|
||||
set_key(key, value, @global_config, global_config_file)
|
||||
end
|
||||
|
||||
def all
|
||||
env_keys = ENV.keys.select { |k| k =~ /BUNDLE_.*/ }
|
||||
|
||||
keys = @global_config.keys | @local_config.keys | env_keys
|
||||
|
||||
keys.map do |key|
|
||||
key.sub(/^BUNDLE_/, '').gsub(/__/, ".").downcase
|
||||
end
|
||||
end
|
||||
|
||||
def local_overrides
|
||||
repos = {}
|
||||
all.each do |k|
|
||||
if k =~ /^local\./
|
||||
repos[$'] = self[k]
|
||||
end
|
||||
end
|
||||
repos
|
||||
end
|
||||
|
||||
def gem_mirrors
|
||||
all.inject({}) do |h, k|
|
||||
if k =~ /^mirror\./
|
||||
uri = normalize_uri($')
|
||||
h[uri] = normalize_uri(self[k])
|
||||
end
|
||||
h
|
||||
end
|
||||
end
|
||||
|
||||
def locations(key)
|
||||
key = key_for(key)
|
||||
locations = {}
|
||||
locations[:local] = @local_config[key] if @local_config.key?(key)
|
||||
locations[:env] = ENV[key] if ENV[key]
|
||||
locations[:global] = @global_config[key] if @global_config.key?(key)
|
||||
locations
|
||||
end
|
||||
|
||||
def pretty_values_for(exposed_key)
|
||||
key = key_for(exposed_key)
|
||||
|
||||
locations = []
|
||||
if @local_config.key?(key)
|
||||
locations << "Set for your local app (#{local_config_file}): #{@local_config[key].inspect}"
|
||||
end
|
||||
|
||||
if value = ENV[key]
|
||||
locations << "Set via #{key}: #{value.inspect}"
|
||||
end
|
||||
|
||||
if @global_config.key?(key)
|
||||
locations << "Set for the current user (#{global_config_file}): #{@global_config[key].inspect}"
|
||||
end
|
||||
|
||||
return ["You have not configured a value for `#{exposed_key}`"] if locations.empty?
|
||||
locations
|
||||
end
|
||||
|
||||
def without=(array)
|
||||
self[:without] = (array.empty? ? nil : array.join(":")) if array
|
||||
end
|
||||
|
||||
def without
|
||||
self[:without] ? self[:without].split(":").map { |w| w.to_sym } : []
|
||||
end
|
||||
|
||||
# @local_config["BUNDLE_PATH"] should be prioritized over ENV["BUNDLE_PATH"]
|
||||
def path
|
||||
key = key_for(:path)
|
||||
path = ENV[key] || @global_config[key]
|
||||
return path if path && !@local_config.key?(key)
|
||||
|
||||
if path = self[:path]
|
||||
"#{path}/#{Bundler.ruby_scope}"
|
||||
else
|
||||
Bundler.rubygems.gem_dir
|
||||
end
|
||||
end
|
||||
|
||||
def allow_sudo?
|
||||
!@local_config.key?(key_for(:path))
|
||||
end
|
||||
|
||||
def ignore_config?
|
||||
ENV['BUNDLE_IGNORE_CONFIG']
|
||||
end
|
||||
|
||||
private
|
||||
def key_for(key)
|
||||
key = key.to_s.sub(".", "__").upcase
|
||||
"BUNDLE_#{key}"
|
||||
end
|
||||
|
||||
def is_bool(key)
|
||||
%w(frozen cache_all no_prune disable_local_branch_check).include? key.to_s
|
||||
end
|
||||
|
||||
def to_bool(value)
|
||||
!(value.nil? || value == '' || value =~ /^(false|f|no|n|0)$/i)
|
||||
end
|
||||
|
||||
def set_key(key, value, hash, file)
|
||||
key = key_for(key)
|
||||
|
||||
unless hash[key] == value
|
||||
hash[key] = value
|
||||
hash.delete(key) if value.nil?
|
||||
FileUtils.mkdir_p(file.dirname)
|
||||
require 'bundler/psyched_yaml'
|
||||
File.open(file, "w") { |f| f.puts YAML.dump(hash) }
|
||||
end
|
||||
value
|
||||
end
|
||||
|
||||
def global_config_file
|
||||
file = ENV["BUNDLE_CONFIG"] || File.join(Bundler.rubygems.user_home, ".bundle/config")
|
||||
Pathname.new(file)
|
||||
end
|
||||
|
||||
def local_config_file
|
||||
Pathname.new(@root).join("config") if @root
|
||||
end
|
||||
|
||||
def load_config(config_file)
|
||||
valid_file = config_file && config_file.exist? && !config_file.size.zero?
|
||||
if !ignore_config? && valid_file
|
||||
config_regex =/^(BUNDLE_.+): (?:['"](.*)['"]|(.+(?:\n(?!BUNDLE).+))|(.+))$/
|
||||
config_pairs = config_file.read.scan(config_regex).map{|m| m.compact.map { |n| n.gsub(/\n\s?/, "") } }
|
||||
Hash[config_pairs]
|
||||
else
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: duplicates Rubygems#normalize_uri
|
||||
# TODO: is this the correct place to validate mirror URIs?
|
||||
def normalize_uri(uri)
|
||||
uri = uri.to_s
|
||||
uri = "#{uri}/" unless uri =~ %r[/\Z]
|
||||
uri = URI(uri)
|
||||
raise ArgumentError, "Gem mirror sources must be absolute URIs (configured: #{mirror_source})" unless uri.absolute?
|
||||
uri
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
require 'bundler/shared_helpers'
|
||||
|
||||
if Bundler::SharedHelpers.in_bundle?
|
||||
require 'bundler'
|
||||
if STDOUT.tty?
|
||||
begin
|
||||
Bundler.setup
|
||||
rescue Bundler::BundlerError => e
|
||||
puts "\e[31m#{e.message}\e[0m"
|
||||
puts e.backtrace.join("\n") if ENV["DEBUG"]
|
||||
if e.is_a?(Bundler::GemNotFound)
|
||||
puts "\e[33mRun `bundle install` to install missing gems.\e[0m"
|
||||
end
|
||||
exit e.status_code
|
||||
end
|
||||
else
|
||||
Bundler.setup
|
||||
end
|
||||
|
||||
# Add bundler to the load path after disabling system gems
|
||||
bundler_lib = File.expand_path("../..", __FILE__)
|
||||
$LOAD_PATH.unshift(bundler_lib) unless $LOAD_PATH.include?(bundler_lib)
|
||||
end
|
||||
@@ -0,0 +1,110 @@
|
||||
require 'pathname'
|
||||
require 'rubygems'
|
||||
|
||||
require 'bundler/constants'
|
||||
require 'bundler/rubygems_integration'
|
||||
require 'bundler/current_ruby'
|
||||
|
||||
module Gem
|
||||
class Dependency
|
||||
if !instance_methods.map { |m| m.to_s }.include?("requirement")
|
||||
def requirement
|
||||
version_requirements
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Bundler
|
||||
module SharedHelpers
|
||||
attr_accessor :gem_loaded
|
||||
|
||||
def default_gemfile
|
||||
gemfile = find_gemfile
|
||||
raise GemfileNotFound, "Could not locate Gemfile" unless gemfile
|
||||
Pathname.new(gemfile)
|
||||
end
|
||||
|
||||
def default_lockfile
|
||||
Pathname.new("#{default_gemfile}.lock")
|
||||
end
|
||||
|
||||
def in_bundle?
|
||||
find_gemfile
|
||||
end
|
||||
|
||||
if Bundler.current_ruby.mswin? || Bundler.current_ruby.jruby?
|
||||
require 'monitor'
|
||||
@chdir_monitor = Monitor.new
|
||||
def chdir(dir, &blk)
|
||||
@chdir_monitor.synchronize do
|
||||
Dir.chdir dir, &blk
|
||||
end
|
||||
end
|
||||
|
||||
def pwd
|
||||
@chdir_monitor.synchronize do
|
||||
Dir.pwd
|
||||
end
|
||||
end
|
||||
else
|
||||
def chdir(dir, &blk)
|
||||
Dir.chdir dir, &blk
|
||||
end
|
||||
|
||||
def pwd
|
||||
Dir.pwd
|
||||
end
|
||||
end
|
||||
|
||||
def with_clean_git_env(&block)
|
||||
keys = %w[GIT_DIR GIT_WORK_TREE]
|
||||
old_env = keys.inject({}) do |h, k|
|
||||
h.update(k => ENV[k])
|
||||
end
|
||||
|
||||
keys.each {|key| ENV.delete(key) }
|
||||
|
||||
block.call
|
||||
ensure
|
||||
keys.each {|key| ENV[key] = old_env[key] }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_gemfile
|
||||
given = ENV['BUNDLE_GEMFILE']
|
||||
return given if given && !given.empty?
|
||||
|
||||
previous = nil
|
||||
current = File.expand_path(SharedHelpers.pwd)
|
||||
|
||||
until !File.directory?(current) || current == previous
|
||||
if ENV['BUNDLE_SPEC_RUN']
|
||||
# avoid stepping above the tmp directory when testing
|
||||
return nil if File.file?(File.join(current, 'bundler.gemspec'))
|
||||
end
|
||||
|
||||
# otherwise return the Gemfile if it's there
|
||||
filename = File.join(current, 'Gemfile')
|
||||
return filename if File.file?(filename)
|
||||
current, previous = File.expand_path("..", current), current
|
||||
end
|
||||
end
|
||||
|
||||
def clean_load_path
|
||||
# handle 1.9 where system gems are always on the load path
|
||||
if defined?(::Gem)
|
||||
me = File.expand_path("../../", __FILE__)
|
||||
$LOAD_PATH.reject! do |p|
|
||||
next if File.expand_path(p) =~ /^#{Regexp.escape(me)}/
|
||||
p != File.dirname(__FILE__) &&
|
||||
Bundler.rubygems.gem_path.any?{|gp| p =~ /^#{Regexp.escape(gp)}/ }
|
||||
end
|
||||
$LOAD_PATH.uniq!
|
||||
end
|
||||
end
|
||||
|
||||
extend self
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,63 @@
|
||||
module Bundler
|
||||
class SimilarityDetector
|
||||
SimilarityScore = Struct.new(:string, :distance)
|
||||
|
||||
# initialize with an array of words to be matched against
|
||||
def initialize(corpus)
|
||||
@corpus = corpus
|
||||
end
|
||||
|
||||
# return an array of words similar to 'word' from the corpus
|
||||
def similar_words(word, limit=3)
|
||||
words_by_similarity = @corpus.map{|w| SimilarityScore.new(w, levenshtein_distance(word, w))}
|
||||
words_by_similarity.select{|s| s.distance<=limit}.sort_by(&:distance).map(&:string)
|
||||
end
|
||||
|
||||
# return the result of 'similar_words', concatenated into a list
|
||||
# (eg "a, b, or c")
|
||||
def similar_word_list(word, limit=3)
|
||||
words = similar_words(word,limit)
|
||||
if words.length==1
|
||||
words[0]
|
||||
elsif words.length>1
|
||||
[words[0..-2].join(', '), words[-1]].join(' or ')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
# http://www.informit.com/articles/article.aspx?p=683059&seqNum=36
|
||||
def levenshtein_distance(this, that, ins=2, del=2, sub=1)
|
||||
# ins, del, sub are weighted costs
|
||||
return nil if this.nil?
|
||||
return nil if that.nil?
|
||||
dm = [] # distance matrix
|
||||
|
||||
# Initialize first row values
|
||||
dm[0] = (0..this.length).collect { |i| i * ins }
|
||||
fill = [0] * (this.length - 1)
|
||||
|
||||
# Initialize first column values
|
||||
for i in 1..that.length
|
||||
dm[i] = [i * del, fill.flatten]
|
||||
end
|
||||
|
||||
# populate matrix
|
||||
for i in 1..that.length
|
||||
for j in 1..this.length
|
||||
# critical comparison
|
||||
dm[i][j] = [
|
||||
dm[i-1][j-1] +
|
||||
(this[j-1] == that[i-1] ? 0 : sub),
|
||||
dm[i][j-1] + ins,
|
||||
dm[i-1][j] + del
|
||||
].min
|
||||
end
|
||||
end
|
||||
|
||||
# The last value in matrix is the Levenshtein distance between the strings
|
||||
dm[that.length][this.length]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
module Bundler
|
||||
class Source
|
||||
autoload :Rubygems, 'bundler/source/rubygems'
|
||||
autoload :Path, 'bundler/source/path'
|
||||
autoload :Git, 'bundler/source/git'
|
||||
|
||||
def self.mirror_for(uri)
|
||||
uri = URI(uri.to_s) unless uri.is_a?(URI)
|
||||
|
||||
# Settings keys are all downcased
|
||||
mirrors = Bundler.settings.gem_mirrors
|
||||
normalized_key = URI(uri.to_s.downcase)
|
||||
|
||||
mirrors[normalized_key] || uri
|
||||
end
|
||||
|
||||
attr_accessor :dependency_names
|
||||
|
||||
def unmet_deps
|
||||
specs.unmet_dependency_names
|
||||
end
|
||||
|
||||
def version_message(spec)
|
||||
locked_spec = Bundler.locked_gems.specs.find { |s| s.name == spec.name } if Bundler.locked_gems
|
||||
locked_spec_version = locked_spec.version if locked_spec
|
||||
message = "#{spec.name} #{spec.version}"
|
||||
if locked_spec_version && spec.version != locked_spec_version
|
||||
message << " (was #{locked_spec_version})"
|
||||
end
|
||||
message
|
||||
end
|
||||
|
||||
def can_lock?(spec)
|
||||
spec.source == self
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,290 @@
|
||||
require 'fileutils'
|
||||
require 'uri'
|
||||
require 'digest/sha1'
|
||||
|
||||
module Bundler
|
||||
class Source
|
||||
|
||||
class Git < Path
|
||||
autoload :GitProxy, 'bundler/source/git/git_proxy'
|
||||
|
||||
attr_reader :uri, :ref, :branch, :options, :submodules
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
@glob = options["glob"] || DEFAULT_GLOB
|
||||
|
||||
@allow_cached = false
|
||||
@allow_remote = false
|
||||
|
||||
# Stringify options that could be set as symbols
|
||||
%w(ref branch tag revision).each{|k| options[k] = options[k].to_s if options[k] }
|
||||
|
||||
@uri = options["uri"]
|
||||
@branch = options["branch"]
|
||||
@ref = options["ref"] || options["branch"] || options["tag"] || 'master'
|
||||
@submodules = options["submodules"]
|
||||
@name = options["name"]
|
||||
@version = options["version"]
|
||||
|
||||
@copied = false
|
||||
@local = false
|
||||
end
|
||||
|
||||
def self.from_lock(options)
|
||||
new(options.merge("uri" => options.delete("remote")))
|
||||
end
|
||||
|
||||
def to_lock
|
||||
out = "GIT\n"
|
||||
out << " remote: #{@uri}\n"
|
||||
out << " revision: #{revision}\n"
|
||||
%w(ref branch tag submodules).each do |opt|
|
||||
out << " #{opt}: #{options[opt]}\n" if options[opt]
|
||||
end
|
||||
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
|
||||
out << " specs:\n"
|
||||
end
|
||||
|
||||
def eql?(o)
|
||||
o.is_a?(Git) &&
|
||||
uri == o.uri &&
|
||||
ref == o.ref &&
|
||||
branch == o.branch &&
|
||||
name == o.name &&
|
||||
version == o.version &&
|
||||
submodules == o.submodules
|
||||
end
|
||||
|
||||
alias == eql?
|
||||
|
||||
def to_s
|
||||
at = if local?
|
||||
path
|
||||
elsif options["ref"]
|
||||
shortref_for_display(options["ref"])
|
||||
else
|
||||
ref
|
||||
end
|
||||
"#{uri} (at #{at})"
|
||||
end
|
||||
|
||||
def name
|
||||
File.basename(@uri, '.git')
|
||||
end
|
||||
|
||||
# This is the path which is going to contain a specific
|
||||
# checkout of the git repository. When using local git
|
||||
# repos, this is set to the local repo.
|
||||
def install_path
|
||||
@install_path ||= begin
|
||||
git_scope = "#{base_name}-#{shortref_for_path(revision)}"
|
||||
path = Bundler.install_path.join(git_scope)
|
||||
|
||||
if !path.exist? && Bundler.requires_sudo?
|
||||
Bundler.user_bundle_path.join(Bundler.ruby_scope).join(git_scope)
|
||||
else
|
||||
path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias :path :install_path
|
||||
|
||||
def extension_dir_name
|
||||
"#{base_name}-#{shortref_for_path(revision)}"
|
||||
end
|
||||
|
||||
def unlock!
|
||||
git_proxy.revision = nil
|
||||
@unlocked = true
|
||||
end
|
||||
|
||||
def local_override!(path)
|
||||
return false if local?
|
||||
|
||||
path = Pathname.new(path)
|
||||
path = path.expand_path(Bundler.root) unless path.relative?
|
||||
|
||||
unless options["branch"] || Bundler.settings[:disable_local_branch_check]
|
||||
raise GitError, "Cannot use local override for #{name} at #{path} because " \
|
||||
":branch is not specified in Gemfile. Specify a branch or use " \
|
||||
"`bundle config --delete` to remove the local override"
|
||||
end
|
||||
|
||||
unless path.exist?
|
||||
raise GitError, "Cannot use local override for #{name} because #{path} " \
|
||||
"does not exist. Check `bundle config --delete` to remove the local override"
|
||||
end
|
||||
|
||||
set_local!(path)
|
||||
|
||||
# Create a new git proxy without the cached revision
|
||||
# so the Gemfile.lock always picks up the new revision.
|
||||
@git_proxy = GitProxy.new(path, uri, ref)
|
||||
|
||||
if git_proxy.branch != options["branch"] && !Bundler.settings[:disable_local_branch_check]
|
||||
raise GitError, "Local override for #{name} at #{path} is using branch " \
|
||||
"#{git_proxy.branch} but Gemfile specifies #{options["branch"]}"
|
||||
end
|
||||
|
||||
changed = cached_revision && cached_revision != git_proxy.revision
|
||||
|
||||
if changed && !@unlocked && !git_proxy.contains?(cached_revision)
|
||||
raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \
|
||||
"but the current branch in your local override for #{name} does not contain such commit. " \
|
||||
"Please make sure your branch is up to date."
|
||||
end
|
||||
|
||||
changed
|
||||
end
|
||||
|
||||
# TODO: actually cache git specs
|
||||
def specs(*)
|
||||
if has_app_cache? && !local?
|
||||
set_local!(app_cache_path)
|
||||
end
|
||||
|
||||
if requires_checkout? && !@copied
|
||||
git_proxy.checkout
|
||||
git_proxy.copy_to(install_path, submodules)
|
||||
serialize_gemspecs_in(install_path)
|
||||
@copied = true
|
||||
end
|
||||
|
||||
local_specs
|
||||
end
|
||||
|
||||
def install(spec)
|
||||
debug = nil
|
||||
if requires_checkout? && !@copied
|
||||
debug = " * Checking out revision: #{ref}"
|
||||
git_proxy.copy_to(install_path, submodules)
|
||||
serialize_gemspecs_in(install_path)
|
||||
@copied = true
|
||||
end
|
||||
generate_bin(spec)
|
||||
if requires_checkout? && spec.post_install_message
|
||||
Installer.post_install_messages[spec.name] = spec.post_install_message
|
||||
end
|
||||
["Using #{version_message(spec)} from #{to_s}", nil, debug]
|
||||
end
|
||||
|
||||
def cache(spec, custom_path = nil)
|
||||
app_cache_path = app_cache_path(custom_path)
|
||||
return unless Bundler.settings[:cache_all]
|
||||
return if path == app_cache_path
|
||||
cached!
|
||||
FileUtils.rm_rf(app_cache_path)
|
||||
git_proxy.checkout if requires_checkout?
|
||||
git_proxy.copy_to(app_cache_path, @submodules)
|
||||
serialize_gemspecs_in(app_cache_path)
|
||||
end
|
||||
|
||||
def load_spec_files
|
||||
super
|
||||
rescue PathError => e
|
||||
Bundler.ui.trace e
|
||||
raise GitError, "#{to_s} is not yet checked out. Run `bundle install` first."
|
||||
end
|
||||
|
||||
# This is the path which is going to contain a cache
|
||||
# of the git repository. When using the same git repository
|
||||
# across different projects, this cache will be shared.
|
||||
# When using local git repos, this is set to the local repo.
|
||||
def cache_path
|
||||
@cache_path ||= begin
|
||||
git_scope = "#{base_name}-#{uri_hash}"
|
||||
|
||||
if Bundler.requires_sudo?
|
||||
Bundler.user_bundle_path.join("cache/git", git_scope)
|
||||
else
|
||||
Bundler.cache.join("git", git_scope)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def app_cache_dirname
|
||||
"#{base_name}-#{shortref_for_path(cached_revision || revision)}"
|
||||
end
|
||||
|
||||
def revision
|
||||
git_proxy.revision
|
||||
end
|
||||
|
||||
def allow_git_ops?
|
||||
@allow_remote || @allow_cached
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def serialize_gemspecs_in(destination)
|
||||
expanded_path = destination.expand_path(Bundler.root)
|
||||
Dir["#{expanded_path}/#{@glob}"].each do |spec_path|
|
||||
# Evaluate gemspecs and cache the result. Gemspecs
|
||||
# in git might require git or other dependencies.
|
||||
# The gemspecs we cache should already be evaluated.
|
||||
spec = Bundler.load_gemspec(spec_path)
|
||||
next unless spec
|
||||
File.open(spec_path, 'wb') {|file| file.write(spec.to_ruby) }
|
||||
end
|
||||
end
|
||||
|
||||
def set_local!(path)
|
||||
@local = true
|
||||
@local_specs = @git_proxy = nil
|
||||
@cache_path = @install_path = path
|
||||
end
|
||||
|
||||
def has_app_cache?
|
||||
cached_revision && super
|
||||
end
|
||||
|
||||
def local?
|
||||
@local
|
||||
end
|
||||
|
||||
def requires_checkout?
|
||||
allow_git_ops? && !local?
|
||||
end
|
||||
|
||||
def base_name
|
||||
File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)?(//\w*/)?(\w*/)*},''),".git")
|
||||
end
|
||||
|
||||
def shortref_for_display(ref)
|
||||
ref[0..6]
|
||||
end
|
||||
|
||||
def shortref_for_path(ref)
|
||||
ref[0..11]
|
||||
end
|
||||
|
||||
def uri_hash
|
||||
if uri =~ %r{^\w+://(\w+@)?}
|
||||
# Downcase the domain component of the URI
|
||||
# and strip off a trailing slash, if one is present
|
||||
input = URI.parse(uri).normalize.to_s.sub(%r{/$},'')
|
||||
else
|
||||
# If there is no URI scheme, assume it is an ssh/git URI
|
||||
input = uri
|
||||
end
|
||||
Digest::SHA1.hexdigest(input)
|
||||
end
|
||||
|
||||
def cached_revision
|
||||
options["revision"]
|
||||
end
|
||||
|
||||
def cached?
|
||||
cache_path.exist?
|
||||
end
|
||||
|
||||
def git_proxy
|
||||
@git_proxy ||= GitProxy.new(cache_path, uri, ref, cached_revision, self)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,158 @@
|
||||
module Bundler
|
||||
class Source
|
||||
class Git < Path
|
||||
|
||||
class GitNotInstalledError < GitError
|
||||
def initialize
|
||||
msg = "You need to install git to be able to use gems from git repositories. "
|
||||
msg << "For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git"
|
||||
super msg
|
||||
end
|
||||
end
|
||||
|
||||
class GitNotAllowedError < GitError
|
||||
def initialize(command)
|
||||
msg = "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, "
|
||||
msg << "this error message could probably be more useful. Please submit a ticket at http://github.com/bundler/bundler/issues "
|
||||
msg << "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
|
||||
super msg
|
||||
end
|
||||
end
|
||||
|
||||
class GitCommandError < GitError
|
||||
def initialize(command, path = nil)
|
||||
msg = "Git error: command `git #{command}` in directory #{Dir.pwd} has failed."
|
||||
msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path && path.exist?
|
||||
super msg
|
||||
end
|
||||
end
|
||||
|
||||
# The GitProxy is responsible to interact with git repositories.
|
||||
# All actions required by the Git source is encapsulated in this
|
||||
# object.
|
||||
class GitProxy
|
||||
attr_accessor :path, :uri, :ref
|
||||
attr_writer :revision
|
||||
|
||||
def initialize(path, uri, ref, revision = nil, git = nil)
|
||||
@path = path
|
||||
@uri = uri
|
||||
@ref = ref
|
||||
@revision = revision
|
||||
@git = git
|
||||
raise GitNotInstalledError.new if allow? && !Bundler.git_present?
|
||||
end
|
||||
|
||||
def revision
|
||||
@revision ||= allowed_in_path { git("rev-parse #{ref}").strip }
|
||||
end
|
||||
|
||||
def branch
|
||||
@branch ||= allowed_in_path do
|
||||
git("branch") =~ /^\* (.*)$/ && $1.strip
|
||||
end
|
||||
end
|
||||
|
||||
def contains?(commit)
|
||||
allowed_in_path do
|
||||
result = git_null("branch --contains #{commit}")
|
||||
$? == 0 && result =~ /^\* (.*)$/
|
||||
end
|
||||
end
|
||||
|
||||
def checkout
|
||||
if path.exist?
|
||||
return if has_revision_cached?
|
||||
Bundler.ui.confirm "Updating #{uri}"
|
||||
in_path do
|
||||
git_retry %|fetch --force --quiet --tags #{uri_escaped} "refs/heads/*:refs/heads/*"|
|
||||
end
|
||||
else
|
||||
Bundler.ui.info "Fetching #{uri}"
|
||||
FileUtils.mkdir_p(path.dirname)
|
||||
git_retry %|clone #{uri_escaped} "#{path}" --bare --no-hardlinks --quiet|
|
||||
end
|
||||
end
|
||||
|
||||
def copy_to(destination, submodules=false)
|
||||
unless File.exist?(destination.join(".git"))
|
||||
FileUtils.mkdir_p(destination.dirname)
|
||||
FileUtils.rm_rf(destination)
|
||||
git_retry %|clone --no-checkout --quiet "#{path}" "#{destination}"|
|
||||
File.chmod((0777 & ~File.umask), destination)
|
||||
end
|
||||
|
||||
SharedHelpers.chdir(destination) do
|
||||
git_retry %|fetch --force --quiet --tags "#{path}"|
|
||||
git "reset --hard #{@revision}"
|
||||
|
||||
if submodules
|
||||
git_retry "submodule update --init --recursive"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# TODO: Do not rely on /dev/null.
|
||||
# Given that open3 is not cross platform until Ruby 1.9.3,
|
||||
# the best solution is to pipe to /dev/null if it exists.
|
||||
# If it doesn't, everything will work fine, but the user
|
||||
# will get the $stderr messages as well.
|
||||
def git_null(command)
|
||||
git("#{command} 2>#{Bundler::NULL}", false)
|
||||
end
|
||||
|
||||
def git_retry(command)
|
||||
Bundler::Retry.new("git #{command}", GitNotAllowedError).attempts do
|
||||
git(command)
|
||||
end
|
||||
end
|
||||
|
||||
def git(command, check_errors=true)
|
||||
raise GitNotAllowedError.new(command) unless allow?
|
||||
out = SharedHelpers.with_clean_git_env { %x{git #{command}} }
|
||||
raise GitCommandError.new(command, path) if check_errors && !$?.success?
|
||||
out
|
||||
end
|
||||
|
||||
def has_revision_cached?
|
||||
return unless @revision
|
||||
in_path { git("cat-file -e #{@revision}") }
|
||||
true
|
||||
rescue GitError
|
||||
false
|
||||
end
|
||||
|
||||
# Escape the URI for git commands
|
||||
def uri_escaped
|
||||
if Bundler::WINDOWS
|
||||
# Windows quoting requires double quotes only, with double quotes
|
||||
# inside the string escaped by being doubled.
|
||||
'"' + uri.gsub('"') {|s| '""'} + '"'
|
||||
else
|
||||
# Bash requires single quoted strings, with the single quotes escaped
|
||||
# by ending the string, escaping the quote, and restarting the string.
|
||||
"'" + uri.gsub("'") {|s| "'\\''"} + "'"
|
||||
end
|
||||
end
|
||||
|
||||
def allow?
|
||||
@git ? @git.allow_git_ops? : true
|
||||
end
|
||||
|
||||
def in_path(&blk)
|
||||
checkout unless path.exist?
|
||||
SharedHelpers.chdir(path, &blk)
|
||||
end
|
||||
|
||||
def allowed_in_path
|
||||
return in_path { yield } if allow?
|
||||
raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,225 @@
|
||||
module Bundler
|
||||
class Source
|
||||
|
||||
class Path < Source
|
||||
autoload :Installer, 'bundler/source/path/installer'
|
||||
|
||||
attr_reader :path, :options
|
||||
attr_writer :name
|
||||
attr_accessor :version
|
||||
|
||||
DEFAULT_GLOB = "{,*,*/*}.gemspec"
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
@glob = options["glob"] || DEFAULT_GLOB
|
||||
|
||||
@allow_cached = false
|
||||
@allow_remote = false
|
||||
|
||||
if options["path"]
|
||||
@path = Pathname.new(options["path"])
|
||||
@path = expand(@path) unless @path.relative?
|
||||
end
|
||||
|
||||
@name = options["name"]
|
||||
@version = options["version"]
|
||||
|
||||
# Stores the original path. If at any point we move to the
|
||||
# cached directory, we still have the original path to copy from.
|
||||
@original_path = @path
|
||||
end
|
||||
|
||||
def remote!
|
||||
@allow_remote = true
|
||||
end
|
||||
|
||||
def cached!
|
||||
@allow_cached = true
|
||||
end
|
||||
|
||||
def self.from_lock(options)
|
||||
new(options.merge("path" => options.delete("remote")))
|
||||
end
|
||||
|
||||
def to_lock
|
||||
out = "PATH\n"
|
||||
out << " remote: #{relative_path}\n"
|
||||
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
|
||||
out << " specs:\n"
|
||||
end
|
||||
|
||||
def to_s
|
||||
"source at #{@path}"
|
||||
end
|
||||
|
||||
def hash
|
||||
self.class.hash
|
||||
end
|
||||
|
||||
def eql?(o)
|
||||
o.instance_of?(Path) &&
|
||||
expand(path) == expand(o.path) &&
|
||||
version == o.version
|
||||
end
|
||||
|
||||
alias == eql?
|
||||
|
||||
def name
|
||||
File.basename(expand(path).to_s)
|
||||
end
|
||||
|
||||
def install(spec)
|
||||
generate_bin(spec, :disable_extensions)
|
||||
["Using #{version_message(spec)} from #{to_s}", nil]
|
||||
end
|
||||
|
||||
def cache(spec, custom_path = nil)
|
||||
app_cache_path = app_cache_path(custom_path)
|
||||
return unless Bundler.settings[:cache_all]
|
||||
return if expand(@original_path).to_s.index(Bundler.root.to_s) == 0
|
||||
|
||||
unless @original_path.exist?
|
||||
raise GemNotFound, "Can't cache gem #{version_message(spec)} because #{to_s} is missing!"
|
||||
end
|
||||
|
||||
FileUtils.rm_rf(app_cache_path)
|
||||
FileUtils.cp_r("#{@original_path}/.", app_cache_path)
|
||||
FileUtils.touch(app_cache_path.join(".bundlecache"))
|
||||
end
|
||||
|
||||
def local_specs(*)
|
||||
@local_specs ||= load_spec_files
|
||||
end
|
||||
|
||||
def specs
|
||||
if has_app_cache?
|
||||
@path = app_cache_path
|
||||
end
|
||||
local_specs
|
||||
end
|
||||
|
||||
def app_cache_dirname
|
||||
name
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expand(somepath)
|
||||
somepath.expand_path(Bundler.root)
|
||||
rescue ArgumentError => e
|
||||
Bundler.ui.debug(e)
|
||||
raise PathError, "There was an error while trying to use the path " \
|
||||
"`#{somepath}`.\nThe error message was: #{e.message}."
|
||||
end
|
||||
|
||||
def app_cache_path(custom_path = nil)
|
||||
@app_cache_path ||= Bundler.app_cache(custom_path).join(app_cache_dirname)
|
||||
end
|
||||
|
||||
def has_app_cache?
|
||||
SharedHelpers.in_bundle? && app_cache_path.exist?
|
||||
end
|
||||
|
||||
def load_spec_files
|
||||
index = Index.new
|
||||
expanded_path = expand(path)
|
||||
|
||||
if File.directory?(expanded_path)
|
||||
Dir["#{expanded_path}/#{@glob}"].each do |file|
|
||||
spec = Bundler.load_gemspec(file)
|
||||
if spec
|
||||
spec.loaded_from = file.to_s
|
||||
spec.source = self
|
||||
index << spec
|
||||
end
|
||||
end
|
||||
|
||||
if index.empty? && @name && @version
|
||||
index << Gem::Specification.new do |s|
|
||||
s.name = @name
|
||||
s.source = self
|
||||
s.version = Gem::Version.new(@version)
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.summary = "Fake gemspec for #{@name}"
|
||||
s.relative_loaded_from = "#{@name}.gemspec"
|
||||
s.authors = ["no one"]
|
||||
if expanded_path.join("bin").exist?
|
||||
executables = expanded_path.join("bin").children
|
||||
executables.reject!{|p| File.directory?(p) }
|
||||
s.executables = executables.map{|c| c.basename.to_s }
|
||||
end
|
||||
end
|
||||
end
|
||||
elsif File.exist?(expanded_path)
|
||||
raise PathError, "The path `#{expanded_path}` is not a directory."
|
||||
else
|
||||
raise PathError, "The path `#{expanded_path}` does not exist."
|
||||
end
|
||||
|
||||
index
|
||||
end
|
||||
|
||||
def relative_path
|
||||
if path.to_s.match(%r{^#{Regexp.escape Bundler.root.to_s}})
|
||||
return path.relative_path_from(Bundler.root)
|
||||
end
|
||||
path
|
||||
end
|
||||
|
||||
def generate_bin(spec, disable_extensions = false)
|
||||
gem_dir = Pathname.new(spec.full_gem_path)
|
||||
|
||||
# Some gem authors put absolute paths in their gemspec
|
||||
# and we have to save them from themselves
|
||||
spec.files = spec.files.map do |p|
|
||||
next if File.directory?(p)
|
||||
begin
|
||||
Pathname.new(p).relative_path_from(gem_dir).to_s
|
||||
rescue ArgumentError
|
||||
p
|
||||
end
|
||||
end.compact
|
||||
|
||||
gem_file = Bundler.rubygems.build_gem gem_dir, spec
|
||||
|
||||
installer = Path::Installer.new(spec, :env_shebang => false)
|
||||
run_hooks(:pre_install, installer)
|
||||
installer.build_extensions unless disable_extensions
|
||||
run_hooks(:post_build, installer)
|
||||
installer.generate_bin
|
||||
run_hooks(:post_install, installer)
|
||||
rescue Gem::InvalidSpecificationException => e
|
||||
Bundler.ui.warn "\n#{spec.name} at #{spec.full_gem_path} did not have a valid gemspec.\n" \
|
||||
"This prevents bundler from installing bins or native extensions, but " \
|
||||
"that may not affect its functionality."
|
||||
|
||||
if !spec.extensions.empty? && !spec.email.empty?
|
||||
Bundler.ui.warn "If you need to use this package without installing it from a gem " \
|
||||
"repository, please contact #{spec.email} and ask them " \
|
||||
"to modify their .gemspec so it can work with `gem build`."
|
||||
end
|
||||
|
||||
Bundler.ui.warn "The validation message from Rubygems was:\n #{e.message}"
|
||||
ensure
|
||||
if gem_dir && gem_file
|
||||
FileUtils.rm_rf(gem_dir.join gem_file)
|
||||
end
|
||||
end
|
||||
|
||||
def run_hooks(type, installer)
|
||||
hooks_meth = "#{type}_hooks"
|
||||
return unless Gem.respond_to?(hooks_meth)
|
||||
Gem.send(hooks_meth).each do |hook|
|
||||
result = hook.call(installer)
|
||||
if result == false
|
||||
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
|
||||
message = "#{type} hook#{location} failed for #{installer.spec.full_name}"
|
||||
raise InstallHookError, message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
module Bundler
|
||||
class Source
|
||||
class Path
|
||||
|
||||
class Installer < Bundler::GemInstaller
|
||||
def initialize(spec, options = {})
|
||||
@spec = spec
|
||||
@tmp_bin_dir = "#{Bundler.tmp(spec.full_name)}/bin"
|
||||
@gem_bin_dir = "#{Bundler.rubygems.gem_dir}/bin"
|
||||
@bin_dir = Bundler.requires_sudo? ? @tmp_bin_dir : @gem_bin_dir
|
||||
@gem_dir = Bundler.rubygems.path(spec.full_gem_path)
|
||||
@wrappers = options[:wrappers] || true
|
||||
@env_shebang = options[:env_shebang] || true
|
||||
@format_executable = options[:format_executable] || false
|
||||
@build_args = options[:build_args] || Bundler.rubygems.build_args
|
||||
end
|
||||
|
||||
def generate_bin
|
||||
return if spec.executables.nil? || spec.executables.empty?
|
||||
|
||||
if Bundler.requires_sudo?
|
||||
FileUtils.mkdir_p(@tmp_bin_dir) unless File.exist?(@tmp_bin_dir)
|
||||
end
|
||||
|
||||
super
|
||||
|
||||
if Bundler.requires_sudo?
|
||||
Bundler.mkdir_p @gem_bin_dir
|
||||
spec.executables.each do |exe|
|
||||
Bundler.sudo "cp -R #{@tmp_bin_dir}/#{exe} #{@gem_bin_dir}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,350 @@
|
||||
require 'uri'
|
||||
require 'rubygems/user_interaction'
|
||||
require 'rubygems/spec_fetcher'
|
||||
|
||||
module Bundler
|
||||
class Source
|
||||
class Rubygems < Source
|
||||
API_REQUEST_LIMIT = 100 # threshold for switching back to the modern index instead of fetching every spec
|
||||
|
||||
attr_reader :remotes, :caches
|
||||
|
||||
def initialize(options = {})
|
||||
@options = options
|
||||
@remotes = []
|
||||
@dependency_names = []
|
||||
@allow_remote = false
|
||||
@allow_cached = false
|
||||
@caches = [Bundler.app_cache, *Bundler.rubygems.gem_cache]
|
||||
|
||||
Array(options["remotes"] || []).reverse_each{|r| add_remote(r) }
|
||||
end
|
||||
|
||||
def remote!
|
||||
@allow_remote = true
|
||||
end
|
||||
|
||||
def cached!
|
||||
@allow_cached = true
|
||||
end
|
||||
|
||||
def hash
|
||||
@remotes.hash
|
||||
end
|
||||
|
||||
def eql?(o)
|
||||
o.is_a?(Rubygems) && remotes_equal?(o.remotes)
|
||||
end
|
||||
|
||||
alias == eql?
|
||||
|
||||
def can_lock?(spec)
|
||||
spec.source.is_a?(Rubygems)
|
||||
end
|
||||
|
||||
def options
|
||||
{ "remotes" => @remotes.map { |r| r.to_s } }
|
||||
end
|
||||
|
||||
def self.from_lock(options)
|
||||
new(options)
|
||||
end
|
||||
|
||||
def to_lock
|
||||
out = "GEM\n"
|
||||
out << remotes.map { |remote|
|
||||
" remote: #{suppress_configured_credentials remote}\n"
|
||||
}.join
|
||||
out << " specs:\n"
|
||||
end
|
||||
|
||||
def to_s
|
||||
remote_names = self.remotes.map { |r| r.to_s }.join(', ')
|
||||
"rubygems repository #{remote_names}"
|
||||
end
|
||||
alias_method :name, :to_s
|
||||
|
||||
def specs
|
||||
@specs ||= begin
|
||||
# remote_specs usually generates a way larger Index than the other
|
||||
# sources, and large_idx.use small_idx is way faster than
|
||||
# small_idx.use large_idx.
|
||||
idx = @allow_remote ? remote_specs.dup : Index.new
|
||||
idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote
|
||||
idx.use(installed_specs, :override_dupes)
|
||||
idx
|
||||
end
|
||||
end
|
||||
|
||||
def install(spec)
|
||||
return ["Using #{version_message(spec)}", nil] if installed_specs[spec].any?
|
||||
|
||||
# Download the gem to get the spec, because some specs that are returned
|
||||
# by rubygems.org are broken and wrong.
|
||||
if spec.source_uri
|
||||
# Check for this spec from other sources
|
||||
uris = [spec.source_uri]
|
||||
uris += source_uris_for_spec(spec)
|
||||
uris.compact!
|
||||
uris.uniq!
|
||||
Installer.ambiguous_gems << [spec.name, *uris] if uris.length > 1
|
||||
|
||||
s = Bundler.rubygems.spec_from_gem(fetch_gem(spec), Bundler.settings["trust-policy"])
|
||||
spec.__swap__(s)
|
||||
end
|
||||
|
||||
path = cached_gem(spec)
|
||||
if Bundler.requires_sudo?
|
||||
install_path = Bundler.tmp(spec.full_name)
|
||||
bin_path = install_path.join("bin")
|
||||
else
|
||||
install_path = Bundler.rubygems.gem_dir
|
||||
bin_path = Bundler.system_bindir
|
||||
end
|
||||
|
||||
installed_spec = nil
|
||||
Bundler.rubygems.preserve_paths do
|
||||
installed_spec = Bundler::GemInstaller.new(path,
|
||||
:install_dir => install_path.to_s,
|
||||
:bin_dir => bin_path.to_s,
|
||||
:ignore_dependencies => true,
|
||||
:wrappers => true,
|
||||
:env_shebang => true
|
||||
).install
|
||||
end
|
||||
|
||||
# SUDO HAX
|
||||
if Bundler.requires_sudo?
|
||||
Bundler.rubygems.repository_subdirectories.each do |name|
|
||||
src = File.join(install_path, name, "*")
|
||||
dst = File.join(Bundler.rubygems.gem_dir, name)
|
||||
if name == "extensions" && Dir.glob(src).any?
|
||||
src = File.join(src, "*/*")
|
||||
ext_src = Dir.glob(src).first
|
||||
ext_src.gsub!(src[0..-6], '')
|
||||
dst = File.dirname(File.join(dst, ext_src))
|
||||
end
|
||||
Bundler.mkdir_p dst
|
||||
Bundler.sudo "cp -R #{src} #{dst}" if Dir[src].any?
|
||||
end
|
||||
|
||||
spec.executables.each do |exe|
|
||||
Bundler.mkdir_p Bundler.system_bindir
|
||||
Bundler.sudo "cp -R #{install_path}/bin/#{exe} #{Bundler.system_bindir}/"
|
||||
end
|
||||
end
|
||||
|
||||
spec.loaded_from = "#{Bundler.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec"
|
||||
installed_spec.loaded_from = spec.loaded_from
|
||||
["Installing #{version_message(spec)}", spec.post_install_message]
|
||||
ensure
|
||||
if install_path && Bundler.requires_sudo?
|
||||
FileUtils.remove_entry_secure(install_path)
|
||||
end
|
||||
end
|
||||
|
||||
def cache(spec, custom_path = nil)
|
||||
if builtin_gem?(spec)
|
||||
cached_path = cached_built_in_gem(spec)
|
||||
else
|
||||
cached_path = cached_gem(spec)
|
||||
end
|
||||
raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
|
||||
return if File.dirname(cached_path) == Bundler.app_cache.to_s
|
||||
Bundler.ui.info " * #{File.basename(cached_path)}"
|
||||
FileUtils.cp(cached_path, Bundler.app_cache(custom_path))
|
||||
end
|
||||
|
||||
def cached_built_in_gem(spec)
|
||||
cached_path = cached_path(spec)
|
||||
if cached_path.nil?
|
||||
remote_spec = remote_specs.search(spec).first
|
||||
cached_path = fetch_gem(remote_spec)
|
||||
end
|
||||
cached_path
|
||||
end
|
||||
|
||||
def add_remote(source)
|
||||
uri = normalize_uri(source)
|
||||
@remotes.unshift(uri) unless @remotes.include?(uri)
|
||||
end
|
||||
|
||||
def replace_remotes(other_remotes)
|
||||
return false if other_remotes == @remotes
|
||||
|
||||
@remotes = []
|
||||
other_remotes.reverse_each do |r|
|
||||
add_remote r.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def unmet_deps
|
||||
if @allow_remote && api_fetchers.any?
|
||||
remote_specs.unmet_dependency_names
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def source_uris_for_spec(spec)
|
||||
specs.search_all(spec.name).map{|s| s.source_uri }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cached_gem(spec)
|
||||
cached_gem = cached_path(spec)
|
||||
unless cached_gem
|
||||
raise Bundler::GemNotFound, "Could not find #{spec.file_name} for installation"
|
||||
end
|
||||
cached_gem
|
||||
end
|
||||
|
||||
def cached_path(spec)
|
||||
possibilities = @caches.map { |p| "#{p}/#{spec.file_name}" }
|
||||
possibilities.find { |p| File.exist?(p) }
|
||||
end
|
||||
|
||||
def normalize_uri(uri)
|
||||
uri = uri.to_s
|
||||
uri = "#{uri}/" unless uri =~ %r'/$'
|
||||
uri = URI(uri)
|
||||
raise ArgumentError, "The source must be an absolute URI" unless uri.absolute?
|
||||
uri
|
||||
end
|
||||
|
||||
def suppress_configured_credentials(remote)
|
||||
remote_nouser = remote.dup.tap { |uri| uri.user = uri.password = nil }.to_s
|
||||
if remote.userinfo && remote.userinfo == Bundler.settings[remote_nouser]
|
||||
remote_nouser
|
||||
else
|
||||
remote
|
||||
end
|
||||
end
|
||||
|
||||
def installed_specs
|
||||
@installed_specs ||= begin
|
||||
idx = Index.new
|
||||
have_bundler = false
|
||||
Bundler.rubygems.all_specs.reverse.each do |spec|
|
||||
next if spec.name == 'bundler' && spec.version.to_s != VERSION
|
||||
have_bundler = true if spec.name == 'bundler'
|
||||
spec.source = self
|
||||
idx << spec
|
||||
end
|
||||
|
||||
# Always have bundler locally
|
||||
unless have_bundler
|
||||
# We're running bundler directly from the source
|
||||
# so, let's create a fake gemspec for it (it's a path)
|
||||
# gemspec
|
||||
bundler = Gem::Specification.new do |s|
|
||||
s.name = 'bundler'
|
||||
s.version = VERSION
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.source = self
|
||||
s.authors = ["bundler team"]
|
||||
s.loaded_from = File.expand_path("..", __FILE__)
|
||||
end
|
||||
idx << bundler
|
||||
end
|
||||
idx
|
||||
end
|
||||
end
|
||||
|
||||
def cached_specs
|
||||
@cached_specs ||= begin
|
||||
idx = installed_specs.dup
|
||||
|
||||
path = Bundler.app_cache
|
||||
Dir["#{path}/*.gem"].each do |gemfile|
|
||||
next if gemfile =~ /^bundler\-[\d\.]+?\.gem/
|
||||
s ||= Bundler.rubygems.spec_from_gem(gemfile)
|
||||
s.source = self
|
||||
idx << s
|
||||
end
|
||||
end
|
||||
|
||||
idx
|
||||
end
|
||||
|
||||
def fetchers
|
||||
@fetchers ||= remotes.map do |url|
|
||||
Bundler::Fetcher.new(url)
|
||||
end
|
||||
end
|
||||
|
||||
def api_fetchers
|
||||
fetchers.select{|f| f.use_api }
|
||||
end
|
||||
|
||||
def remote_specs
|
||||
@remote_specs ||= Index.build do |idx|
|
||||
index_fetchers = fetchers - api_fetchers
|
||||
|
||||
# gather lists from non-api sites
|
||||
index_fetchers.each do |f|
|
||||
Bundler.ui.info "Fetching source index from #{f.uri}"
|
||||
idx.use f.specs(nil, self)
|
||||
end
|
||||
|
||||
# because ensuring we have all the gems we need involves downloading
|
||||
# the gemspecs of those gems, if the non-api sites contain more than
|
||||
# about 100 gems, we just treat all sites as non-api for speed.
|
||||
allow_api = idx.size < API_REQUEST_LIMIT && dependency_names.size < API_REQUEST_LIMIT
|
||||
|
||||
if allow_api
|
||||
api_fetchers.each do |f|
|
||||
Bundler.ui.info "Fetching gem metadata from #{f.uri}", Bundler.ui.debug?
|
||||
idx.use f.specs(dependency_names, self)
|
||||
Bundler.ui.info "" if !Bundler.ui.debug? # new line now that the dots are over
|
||||
end
|
||||
|
||||
if api_fetchers.any? && api_fetchers.all?{|f| f.use_api }
|
||||
# it's possible that gems from one source depend on gems from some
|
||||
# other source, so now we download gemspecs and iterate over those
|
||||
# dependencies, looking for gems we don't have info on yet.
|
||||
unmet = idx.unmet_dependency_names
|
||||
|
||||
# if there are any cross-site gems we missed, get them now
|
||||
api_fetchers.each do |f|
|
||||
Bundler.ui.info "Fetching additional metadata from #{f.uri}", Bundler.ui.debug?
|
||||
idx.use f.specs(unmet, self)
|
||||
Bundler.ui.info "" if !Bundler.ui.debug? # new line now that the dots are over
|
||||
end if unmet.any?
|
||||
else
|
||||
allow_api = false
|
||||
end
|
||||
end
|
||||
|
||||
if !allow_api
|
||||
api_fetchers.each do |f|
|
||||
Bundler.ui.info "Fetching source index from #{f.uri}"
|
||||
idx.use f.specs(nil, self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_gem(spec)
|
||||
return false unless spec.source_uri
|
||||
Fetcher.download_gem_from_uri(spec, spec.source_uri)
|
||||
end
|
||||
|
||||
def builtin_gem?(spec)
|
||||
# Ruby 2.1, where all included gems have this summary
|
||||
return true if spec.summary =~ /is bundled with Ruby/
|
||||
|
||||
# Ruby 2.0, where gemspecs are stored in specifications/default/
|
||||
spec.loaded_from && spec.loaded_from.include?("specifications/default/")
|
||||
end
|
||||
|
||||
def remotes_equal?(other_remotes)
|
||||
remotes.map(&method(:suppress_configured_credentials)) == other_remotes.map(&method(:suppress_configured_credentials))
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,80 @@
|
||||
module Bundler
|
||||
class SourceList
|
||||
attr_reader :path_sources,
|
||||
:git_sources,
|
||||
:rubygems_sources
|
||||
|
||||
def initialize
|
||||
@path_sources = []
|
||||
@git_sources = []
|
||||
@rubygems_aggregate = Source::Rubygems.new
|
||||
@rubygems_sources = [@rubygems_aggregate]
|
||||
end
|
||||
|
||||
def add_path_source(options = {})
|
||||
add_source_to_list Source::Path.new(options), path_sources
|
||||
end
|
||||
|
||||
def add_git_source(options = {})
|
||||
add_source_to_list Source::Git.new(options), git_sources
|
||||
end
|
||||
|
||||
def add_rubygems_source(options = {})
|
||||
add_source_to_list Source::Rubygems.new(options), @rubygems_sources
|
||||
end
|
||||
|
||||
def add_rubygems_remote(uri)
|
||||
@rubygems_aggregate.add_remote(uri)
|
||||
@rubygems_aggregate
|
||||
end
|
||||
|
||||
def all_sources
|
||||
path_sources + git_sources + rubygems_sources
|
||||
end
|
||||
|
||||
def get(source)
|
||||
source_list_for(source).find { |s| source == s }
|
||||
end
|
||||
|
||||
def lock_sources
|
||||
lock_sources = (path_sources + git_sources).sort_by(&:to_s)
|
||||
lock_sources << combine_rubygems_sources
|
||||
end
|
||||
|
||||
def replace_sources!(replacement_sources)
|
||||
[path_sources, git_sources, rubygems_sources].each do |source_list|
|
||||
source_list.map! do |source|
|
||||
replacement_sources.find { |s| s == source } || source
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cached!
|
||||
all_sources.each(&:cached!)
|
||||
end
|
||||
|
||||
def remote!
|
||||
all_sources.each(&:remote!)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_source_to_list(source, list)
|
||||
list.unshift(source).uniq!
|
||||
source
|
||||
end
|
||||
|
||||
def source_list_for(source)
|
||||
case source
|
||||
when Source::Git then git_sources
|
||||
when Source::Path then path_sources
|
||||
when Source::Rubygems then rubygems_sources
|
||||
else raise ArgumentError, "Invalid source: #{source.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
def combine_rubygems_sources
|
||||
Source::Rubygems.new("remotes" => rubygems_sources.map(&:remotes).flatten.uniq.reverse)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,154 @@
|
||||
require 'tsort'
|
||||
require 'forwardable'
|
||||
|
||||
module Bundler
|
||||
class SpecSet
|
||||
extend Forwardable
|
||||
include TSort, Enumerable
|
||||
|
||||
def_delegators :@specs, :<<, :length, :add, :remove
|
||||
def_delegators :sorted, :each
|
||||
|
||||
def initialize(specs)
|
||||
@specs = specs.sort_by { |s| s.name }
|
||||
end
|
||||
|
||||
def for(dependencies, skip = [], check = false, match_current_platform = false)
|
||||
handled, deps, specs = {}, dependencies.dup, []
|
||||
skip << 'bundler'
|
||||
|
||||
until deps.empty?
|
||||
dep = deps.shift
|
||||
next if handled[dep] || skip.include?(dep.name)
|
||||
|
||||
spec = lookup[dep.name].find do |s|
|
||||
if match_current_platform
|
||||
Gem::Platform.match(s.platform)
|
||||
else
|
||||
s.match_platform(dep.__platform)
|
||||
end
|
||||
end
|
||||
|
||||
handled[dep] = true
|
||||
|
||||
if spec
|
||||
specs << spec
|
||||
|
||||
spec.dependencies.each do |d|
|
||||
next if d.type == :development
|
||||
d = DepProxy.new(d, dep.__platform) unless match_current_platform
|
||||
deps << d
|
||||
end
|
||||
elsif check
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if spec = lookup['bundler'].first
|
||||
specs << spec
|
||||
end
|
||||
|
||||
check ? true : SpecSet.new(specs)
|
||||
end
|
||||
|
||||
def valid_for?(deps)
|
||||
self.for(deps, [], true)
|
||||
end
|
||||
|
||||
def [](key)
|
||||
key = key.name if key.respond_to?(:name)
|
||||
lookup[key].reverse
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
@specs << value
|
||||
@lookup = nil
|
||||
@sorted = nil
|
||||
value
|
||||
end
|
||||
|
||||
def sort!
|
||||
self
|
||||
end
|
||||
|
||||
def to_a
|
||||
sorted.dup
|
||||
end
|
||||
|
||||
def to_hash
|
||||
lookup.dup
|
||||
end
|
||||
|
||||
def materialize(deps, missing_specs = nil)
|
||||
materialized = self.for(deps, [], false, true).to_a
|
||||
deps = materialized.map {|s| s.name }.uniq
|
||||
materialized.map! do |s|
|
||||
next s unless s.is_a?(LazySpecification)
|
||||
s.source.dependency_names = deps if s.source.respond_to?(:dependency_names=)
|
||||
spec = s.__materialize__
|
||||
if missing_specs
|
||||
missing_specs << s unless spec
|
||||
else
|
||||
raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec
|
||||
end
|
||||
spec if spec
|
||||
end
|
||||
SpecSet.new(materialized.compact)
|
||||
end
|
||||
|
||||
def merge(set)
|
||||
arr = sorted.dup
|
||||
set.each do |s|
|
||||
next if arr.any? { |s2| s2.name == s.name && s2.version == s.version && s2.platform == s.platform }
|
||||
arr << s
|
||||
end
|
||||
SpecSet.new(arr)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sorted
|
||||
rake = @specs.find { |s| s.name == 'rake' }
|
||||
begin
|
||||
@sorted ||= ([rake] + tsort).compact.uniq
|
||||
rescue TSort::Cyclic => error
|
||||
cgems = extract_circular_gems(error)
|
||||
raise CyclicDependencyError, "Your Gemfile requires gems that depend" \
|
||||
" depend on each other, creating an infinite loop. Please remove" \
|
||||
" either gem '#{cgems[1]}' or gem '#{cgems[0]}' and try again."
|
||||
end
|
||||
end
|
||||
|
||||
def extract_circular_gems(error)
|
||||
if Bundler.current_ruby.mri? && Bundler.current_ruby.on_19?
|
||||
error.message.scan(/(\w+) \([^)]/).flatten
|
||||
else
|
||||
error.message.scan(/@name="(.*?)"/).flatten
|
||||
end
|
||||
end
|
||||
|
||||
def lookup
|
||||
@lookup ||= begin
|
||||
lookup = Hash.new { |h,k| h[k] = [] }
|
||||
specs = @specs.sort_by do |s|
|
||||
s.platform.to_s == 'ruby' ? "\0" : s.platform.to_s
|
||||
end
|
||||
specs.reverse_each do |s|
|
||||
lookup[s.name] << s
|
||||
end
|
||||
lookup
|
||||
end
|
||||
end
|
||||
|
||||
def tsort_each_node
|
||||
@specs.each { |s| yield s }
|
||||
end
|
||||
|
||||
def tsort_each_child(s)
|
||||
s.dependencies.sort_by { |d| d.name }.each do |d|
|
||||
next if d.type == :development
|
||||
lookup[d.name].each { |s2| yield s2 }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
# Ignore all files in this directory
|
||||
@@ -0,0 +1,14 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
|
||||
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
|
||||
cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
|
||||
MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
|
||||
BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
|
||||
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
|
||||
ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
|
||||
BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
|
||||
I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
|
||||
CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
|
||||
lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
|
||||
AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,23 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
||||
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
||||
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
||||
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
||||
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
||||
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
||||
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
||||
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
||||
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
||||
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
||||
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
||||
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
||||
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
||||
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
||||
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
+OkuE6N36B9K
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
|
||||
VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
|
||||
ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
|
||||
KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
|
||||
ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
|
||||
MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
|
||||
ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
|
||||
b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
|
||||
bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
|
||||
U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
|
||||
A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
|
||||
I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
|
||||
wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
|
||||
AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
|
||||
oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
|
||||
BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
|
||||
dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
|
||||
MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
|
||||
b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
|
||||
dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
|
||||
MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
|
||||
E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
|
||||
MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
|
||||
hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
|
||||
95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
|
||||
2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
|
||||
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
|
||||
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
|
||||
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
|
||||
R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
|
||||
9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
|
||||
fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
|
||||
iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
|
||||
1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
|
||||
bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
|
||||
MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
|
||||
ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
|
||||
uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
|
||||
Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
|
||||
tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
|
||||
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
|
||||
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
|
||||
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,41 @@
|
||||
require 'fileutils'
|
||||
|
||||
module Bundler
|
||||
module SSLCerts
|
||||
class CertificateManager
|
||||
attr_reader :bundler_cert_path, :bundler_certs, :rubygems_certs
|
||||
|
||||
def self.update_from!(rubygems_path)
|
||||
new(rubygems_path).update!
|
||||
end
|
||||
|
||||
def initialize(rubygems_path)
|
||||
rubygems_certs = File.join(rubygems_path, 'lib/rubygems/ssl_certs')
|
||||
@rubygems_certs = certificates_in(rubygems_certs)
|
||||
|
||||
@bundler_cert_path = File.expand_path("..", __FILE__)
|
||||
@bundler_certs = certificates_in(bundler_cert_path)
|
||||
end
|
||||
|
||||
def up_to_date?
|
||||
bundler_certs.zip(rubygems_certs).all? do |bc, rc|
|
||||
File.basename(bc) == File.basename(rc) && FileUtils.compare_file(bc, rc)
|
||||
end
|
||||
end
|
||||
|
||||
def update!
|
||||
return if up_to_date?
|
||||
|
||||
FileUtils.rm bundler_certs
|
||||
FileUtils.cp rubygems_certs, bundler_cert_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def certificates_in(path)
|
||||
Dir[File.join(path, "*.pem")].sort
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG['ruby_install_name'] %>
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application '<%= executable %>' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require 'pathname'
|
||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../<%= relative_gemfile_path %>",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
|
||||
require 'rubygems'
|
||||
require 'bundler/setup'
|
||||
|
||||
load Gem.bin_path('<%= spec.name %>', '<%= executable %>')
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG['ruby_install_name'] %>
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application '<%= executable %>' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
$:.unshift File.expand_path '../<%= standalone_path %>', __FILE__
|
||||
|
||||
require 'bundler/setup'
|
||||
load File.expand_path '../<%= executable_path %>', __FILE__
|
||||
@@ -0,0 +1,4 @@
|
||||
# A sample Gemfile
|
||||
source "https://rubygems.org"
|
||||
|
||||
# gem "rails"
|
||||
@@ -0,0 +1,3 @@
|
||||
language: ruby
|
||||
rvm:
|
||||
- <%= RUBY_VERSION %>
|
||||
@@ -0,0 +1,4 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# Specify your gem's dependencies in <%=config[:name]%>.gemspec
|
||||
gemspec
|
||||
@@ -0,0 +1,22 @@
|
||||
Copyright (c) <%=Time.now.year%> <%=config[:author]%>
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,31 @@
|
||||
# <%=config[:constant_name]%>
|
||||
|
||||
TODO: Write a gem description
|
||||
|
||||
## Installation
|
||||
|
||||
Add this line to your application's Gemfile:
|
||||
|
||||
```ruby
|
||||
gem '<%=config[:name]%>'
|
||||
```
|
||||
|
||||
And then execute:
|
||||
|
||||
$ bundle
|
||||
|
||||
Or install it yourself as:
|
||||
|
||||
$ gem install <%=config[:name]%>
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Write usage instructions here
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork it ( https://github.com/[my-github-username]/<%=config[:name]%>/fork )
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create a new Pull Request
|
||||
@@ -0,0 +1,24 @@
|
||||
require "bundler/gem_tasks"
|
||||
<% if config[:test] == 'minitest' -%>
|
||||
require "rake/testtask"
|
||||
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << "test"
|
||||
end
|
||||
|
||||
task :default => :test
|
||||
<% elsif config[:test] == 'rspec' -%>
|
||||
require "rspec/core/rake_task"
|
||||
|
||||
RSpec::Core::RakeTask.new(:spec)
|
||||
|
||||
task :default => :spec
|
||||
<% end -%>
|
||||
|
||||
<% if config[:ext] -%>
|
||||
require "rake/extensiontask"
|
||||
|
||||
Rake::ExtensionTask.new("<%=config[:underscored_name]%>") do |ext|
|
||||
ext.lib_dir = "lib/<%=config[:namespaced_path]%>"
|
||||
end
|
||||
<% end -%>
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require '<%= config[:namespaced_path] %>'
|
||||
@@ -0,0 +1,3 @@
|
||||
require "mkmf"
|
||||
|
||||
create_makefile(<%=config[:underscored_name].inspect%>)
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "<%=config[:underscored_name]%>.h"
|
||||
|
||||
VALUE rb_m<%=config[:constant_array].join%>;
|
||||
|
||||
void
|
||||
Init_<%=config[:underscored_name]%>(void)
|
||||
{
|
||||
rb_m<%=config[:constant_array].join%> = rb_define_module(<%=config[:constant_name].inspect%>);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef <%=config[:underscored_name].upcase%>_H
|
||||
#define <%=config[:underscored_name].upcase%>_H 1
|
||||
|
||||
#include "ruby.h"
|
||||
|
||||
#endif /* <%=config[:underscored_name].upcase%>_H */
|
||||
@@ -0,0 +1,14 @@
|
||||
/.bundle/
|
||||
/.yardoc
|
||||
/Gemfile.lock
|
||||
/_yardoc/
|
||||
/coverage/
|
||||
/doc/
|
||||
/pkg/
|
||||
/spec/reports/
|
||||
/tmp/
|
||||
*.bundle
|
||||
*.so
|
||||
*.o
|
||||
*.a
|
||||
mkmf.log
|
||||
@@ -0,0 +1,12 @@
|
||||
require "<%=config[:namespaced_path]%>/version"
|
||||
<%- if config[:ext] -%>
|
||||
require "<%=config[:namespaced_path]%>/<%=config[:underscored_name]%>"
|
||||
<%- end -%>
|
||||
|
||||
<%- config[:constant_array].each_with_index do |c,i| -%>
|
||||
<%= ' '*i %>module <%= c %>
|
||||
<%- end -%>
|
||||
<%= ' '*config[:constant_array].size %># Your code goes here...
|
||||
<%- (config[:constant_array].size-1).downto(0) do |i| -%>
|
||||
<%= ' '*i %>end
|
||||
<%- end -%>
|
||||
@@ -0,0 +1,7 @@
|
||||
<%- config[:constant_array].each_with_index do |c,i| -%>
|
||||
<%= ' '*i %>module <%= c %>
|
||||
<%- end -%>
|
||||
<%= ' '*config[:constant_array].size %>VERSION = "0.0.1"
|
||||
<%- (config[:constant_array].size-1).downto(0) do |i| -%>
|
||||
<%= ' '*i %>end
|
||||
<%- end -%>
|
||||
@@ -0,0 +1,32 @@
|
||||
# coding: utf-8
|
||||
lib = File.expand_path('../lib', __FILE__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require '<%=config[:namespaced_path]%>/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = <%=config[:name].inspect%>
|
||||
spec.version = <%=config[:constant_name]%>::VERSION
|
||||
spec.authors = [<%=config[:author].inspect%>]
|
||||
spec.email = [<%=config[:email].inspect%>]
|
||||
<% if config[:ext] -%>
|
||||
spec.extensions = ["ext/<%=config[:underscored_name]%>/extconf.rb"]
|
||||
<% end -%>
|
||||
spec.summary = %q{TODO: Write a short summary. Required.}
|
||||
spec.description = %q{TODO: Write a longer description. Optional.}
|
||||
spec.homepage = ""
|
||||
spec.license = "MIT"
|
||||
|
||||
spec.files = `git ls-files -z`.split("\x0")
|
||||
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
||||
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
||||
spec.require_paths = ["lib"]
|
||||
|
||||
spec.add_development_dependency "bundler", "~> <%= Bundler::VERSION.split(".")[0..1].join(".") %>"
|
||||
spec.add_development_dependency "rake", "~> 10.0"
|
||||
<% if config[:ext] -%>
|
||||
spec.add_development_dependency "rake-compiler"
|
||||
<% end -%>
|
||||
<% if config[:test] -%>
|
||||
spec.add_development_dependency "<%=config[:test]%>"
|
||||
<% end -%>
|
||||
end
|
||||
@@ -0,0 +1,2 @@
|
||||
--format documentation
|
||||
--color
|
||||
@@ -0,0 +1,11 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe <%= config[:constant_name] %> do
|
||||
it 'has a version number' do
|
||||
expect(<%= config[:constant_name] %>::VERSION).not_to be nil
|
||||
end
|
||||
|
||||
it 'does something useful' do
|
||||
expect(false).to eq(true)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,2 @@
|
||||
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
||||
require '<%= config[:namespaced_path] %>'
|
||||
@@ -0,0 +1,4 @@
|
||||
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
||||
require '<%= config[:namespaced_path] %>'
|
||||
|
||||
require 'minitest/autorun'
|
||||
@@ -0,0 +1,11 @@
|
||||
require 'minitest_helper'
|
||||
|
||||
class Test<%= config[:constant_name] %> < MiniTest::Unit::TestCase
|
||||
def test_that_it_has_a_version_number
|
||||
refute_nil ::<%= config[:constant_name] %>::VERSION
|
||||
end
|
||||
|
||||
def test_it_does_something_useful
|
||||
assert false
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
module Bundler
|
||||
module UI
|
||||
autoload :RGProxy, 'bundler/ui/rg_proxy'
|
||||
autoload :Shell, 'bundler/ui/shell'
|
||||
autoload :Silent, 'bundler/ui/silent'
|
||||
end
|
||||
end
|
||||
Loaded 100 of 275 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user