bundler 0.9.26 → 1.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bundler might be problematic. Click here for more details.
- data/CHANGELOG.md +7 -0
- data/README.md +3 -1
- data/ROADMAP.md +19 -34
- data/TODO.md +16 -0
- data/lib/bundler.rb +87 -41
- data/lib/bundler/cli.rb +115 -73
- data/lib/bundler/definition.rb +203 -34
- data/lib/bundler/dependency.rb +77 -0
- data/lib/bundler/dsl.rb +57 -16
- data/lib/bundler/environment.rb +13 -109
- data/lib/bundler/graph.rb +130 -0
- data/lib/bundler/index.rb +19 -41
- data/lib/bundler/installer.rb +22 -58
- data/lib/bundler/lazy_specification.rb +67 -0
- data/lib/bundler/lockfile_parser.rb +100 -0
- data/lib/bundler/remote_specification.rb +2 -1
- data/lib/bundler/resolver.rb +185 -45
- data/lib/bundler/rubygems_ext.rb +105 -6
- data/lib/bundler/runtime.rb +24 -82
- data/lib/bundler/settings.rb +9 -5
- data/lib/bundler/setup.rb +6 -14
- data/lib/bundler/shared_helpers.rb +2 -25
- data/lib/bundler/source.rb +306 -132
- data/lib/bundler/spec_set.rb +77 -17
- data/lib/bundler/templates/Executable +28 -0
- data/lib/bundler/vendor/thor.rb +31 -4
- data/lib/bundler/vendor/thor/invocation.rb +1 -1
- data/lib/bundler/vendor/thor/parser/arguments.rb +14 -3
- data/lib/bundler/vendor/thor/parser/options.rb +0 -5
- data/lib/bundler/vendor/thor/shell/basic.rb +28 -0
- data/lib/bundler/vendor/thor/task.rb +6 -6
- data/lib/bundler/vendor/thor/util.rb +13 -14
- data/lib/bundler/vendor/thor/version.rb +1 -1
- data/lib/bundler/version.rb +1 -1
- metadata +11 -5
- data/lib/bundler/templates/environment.erb +0 -91
data/lib/bundler/rubygems_ext.rb
CHANGED
@@ -1,13 +1,79 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
if defined?(Gem::QuickLoader)
|
4
|
+
# Gem Prelude makes me a sad panda :'(
|
5
|
+
Gem::QuickLoader.load_full_rubygems_library
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'rubygems/specification'
|
10
|
+
|
11
|
+
module Bundler
|
12
|
+
class DepProxy
|
13
|
+
|
14
|
+
attr_reader :required_by, :__platform, :dep
|
15
|
+
|
16
|
+
def initialize(dep, platform)
|
17
|
+
@dep, @__platform, @required_by = dep, platform, []
|
18
|
+
end
|
19
|
+
|
20
|
+
def hash
|
21
|
+
@hash ||= dep.hash
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(o)
|
25
|
+
dep == o.dep && __platform == o.__platform
|
26
|
+
end
|
27
|
+
|
28
|
+
alias eql? ==
|
29
|
+
|
30
|
+
def type
|
31
|
+
@dep.type
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
@dep.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def method_missing(*args)
|
41
|
+
@dep.send(*args)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
4
45
|
end
|
5
46
|
|
6
47
|
module Gem
|
7
48
|
@loaded_stacks = Hash.new { |h,k| h[k] = [] }
|
8
49
|
|
50
|
+
module MatchPlatform
|
51
|
+
def match_platform(p)
|
52
|
+
Gem::Platform::RUBY == platform or
|
53
|
+
platform.nil? or p == platform or
|
54
|
+
Gem::Platform.new(platform).to_generic == p
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
9
58
|
class Specification
|
10
|
-
attr_accessor :source, :location
|
59
|
+
attr_accessor :source, :location, :relative_loaded_from
|
60
|
+
|
61
|
+
alias_method :rg_full_gem_path, :full_gem_path
|
62
|
+
alias_method :rg_loaded_from, :loaded_from
|
63
|
+
|
64
|
+
include MatchPlatform
|
65
|
+
|
66
|
+
def full_gem_path
|
67
|
+
source.respond_to?(:path) ?
|
68
|
+
Pathname.new(loaded_from).dirname.expand_path.to_s :
|
69
|
+
rg_full_gem_path
|
70
|
+
end
|
71
|
+
|
72
|
+
def loaded_from
|
73
|
+
relative_loaded_from ?
|
74
|
+
source.path.join(relative_loaded_from).to_s :
|
75
|
+
rg_loaded_from
|
76
|
+
end
|
11
77
|
|
12
78
|
def load_paths
|
13
79
|
require_paths.map do |require_path|
|
@@ -26,8 +92,7 @@ module Gem
|
|
26
92
|
def git_version
|
27
93
|
if @loaded_from && File.exist?(File.join(full_gem_path, ".git"))
|
28
94
|
sha = Dir.chdir(full_gem_path){ `git rev-parse HEAD`.strip }
|
29
|
-
|
30
|
-
(branch && branch != sha) ? " #{branch}-#{sha[0...6]}" : " #{sha[0...6]}"
|
95
|
+
" #{sha[0..6]}"
|
31
96
|
end
|
32
97
|
end
|
33
98
|
|
@@ -79,8 +144,42 @@ module Gem
|
|
79
144
|
class Dependency
|
80
145
|
attr_accessor :source, :groups
|
81
146
|
|
147
|
+
alias eql? ==
|
148
|
+
|
82
149
|
def to_yaml_properties
|
83
150
|
instance_variables.reject { |p| ["@source", "@groups"].include?(p.to_s) }
|
84
151
|
end
|
152
|
+
|
153
|
+
def to_lock
|
154
|
+
out = " #{name}"
|
155
|
+
unless requirement == Gem::Requirement.default
|
156
|
+
out << " (#{requirement.to_s})"
|
157
|
+
end
|
158
|
+
out
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class Platform
|
163
|
+
JAVA = Gem::Platform.new('java')
|
164
|
+
MSWIN = Gem::Platform.new('mswin32')
|
165
|
+
MING = Gem::Platform.new('x86-mingw32')
|
166
|
+
|
167
|
+
GENERIC_CACHE = {}
|
168
|
+
|
169
|
+
class << RUBY
|
170
|
+
def to_generic ; self ; end
|
171
|
+
end
|
172
|
+
|
173
|
+
GENERICS = [JAVA, MSWIN, MING, RUBY]
|
174
|
+
|
175
|
+
def hash
|
176
|
+
@cpu.hash + @os.hash + @version.hash
|
177
|
+
end
|
178
|
+
|
179
|
+
alias eql? ==
|
180
|
+
|
181
|
+
def to_generic
|
182
|
+
GENERIC_CACHE[self] ||= GENERICS.find { |p| self =~ p } || RUBY
|
183
|
+
end
|
85
184
|
end
|
86
185
|
end
|
data/lib/bundler/runtime.rb
CHANGED
@@ -6,17 +6,16 @@ module Bundler
|
|
6
6
|
|
7
7
|
def initialize(*)
|
8
8
|
super
|
9
|
-
|
9
|
+
lock
|
10
10
|
end
|
11
11
|
|
12
12
|
def setup(*groups)
|
13
13
|
# Has to happen first
|
14
14
|
clean_load_path
|
15
15
|
|
16
|
-
specs = groups.any? ? specs_for(groups) : requested_specs
|
16
|
+
specs = groups.any? ? @definition.specs_for(groups) : requested_specs
|
17
17
|
|
18
18
|
cripple_rubygems(specs)
|
19
|
-
replace_rubygems_paths
|
20
19
|
|
21
20
|
# Activate the specs
|
22
21
|
specs.each do |spec|
|
@@ -35,37 +34,27 @@ module Bundler
|
|
35
34
|
def require(*groups)
|
36
35
|
groups.map! { |g| g.to_sym }
|
37
36
|
groups = [:default] if groups.empty?
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
37
|
+
|
38
|
+
@definition.dependencies.each do |dep|
|
39
|
+
# Skip the dependency if it is not in any of the requested
|
40
|
+
# groups
|
41
|
+
next unless (dep.groups & groups).any?
|
42
|
+
|
43
|
+
begin
|
44
|
+
# Loop through all the specified autorequires for the
|
45
|
+
# dependency. If there are none, use the dependency's name
|
46
|
+
# as the autorequire.
|
47
|
+
Array(dep.autorequire || dep.name).each do |file|
|
48
|
+
Kernel.require file
|
49
49
|
end
|
50
|
+
rescue LoadError
|
51
|
+
# Only let a LoadError through if the autorequire was explicitly
|
52
|
+
# specified by the user.
|
53
|
+
raise if dep.autorequire
|
50
54
|
end
|
51
55
|
end
|
52
56
|
end
|
53
57
|
|
54
|
-
def dependencies
|
55
|
-
@definition.dependencies
|
56
|
-
end
|
57
|
-
|
58
|
-
def actual_dependencies
|
59
|
-
@definition.actual_dependencies
|
60
|
-
end
|
61
|
-
|
62
|
-
def lock
|
63
|
-
sources.each { |s| s.lock if s.respond_to?(:lock) }
|
64
|
-
FileUtils.mkdir_p("#{root}/.bundle")
|
65
|
-
write_yml_lock
|
66
|
-
write_rb_lock
|
67
|
-
end
|
68
|
-
|
69
58
|
def dependencies_for(*groups)
|
70
59
|
if groups.empty?
|
71
60
|
dependencies
|
@@ -79,78 +68,31 @@ module Bundler
|
|
79
68
|
def cache
|
80
69
|
FileUtils.mkdir_p(cache_path)
|
81
70
|
|
82
|
-
Bundler.ui.info "
|
71
|
+
Bundler.ui.info "Updating .gem files in vendor/cache"
|
83
72
|
specs.each do |spec|
|
84
|
-
|
85
|
-
possibilities = Gem.path.map { |p| "#{p.to_s}/cache/#{spec.full_name}.gem" }
|
86
|
-
cached_path = possibilities.find { |p| File.exist? p }
|
87
|
-
raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
|
88
|
-
Bundler.ui.info " * #{File.basename(cached_path)}"
|
89
|
-
next if File.expand_path(File.dirname(cached_path)) == File.expand_path(cache_path)
|
90
|
-
FileUtils.cp(cached_path, cache_path)
|
73
|
+
spec.source.cache(spec) if spec.source.respond_to?(:cache)
|
91
74
|
end
|
92
75
|
end
|
93
76
|
|
94
77
|
def prune_cache
|
95
78
|
FileUtils.mkdir_p(cache_path)
|
79
|
+
|
96
80
|
Bundler.ui.info "Removing outdated .gem files from vendor/cache"
|
97
81
|
Pathname.glob(cache_path.join("*.gem").to_s).each do |gem_path|
|
98
|
-
cached_spec = Gem::Format.from_file_by_path(gem_path
|
82
|
+
cached_spec = Gem::Format.from_file_by_path(gem_path).spec
|
99
83
|
next unless Gem::Platform.match(cached_spec.platform)
|
100
84
|
unless specs.any?{|s| s.full_name == cached_spec.full_name }
|
101
|
-
Bundler.ui.info " * #{File.basename(gem_path
|
85
|
+
Bundler.ui.info " * #{File.basename(gem_path)}"
|
102
86
|
gem_path.rmtree
|
103
87
|
end
|
104
88
|
end
|
105
89
|
end
|
106
90
|
|
107
|
-
|
108
|
-
|
109
|
-
def load_paths
|
110
|
-
specs.map { |s| s.load_paths }.flatten
|
111
|
-
end
|
91
|
+
private
|
112
92
|
|
113
93
|
def cache_path
|
114
94
|
root.join("vendor/cache")
|
115
95
|
end
|
116
96
|
|
117
|
-
def write_yml_lock
|
118
|
-
yml = details.to_yaml
|
119
|
-
File.open("#{root}/Gemfile.lock", 'w') do |f|
|
120
|
-
f.puts yml
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def details
|
125
|
-
details = {}
|
126
|
-
details["hash"] = gemfile_fingerprint
|
127
|
-
details["sources"] = sources.map { |s| { s.class.name.split("::").last => s.options} }
|
128
|
-
|
129
|
-
details["specs"] = specs.map do |s|
|
130
|
-
options = {"version" => s.version.to_s}
|
131
|
-
options["source"] = sources.index(s.source) if sources.include?(s.source)
|
132
|
-
{ s.name => options }
|
133
|
-
end
|
134
|
-
|
135
|
-
details["dependencies"] = @definition.dependencies.inject({}) do |h,d|
|
136
|
-
info = {"version" => d.requirement.to_s, "group" => d.groups}
|
137
|
-
info.merge!("require" => d.autorequire) if d.autorequire
|
138
|
-
h.merge(d.name => info)
|
139
|
-
end
|
140
|
-
details
|
141
|
-
end
|
142
|
-
|
143
|
-
def replace_rubygems_paths
|
144
|
-
Gem.instance_eval do
|
145
|
-
def path
|
146
|
-
[Bundler.bundle_path.to_s]
|
147
|
-
end
|
148
|
-
|
149
|
-
def source_index
|
150
|
-
@source_index ||= Gem::SourceIndex.from_installed_gems
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
97
|
end
|
156
98
|
end
|
data/lib/bundler/settings.rb
CHANGED
@@ -12,16 +12,20 @@ module Bundler
|
|
12
12
|
|
13
13
|
def []=(key, value)
|
14
14
|
key = "BUNDLE_#{key.to_s.upcase}"
|
15
|
-
@config[key]
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
unless @config[key] == value
|
16
|
+
@config[key] = value
|
17
|
+
FileUtils.mkdir_p(config_file.dirname)
|
18
|
+
File.open(config_file, 'w') do |f|
|
19
|
+
f.puts @config.to_yaml
|
20
|
+
end
|
19
21
|
end
|
20
22
|
value
|
21
23
|
end
|
22
24
|
|
23
25
|
def without=(array)
|
24
|
-
|
26
|
+
unless array.empty? && without.empty?
|
27
|
+
self[:without] = array.join(":")
|
28
|
+
end
|
25
29
|
end
|
26
30
|
|
27
31
|
def without
|
data/lib/bundler/setup.rb
CHANGED
@@ -1,20 +1,12 @@
|
|
1
|
-
# This is not actually required by the actual library
|
2
|
-
# loads the bundled environment
|
3
1
|
require 'bundler/shared_helpers'
|
4
2
|
|
5
3
|
if Bundler::SharedHelpers.in_bundle?
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
begin
|
13
|
-
Bundler.setup
|
14
|
-
rescue Bundler::BundlerError => e
|
15
|
-
puts "\e[31m#{e.message}\e[0m"
|
16
|
-
exit e.status_code
|
17
|
-
end
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
puts "\e[31m#{e.message}\e[0m"
|
9
|
+
exit e.status_code
|
18
10
|
end
|
19
11
|
|
20
12
|
# Add bundler to the load path after disabling system gems
|
@@ -13,27 +13,6 @@ module Gem
|
|
13
13
|
end
|
14
14
|
|
15
15
|
module Bundler
|
16
|
-
class Specification < Gem::Specification
|
17
|
-
attr_accessor :relative_loaded_from
|
18
|
-
|
19
|
-
def self.from_gemspec(gemspec)
|
20
|
-
spec = allocate
|
21
|
-
gemspec.instance_variables.each do |ivar|
|
22
|
-
spec.instance_variable_set(ivar, gemspec.instance_variable_get(ivar))
|
23
|
-
end
|
24
|
-
spec
|
25
|
-
end
|
26
|
-
|
27
|
-
def loaded_from
|
28
|
-
return super unless relative_loaded_from
|
29
|
-
source.path.join(relative_loaded_from).to_s
|
30
|
-
end
|
31
|
-
|
32
|
-
def full_gem_path
|
33
|
-
Pathname.new(loaded_from).dirname.expand_path.to_s
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
16
|
module SharedHelpers
|
38
17
|
attr_accessor :gem_loaded
|
39
18
|
|
@@ -47,10 +26,6 @@ module Bundler
|
|
47
26
|
find_gemfile
|
48
27
|
end
|
49
28
|
|
50
|
-
def env_file
|
51
|
-
default_gemfile.dirname.join(".bundle/environment.rb")
|
52
|
-
end
|
53
|
-
|
54
29
|
private
|
55
30
|
|
56
31
|
def find_gemfile
|
@@ -160,6 +135,8 @@ module Bundler
|
|
160
135
|
gem_from_path_bin = File.join(File.dirname(spec.loaded_from), spec.bindir, exec_name)
|
161
136
|
File.exist?(gem_bin) ? gem_bin : gem_from_path_bin
|
162
137
|
end
|
138
|
+
|
139
|
+
Gem.clear_paths
|
163
140
|
end
|
164
141
|
|
165
142
|
extend self
|
data/lib/bundler/source.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "uri"
|
2
|
+
require "rubygems/installer"
|
2
3
|
require "rubygems/spec_fetcher"
|
3
4
|
require "rubygems/format"
|
4
5
|
require "digest/sha1"
|
@@ -6,178 +7,245 @@ require "open3"
|
|
6
7
|
|
7
8
|
module Bundler
|
8
9
|
module Source
|
10
|
+
# TODO: Refactor this class
|
9
11
|
class Rubygems
|
10
|
-
attr_reader :
|
12
|
+
attr_reader :remotes
|
11
13
|
|
12
14
|
def initialize(options = {})
|
13
15
|
@options = options
|
14
|
-
@
|
15
|
-
@
|
16
|
-
|
17
|
-
|
16
|
+
@remotes = (options["remotes"] || []).map { |r| normalize_uri(r) }
|
17
|
+
@allow_remote = false
|
18
|
+
# Hardcode the paths for now
|
19
|
+
@installed = {}
|
20
|
+
@caches = [ Bundler.app_cache ] + Gem.path.map { |p| File.expand_path("#{p}/cache") }
|
21
|
+
@spec_fetch_map = {}
|
18
22
|
end
|
19
23
|
|
20
|
-
def
|
21
|
-
|
24
|
+
def remote!
|
25
|
+
@allow_remote = true
|
22
26
|
end
|
23
27
|
|
24
|
-
def
|
25
|
-
|
28
|
+
def [](spec)
|
29
|
+
installed_specs[spec].first ||
|
30
|
+
@allow_remote && (
|
31
|
+
cached_specs[spec].first ||
|
32
|
+
remote_specs[spec].first)
|
26
33
|
end
|
27
34
|
|
28
|
-
def
|
29
|
-
|
30
|
-
spec.fetch_platform
|
31
|
-
Gem::RemoteFetcher.fetcher.download(spec, uri, Gem.dir)
|
35
|
+
def hash
|
36
|
+
Rubygems.hash
|
32
37
|
end
|
33
38
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
:install_dir => Gem.dir,
|
38
|
-
:ignore_dependencies => true,
|
39
|
-
:wrappers => true,
|
40
|
-
:env_shebang => true,
|
41
|
-
:bin_dir => "#{Gem.dir}/bin"
|
39
|
+
def eql?(o)
|
40
|
+
Rubygems === o
|
41
|
+
end
|
42
42
|
|
43
|
-
|
43
|
+
alias == eql?
|
44
44
|
|
45
|
-
|
45
|
+
# Not really needed, but it seems good to implement this method for interface
|
46
|
+
# consistency. Source name is mostly used to identify Path & Git sources
|
47
|
+
def name
|
48
|
+
":gems"
|
46
49
|
end
|
47
50
|
|
48
|
-
|
51
|
+
def options
|
52
|
+
{ "remotes" => @remotes.map { |r| r.to_s } }
|
53
|
+
end
|
49
54
|
|
50
|
-
def
|
51
|
-
|
55
|
+
def self.from_lock(options)
|
56
|
+
s = new(options)
|
57
|
+
Array(options["remote"]).each { |r| s.add_remote(r) }
|
58
|
+
s
|
52
59
|
end
|
53
60
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
|
61
|
+
def to_lock
|
62
|
+
out = "GEM\n"
|
63
|
+
out << remotes.map {|r| " remote: #{r}\n" }.join
|
64
|
+
out << " specs:\n"
|
65
|
+
end
|
58
66
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
spec.source = self
|
64
|
-
index << spec
|
65
|
-
end
|
66
|
-
end
|
67
|
+
def to_s
|
68
|
+
remotes = self.remotes.map { |r| r.to_s }.join(', ')
|
69
|
+
"rubygems repository #{remotes}"
|
70
|
+
end
|
67
71
|
|
68
|
-
|
69
|
-
|
70
|
-
Gem.sources = old
|
72
|
+
def specs
|
73
|
+
@specs ||= @allow_remote ? fetch_specs : installed_specs
|
71
74
|
end
|
72
75
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
# Then fetch the prerelease specs
|
77
|
-
begin
|
78
|
-
Gem::SpecFetcher.new.list(false, true).each(&blk)
|
79
|
-
rescue Gem::RemoteFetcher::FetchError
|
80
|
-
Bundler.ui.warn "Could not fetch prerelease specs from #{self}"
|
81
|
-
end
|
76
|
+
def fetch(spec)
|
77
|
+
action = @spec_fetch_map[spec.full_name]
|
78
|
+
action.call if action
|
82
79
|
end
|
83
|
-
end
|
84
80
|
|
85
|
-
|
86
|
-
|
87
|
-
@specs ||= begin
|
88
|
-
index = Index.new
|
81
|
+
def install(spec)
|
82
|
+
path = cached_gem(spec)
|
89
83
|
|
90
|
-
|
91
|
-
|
84
|
+
if @installed[spec.full_name]
|
85
|
+
Bundler.ui.info "Using #{spec.name} (#{spec.version}) "
|
86
|
+
return
|
87
|
+
else
|
88
|
+
Bundler.ui.info "Installing #{spec.name} (#{spec.version}) "
|
89
|
+
end
|
92
90
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
91
|
+
install_path = Bundler.requires_sudo? ? Bundler.tmp : Gem.dir
|
92
|
+
installer = Gem::Installer.new path,
|
93
|
+
:install_dir => install_path,
|
94
|
+
:ignore_dependencies => true,
|
95
|
+
:wrappers => true,
|
96
|
+
:env_shebang => true,
|
97
|
+
:bin_dir => "#{install_path}/bin"
|
98
|
+
installer.install
|
98
99
|
|
99
|
-
|
100
|
+
# SUDO HAX
|
101
|
+
if Bundler.requires_sudo?
|
102
|
+
sudo "mkdir -p #{Gem.dir}/gems #{Gem.dir}/specifications"
|
103
|
+
sudo "mv #{Bundler.tmp}/gems/#{spec.full_name} #{Gem.dir}/gems/"
|
104
|
+
sudo "mv #{Bundler.tmp}/specifications/#{spec.full_name}.gemspec #{Gem.dir}/specifications/"
|
100
105
|
end
|
106
|
+
|
107
|
+
spec.loaded_from = "#{Gem.dir}/specifications/#{spec.full_name}.gemspec"
|
101
108
|
end
|
102
109
|
|
103
|
-
def
|
104
|
-
|
110
|
+
def sudo(str)
|
111
|
+
`sudo -E #{str}`
|
105
112
|
end
|
106
113
|
|
107
|
-
def
|
108
|
-
|
114
|
+
def cache(spec)
|
115
|
+
cached_path = cached_gem(spec)
|
116
|
+
raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
|
117
|
+
return if File.dirname(cached_path) == Bundler.app_cache.to_s
|
118
|
+
Bundler.ui.info " * #{File.basename(cached_path)}"
|
119
|
+
FileUtils.cp(cached_path, Bundler.app_cache)
|
109
120
|
end
|
110
|
-
end
|
111
121
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
index = Index.new
|
122
|
+
def add_remote(source)
|
123
|
+
@remotes << normalize_uri(source)
|
124
|
+
end
|
116
125
|
|
117
|
-
|
118
|
-
bundle_index.to_a.reverse.each do |name, spec|
|
119
|
-
spec.source = self
|
120
|
-
index << spec
|
121
|
-
end
|
126
|
+
private
|
122
127
|
|
123
|
-
|
124
|
-
|
128
|
+
def cached_gem(spec)
|
129
|
+
possibilities = @caches.map { |p| "#{p}/#{spec.full_name}.gem" }
|
130
|
+
possibilities.find { |p| File.exist?(p) }
|
125
131
|
end
|
126
132
|
|
127
|
-
def
|
128
|
-
|
133
|
+
def normalize_uri(uri)
|
134
|
+
uri = uri.to_s
|
135
|
+
uri = "#{uri}/" unless uri =~ %r'/$'
|
136
|
+
uri = URI(uri)
|
137
|
+
raise ArgumentError, "The source must be an absolute URI" unless uri.absolute?
|
138
|
+
uri
|
129
139
|
end
|
130
|
-
end
|
131
|
-
|
132
|
-
class GemCache
|
133
|
-
attr_reader :options
|
134
140
|
|
135
|
-
def
|
136
|
-
|
137
|
-
|
141
|
+
def fetch_specs
|
142
|
+
Index.build do |idx|
|
143
|
+
idx.use installed_specs
|
144
|
+
idx.use cached_specs
|
145
|
+
idx.use remote_specs
|
146
|
+
end
|
138
147
|
end
|
139
148
|
|
140
|
-
def
|
141
|
-
|
149
|
+
def installed_specs
|
150
|
+
@installed_specs ||= begin
|
151
|
+
idx = Index.new
|
152
|
+
Gem::SourceIndex.from_installed_gems.to_a.reverse.each do |name, spec|
|
153
|
+
@installed[spec.full_name] = true
|
154
|
+
spec.source = self
|
155
|
+
idx << spec
|
156
|
+
end
|
157
|
+
idx
|
158
|
+
end
|
142
159
|
end
|
143
160
|
|
144
|
-
def
|
145
|
-
@
|
146
|
-
|
161
|
+
def cached_specs
|
162
|
+
@cached_specs ||= begin
|
163
|
+
idx = Index.new
|
164
|
+
@caches.each do |path|
|
165
|
+
Dir["#{path}/*.gem"].each do |gemfile|
|
166
|
+
s = Gem::Format.from_file_by_path(gemfile).spec
|
167
|
+
s.source = self
|
168
|
+
idx << s
|
169
|
+
end
|
170
|
+
end
|
171
|
+
idx
|
172
|
+
end
|
173
|
+
end
|
147
174
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
175
|
+
def remote_specs
|
176
|
+
@remote_specs ||= begin
|
177
|
+
idx = Index.new
|
178
|
+
remotes = self.remotes.map { |uri| uri.to_s }
|
179
|
+
old = Gem.sources
|
180
|
+
|
181
|
+
remotes.each do |uri|
|
182
|
+
Bundler.ui.info "Fetching source index for #{uri}"
|
183
|
+
Gem.sources = ["#{uri}"]
|
184
|
+
fetch_all_remote_specs do |n,v|
|
185
|
+
v.each do |name, version, platform|
|
186
|
+
spec = RemoteSpecification.new(name, version, platform, uri)
|
187
|
+
spec.source = self
|
188
|
+
# Temporary hack until this can be figured out better
|
189
|
+
@spec_fetch_map[spec.full_name] = lambda do
|
190
|
+
path = download_gem_from_uri(spec, uri)
|
191
|
+
s = Gem::Format.from_file_by_path(path).spec
|
192
|
+
spec.__swap__(s)
|
193
|
+
end
|
194
|
+
idx << spec
|
195
|
+
end
|
196
|
+
end
|
153
197
|
end
|
198
|
+
idx
|
199
|
+
ensure
|
200
|
+
Gem.sources = old
|
201
|
+
end
|
202
|
+
end
|
154
203
|
|
155
|
-
|
204
|
+
def fetch_all_remote_specs(&blk)
|
205
|
+
begin
|
206
|
+
# Fetch all specs, minus prerelease specs
|
207
|
+
Gem::SpecFetcher.new.list(true, false).each(&blk)
|
208
|
+
# Then fetch the prerelease specs
|
209
|
+
begin
|
210
|
+
Gem::SpecFetcher.new.list(false, true).each(&blk)
|
211
|
+
rescue Gem::RemoteFetcher::FetchError
|
212
|
+
Bundler.ui.warn "Could not fetch prerelease specs from #{self}"
|
213
|
+
end
|
214
|
+
rescue Gem::RemoteFetcher::FetchError
|
215
|
+
Bundler.ui.warn "Could not reach #{self}"
|
156
216
|
end
|
157
217
|
end
|
158
218
|
|
159
|
-
def
|
160
|
-
|
219
|
+
def download_gem_from_uri(spec, uri)
|
220
|
+
spec.fetch_platform
|
161
221
|
|
162
|
-
Bundler.
|
163
|
-
|
164
|
-
:install_dir => Gem.dir,
|
165
|
-
:ignore_dependencies => true,
|
166
|
-
:wrappers => true,
|
167
|
-
:env_shebang => true,
|
168
|
-
:bin_dir => "#{Gem.dir}/bin"
|
222
|
+
download_path = Bundler.requires_sudo? ? Bundler.tmp : Gem.dir
|
223
|
+
gem_path = "#{Gem.dir}/cache/#{spec.full_name}.gem"
|
169
224
|
|
170
|
-
|
171
|
-
spec
|
225
|
+
FileUtils.mkdir_p("#{download_path}/cache")
|
226
|
+
Gem::RemoteFetcher.fetcher.download(spec, uri, download_path)
|
227
|
+
|
228
|
+
if Bundler.requires_sudo?
|
229
|
+
sudo "mkdir -p #{Gem.dir}/cache"
|
230
|
+
sudo "mv #{Bundler.tmp}/cache/#{spec.full_name}.gem #{gem_path}"
|
231
|
+
end
|
232
|
+
|
233
|
+
gem_path
|
172
234
|
end
|
173
235
|
end
|
174
236
|
|
175
237
|
class Path
|
176
238
|
attr_reader :path, :options
|
239
|
+
# Kind of a hack, but needed for the lock file parser
|
240
|
+
attr_accessor :name, :version
|
241
|
+
|
242
|
+
DEFAULT_GLOB = "{,*/}*.gemspec"
|
177
243
|
|
178
244
|
def initialize(options)
|
179
245
|
@options = options
|
180
|
-
@glob = options["glob"] ||
|
246
|
+
@glob = options["glob"] || DEFAULT_GLOB
|
247
|
+
|
248
|
+
@allow_remote = false
|
181
249
|
|
182
250
|
if options["path"]
|
183
251
|
@path = Pathname.new(options["path"]).expand_path(Bundler.root)
|
@@ -187,8 +255,40 @@ module Bundler
|
|
187
255
|
@version = options["version"]
|
188
256
|
end
|
189
257
|
|
258
|
+
def remote!
|
259
|
+
@allow_remote = true
|
260
|
+
end
|
261
|
+
|
262
|
+
def self.from_lock(options)
|
263
|
+
new(options.merge("path" => options.delete("remote")))
|
264
|
+
end
|
265
|
+
|
266
|
+
def to_lock
|
267
|
+
out = "PATH\n"
|
268
|
+
out << " remote: #{relative_path}\n"
|
269
|
+
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
|
270
|
+
out << " specs:\n"
|
271
|
+
end
|
272
|
+
|
190
273
|
def to_s
|
191
|
-
"source
|
274
|
+
"source at #{@path}"
|
275
|
+
end
|
276
|
+
|
277
|
+
def hash
|
278
|
+
self.class.hash
|
279
|
+
end
|
280
|
+
|
281
|
+
def eql?(o)
|
282
|
+
Path === o &&
|
283
|
+
path == o.path &&
|
284
|
+
name == o.name &&
|
285
|
+
version == o.version
|
286
|
+
end
|
287
|
+
|
288
|
+
alias == eql?
|
289
|
+
|
290
|
+
def name
|
291
|
+
File.basename(@path.to_s)
|
192
292
|
end
|
193
293
|
|
194
294
|
def load_spec_files
|
@@ -213,7 +313,6 @@ module Bundler
|
|
213
313
|
end
|
214
314
|
|
215
315
|
if spec
|
216
|
-
spec = Specification.from_gemspec(spec)
|
217
316
|
spec.loaded_from = file.to_s
|
218
317
|
spec.source = self
|
219
318
|
index << spec
|
@@ -221,10 +320,11 @@ module Bundler
|
|
221
320
|
end
|
222
321
|
|
223
322
|
if index.empty? && @name && @version
|
224
|
-
index << Specification.new do |s|
|
323
|
+
index << Gem::Specification.new do |s|
|
225
324
|
s.name = @name
|
226
325
|
s.source = self
|
227
326
|
s.version = Gem::Version.new(@version)
|
327
|
+
s.platform = Gem::Platform::RUBY
|
228
328
|
s.summary = "Fake gemspec for #{@name}"
|
229
329
|
s.relative_loaded_from = "#{@name}.gemspec"
|
230
330
|
if path.join("bin").exist?
|
@@ -240,19 +340,53 @@ module Bundler
|
|
240
340
|
index
|
241
341
|
end
|
242
342
|
|
343
|
+
def [](spec)
|
344
|
+
specs[spec].first
|
345
|
+
end
|
346
|
+
|
243
347
|
def local_specs
|
244
348
|
@local_specs ||= load_spec_files
|
245
349
|
end
|
246
350
|
|
351
|
+
class Installer < Gem::Installer
|
352
|
+
def initialize(spec)
|
353
|
+
@spec = spec
|
354
|
+
@bin_dir = "#{Gem.dir}/bin"
|
355
|
+
@gem_dir = spec.full_gem_path
|
356
|
+
@wrappers = true
|
357
|
+
@env_shebang = true
|
358
|
+
@format_executable = false
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
247
362
|
def install(spec)
|
248
|
-
Bundler.ui.
|
249
|
-
|
363
|
+
Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
|
364
|
+
# Let's be honest, when we're working from a path, we can't
|
365
|
+
# really expect native extensions to work because the whole point
|
366
|
+
# is to just be able to modify what's in that path and go. So, let's
|
367
|
+
# not put ourselves through the pain of actually trying to generate
|
368
|
+
# the full gem.
|
369
|
+
Installer.new(spec).generate_bin
|
250
370
|
end
|
251
371
|
|
252
372
|
alias specs local_specs
|
253
373
|
|
374
|
+
def cache(spec)
|
375
|
+
unless path.to_s.index(Bundler.root.to_s) == 0
|
376
|
+
Bundler.ui.warn " * #{spec.name} at `#{path}` will not be cached."
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
254
380
|
private
|
255
381
|
|
382
|
+
def relative_path
|
383
|
+
if path.to_s.include?(Bundler.root.to_s)
|
384
|
+
return path.relative_path_from(Bundler.root)
|
385
|
+
end
|
386
|
+
|
387
|
+
path
|
388
|
+
end
|
389
|
+
|
256
390
|
def generate_bin(spec)
|
257
391
|
gem_dir = Pathname.new(spec.full_gem_path)
|
258
392
|
|
@@ -302,32 +436,68 @@ module Bundler
|
|
302
436
|
|
303
437
|
def initialize(options)
|
304
438
|
super
|
305
|
-
@uri
|
306
|
-
@ref
|
439
|
+
@uri = options["uri"]
|
440
|
+
@ref = options["ref"] || options["branch"] || options["tag"] || 'master'
|
441
|
+
@revision = options["revision"]
|
442
|
+
@update = false
|
443
|
+
end
|
444
|
+
|
445
|
+
def self.from_lock(options)
|
446
|
+
new(options.merge("uri" => options.delete("remote")))
|
447
|
+
end
|
448
|
+
|
449
|
+
def to_lock
|
450
|
+
out = "GIT\n"
|
451
|
+
out << " remote: #{@uri}\n"
|
452
|
+
out << " revision: #{shortref_for(revision)}\n"
|
453
|
+
%w(ref branch tag).each do |opt|
|
454
|
+
out << " #{opt}: #{options[opt]}\n" if options[opt]
|
455
|
+
end
|
456
|
+
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
|
457
|
+
out << " specs:\n"
|
307
458
|
end
|
308
459
|
|
460
|
+
def eql?(o)
|
461
|
+
Git === o &&
|
462
|
+
uri == o.uri &&
|
463
|
+
ref == o.ref &&
|
464
|
+
name == o.name &&
|
465
|
+
version == o.version
|
466
|
+
end
|
467
|
+
|
468
|
+
alias == eql?
|
469
|
+
|
309
470
|
def to_s
|
310
|
-
ref = @options["ref"] ? @options["ref"]
|
471
|
+
ref = @options["ref"] ? shortref_for(@options["ref"]) : @ref
|
311
472
|
"#{@uri} (at #{ref})"
|
312
473
|
end
|
313
474
|
|
475
|
+
def name
|
476
|
+
File.basename(@uri, '.git')
|
477
|
+
end
|
478
|
+
|
314
479
|
def path
|
315
|
-
Bundler.install_path.join("#{base_name}-#{
|
480
|
+
Bundler.install_path.join("#{base_name}-#{shortref_for(revision)}")
|
481
|
+
end
|
482
|
+
|
483
|
+
def unlock!
|
484
|
+
@revision = nil
|
316
485
|
end
|
317
486
|
|
318
487
|
def specs
|
488
|
+
if @allow_remote && !@update
|
319
489
|
# Start by making sure the git cache is up to date
|
320
|
-
|
321
|
-
|
490
|
+
cache
|
491
|
+
checkout
|
492
|
+
@update = true
|
493
|
+
end
|
322
494
|
local_specs
|
323
495
|
end
|
324
496
|
|
325
497
|
def install(spec)
|
326
|
-
Bundler.ui.
|
498
|
+
Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
|
327
499
|
|
328
|
-
|
329
|
-
Bundler.ui.debug " * Already checked out revision: #{ref}"
|
330
|
-
else
|
500
|
+
unless @installed
|
331
501
|
Bundler.ui.debug " * Checking out revision: #{ref}"
|
332
502
|
checkout
|
333
503
|
@installed = true
|
@@ -335,11 +505,6 @@ module Bundler
|
|
335
505
|
generate_bin(spec)
|
336
506
|
end
|
337
507
|
|
338
|
-
def lock
|
339
|
-
@ref = @options["ref"] = revision
|
340
|
-
checkout
|
341
|
-
end
|
342
|
-
|
343
508
|
def load_spec_files
|
344
509
|
super
|
345
510
|
rescue PathError
|
@@ -349,7 +514,12 @@ module Bundler
|
|
349
514
|
private
|
350
515
|
|
351
516
|
def git(command)
|
352
|
-
|
517
|
+
if Bundler.requires_sudo?
|
518
|
+
out = %x{sudo -E git #{command}}
|
519
|
+
else
|
520
|
+
out = %x{git #{command}}
|
521
|
+
end
|
522
|
+
|
353
523
|
if $? != 0
|
354
524
|
raise GitError, "An error has occurred in git. Cannot complete bundling."
|
355
525
|
end
|
@@ -360,6 +530,10 @@ module Bundler
|
|
360
530
|
File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)},''), ".git")
|
361
531
|
end
|
362
532
|
|
533
|
+
def shortref_for(ref)
|
534
|
+
ref[0..6]
|
535
|
+
end
|
536
|
+
|
363
537
|
def uri_hash
|
364
538
|
if uri =~ %r{^\w+://(\w+@)?}
|
365
539
|
# Downcase the domain component of the URI
|