bundler 1.2.5 → 1.3.0.pre
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/.gitignore +10 -7
- data/.travis.yml +12 -3
- data/CHANGELOG.md +26 -19
- data/CONTRIBUTE.md +97 -0
- data/README.md +4 -2
- data/Rakefile +17 -59
- data/bundler.gemspec +2 -1
- data/lib/bundler.rb +23 -20
- data/lib/bundler/cli.rb +68 -22
- data/lib/bundler/definition.rb +3 -2
- data/lib/bundler/deprecate.rb +15 -0
- data/lib/bundler/dsl.rb +14 -16
- data/lib/bundler/environment.rb +0 -5
- data/lib/bundler/fetcher.rb +23 -78
- data/lib/bundler/friendly_errors.rb +4 -5
- data/lib/bundler/gem_helper.rb +14 -16
- data/lib/bundler/injector.rb +64 -0
- data/lib/bundler/installer.rb +1 -7
- data/lib/bundler/lazy_specification.rb +6 -3
- data/lib/bundler/lockfile_parser.rb +25 -13
- data/lib/bundler/resolver.rb +0 -1
- data/lib/bundler/rubygems_integration.rb +83 -17
- data/lib/bundler/settings.rb +4 -2
- data/lib/bundler/similarity_detector.rb +63 -0
- data/lib/bundler/source.rb +3 -886
- data/lib/bundler/source/git.rb +267 -0
- data/lib/bundler/source/git/git_proxy.rb +142 -0
- data/lib/bundler/source/path.rb +209 -0
- data/lib/bundler/source/path/installer.rb +33 -0
- data/lib/bundler/source/rubygems.rb +261 -0
- data/lib/bundler/templates/newgem/newgem.gemspec.tt +3 -0
- data/lib/bundler/templates/newgem/rspec.tt +2 -0
- data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +9 -0
- data/lib/bundler/templates/newgem/spec/spec_helper.rb.tt +2 -0
- data/lib/bundler/templates/newgem/test/minitest_helper.rb.tt +4 -0
- data/lib/bundler/templates/newgem/test/test_newgem.rb.tt +11 -0
- data/lib/bundler/ui.rb +20 -5
- data/lib/bundler/vendor/.document +0 -0
- data/lib/bundler/vendor/thor.rb +74 -5
- data/lib/bundler/vendor/thor/actions.rb +5 -5
- data/lib/bundler/vendor/thor/actions/directory.rb +1 -0
- data/lib/bundler/vendor/thor/actions/file_manipulation.rb +7 -1
- data/lib/bundler/vendor/thor/base.rb +44 -11
- data/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb +5 -0
- data/lib/bundler/vendor/thor/parser/argument.rb +14 -7
- data/lib/bundler/vendor/thor/parser/arguments.rb +7 -1
- data/lib/bundler/vendor/thor/parser/option.rb +8 -8
- data/lib/bundler/vendor/thor/parser/options.rb +62 -24
- data/lib/bundler/vendor/thor/runner.rb +1 -1
- data/lib/bundler/vendor/thor/shell/basic.rb +2 -2
- data/lib/bundler/vendor/thor/task.rb +2 -2
- data/lib/bundler/vendor/thor/version.rb +1 -1
- data/lib/bundler/vendored_persistent.rb +3 -15
- data/lib/bundler/version.rb +1 -1
- data/man/bundle-exec.ronn +1 -1
- data/man/bundle-update.ronn +1 -1
- data/man/bundle.ronn +4 -1
- data/spec/bundler/bundler_spec.rb +2 -28
- data/spec/bundler/cli_rspec.rb +9 -0
- data/spec/bundler/definition_spec.rb +1 -1
- data/spec/bundler/dsl_spec.rb +15 -8
- data/spec/bundler/gem_helper_spec.rb +38 -21
- data/spec/bundler/psyched_yaml_spec.rb +1 -0
- data/spec/bundler/source_spec.rb +3 -3
- data/spec/cache/gems_spec.rb +24 -24
- data/spec/cache/git_spec.rb +21 -23
- data/spec/cache/path_spec.rb +11 -11
- data/spec/cache/platform_spec.rb +6 -6
- data/spec/install/deploy_spec.rb +38 -38
- data/spec/install/gems/c_ext_spec.rb +2 -2
- data/spec/install/gems/dependency_api_spec.rb +23 -116
- data/spec/install/gems/env_spec.rb +1 -1
- data/spec/install/gems/flex_spec.rb +7 -8
- data/spec/install/gems/groups_spec.rb +10 -10
- data/spec/install/gems/packed_spec.rb +4 -4
- data/spec/install/gems/platform_spec.rb +3 -3
- data/spec/install/gems/post_install_spec.rb +9 -9
- data/spec/install/gems/resolving_spec.rb +2 -2
- data/spec/install/gems/simple_case_spec.rb +50 -53
- data/spec/install/gems/standalone_spec.rb +19 -19
- data/spec/install/gems/sudo_spec.rb +31 -16
- data/spec/install/gems/win32_spec.rb +1 -1
- data/spec/install/gemspec_spec.rb +6 -6
- data/spec/install/git_spec.rb +34 -34
- data/spec/install/invalid_spec.rb +3 -3
- data/spec/install/path_spec.rb +71 -8
- data/spec/install/upgrade_spec.rb +2 -2
- data/spec/integration/inject.rb +78 -0
- data/spec/lock/git_spec.rb +2 -2
- data/spec/lock/lockfile_spec.rb +14 -14
- data/spec/other/check_spec.rb +29 -29
- data/spec/other/clean_spec.rb +47 -48
- data/spec/other/config_spec.rb +20 -20
- data/spec/other/console_spec.rb +5 -5
- data/spec/other/exec_spec.rb +48 -28
- data/spec/other/ext_spec.rb +3 -3
- data/spec/other/help_spec.rb +6 -6
- data/spec/other/init_spec.rb +8 -8
- data/spec/other/newgem_spec.rb +95 -15
- data/spec/other/open_spec.rb +10 -5
- data/spec/other/outdated_spec.rb +8 -8
- data/spec/other/platform_spec.rb +45 -45
- data/spec/other/show_spec.rb +10 -10
- data/spec/quality_spec.rb +2 -2
- data/spec/realworld/dependency_api_spec.rb +61 -0
- data/spec/realworld/edgecases_spec.rb +8 -8
- data/spec/runtime/executable_spec.rb +13 -13
- data/spec/runtime/load_spec.rb +12 -12
- data/spec/runtime/platform_spec.rb +1 -1
- data/spec/runtime/require_spec.rb +24 -24
- data/spec/runtime/setup_spec.rb +113 -56
- data/spec/runtime/with_clean_env_spec.rb +11 -13
- data/spec/spec_helper.rb +6 -0
- data/spec/support/artifice/endpoint.rb +28 -13
- data/spec/support/artifice/endpoint_extra.rb +4 -0
- data/spec/support/builders.rb +1 -1
- data/spec/support/helpers.rb +2 -7
- data/spec/support/indexes.rb +3 -3
- data/spec/support/matchers.rb +6 -6
- data/spec/update/gems_spec.rb +19 -8
- data/spec/update/git_spec.rb +10 -10
- data/spec/update/source_spec.rb +1 -1
- metadata +86 -55
- data/.rspec +0 -2
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'uri'
|
3
|
+
require 'digest/sha1'
|
4
|
+
|
5
|
+
module Bundler
|
6
|
+
module Source
|
7
|
+
|
8
|
+
class Git < Path
|
9
|
+
autoload :GitProxy, 'bundler/source/git/git_proxy'
|
10
|
+
|
11
|
+
attr_reader :uri, :ref, :branch, :options, :submodules
|
12
|
+
|
13
|
+
def initialize(options)
|
14
|
+
@options = options
|
15
|
+
@glob = options["glob"] || DEFAULT_GLOB
|
16
|
+
|
17
|
+
@allow_cached = false
|
18
|
+
@allow_remote = false
|
19
|
+
|
20
|
+
# Stringify options that could be set as symbols
|
21
|
+
%w(ref branch tag revision).each{|k| options[k] = options[k].to_s if options[k] }
|
22
|
+
|
23
|
+
@uri = options["uri"]
|
24
|
+
@branch = options["branch"]
|
25
|
+
@ref = options["ref"] || options["branch"] || options["tag"] || 'master'
|
26
|
+
@submodules = options["submodules"]
|
27
|
+
@name = options["name"]
|
28
|
+
@version = options["version"]
|
29
|
+
|
30
|
+
@update = false
|
31
|
+
@installed = nil
|
32
|
+
@local = false
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.from_lock(options)
|
36
|
+
new(options.merge("uri" => options.delete("remote")))
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_lock
|
40
|
+
out = "GIT\n"
|
41
|
+
out << " remote: #{@uri}\n"
|
42
|
+
out << " revision: #{revision}\n"
|
43
|
+
%w(ref branch tag submodules).each do |opt|
|
44
|
+
out << " #{opt}: #{options[opt]}\n" if options[opt]
|
45
|
+
end
|
46
|
+
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
|
47
|
+
out << " specs:\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
def eql?(o)
|
51
|
+
Git === o &&
|
52
|
+
uri == o.uri &&
|
53
|
+
ref == o.ref &&
|
54
|
+
branch == o.branch &&
|
55
|
+
name == o.name &&
|
56
|
+
version == o.version &&
|
57
|
+
submodules == o.submodules
|
58
|
+
end
|
59
|
+
|
60
|
+
alias == eql?
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
at = if local?
|
64
|
+
path
|
65
|
+
elsif options["ref"]
|
66
|
+
shortref_for_display(options["ref"])
|
67
|
+
else
|
68
|
+
ref
|
69
|
+
end
|
70
|
+
"#{uri} (at #{at})"
|
71
|
+
end
|
72
|
+
|
73
|
+
def name
|
74
|
+
File.basename(@uri, '.git')
|
75
|
+
end
|
76
|
+
|
77
|
+
# This is the path which is going to contain a specific
|
78
|
+
# checkout of the git repository. When using local git
|
79
|
+
# repos, this is set to the local repo.
|
80
|
+
def install_path
|
81
|
+
@install_path ||= begin
|
82
|
+
git_scope = "#{base_name}-#{shortref_for_path(revision)}"
|
83
|
+
|
84
|
+
if Bundler.requires_sudo?
|
85
|
+
Bundler.user_bundle_path.join(Bundler.ruby_scope).join(git_scope)
|
86
|
+
else
|
87
|
+
Bundler.install_path.join(git_scope)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
alias :path :install_path
|
93
|
+
|
94
|
+
def unlock!
|
95
|
+
git_proxy.revision = nil
|
96
|
+
end
|
97
|
+
|
98
|
+
def local_override!(path)
|
99
|
+
return false if local?
|
100
|
+
|
101
|
+
path = Pathname.new(path)
|
102
|
+
path = path.expand_path(Bundler.root) unless path.relative?
|
103
|
+
|
104
|
+
unless options["branch"] || Bundler.settings[:disable_local_branch_check]
|
105
|
+
raise GitError, "Cannot use local override for #{name} at #{path} because " \
|
106
|
+
":branch is not specified in Gemfile. Specify a branch or use " \
|
107
|
+
"`bundle config --delete` to remove the local override"
|
108
|
+
end
|
109
|
+
|
110
|
+
unless path.exist?
|
111
|
+
raise GitError, "Cannot use local override for #{name} because #{path} " \
|
112
|
+
"does not exist. Check `bundle config --delete` to remove the local override"
|
113
|
+
end
|
114
|
+
|
115
|
+
set_local!(path)
|
116
|
+
|
117
|
+
# Create a new git proxy without the cached revision
|
118
|
+
# so the Gemfile.lock always picks up the new revision.
|
119
|
+
@git_proxy = GitProxy.new(path, uri, ref)
|
120
|
+
|
121
|
+
if git_proxy.branch != options["branch"] && !Bundler.settings[:disable_local_branch_check]
|
122
|
+
raise GitError, "Local override for #{name} at #{path} is using branch " \
|
123
|
+
"#{git_proxy.branch} but Gemfile specifies #{options["branch"]}"
|
124
|
+
end
|
125
|
+
|
126
|
+
changed = cached_revision && cached_revision != git_proxy.revision
|
127
|
+
|
128
|
+
if changed && !git_proxy.contains?(cached_revision)
|
129
|
+
raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \
|
130
|
+
"but the current branch in your local override for #{name} does not contain such commit. " \
|
131
|
+
"Please make sure your branch is up to date."
|
132
|
+
end
|
133
|
+
|
134
|
+
changed
|
135
|
+
end
|
136
|
+
|
137
|
+
# TODO: actually cache git specs
|
138
|
+
def specs(*)
|
139
|
+
if has_app_cache? && !local?
|
140
|
+
set_local!(app_cache_path)
|
141
|
+
end
|
142
|
+
|
143
|
+
if requires_checkout? && !@update
|
144
|
+
git_proxy.checkout
|
145
|
+
git_proxy.copy_to(install_path, submodules)
|
146
|
+
@update = true
|
147
|
+
end
|
148
|
+
|
149
|
+
local_specs
|
150
|
+
end
|
151
|
+
|
152
|
+
def install(spec)
|
153
|
+
Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
|
154
|
+
if requires_checkout? && !@installed
|
155
|
+
Bundler.ui.debug " * Checking out revision: #{ref}"
|
156
|
+
git_proxy.copy_to(install_path, submodules)
|
157
|
+
@installed = true
|
158
|
+
end
|
159
|
+
generate_bin(spec)
|
160
|
+
end
|
161
|
+
|
162
|
+
def cache(spec)
|
163
|
+
return unless Bundler.settings[:cache_all]
|
164
|
+
return if path == app_cache_path
|
165
|
+
cached!
|
166
|
+
FileUtils.rm_rf(app_cache_path)
|
167
|
+
git_proxy.checkout if requires_checkout?
|
168
|
+
git_proxy.copy_to(app_cache_path, @submodules)
|
169
|
+
FileUtils.rm_rf(app_cache_path.join(".git"))
|
170
|
+
FileUtils.touch(app_cache_path.join(".bundlecache"))
|
171
|
+
end
|
172
|
+
|
173
|
+
def load_spec_files
|
174
|
+
super
|
175
|
+
rescue PathError, GitError
|
176
|
+
raise GitError, "#{to_s} is not checked out. Please run `bundle install`"
|
177
|
+
end
|
178
|
+
|
179
|
+
# This is the path which is going to contain a cache
|
180
|
+
# of the git repository. When using the same git repository
|
181
|
+
# across different projects, this cache will be shared.
|
182
|
+
# When using local git repos, this is set to the local repo.
|
183
|
+
def cache_path
|
184
|
+
@cache_path ||= begin
|
185
|
+
git_scope = "#{base_name}-#{uri_hash}"
|
186
|
+
|
187
|
+
if Bundler.requires_sudo?
|
188
|
+
Bundler.user_bundle_path.join("cache/git", git_scope)
|
189
|
+
else
|
190
|
+
Bundler.cache.join("git", git_scope)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def app_cache_dirname
|
196
|
+
"#{base_name}-#{shortref_for_path(cached_revision || revision)}"
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def set_local!(path)
|
202
|
+
@local = true
|
203
|
+
@local_specs = @git_proxy = nil
|
204
|
+
@cache_path = @install_path = path
|
205
|
+
end
|
206
|
+
|
207
|
+
def has_app_cache?
|
208
|
+
cached_revision && super
|
209
|
+
end
|
210
|
+
|
211
|
+
def local?
|
212
|
+
@local
|
213
|
+
end
|
214
|
+
|
215
|
+
def requires_checkout?
|
216
|
+
allow_git_ops? && !local?
|
217
|
+
end
|
218
|
+
|
219
|
+
def base_name
|
220
|
+
File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)?(//\w*/)?(\w*/)*},''),".git")
|
221
|
+
end
|
222
|
+
|
223
|
+
def shortref_for_display(ref)
|
224
|
+
ref[0..6]
|
225
|
+
end
|
226
|
+
|
227
|
+
def shortref_for_path(ref)
|
228
|
+
ref[0..11]
|
229
|
+
end
|
230
|
+
|
231
|
+
def uri_hash
|
232
|
+
if uri =~ %r{^\w+://(\w+@)?}
|
233
|
+
# Downcase the domain component of the URI
|
234
|
+
# and strip off a trailing slash, if one is present
|
235
|
+
input = URI.parse(uri).normalize.to_s.sub(%r{/$},'')
|
236
|
+
else
|
237
|
+
# If there is no URI scheme, assume it is an ssh/git URI
|
238
|
+
input = uri
|
239
|
+
end
|
240
|
+
Digest::SHA1.hexdigest(input)
|
241
|
+
end
|
242
|
+
|
243
|
+
def allow_git_ops?
|
244
|
+
@allow_remote || @allow_cached
|
245
|
+
end
|
246
|
+
|
247
|
+
def cached_revision
|
248
|
+
options["revision"]
|
249
|
+
end
|
250
|
+
|
251
|
+
def revision
|
252
|
+
git_proxy.revision
|
253
|
+
end
|
254
|
+
|
255
|
+
def cached?
|
256
|
+
cache_path.exist?
|
257
|
+
end
|
258
|
+
|
259
|
+
def git_proxy
|
260
|
+
@git_proxy ||= GitProxy.new(cache_path, uri, ref, cached_revision){ allow_git_ops? }
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Bundler
|
2
|
+
module Source
|
3
|
+
|
4
|
+
class Git < Path
|
5
|
+
# The GitProxy is responsible to iteract with git repositories.
|
6
|
+
# All actions required by the Git source is encapsualted in this
|
7
|
+
# object.
|
8
|
+
class GitProxy
|
9
|
+
attr_accessor :path, :uri, :ref
|
10
|
+
attr_writer :revision
|
11
|
+
|
12
|
+
def initialize(path, uri, ref, revision=nil, &allow)
|
13
|
+
@path = path
|
14
|
+
@uri = uri
|
15
|
+
@ref = ref
|
16
|
+
@revision = revision
|
17
|
+
@allow = allow || Proc.new { true }
|
18
|
+
end
|
19
|
+
|
20
|
+
def revision
|
21
|
+
@revision ||= allowed_in_path { git("rev-parse #{ref}").strip }
|
22
|
+
end
|
23
|
+
|
24
|
+
def branch
|
25
|
+
@branch ||= allowed_in_path do
|
26
|
+
git("branch") =~ /^\* (.*)$/ && $1.strip
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def contains?(commit)
|
31
|
+
allowed_in_path do
|
32
|
+
result = git_null("branch --contains #{commit}")
|
33
|
+
$? == 0 && result =~ /^\* (.*)$/
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def checkout
|
38
|
+
if path.exist?
|
39
|
+
return if has_revision_cached?
|
40
|
+
Bundler.ui.info "Updating #{uri}"
|
41
|
+
in_path do
|
42
|
+
git %|fetch --force --quiet --tags #{uri_escaped} "refs/heads/*:refs/heads/*"|
|
43
|
+
end
|
44
|
+
else
|
45
|
+
Bundler.ui.info "Fetching #{uri}"
|
46
|
+
FileUtils.mkdir_p(path.dirname)
|
47
|
+
git %|clone #{uri_escaped} "#{path}" --bare --no-hardlinks|
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def copy_to(destination, submodules=false)
|
52
|
+
unless File.exist?(destination.join(".git"))
|
53
|
+
FileUtils.mkdir_p(destination.dirname)
|
54
|
+
FileUtils.rm_rf(destination)
|
55
|
+
git %|clone --no-checkout "#{path}" "#{destination}"|
|
56
|
+
File.chmod((0777 & ~File.umask), destination)
|
57
|
+
end
|
58
|
+
|
59
|
+
Dir.chdir(destination) do
|
60
|
+
git %|fetch --force --quiet --tags "#{path}"|
|
61
|
+
git "reset --hard #{@revision}"
|
62
|
+
|
63
|
+
if submodules
|
64
|
+
git "submodule update --init --recursive"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# TODO: Do not rely on /dev/null.
|
72
|
+
# Given that open3 is not cross platform until Ruby 1.9.3,
|
73
|
+
# the best solution is to pipe to /dev/null if it exists.
|
74
|
+
# If it doesn't, everything will work fine, but the user
|
75
|
+
# will get the $stderr messages as well.
|
76
|
+
def git_null(command)
|
77
|
+
if !Bundler::WINDOWS && File.exist?("/dev/null")
|
78
|
+
git("#{command} 2>/dev/null", false)
|
79
|
+
else
|
80
|
+
git(command, false)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def git(command, check_errors=true)
|
85
|
+
if allow?
|
86
|
+
out = %x{git #{command}}
|
87
|
+
|
88
|
+
if check_errors && $?.exitstatus != 0
|
89
|
+
msg = "Git error: command `git #{command}` in directory #{Dir.pwd} has failed."
|
90
|
+
msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path.exist?
|
91
|
+
raise GitError, msg
|
92
|
+
end
|
93
|
+
out
|
94
|
+
else
|
95
|
+
raise GitError, "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, " \
|
96
|
+
"this error message could probably be more useful. Please submit a ticket at http://github.com/carlhuda/bundler/issues " \
|
97
|
+
"with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def has_revision_cached?
|
102
|
+
return unless @revision
|
103
|
+
in_path { git("cat-file -e #{@revision}") }
|
104
|
+
true
|
105
|
+
rescue GitError
|
106
|
+
false
|
107
|
+
end
|
108
|
+
|
109
|
+
# Escape the URI for git commands
|
110
|
+
def uri_escaped
|
111
|
+
if Bundler::WINDOWS
|
112
|
+
# Windows quoting requires double quotes only, with double quotes
|
113
|
+
# inside the string escaped by being doubled.
|
114
|
+
'"' + uri.gsub('"') {|s| '""'} + '"'
|
115
|
+
else
|
116
|
+
# Bash requires single quoted strings, with the single quotes escaped
|
117
|
+
# by ending the string, escaping the quote, and restarting the string.
|
118
|
+
"'" + uri.gsub("'") {|s| "'\\''"} + "'"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def allow?
|
123
|
+
@allow.call
|
124
|
+
end
|
125
|
+
|
126
|
+
def in_path(&blk)
|
127
|
+
checkout unless path.exist?
|
128
|
+
Dir.chdir(path, &blk)
|
129
|
+
end
|
130
|
+
|
131
|
+
def allowed_in_path
|
132
|
+
if allow?
|
133
|
+
in_path { yield }
|
134
|
+
else
|
135
|
+
raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
module Bundler
|
2
|
+
module Source
|
3
|
+
|
4
|
+
class Path
|
5
|
+
autoload :Installer, 'bundler/source/path/installer'
|
6
|
+
|
7
|
+
attr_reader :path, :options
|
8
|
+
attr_writer :name
|
9
|
+
attr_accessor :version
|
10
|
+
|
11
|
+
DEFAULT_GLOB = "{,*,*/*}.gemspec"
|
12
|
+
|
13
|
+
def initialize(options)
|
14
|
+
@options = options
|
15
|
+
@glob = options["glob"] || DEFAULT_GLOB
|
16
|
+
|
17
|
+
@allow_cached = false
|
18
|
+
@allow_remote = false
|
19
|
+
|
20
|
+
if options["path"]
|
21
|
+
@path = Pathname.new(options["path"])
|
22
|
+
@path = @path.expand_path(Bundler.root) unless @path.relative?
|
23
|
+
end
|
24
|
+
|
25
|
+
@name = options["name"]
|
26
|
+
@version = options["version"]
|
27
|
+
|
28
|
+
# Stores the original path. If at any point we move to the
|
29
|
+
# cached directory, we still have the original path to copy from.
|
30
|
+
@original_path = @path
|
31
|
+
end
|
32
|
+
|
33
|
+
def remote!
|
34
|
+
@allow_remote = true
|
35
|
+
end
|
36
|
+
|
37
|
+
def cached!
|
38
|
+
@allow_cached = true
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.from_lock(options)
|
42
|
+
new(options.merge("path" => options.delete("remote")))
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_lock
|
46
|
+
out = "PATH\n"
|
47
|
+
out << " remote: #{relative_path}\n"
|
48
|
+
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
|
49
|
+
out << " specs:\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"source at #{@path}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def hash
|
57
|
+
self.class.hash
|
58
|
+
end
|
59
|
+
|
60
|
+
def eql?(o)
|
61
|
+
o.instance_of?(Path) &&
|
62
|
+
path.expand_path(Bundler.root) == o.path.expand_path(Bundler.root) &&
|
63
|
+
version == o.version
|
64
|
+
end
|
65
|
+
|
66
|
+
alias == eql?
|
67
|
+
|
68
|
+
def name
|
69
|
+
File.basename(path.expand_path(Bundler.root).to_s)
|
70
|
+
end
|
71
|
+
|
72
|
+
def install(spec)
|
73
|
+
Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
|
74
|
+
generate_bin(spec, :disable_extensions)
|
75
|
+
end
|
76
|
+
|
77
|
+
def cache(spec)
|
78
|
+
return unless Bundler.settings[:cache_all]
|
79
|
+
return if @original_path.expand_path(Bundler.root).to_s.index(Bundler.root.to_s) == 0
|
80
|
+
FileUtils.rm_rf(app_cache_path)
|
81
|
+
FileUtils.cp_r("#{@original_path}/.", app_cache_path)
|
82
|
+
FileUtils.touch(app_cache_path.join(".bundlecache"))
|
83
|
+
end
|
84
|
+
|
85
|
+
def local_specs(*)
|
86
|
+
@local_specs ||= load_spec_files
|
87
|
+
end
|
88
|
+
|
89
|
+
def specs
|
90
|
+
if has_app_cache?
|
91
|
+
@path = app_cache_path
|
92
|
+
end
|
93
|
+
local_specs
|
94
|
+
end
|
95
|
+
|
96
|
+
def app_cache_dirname
|
97
|
+
name
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def app_cache_path
|
103
|
+
@app_cache_path ||= Bundler.app_cache.join(app_cache_dirname)
|
104
|
+
end
|
105
|
+
|
106
|
+
def has_app_cache?
|
107
|
+
SharedHelpers.in_bundle? && app_cache_path.exist?
|
108
|
+
end
|
109
|
+
|
110
|
+
def load_spec_files
|
111
|
+
index = Index.new
|
112
|
+
expanded_path = path.expand_path(Bundler.root)
|
113
|
+
|
114
|
+
if File.directory?(expanded_path)
|
115
|
+
Dir["#{expanded_path}/#{@glob}"].each do |file|
|
116
|
+
spec = Bundler.load_gemspec(file)
|
117
|
+
if spec
|
118
|
+
spec.loaded_from = file.to_s
|
119
|
+
spec.source = self
|
120
|
+
index << spec
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
if index.empty? && @name && @version
|
125
|
+
index << Gem::Specification.new do |s|
|
126
|
+
s.name = @name
|
127
|
+
s.source = self
|
128
|
+
s.version = Gem::Version.new(@version)
|
129
|
+
s.platform = Gem::Platform::RUBY
|
130
|
+
s.summary = "Fake gemspec for #{@name}"
|
131
|
+
s.relative_loaded_from = "#{@name}.gemspec"
|
132
|
+
s.authors = ["no one"]
|
133
|
+
if expanded_path.join("bin").exist?
|
134
|
+
executables = expanded_path.join("bin").children
|
135
|
+
executables.reject!{|p| File.directory?(p) }
|
136
|
+
s.executables = executables.map{|c| c.basename.to_s }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
else
|
141
|
+
raise PathError, "The path `#{expanded_path}` does not exist."
|
142
|
+
end
|
143
|
+
|
144
|
+
index
|
145
|
+
end
|
146
|
+
|
147
|
+
def relative_path
|
148
|
+
if path.to_s.match(%r{^#{Regexp.escape Bundler.root.to_s}})
|
149
|
+
return path.relative_path_from(Bundler.root)
|
150
|
+
end
|
151
|
+
path
|
152
|
+
end
|
153
|
+
|
154
|
+
def generate_bin(spec, disable_extensions = false)
|
155
|
+
gem_dir = Pathname.new(spec.full_gem_path)
|
156
|
+
|
157
|
+
# Some gem authors put absolute paths in their gemspec
|
158
|
+
# and we have to save them from themselves
|
159
|
+
spec.files = spec.files.map do |p|
|
160
|
+
next if File.directory?(p)
|
161
|
+
begin
|
162
|
+
Pathname.new(p).relative_path_from(gem_dir).to_s
|
163
|
+
rescue ArgumentError
|
164
|
+
p
|
165
|
+
end
|
166
|
+
end.compact
|
167
|
+
|
168
|
+
gem_file = Bundler.rubygems.build_gem gem_dir, spec
|
169
|
+
|
170
|
+
installer = Path::Installer.new(spec, :env_shebang => false)
|
171
|
+
run_hooks(:pre_install, installer)
|
172
|
+
installer.build_extensions unless disable_extensions
|
173
|
+
run_hooks(:post_build, installer)
|
174
|
+
installer.generate_bin
|
175
|
+
run_hooks(:post_install, installer)
|
176
|
+
rescue Gem::InvalidSpecificationException => e
|
177
|
+
Bundler.ui.warn "\n#{spec.name} at #{spec.full_gem_path} did not have a valid gemspec.\n" \
|
178
|
+
"This prevents bundler from installing bins or native extensions, but " \
|
179
|
+
"that may not affect its functionality."
|
180
|
+
|
181
|
+
if !spec.extensions.empty? && !spec.email.empty?
|
182
|
+
Bundler.ui.warn "If you need to use this package without installing it from a gem " \
|
183
|
+
"repository, please contact #{spec.email} and ask them " \
|
184
|
+
"to modify their .gemspec so it can work with `gem build`."
|
185
|
+
end
|
186
|
+
|
187
|
+
Bundler.ui.warn "The validation message from Rubygems was:\n #{e.message}"
|
188
|
+
ensure
|
189
|
+
if gem_dir && gem_file
|
190
|
+
Dir.chdir(gem_dir){ FileUtils.rm_rf(gem_file) if File.exist?(gem_file) }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def run_hooks(type, installer)
|
195
|
+
hooks_meth = "#{type}_hooks"
|
196
|
+
return unless Gem.respond_to?(hooks_meth)
|
197
|
+
Gem.send(hooks_meth).each do |hook|
|
198
|
+
result = hook.call(installer)
|
199
|
+
if result == false
|
200
|
+
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
|
201
|
+
message = "#{type} hook#{location} failed for #{installer.spec.full_name}"
|
202
|
+
raise InstallHookError, message
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|