bundler-maglev- 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +22 -0
- data/.travis.yml +32 -0
- data/CHANGELOG.md +805 -0
- data/ISSUES.md +62 -0
- data/LICENSE +23 -0
- data/README.md +29 -0
- data/Rakefile +212 -0
- data/UPGRADING.md +103 -0
- data/bin/bundle +22 -0
- data/bundler.gemspec +30 -0
- data/lib/bundler.rb +286 -0
- data/lib/bundler/capistrano.rb +11 -0
- data/lib/bundler/cli.rb +520 -0
- data/lib/bundler/definition.rb +438 -0
- data/lib/bundler/dependency.rb +134 -0
- data/lib/bundler/deployment.rb +58 -0
- data/lib/bundler/dsl.rb +257 -0
- data/lib/bundler/environment.rb +47 -0
- data/lib/bundler/gem_helper.rb +151 -0
- data/lib/bundler/gem_installer.rb +9 -0
- data/lib/bundler/gem_tasks.rb +2 -0
- data/lib/bundler/graph.rb +130 -0
- data/lib/bundler/index.rb +138 -0
- data/lib/bundler/installer.rb +97 -0
- data/lib/bundler/lazy_specification.rb +74 -0
- data/lib/bundler/lockfile_parser.rb +108 -0
- data/lib/bundler/remote_specification.rb +59 -0
- data/lib/bundler/resolver.rb +464 -0
- data/lib/bundler/rubygems_ext.rb +237 -0
- data/lib/bundler/rubygems_integration.rb +349 -0
- data/lib/bundler/runtime.rb +152 -0
- data/lib/bundler/settings.rb +115 -0
- data/lib/bundler/setup.rb +23 -0
- data/lib/bundler/shared_helpers.rb +71 -0
- data/lib/bundler/source.rb +708 -0
- data/lib/bundler/spec_set.rb +135 -0
- data/lib/bundler/templates/Executable +16 -0
- data/lib/bundler/templates/Gemfile +4 -0
- data/lib/bundler/templates/newgem/Gemfile.tt +4 -0
- data/lib/bundler/templates/newgem/Rakefile.tt +1 -0
- data/lib/bundler/templates/newgem/bin/newgem.tt +3 -0
- data/lib/bundler/templates/newgem/gitignore.tt +4 -0
- data/lib/bundler/templates/newgem/lib/newgem.rb.tt +9 -0
- data/lib/bundler/templates/newgem/lib/newgem/version.rb.tt +7 -0
- data/lib/bundler/templates/newgem/newgem.gemspec.tt +24 -0
- data/lib/bundler/ui.rb +73 -0
- data/lib/bundler/vendor/thor.rb +358 -0
- data/lib/bundler/vendor/thor/actions.rb +314 -0
- data/lib/bundler/vendor/thor/actions/create_file.rb +105 -0
- data/lib/bundler/vendor/thor/actions/create_link.rb +57 -0
- data/lib/bundler/vendor/thor/actions/directory.rb +93 -0
- data/lib/bundler/vendor/thor/actions/empty_directory.rb +134 -0
- data/lib/bundler/vendor/thor/actions/file_manipulation.rb +270 -0
- data/lib/bundler/vendor/thor/actions/inject_into_file.rb +109 -0
- data/lib/bundler/vendor/thor/base.rb +576 -0
- data/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +9 -0
- data/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb +75 -0
- data/lib/bundler/vendor/thor/core_ext/ordered_hash.rb +100 -0
- data/lib/bundler/vendor/thor/error.rb +30 -0
- data/lib/bundler/vendor/thor/group.rb +273 -0
- data/lib/bundler/vendor/thor/invocation.rb +168 -0
- data/lib/bundler/vendor/thor/parser.rb +4 -0
- data/lib/bundler/vendor/thor/parser/argument.rb +67 -0
- data/lib/bundler/vendor/thor/parser/arguments.rb +161 -0
- data/lib/bundler/vendor/thor/parser/option.rb +120 -0
- data/lib/bundler/vendor/thor/parser/options.rb +175 -0
- data/lib/bundler/vendor/thor/rake_compat.rb +66 -0
- data/lib/bundler/vendor/thor/runner.rb +309 -0
- data/lib/bundler/vendor/thor/shell.rb +88 -0
- data/lib/bundler/vendor/thor/shell/basic.rb +302 -0
- data/lib/bundler/vendor/thor/shell/color.rb +108 -0
- data/lib/bundler/vendor/thor/shell/html.rb +121 -0
- data/lib/bundler/vendor/thor/task.rb +113 -0
- data/lib/bundler/vendor/thor/util.rb +229 -0
- data/lib/bundler/vendor/thor/version.rb +3 -0
- data/lib/bundler/vendored_thor.rb +7 -0
- data/lib/bundler/version.rb +6 -0
- data/lib/bundler/vlad.rb +11 -0
- data/man/bundle-config.ronn +90 -0
- data/man/bundle-exec.ronn +111 -0
- data/man/bundle-install.ronn +317 -0
- data/man/bundle-package.ronn +59 -0
- data/man/bundle-update.ronn +176 -0
- data/man/bundle.ronn +80 -0
- data/man/gemfile.5.ronn +284 -0
- data/man/index.txt +6 -0
- data/spec/bundler/gem_helper_spec.rb +143 -0
- data/spec/cache/gems_spec.rb +230 -0
- data/spec/cache/git_spec.rb +12 -0
- data/spec/cache/path_spec.rb +27 -0
- data/spec/cache/platform_spec.rb +57 -0
- data/spec/install/deploy_spec.rb +197 -0
- data/spec/install/deprecated_spec.rb +37 -0
- data/spec/install/gems/c_ext_spec.rb +48 -0
- data/spec/install/gems/env_spec.rb +107 -0
- data/spec/install/gems/flex_spec.rb +313 -0
- data/spec/install/gems/groups_spec.rb +259 -0
- data/spec/install/gems/packed_spec.rb +84 -0
- data/spec/install/gems/platform_spec.rb +192 -0
- data/spec/install/gems/resolving_spec.rb +72 -0
- data/spec/install/gems/simple_case_spec.rb +770 -0
- data/spec/install/gems/sudo_spec.rb +74 -0
- data/spec/install/gems/win32_spec.rb +26 -0
- data/spec/install/gemspec_spec.rb +125 -0
- data/spec/install/git_spec.rb +570 -0
- data/spec/install/invalid_spec.rb +35 -0
- data/spec/install/path_spec.rb +405 -0
- data/spec/install/upgrade_spec.rb +26 -0
- data/spec/lock/git_spec.rb +35 -0
- data/spec/lock/lockfile_spec.rb +739 -0
- data/spec/other/check_spec.rb +221 -0
- data/spec/other/config_spec.rb +40 -0
- data/spec/other/console_spec.rb +54 -0
- data/spec/other/exec_spec.rb +248 -0
- data/spec/other/ext_spec.rb +37 -0
- data/spec/other/help_spec.rb +39 -0
- data/spec/other/init_spec.rb +40 -0
- data/spec/other/newgem_spec.rb +46 -0
- data/spec/other/open_spec.rb +35 -0
- data/spec/other/show_spec.rb +82 -0
- data/spec/quality_spec.rb +62 -0
- data/spec/resolver/basic_spec.rb +20 -0
- data/spec/resolver/platform_spec.rb +82 -0
- data/spec/runtime/executable_spec.rb +110 -0
- data/spec/runtime/load_spec.rb +107 -0
- data/spec/runtime/platform_spec.rb +90 -0
- data/spec/runtime/require_spec.rb +231 -0
- data/spec/runtime/setup_spec.rb +730 -0
- data/spec/runtime/with_clean_env_spec.rb +15 -0
- data/spec/spec_helper.rb +92 -0
- data/spec/support/builders.rb +597 -0
- data/spec/support/helpers.rb +239 -0
- data/spec/support/indexes.rb +112 -0
- data/spec/support/matchers.rb +77 -0
- data/spec/support/path.rb +71 -0
- data/spec/support/platforms.rb +53 -0
- data/spec/support/ruby_ext.rb +20 -0
- data/spec/support/rubygems_ext.rb +37 -0
- data/spec/support/rubygems_hax/platform.rb +11 -0
- data/spec/support/sudo.rb +21 -0
- data/spec/update/gems_spec.rb +122 -0
- data/spec/update/git_spec.rb +196 -0
- data/spec/update/source_spec.rb +51 -0
- metadata +296 -0
@@ -0,0 +1,152 @@
|
|
1
|
+
require "digest/sha1"
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
class Runtime < Environment
|
5
|
+
include SharedHelpers
|
6
|
+
|
7
|
+
def setup(*groups)
|
8
|
+
# Has to happen first
|
9
|
+
clean_load_path
|
10
|
+
|
11
|
+
specs = groups.any? ? @definition.specs_for(groups) : requested_specs
|
12
|
+
|
13
|
+
setup_environment
|
14
|
+
Bundler.rubygems.replace_entrypoints(specs)
|
15
|
+
|
16
|
+
# Activate the specs
|
17
|
+
specs.each do |spec|
|
18
|
+
unless spec.loaded_from
|
19
|
+
raise GemNotFound, "#{spec.full_name} is missing. Run `bundle` to get it."
|
20
|
+
end
|
21
|
+
|
22
|
+
if activated_spec = Bundler.rubygems.loaded_specs(spec.name) and activated_spec.version != spec.version
|
23
|
+
e = Gem::LoadError.new "You have already activated #{activated_spec.name} #{activated_spec.version}, " \
|
24
|
+
"but your Gemfile requires #{spec.name} #{spec.version}. Using bundle exec may solve this."
|
25
|
+
e.name = spec.name
|
26
|
+
if e.respond_to?(:requirement=)
|
27
|
+
e.requirement = Gem::Requirement.new(spec.version.to_s)
|
28
|
+
else
|
29
|
+
e.version_requirement = Gem::Requirement.new(spec.version.to_s)
|
30
|
+
end
|
31
|
+
raise e
|
32
|
+
end
|
33
|
+
|
34
|
+
Bundler.rubygems.mark_loaded(spec)
|
35
|
+
load_paths = spec.load_paths.reject {|path| $LOAD_PATH.include?(path)}
|
36
|
+
$LOAD_PATH.unshift(*load_paths)
|
37
|
+
end
|
38
|
+
|
39
|
+
lock
|
40
|
+
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
REGEXPS = [
|
45
|
+
/^no such file to load -- (.+)$/i,
|
46
|
+
/^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
|
47
|
+
/^Missing API definition file in (.+)$/i,
|
48
|
+
/^cannot load such file -- (.+)$/i,
|
49
|
+
]
|
50
|
+
|
51
|
+
def require(*groups)
|
52
|
+
groups.map! { |g| g.to_sym }
|
53
|
+
groups = [:default] if groups.empty?
|
54
|
+
|
55
|
+
@definition.dependencies.each do |dep|
|
56
|
+
# Skip the dependency if it is not in any of the requested
|
57
|
+
# groups
|
58
|
+
next unless ((dep.groups & groups).any? && dep.current_platform?)
|
59
|
+
|
60
|
+
required_file = nil
|
61
|
+
|
62
|
+
begin
|
63
|
+
# Loop through all the specified autorequires for the
|
64
|
+
# dependency. If there are none, use the dependency's name
|
65
|
+
# as the autorequire.
|
66
|
+
Array(dep.autorequire || dep.name).each do |file|
|
67
|
+
required_file = file
|
68
|
+
Kernel.require file
|
69
|
+
end
|
70
|
+
rescue LoadError => e
|
71
|
+
REGEXPS.find { |r| r =~ e.message }
|
72
|
+
raise if dep.autorequire || $1 != required_file
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def dependencies_for(*groups)
|
78
|
+
if groups.empty?
|
79
|
+
dependencies
|
80
|
+
else
|
81
|
+
dependencies.select { |d| (groups & d.groups).any? }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
alias gems specs
|
86
|
+
|
87
|
+
def cache
|
88
|
+
FileUtils.mkdir_p(cache_path) unless File.exists?(cache_path)
|
89
|
+
|
90
|
+
Bundler.ui.info "Updating .gem files in vendor/cache"
|
91
|
+
specs.each do |spec|
|
92
|
+
next if spec.name == 'bundler'
|
93
|
+
spec.source.cache(spec) if spec.source.respond_to?(:cache)
|
94
|
+
end
|
95
|
+
prune_cache unless Bundler.settings[:no_prune]
|
96
|
+
end
|
97
|
+
|
98
|
+
def prune_cache
|
99
|
+
FileUtils.mkdir_p(cache_path) unless File.exists?(cache_path)
|
100
|
+
|
101
|
+
resolve = @definition.resolve
|
102
|
+
cached = Dir["#{cache_path}/*.gem"]
|
103
|
+
|
104
|
+
cached = cached.delete_if do |path|
|
105
|
+
spec = Bundler.rubygems.spec_from_gem path
|
106
|
+
|
107
|
+
resolve.any? do |s|
|
108
|
+
s.name == spec.name && s.version == spec.version && !s.source.is_a?(Bundler::Source::Git)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
if cached.any?
|
113
|
+
Bundler.ui.info "Removing outdated .gem files from vendor/cache"
|
114
|
+
|
115
|
+
cached.each do |path|
|
116
|
+
Bundler.ui.info " * #{File.basename(path)}"
|
117
|
+
File.delete(path)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def cache_path
|
125
|
+
root.join("vendor/cache")
|
126
|
+
end
|
127
|
+
|
128
|
+
def setup_environment
|
129
|
+
begin
|
130
|
+
ENV["BUNDLE_BIN_PATH"] = Bundler.rubygems.bin_path("bundler", "bundle", VERSION)
|
131
|
+
rescue Gem::GemNotFoundException
|
132
|
+
ENV["BUNDLE_BIN_PATH"] = File.expand_path("../../../bin/bundle", __FILE__)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Set PATH
|
136
|
+
paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
|
137
|
+
paths.unshift "#{Bundler.bundle_path}/bin"
|
138
|
+
ENV["PATH"] = paths.uniq.join(File::PATH_SEPARATOR)
|
139
|
+
|
140
|
+
# Set BUNDLE_GEMFILE
|
141
|
+
ENV["BUNDLE_GEMFILE"] = default_gemfile.to_s
|
142
|
+
|
143
|
+
# Set RUBYOPT
|
144
|
+
rubyopt = [ENV["RUBYOPT"]].compact
|
145
|
+
if rubyopt.empty? || rubyopt.first !~ /-rbundler\/setup/
|
146
|
+
rubyopt.unshift "-rbundler/setup"
|
147
|
+
rubyopt.unshift "-I#{File.expand_path('../..', __FILE__)}"
|
148
|
+
ENV["RUBYOPT"] = rubyopt.join(' ')
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Bundler
|
2
|
+
class Settings
|
3
|
+
def initialize(root)
|
4
|
+
@root = root
|
5
|
+
@local_config = File.exist?(local_config_file) ? YAML.load_file(local_config_file) : {}
|
6
|
+
@global_config = File.exist?(global_config_file) ? YAML.load_file(global_config_file) : {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](key)
|
10
|
+
key = key_for(key)
|
11
|
+
@local_config[key] || ENV[key] || @global_config[key]
|
12
|
+
end
|
13
|
+
|
14
|
+
def []=(key, value)
|
15
|
+
set_key(key, value, @local_config, local_config_file)
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete(key)
|
19
|
+
@local_config.delete(key_for(key))
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_global(key, value)
|
23
|
+
set_key(key, value, @global_config, global_config_file)
|
24
|
+
end
|
25
|
+
|
26
|
+
def all
|
27
|
+
env_keys = ENV.keys.select { |k| k =~ /BUNDLE_.*/ }
|
28
|
+
keys = @global_config.keys | @local_config.keys | env_keys
|
29
|
+
|
30
|
+
keys.map do |key|
|
31
|
+
key.sub(/^BUNDLE_/, '').gsub(/__/, ".").downcase
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def locations(key)
|
36
|
+
locations = {}
|
37
|
+
|
38
|
+
locations[:local] = @local_config[key] if @local_config.key?(key)
|
39
|
+
locations[:env] = ENV[key] if ENV[key]
|
40
|
+
locations[:global] = @global_config[key] if @global_config.key?(key)
|
41
|
+
locations
|
42
|
+
end
|
43
|
+
|
44
|
+
def pretty_values_for(exposed_key)
|
45
|
+
key = key_for(exposed_key)
|
46
|
+
|
47
|
+
locations = []
|
48
|
+
if @local_config.key?(key)
|
49
|
+
locations << "Set for your local app (#{local_config_file}): #{@local_config[key].inspect}"
|
50
|
+
end
|
51
|
+
|
52
|
+
if value = ENV[key]
|
53
|
+
locations << "Set via #{key}: #{value.inspect}"
|
54
|
+
end
|
55
|
+
|
56
|
+
if @global_config.key?(key)
|
57
|
+
locations << "Set for the current user (#{global_config_file}): #{@global_config[key].inspect}"
|
58
|
+
end
|
59
|
+
|
60
|
+
return ["You have not configured a value for `#{exposed_key}`"] if locations.empty?
|
61
|
+
locations
|
62
|
+
end
|
63
|
+
|
64
|
+
def without=(array)
|
65
|
+
self[:without] = (array.empty? ? nil : array.join(":")) if array
|
66
|
+
end
|
67
|
+
|
68
|
+
def without
|
69
|
+
self[:without] ? self[:without].split(":").map { |w| w.to_sym } : []
|
70
|
+
end
|
71
|
+
|
72
|
+
# @local_config["BUNDLE_PATH"] should be prioritized over ENV["BUNDLE_PATH"]
|
73
|
+
def path
|
74
|
+
path = ENV[key_for(:path)] || @global_config[key_for(:path)]
|
75
|
+
return path if path && !@local_config.key?(key_for(:path))
|
76
|
+
|
77
|
+
if path = self[:path]
|
78
|
+
"#{path}/#{Bundler.ruby_scope}"
|
79
|
+
else
|
80
|
+
Bundler.rubygems.gem_dir
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def allow_sudo?
|
85
|
+
!@local_config.key?(key_for(:path))
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def key_for(key)
|
90
|
+
key = key.to_s.sub(".", "__").upcase
|
91
|
+
"BUNDLE_#{key}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def set_key(key, value, hash, file)
|
95
|
+
key = key_for(key)
|
96
|
+
|
97
|
+
unless hash[key] == value
|
98
|
+
hash[key] = value
|
99
|
+
hash.delete(key) if value.nil?
|
100
|
+
FileUtils.mkdir_p(file.dirname)
|
101
|
+
File.open(file, "w") { |f| f.puts hash.to_yaml }
|
102
|
+
end
|
103
|
+
value
|
104
|
+
end
|
105
|
+
|
106
|
+
def global_config_file
|
107
|
+
file = ENV["BUNDLE_CONFIG"] || File.join(Bundler.rubygems.user_home, ".bundle/config")
|
108
|
+
Pathname.new(file)
|
109
|
+
end
|
110
|
+
|
111
|
+
def local_config_file
|
112
|
+
Pathname.new("#{@root}/config")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'bundler/shared_helpers'
|
2
|
+
|
3
|
+
if Bundler::SharedHelpers.in_bundle?
|
4
|
+
require 'bundler'
|
5
|
+
if STDOUT.tty?
|
6
|
+
begin
|
7
|
+
Bundler.setup
|
8
|
+
rescue Bundler::BundlerError => e
|
9
|
+
puts "\e[31m#{e.message}\e[0m"
|
10
|
+
puts e.backtrace.join("\n") if ENV["DEBUG"]
|
11
|
+
if Bundler::GemNotFound === e
|
12
|
+
puts "\e[33mRun `bundle install` to install missing gems.\e[0m"
|
13
|
+
end
|
14
|
+
exit e.status_code
|
15
|
+
end
|
16
|
+
else
|
17
|
+
Bundler.setup
|
18
|
+
end
|
19
|
+
|
20
|
+
# Add bundler to the load path after disabling system gems
|
21
|
+
bundler_lib = File.expand_path("../..", __FILE__)
|
22
|
+
$LOAD_PATH.unshift(bundler_lib) unless $LOAD_PATH.include?(bundler_lib)
|
23
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
require 'bundler/rubygems_integration'
|
5
|
+
|
6
|
+
module Gem
|
7
|
+
class Dependency
|
8
|
+
if !instance_methods.map { |m| m.to_s }.include?("requirement")
|
9
|
+
def requirement
|
10
|
+
version_requirements
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Bundler
|
17
|
+
module SharedHelpers
|
18
|
+
attr_accessor :gem_loaded
|
19
|
+
|
20
|
+
def default_gemfile
|
21
|
+
gemfile = find_gemfile
|
22
|
+
raise GemfileNotFound, "Could not locate Gemfile" unless gemfile
|
23
|
+
Pathname.new(gemfile)
|
24
|
+
end
|
25
|
+
|
26
|
+
def default_lockfile
|
27
|
+
Pathname.new("#{default_gemfile}.lock")
|
28
|
+
end
|
29
|
+
|
30
|
+
def in_bundle?
|
31
|
+
find_gemfile
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def find_gemfile
|
37
|
+
given = ENV['BUNDLE_GEMFILE']
|
38
|
+
return given if given && !given.empty?
|
39
|
+
|
40
|
+
previous = nil
|
41
|
+
current = File.expand_path(Dir.pwd)
|
42
|
+
|
43
|
+
until !File.directory?(current) || current == previous
|
44
|
+
if ENV['BUNDLE_SPEC_RUN']
|
45
|
+
# avoid stepping above the tmp directory when testing
|
46
|
+
return nil if File.file?(File.join(current, 'bundler.gemspec'))
|
47
|
+
end
|
48
|
+
|
49
|
+
# otherwise return the Gemfile if it's there
|
50
|
+
filename = File.join(current, 'Gemfile')
|
51
|
+
return filename if File.file?(filename)
|
52
|
+
current, previous = File.expand_path("..", current), current
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def clean_load_path
|
57
|
+
# handle 1.9 where system gems are always on the load path
|
58
|
+
if defined?(::Gem)
|
59
|
+
me = File.expand_path("../../", __FILE__)
|
60
|
+
$LOAD_PATH.reject! do |p|
|
61
|
+
next if File.expand_path(p) =~ /^#{Regexp.escape(me)}/
|
62
|
+
p != File.dirname(__FILE__) &&
|
63
|
+
Bundler.rubygems.gem_path.any?{|gp| p =~ /^#{Regexp.escape(gp)}/ }
|
64
|
+
end
|
65
|
+
$LOAD_PATH.uniq!
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
extend self
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,708 @@
|
|
1
|
+
require "uri"
|
2
|
+
require 'rubygems/user_interaction'
|
3
|
+
require "rubygems/installer"
|
4
|
+
require "rubygems/spec_fetcher"
|
5
|
+
require "rubygems/format"
|
6
|
+
require "digest/sha1"
|
7
|
+
require "open3"
|
8
|
+
|
9
|
+
module Bundler
|
10
|
+
module Source
|
11
|
+
# TODO: Refactor this class
|
12
|
+
class Rubygems
|
13
|
+
attr_reader :remotes
|
14
|
+
|
15
|
+
def initialize(options = {})
|
16
|
+
@options = options
|
17
|
+
@remotes = (options["remotes"] || []).map { |r| normalize_uri(r) }
|
18
|
+
@allow_remote = false
|
19
|
+
@allow_cached = false
|
20
|
+
|
21
|
+
# Hardcode the paths for now
|
22
|
+
@caches = [ Bundler.app_cache ] + Bundler.rubygems.gem_path.map do |x|
|
23
|
+
File.expand_path("#{x}/cache")
|
24
|
+
end
|
25
|
+
|
26
|
+
@spec_fetch_map = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def remote!
|
30
|
+
@allow_remote = true
|
31
|
+
end
|
32
|
+
|
33
|
+
def cached!
|
34
|
+
@allow_cached = true
|
35
|
+
end
|
36
|
+
|
37
|
+
def hash
|
38
|
+
Rubygems.hash
|
39
|
+
end
|
40
|
+
|
41
|
+
def eql?(o)
|
42
|
+
Rubygems === o
|
43
|
+
end
|
44
|
+
|
45
|
+
alias == eql?
|
46
|
+
|
47
|
+
def options
|
48
|
+
{ "remotes" => @remotes.map { |r| r.to_s } }
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.from_lock(options)
|
52
|
+
s = new(options)
|
53
|
+
Array(options["remote"]).each { |r| s.add_remote(r) }
|
54
|
+
s
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_lock
|
58
|
+
out = "GEM\n"
|
59
|
+
out << remotes.map {|r| " remote: #{r}\n" }.join
|
60
|
+
out << " specs:\n"
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
remote_names = self.remotes.map { |r| r.to_s }.join(', ')
|
65
|
+
"rubygems repository #{remote_names}"
|
66
|
+
end
|
67
|
+
alias_method :name, :to_s
|
68
|
+
|
69
|
+
def specs
|
70
|
+
@specs ||= fetch_specs
|
71
|
+
end
|
72
|
+
|
73
|
+
def fetch(spec)
|
74
|
+
spec, uri = @spec_fetch_map[spec.full_name]
|
75
|
+
if spec
|
76
|
+
path = download_gem_from_uri(spec, uri)
|
77
|
+
s = Bundler.rubygems.spec_from_gem(path)
|
78
|
+
spec.__swap__(s)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def install(spec)
|
83
|
+
if installed_specs[spec].any?
|
84
|
+
Bundler.ui.info "Using #{spec.name} (#{spec.version}) "
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
88
|
+
Bundler.ui.info "Installing #{spec.name} (#{spec.version}) "
|
89
|
+
path = cached_gem(spec)
|
90
|
+
|
91
|
+
Bundler.rubygems.preserve_paths do
|
92
|
+
|
93
|
+
install_path = Bundler.requires_sudo? ? Bundler.tmp : Bundler.rubygems.gem_dir
|
94
|
+
options = { :install_dir => install_path,
|
95
|
+
:ignore_dependencies => true,
|
96
|
+
:wrappers => true,
|
97
|
+
:env_shebang => true }
|
98
|
+
options.merge!(:bin_dir => "#{install_path}/bin") unless spec.executables.nil? || spec.executables.empty?
|
99
|
+
|
100
|
+
installer = Bundler::GemInstaller.new path, options
|
101
|
+
installer.install
|
102
|
+
end
|
103
|
+
|
104
|
+
# SUDO HAX
|
105
|
+
if Bundler.requires_sudo?
|
106
|
+
sudo "mkdir -p #{Bundler.rubygems.gem_dir}/gems #{Bundler.rubygems.gem_dir}/specifications"
|
107
|
+
sudo "cp -R #{Bundler.tmp}/gems/#{spec.full_name} #{Bundler.rubygems.gem_dir}/gems/"
|
108
|
+
sudo "cp -R #{Bundler.tmp}/specifications/#{spec.full_name}.gemspec #{Bundler.rubygems.gem_dir}/specifications/"
|
109
|
+
spec.executables.each do |exe|
|
110
|
+
sudo "mkdir -p #{Bundler.rubygems.gem_bindir}"
|
111
|
+
sudo "cp -R #{Bundler.tmp}/bin/#{exe} #{Bundler.rubygems.gem_bindir}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
spec.loaded_from = "#{Bundler.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec"
|
116
|
+
end
|
117
|
+
|
118
|
+
def sudo(str)
|
119
|
+
Bundler.sudo(str)
|
120
|
+
end
|
121
|
+
|
122
|
+
def cache(spec)
|
123
|
+
cached_path = cached_gem(spec)
|
124
|
+
raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
|
125
|
+
return if File.dirname(cached_path) == Bundler.app_cache.to_s
|
126
|
+
Bundler.ui.info " * #{File.basename(cached_path)}"
|
127
|
+
FileUtils.cp(cached_path, Bundler.app_cache)
|
128
|
+
end
|
129
|
+
|
130
|
+
def add_remote(source)
|
131
|
+
@remotes << normalize_uri(source)
|
132
|
+
end
|
133
|
+
|
134
|
+
def merge_remotes(source)
|
135
|
+
@remotes = []
|
136
|
+
source.remotes.each do |r|
|
137
|
+
add_remote r.to_s
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def cached_gem(spec)
|
144
|
+
possibilities = @caches.map { |p| "#{p}/#{spec.file_name}" }
|
145
|
+
cached_gem = possibilities.find { |p| File.exist?(p) }
|
146
|
+
unless cached_gem
|
147
|
+
raise Bundler::GemNotFound, "Could not find #{spec.file_name} for installation"
|
148
|
+
end
|
149
|
+
cached_gem
|
150
|
+
end
|
151
|
+
|
152
|
+
def normalize_uri(uri)
|
153
|
+
uri = uri.to_s
|
154
|
+
uri = "#{uri}/" unless uri =~ %r'/$'
|
155
|
+
uri = URI(uri)
|
156
|
+
raise ArgumentError, "The source must be an absolute URI" unless uri.absolute?
|
157
|
+
uri
|
158
|
+
end
|
159
|
+
|
160
|
+
def fetch_specs
|
161
|
+
# remote_specs usually generates a way larger Index than the other
|
162
|
+
# sources, and large_idx.use small_idx is way faster than
|
163
|
+
# small_idx.use large_idx.
|
164
|
+
if @allow_remote
|
165
|
+
idx = remote_specs.dup
|
166
|
+
else
|
167
|
+
idx = Index.new
|
168
|
+
end
|
169
|
+
idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote
|
170
|
+
idx.use(installed_specs, :override_dupes)
|
171
|
+
idx
|
172
|
+
end
|
173
|
+
|
174
|
+
def installed_specs
|
175
|
+
@installed_specs ||= begin
|
176
|
+
idx = Index.new
|
177
|
+
have_bundler = false
|
178
|
+
Bundler.rubygems.all_specs.reverse.each do |spec|
|
179
|
+
next if spec.name == 'bundler' && spec.version.to_s != VERSION
|
180
|
+
have_bundler = true if spec.name == 'bundler'
|
181
|
+
spec.source = self
|
182
|
+
idx << spec
|
183
|
+
end
|
184
|
+
|
185
|
+
# Always have bundler locally
|
186
|
+
unless have_bundler
|
187
|
+
# We're running bundler directly from the source
|
188
|
+
# so, let's create a fake gemspec for it (it's a path)
|
189
|
+
# gemspec
|
190
|
+
bundler = Gem::Specification.new do |s|
|
191
|
+
s.name = 'bundler'
|
192
|
+
s.version = VERSION
|
193
|
+
s.platform = Gem::Platform::RUBY
|
194
|
+
s.source = self
|
195
|
+
s.authors = ["bundler team"]
|
196
|
+
s.loaded_from = File.expand_path("..", __FILE__)
|
197
|
+
end
|
198
|
+
idx << bundler
|
199
|
+
end
|
200
|
+
idx
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def cached_specs
|
205
|
+
@cached_specs ||= begin
|
206
|
+
idx = installed_specs.dup
|
207
|
+
|
208
|
+
path = Bundler.app_cache
|
209
|
+
Dir["#{path}/*.gem"].each do |gemfile|
|
210
|
+
next if gemfile =~ /^bundler\-[\d\.]+?\.gem/
|
211
|
+
|
212
|
+
begin
|
213
|
+
s ||= Bundler.rubygems.spec_from_gem(gemfile)
|
214
|
+
rescue Gem::Package::FormatError
|
215
|
+
raise GemspecError, "Could not read gem at #{gemfile}. It may be corrupted."
|
216
|
+
end
|
217
|
+
|
218
|
+
s.source = self
|
219
|
+
idx << s
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
idx
|
224
|
+
end
|
225
|
+
|
226
|
+
def remote_specs
|
227
|
+
@remote_specs ||= begin
|
228
|
+
idx = Index.new
|
229
|
+
old = Bundler.rubygems.sources
|
230
|
+
|
231
|
+
remotes.each do |uri|
|
232
|
+
Bundler.ui.info "Fetching source index for #{uri}"
|
233
|
+
Gem.sources = ["#{uri}"]
|
234
|
+
fetch_all_remote_specs do |n,v|
|
235
|
+
v.each do |name, version, platform|
|
236
|
+
next if name == 'bundler'
|
237
|
+
spec = RemoteSpecification.new(name, version, platform, uri)
|
238
|
+
spec.source = self
|
239
|
+
@spec_fetch_map[spec.full_name] = [spec, uri]
|
240
|
+
idx << spec
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
idx
|
245
|
+
ensure
|
246
|
+
Bundler.rubygems.sources = old
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def fetch_all_remote_specs(&blk)
|
251
|
+
begin
|
252
|
+
# Fetch all specs, minus prerelease specs
|
253
|
+
Gem::SpecFetcher.new.list(true, false).each(&blk)
|
254
|
+
# Then fetch the prerelease specs
|
255
|
+
begin
|
256
|
+
Gem::SpecFetcher.new.list(false, true).each(&blk)
|
257
|
+
rescue Gem::RemoteFetcher::FetchError
|
258
|
+
Bundler.ui.warn "Could not fetch prerelease specs from #{self}"
|
259
|
+
end
|
260
|
+
rescue Gem::RemoteFetcher::FetchError
|
261
|
+
Bundler.ui.warn "Could not reach #{self}"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def download_gem_from_uri(spec, uri)
|
266
|
+
spec.fetch_platform
|
267
|
+
|
268
|
+
download_path = Bundler.requires_sudo? ? Bundler.tmp : Bundler.rubygems.gem_dir
|
269
|
+
gem_path = "#{Bundler.rubygems.gem_dir}/cache/#{spec.full_name}.gem"
|
270
|
+
|
271
|
+
FileUtils.mkdir_p("#{download_path}/cache")
|
272
|
+
Bundler.rubygems.download_gem(spec, uri, download_path)
|
273
|
+
|
274
|
+
if Bundler.requires_sudo?
|
275
|
+
sudo "mkdir -p #{Bundler.rubygems.gem_dir}/cache"
|
276
|
+
sudo "mv #{Bundler.tmp}/cache/#{spec.full_name}.gem #{gem_path}"
|
277
|
+
end
|
278
|
+
|
279
|
+
gem_path
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
class Path
|
284
|
+
attr_reader :path, :options
|
285
|
+
# Kind of a hack, but needed for the lock file parser
|
286
|
+
attr_writer :name
|
287
|
+
attr_accessor :version
|
288
|
+
|
289
|
+
DEFAULT_GLOB = "{,*,*/*}.gemspec"
|
290
|
+
|
291
|
+
def initialize(options)
|
292
|
+
@options = options
|
293
|
+
@glob = options["glob"] || DEFAULT_GLOB
|
294
|
+
|
295
|
+
@allow_cached = false
|
296
|
+
@allow_remote = false
|
297
|
+
|
298
|
+
if options["path"]
|
299
|
+
@path = Pathname.new(options["path"])
|
300
|
+
@path = @path.expand_path(Bundler.root) unless @path.relative?
|
301
|
+
end
|
302
|
+
|
303
|
+
@name = options["name"]
|
304
|
+
@version = options["version"]
|
305
|
+
end
|
306
|
+
|
307
|
+
def remote!
|
308
|
+
@allow_remote = true
|
309
|
+
end
|
310
|
+
|
311
|
+
def cached!
|
312
|
+
@allow_cached = true
|
313
|
+
end
|
314
|
+
|
315
|
+
def self.from_lock(options)
|
316
|
+
new(options.merge("path" => options.delete("remote")))
|
317
|
+
end
|
318
|
+
|
319
|
+
def to_lock
|
320
|
+
out = "PATH\n"
|
321
|
+
out << " remote: #{relative_path}\n"
|
322
|
+
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
|
323
|
+
out << " specs:\n"
|
324
|
+
end
|
325
|
+
|
326
|
+
def to_s
|
327
|
+
"source at #{@path}"
|
328
|
+
end
|
329
|
+
|
330
|
+
def hash
|
331
|
+
self.class.hash
|
332
|
+
end
|
333
|
+
|
334
|
+
def eql?(o)
|
335
|
+
o.instance_of?(Path) &&
|
336
|
+
path.expand_path(Bundler.root) == o.path.expand_path(Bundler.root) &&
|
337
|
+
version == o.version
|
338
|
+
end
|
339
|
+
|
340
|
+
alias == eql?
|
341
|
+
|
342
|
+
def name
|
343
|
+
File.basename(path.expand_path(Bundler.root).to_s)
|
344
|
+
end
|
345
|
+
|
346
|
+
def load_spec_files
|
347
|
+
index = Index.new
|
348
|
+
|
349
|
+
expanded_path = path.expand_path(Bundler.root)
|
350
|
+
|
351
|
+
if File.directory?(expanded_path)
|
352
|
+
Dir["#{expanded_path}/#{@glob}"].each do |file|
|
353
|
+
spec = Bundler.load_gemspec(file)
|
354
|
+
if spec
|
355
|
+
spec.loaded_from = file.to_s
|
356
|
+
spec.source = self
|
357
|
+
index << spec
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
if index.empty? && @name && @version
|
362
|
+
index << Gem::Specification.new do |s|
|
363
|
+
s.name = @name
|
364
|
+
s.source = self
|
365
|
+
s.version = Gem::Version.new(@version)
|
366
|
+
s.platform = Gem::Platform::RUBY
|
367
|
+
s.summary = "Fake gemspec for #{@name}"
|
368
|
+
s.relative_loaded_from = "#{@name}.gemspec"
|
369
|
+
s.authors = ["no one"]
|
370
|
+
if expanded_path.join("bin").exist?
|
371
|
+
executables = expanded_path.join("bin").children
|
372
|
+
executables.reject!{|p| File.directory?(p) }
|
373
|
+
s.executables = executables.map{|c| c.basename.to_s }
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
else
|
378
|
+
raise PathError, "The path `#{expanded_path}` does not exist."
|
379
|
+
end
|
380
|
+
|
381
|
+
index
|
382
|
+
end
|
383
|
+
|
384
|
+
def local_specs
|
385
|
+
@local_specs ||= load_spec_files
|
386
|
+
end
|
387
|
+
|
388
|
+
class Installer < Bundler::GemInstaller
|
389
|
+
def initialize(spec, options = {})
|
390
|
+
@spec = spec
|
391
|
+
@bin_dir = Bundler.requires_sudo? ? "#{Bundler.tmp}/bin" : "#{Bundler.rubygems.gem_dir}/bin"
|
392
|
+
@gem_dir = Bundler.rubygems.path(spec.full_gem_path)
|
393
|
+
@wrappers = options[:wrappers] || true
|
394
|
+
@env_shebang = options[:env_shebang] || true
|
395
|
+
@format_executable = options[:format_executable] || false
|
396
|
+
end
|
397
|
+
|
398
|
+
def generate_bin
|
399
|
+
return if spec.executables.nil? || spec.executables.empty?
|
400
|
+
|
401
|
+
if Bundler.requires_sudo?
|
402
|
+
FileUtils.mkdir_p("#{Bundler.tmp}/bin") unless File.exist?("#{Bundler.tmp}/bin")
|
403
|
+
end
|
404
|
+
super
|
405
|
+
if Bundler.requires_sudo?
|
406
|
+
Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/bin"
|
407
|
+
spec.executables.each do |exe|
|
408
|
+
Bundler.sudo "cp -R #{Bundler.tmp}/bin/#{exe} #{Bundler.rubygems.gem_dir}/bin/"
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
def install(spec)
|
415
|
+
Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
|
416
|
+
# Let's be honest, when we're working from a path, we can't
|
417
|
+
# really expect native extensions to work because the whole point
|
418
|
+
# is to just be able to modify what's in that path and go. So, let's
|
419
|
+
# not put ourselves through the pain of actually trying to generate
|
420
|
+
# the full gem.
|
421
|
+
Installer.new(spec).generate_bin
|
422
|
+
end
|
423
|
+
|
424
|
+
alias specs local_specs
|
425
|
+
|
426
|
+
def cache(spec)
|
427
|
+
unless path.expand_path(Bundler.root).to_s.index(Bundler.root.to_s) == 0
|
428
|
+
Bundler.ui.warn " * #{spec.name} at `#{path}` will not be cached."
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
private
|
433
|
+
|
434
|
+
def relative_path
|
435
|
+
if path.to_s.include?(Bundler.root.to_s)
|
436
|
+
return path.relative_path_from(Bundler.root)
|
437
|
+
end
|
438
|
+
|
439
|
+
path
|
440
|
+
end
|
441
|
+
|
442
|
+
def generate_bin(spec)
|
443
|
+
gem_dir = Pathname.new(spec.full_gem_path)
|
444
|
+
|
445
|
+
# Some gem authors put absolute paths in their gemspec
|
446
|
+
# and we have to save them from themselves
|
447
|
+
spec.files = spec.files.map do |p|
|
448
|
+
next if File.directory?(p)
|
449
|
+
begin
|
450
|
+
Pathname.new(p).relative_path_from(gem_dir).to_s
|
451
|
+
rescue ArgumentError
|
452
|
+
p
|
453
|
+
end
|
454
|
+
end.compact
|
455
|
+
|
456
|
+
gem_file = Dir.chdir(gem_dir){ Gem::Builder.new(spec).build }
|
457
|
+
|
458
|
+
installer = Installer.new(spec, :env_shebang => false)
|
459
|
+
installer.build_extensions
|
460
|
+
installer.generate_bin
|
461
|
+
rescue Gem::InvalidSpecificationException => e
|
462
|
+
Bundler.ui.warn "\n#{spec.name} at #{spec.full_gem_path} did not have a valid gemspec.\n" \
|
463
|
+
"This prevents bundler from installing bins or native extensions, but " \
|
464
|
+
"that may not affect its functionality."
|
465
|
+
|
466
|
+
if !spec.extensions.empty? && !spec.email.empty?
|
467
|
+
Bundler.ui.warn "If you need to use this package without installing it from a gem " \
|
468
|
+
"repository, please contact #{spec.email} and ask them " \
|
469
|
+
"to modify their .gemspec so it can work with `gem build`."
|
470
|
+
end
|
471
|
+
|
472
|
+
Bundler.ui.warn "The validation message from Rubygems was:\n #{e.message}"
|
473
|
+
ensure
|
474
|
+
Dir.chdir(gem_dir){ FileUtils.rm_rf(gem_file) if gem_file && File.exist?(gem_file) }
|
475
|
+
end
|
476
|
+
|
477
|
+
end
|
478
|
+
|
479
|
+
class Git < Path
|
480
|
+
attr_reader :uri, :ref, :options, :submodules
|
481
|
+
|
482
|
+
def initialize(options)
|
483
|
+
super
|
484
|
+
|
485
|
+
# stringify options that could be set as symbols
|
486
|
+
%w(ref branch tag revision).each{|k| options[k] = options[k].to_s if options[k] }
|
487
|
+
|
488
|
+
@uri = options["uri"]
|
489
|
+
@ref = options["ref"] || options["branch"] || options["tag"] || 'master'
|
490
|
+
@revision = options["revision"]
|
491
|
+
@submodules = options["submodules"]
|
492
|
+
@update = false
|
493
|
+
@installed = nil
|
494
|
+
end
|
495
|
+
|
496
|
+
def self.from_lock(options)
|
497
|
+
new(options.merge("uri" => options.delete("remote")))
|
498
|
+
end
|
499
|
+
|
500
|
+
def to_lock
|
501
|
+
out = "GIT\n"
|
502
|
+
out << " remote: #{@uri}\n"
|
503
|
+
out << " revision: #{revision}\n"
|
504
|
+
%w(ref branch tag submodules).each do |opt|
|
505
|
+
out << " #{opt}: #{options[opt]}\n" if options[opt]
|
506
|
+
end
|
507
|
+
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
|
508
|
+
out << " specs:\n"
|
509
|
+
end
|
510
|
+
|
511
|
+
def eql?(o)
|
512
|
+
Git === o &&
|
513
|
+
uri == o.uri &&
|
514
|
+
ref == o.ref &&
|
515
|
+
name == o.name &&
|
516
|
+
version == o.version &&
|
517
|
+
submodules == o.submodules
|
518
|
+
end
|
519
|
+
|
520
|
+
alias == eql?
|
521
|
+
|
522
|
+
def to_s
|
523
|
+
sref = options["ref"] ? shortref_for_display(options["ref"]) : ref
|
524
|
+
"#{uri} (at #{sref})"
|
525
|
+
end
|
526
|
+
|
527
|
+
def name
|
528
|
+
File.basename(@uri, '.git')
|
529
|
+
end
|
530
|
+
|
531
|
+
def path
|
532
|
+
@install_path ||= begin
|
533
|
+
git_scope = "#{base_name}-#{shortref_for_path(revision)}"
|
534
|
+
|
535
|
+
if Bundler.requires_sudo?
|
536
|
+
Bundler.user_bundle_path.join(Bundler.ruby_scope).join(git_scope)
|
537
|
+
else
|
538
|
+
Bundler.install_path.join(git_scope)
|
539
|
+
end
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
def unlock!
|
544
|
+
@revision = nil
|
545
|
+
end
|
546
|
+
|
547
|
+
# TODO: actually cache git specs
|
548
|
+
def specs
|
549
|
+
if allow_git_ops? && !@update
|
550
|
+
# Start by making sure the git cache is up to date
|
551
|
+
cache
|
552
|
+
checkout
|
553
|
+
@update = true
|
554
|
+
end
|
555
|
+
local_specs
|
556
|
+
end
|
557
|
+
|
558
|
+
def install(spec)
|
559
|
+
Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
|
560
|
+
|
561
|
+
unless @installed
|
562
|
+
Bundler.ui.debug " * Checking out revision: #{ref}"
|
563
|
+
checkout if allow_git_ops?
|
564
|
+
@installed = true
|
565
|
+
end
|
566
|
+
generate_bin(spec)
|
567
|
+
end
|
568
|
+
|
569
|
+
def load_spec_files
|
570
|
+
super
|
571
|
+
rescue PathError, GitError
|
572
|
+
raise GitError, "#{to_s} is not checked out. Please run `bundle install`"
|
573
|
+
end
|
574
|
+
|
575
|
+
private
|
576
|
+
|
577
|
+
def git(command)
|
578
|
+
if allow_git_ops?
|
579
|
+
out = %x{git #{command}}
|
580
|
+
|
581
|
+
if $?.exitstatus != 0
|
582
|
+
msg = "Git error: command `git #{command}` in directory #{Dir.pwd} has failed."
|
583
|
+
msg << "\nIf this error persists you could try removing the cache directory '#{cache_path}'" if cached?
|
584
|
+
raise GitError, msg
|
585
|
+
end
|
586
|
+
out
|
587
|
+
else
|
588
|
+
raise GitError, "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, " \
|
589
|
+
"this error message could probably be more useful. Please submit a ticket at http://github.com/carlhuda/bundler/issues " \
|
590
|
+
"with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
def base_name
|
595
|
+
File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)?(//\w*/)?(\w*/)*},''),".git")
|
596
|
+
end
|
597
|
+
|
598
|
+
def shortref_for_display(ref)
|
599
|
+
ref[0..6]
|
600
|
+
end
|
601
|
+
|
602
|
+
def shortref_for_path(ref)
|
603
|
+
ref[0..11]
|
604
|
+
end
|
605
|
+
|
606
|
+
def uri_hash
|
607
|
+
if uri =~ %r{^\w+://(\w+@)?}
|
608
|
+
# Downcase the domain component of the URI
|
609
|
+
# and strip off a trailing slash, if one is present
|
610
|
+
input = URI.parse(uri).normalize.to_s.sub(%r{/$},'')
|
611
|
+
else
|
612
|
+
# If there is no URI scheme, assume it is an ssh/git URI
|
613
|
+
input = uri
|
614
|
+
end
|
615
|
+
Digest::SHA1.hexdigest(input)
|
616
|
+
end
|
617
|
+
|
618
|
+
# Escape the URI for git commands
|
619
|
+
def uri_escaped
|
620
|
+
if Bundler::WINDOWS
|
621
|
+
# Windows quoting requires double quotes only, with double quotes
|
622
|
+
# inside the string escaped by being doubled.
|
623
|
+
'"' + uri.gsub('"') {|s| '""'} + '"'
|
624
|
+
else
|
625
|
+
# Bash requires single quoted strings, with the single quotes escaped
|
626
|
+
# by ending the string, escaping the quote, and restarting the string.
|
627
|
+
"'" + uri.gsub("'") {|s| "'\\''"} + "'"
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
def cache_path
|
632
|
+
@cache_path ||= begin
|
633
|
+
git_scope = "#{base_name}-#{uri_hash}"
|
634
|
+
|
635
|
+
if Bundler.requires_sudo?
|
636
|
+
Bundler.user_bundle_path.join("cache/git", git_scope)
|
637
|
+
else
|
638
|
+
Bundler.cache.join("git", git_scope)
|
639
|
+
end
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
def cache
|
644
|
+
if cached?
|
645
|
+
return if has_revision_cached?
|
646
|
+
Bundler.ui.info "Updating #{uri}"
|
647
|
+
in_cache do
|
648
|
+
git %|fetch --force --quiet --tags #{uri_escaped} "refs/heads/*:refs/heads/*"|
|
649
|
+
end
|
650
|
+
else
|
651
|
+
Bundler.ui.info "Fetching #{uri}"
|
652
|
+
FileUtils.mkdir_p(cache_path.dirname)
|
653
|
+
git %|clone #{uri_escaped} "#{cache_path}" --bare --no-hardlinks|
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
def checkout
|
658
|
+
unless File.exist?(path.join(".git"))
|
659
|
+
FileUtils.mkdir_p(path.dirname)
|
660
|
+
FileUtils.rm_rf(path)
|
661
|
+
git %|clone --no-checkout "#{cache_path}" "#{path}"|
|
662
|
+
File.chmod((0777 & ~File.umask), path)
|
663
|
+
end
|
664
|
+
Dir.chdir(path) do
|
665
|
+
git %|fetch --force --quiet --tags "#{cache_path}"|
|
666
|
+
git "reset --hard #{revision}"
|
667
|
+
|
668
|
+
if @submodules
|
669
|
+
git "submodule init"
|
670
|
+
git "submodule update"
|
671
|
+
end
|
672
|
+
end
|
673
|
+
end
|
674
|
+
|
675
|
+
def has_revision_cached?
|
676
|
+
return unless @revision
|
677
|
+
in_cache { git %|cat-file -e #{@revision}| }
|
678
|
+
true
|
679
|
+
rescue GitError
|
680
|
+
false
|
681
|
+
end
|
682
|
+
|
683
|
+
def allow_git_ops?
|
684
|
+
@allow_remote || @allow_cached
|
685
|
+
end
|
686
|
+
|
687
|
+
def revision
|
688
|
+
@revision ||= begin
|
689
|
+
if allow_git_ops?
|
690
|
+
in_cache { git("rev-parse #{ref}").strip }
|
691
|
+
else
|
692
|
+
raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application"
|
693
|
+
end
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
697
|
+
def cached?
|
698
|
+
cache_path.exist?
|
699
|
+
end
|
700
|
+
|
701
|
+
def in_cache(&blk)
|
702
|
+
cache unless cached?
|
703
|
+
Dir.chdir(cache_path, &blk)
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
end
|
708
|
+
end
|