bundler_package_git 1.1.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/.gitignore +22 -0
  2. data/.rvmrc +1 -0
  3. data/CHANGELOG.md +659 -0
  4. data/ISSUES.md +47 -0
  5. data/LICENSE +23 -0
  6. data/README.md +29 -0
  7. data/Rakefile +167 -0
  8. data/UPGRADING.md +103 -0
  9. data/bin/bundle +22 -0
  10. data/bundler.gemspec +30 -0
  11. data/lib/bundler.rb +283 -0
  12. data/lib/bundler/capistrano.rb +11 -0
  13. data/lib/bundler/cli.rb +490 -0
  14. data/lib/bundler/definition.rb +429 -0
  15. data/lib/bundler/dependency.rb +130 -0
  16. data/lib/bundler/deployment.rb +53 -0
  17. data/lib/bundler/dsl.rb +243 -0
  18. data/lib/bundler/environment.rb +47 -0
  19. data/lib/bundler/fetcher.rb +101 -0
  20. data/lib/bundler/gem_helper.rb +146 -0
  21. data/lib/bundler/graph.rb +130 -0
  22. data/lib/bundler/index.rb +131 -0
  23. data/lib/bundler/installer.rb +117 -0
  24. data/lib/bundler/lazy_specification.rb +71 -0
  25. data/lib/bundler/lockfile_parser.rb +108 -0
  26. data/lib/bundler/remote_specification.rb +57 -0
  27. data/lib/bundler/resolver.rb +470 -0
  28. data/lib/bundler/rubygems_ext.rb +226 -0
  29. data/lib/bundler/runtime.rb +201 -0
  30. data/lib/bundler/settings.rb +117 -0
  31. data/lib/bundler/setup.rb +16 -0
  32. data/lib/bundler/shared_helpers.rb +167 -0
  33. data/lib/bundler/source.rb +675 -0
  34. data/lib/bundler/spec_set.rb +134 -0
  35. data/lib/bundler/templates/Executable +16 -0
  36. data/lib/bundler/templates/Gemfile +4 -0
  37. data/lib/bundler/templates/newgem/Gemfile.tt +4 -0
  38. data/lib/bundler/templates/newgem/Rakefile.tt +2 -0
  39. data/lib/bundler/templates/newgem/bin/newgem.tt +3 -0
  40. data/lib/bundler/templates/newgem/gitignore.tt +4 -0
  41. data/lib/bundler/templates/newgem/lib/newgem.rb.tt +7 -0
  42. data/lib/bundler/templates/newgem/lib/newgem/version.rb.tt +7 -0
  43. data/lib/bundler/templates/newgem/newgem.gemspec.tt +21 -0
  44. data/lib/bundler/ui.rb +73 -0
  45. data/lib/bundler/vendor/net/http/faster.rb +27 -0
  46. data/lib/bundler/vendor/net/http/persistent.rb +464 -0
  47. data/lib/bundler/vendor/thor.rb +319 -0
  48. data/lib/bundler/vendor/thor/actions.rb +297 -0
  49. data/lib/bundler/vendor/thor/actions/create_file.rb +105 -0
  50. data/lib/bundler/vendor/thor/actions/directory.rb +93 -0
  51. data/lib/bundler/vendor/thor/actions/empty_directory.rb +134 -0
  52. data/lib/bundler/vendor/thor/actions/file_manipulation.rb +229 -0
  53. data/lib/bundler/vendor/thor/actions/inject_into_file.rb +104 -0
  54. data/lib/bundler/vendor/thor/base.rb +556 -0
  55. data/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +9 -0
  56. data/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  57. data/lib/bundler/vendor/thor/core_ext/ordered_hash.rb +100 -0
  58. data/lib/bundler/vendor/thor/error.rb +30 -0
  59. data/lib/bundler/vendor/thor/invocation.rb +168 -0
  60. data/lib/bundler/vendor/thor/parser.rb +4 -0
  61. data/lib/bundler/vendor/thor/parser/argument.rb +67 -0
  62. data/lib/bundler/vendor/thor/parser/arguments.rb +161 -0
  63. data/lib/bundler/vendor/thor/parser/option.rb +120 -0
  64. data/lib/bundler/vendor/thor/parser/options.rb +174 -0
  65. data/lib/bundler/vendor/thor/shell.rb +88 -0
  66. data/lib/bundler/vendor/thor/shell/basic.rb +275 -0
  67. data/lib/bundler/vendor/thor/shell/color.rb +108 -0
  68. data/lib/bundler/vendor/thor/shell/html.rb +121 -0
  69. data/lib/bundler/vendor/thor/task.rb +114 -0
  70. data/lib/bundler/vendor/thor/util.rb +229 -0
  71. data/lib/bundler/vendor/thor/version.rb +3 -0
  72. data/lib/bundler/version.rb +6 -0
  73. data/lib/bundler/vlad.rb +9 -0
  74. data/man/bundle-config.ronn +90 -0
  75. data/man/bundle-exec.ronn +111 -0
  76. data/man/bundle-install.ronn +314 -0
  77. data/man/bundle-package.ronn +59 -0
  78. data/man/bundle-update.ronn +176 -0
  79. data/man/bundle.ronn +80 -0
  80. data/man/gemfile.5.ronn +279 -0
  81. data/man/index.txt +6 -0
  82. data/spec/cache/gems_spec.rb +219 -0
  83. data/spec/cache/git_spec.rb +9 -0
  84. data/spec/cache/path_spec.rb +27 -0
  85. data/spec/cache/platform_spec.rb +57 -0
  86. data/spec/install/deploy_spec.rb +197 -0
  87. data/spec/install/deprecated_spec.rb +37 -0
  88. data/spec/install/gems/c_ext_spec.rb +48 -0
  89. data/spec/install/gems/dependency_api_spec.rb +85 -0
  90. data/spec/install/gems/env_spec.rb +107 -0
  91. data/spec/install/gems/flex_spec.rb +313 -0
  92. data/spec/install/gems/groups_spec.rb +245 -0
  93. data/spec/install/gems/packed_spec.rb +84 -0
  94. data/spec/install/gems/platform_spec.rb +208 -0
  95. data/spec/install/gems/resolving_spec.rb +72 -0
  96. data/spec/install/gems/simple_case_spec.rb +715 -0
  97. data/spec/install/gems/standalone_spec.rb +162 -0
  98. data/spec/install/gems/sudo_spec.rb +73 -0
  99. data/spec/install/gems/win32_spec.rb +26 -0
  100. data/spec/install/gemspec_spec.rb +108 -0
  101. data/spec/install/git_spec.rb +571 -0
  102. data/spec/install/invalid_spec.rb +17 -0
  103. data/spec/install/path_spec.rb +353 -0
  104. data/spec/install/upgrade_spec.rb +26 -0
  105. data/spec/lock/git_spec.rb +35 -0
  106. data/spec/lock/lockfile_spec.rb +683 -0
  107. data/spec/other/check_spec.rb +221 -0
  108. data/spec/other/clean_spec.rb +202 -0
  109. data/spec/other/config_spec.rb +40 -0
  110. data/spec/other/console_spec.rb +54 -0
  111. data/spec/other/exec_spec.rb +241 -0
  112. data/spec/other/ext_spec.rb +16 -0
  113. data/spec/other/gem_helper_spec.rb +128 -0
  114. data/spec/other/help_spec.rb +38 -0
  115. data/spec/other/init_spec.rb +40 -0
  116. data/spec/other/newgem_spec.rb +24 -0
  117. data/spec/other/open_spec.rb +35 -0
  118. data/spec/other/show_spec.rb +82 -0
  119. data/spec/pack/gems_spec.rb +54 -0
  120. data/spec/quality_spec.rb +58 -0
  121. data/spec/resolver/basic_spec.rb +20 -0
  122. data/spec/resolver/platform_spec.rb +82 -0
  123. data/spec/runtime/executable_spec.rb +110 -0
  124. data/spec/runtime/load_spec.rb +107 -0
  125. data/spec/runtime/platform_spec.rb +90 -0
  126. data/spec/runtime/require_spec.rb +231 -0
  127. data/spec/runtime/setup_spec.rb +688 -0
  128. data/spec/runtime/with_clean_env_spec.rb +15 -0
  129. data/spec/spec_helper.rb +85 -0
  130. data/spec/support/artifice/endpoint.rb +50 -0
  131. data/spec/support/artifice/endpoint_fallback.rb +22 -0
  132. data/spec/support/artifice/endpoint_marshal_fail.rb +11 -0
  133. data/spec/support/artifice/endpoint_redirect.rb +11 -0
  134. data/spec/support/builders.rb +574 -0
  135. data/spec/support/fakeweb/rack-1.0.0.marshal +2 -0
  136. data/spec/support/fakeweb/windows.rb +23 -0
  137. data/spec/support/helpers.rb +246 -0
  138. data/spec/support/indexes.rb +112 -0
  139. data/spec/support/matchers.rb +89 -0
  140. data/spec/support/path.rb +73 -0
  141. data/spec/support/platforms.rb +53 -0
  142. data/spec/support/ruby_ext.rb +20 -0
  143. data/spec/support/rubygems_ext.rb +35 -0
  144. data/spec/support/rubygems_hax/platform.rb +11 -0
  145. data/spec/support/sudo.rb +21 -0
  146. data/spec/update/gems_spec.rb +121 -0
  147. data/spec/update/git_spec.rb +196 -0
  148. data/spec/update/source_spec.rb +51 -0
  149. metadata +294 -0
@@ -0,0 +1,16 @@
1
+ require 'bundler/shared_helpers'
2
+
3
+ if Bundler::SharedHelpers.in_bundle?
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup
7
+ rescue Bundler::BundlerError => e
8
+ puts "\e[31m#{e.message}\e[0m"
9
+ puts e.backtrace.join("\n") if ENV["DEBUG"]
10
+ exit e.status_code
11
+ end
12
+
13
+ # Add bundler to the load path after disabling system gems
14
+ bundler_lib = File.expand_path("../..", __FILE__)
15
+ $LOAD_PATH.unshift(bundler_lib) unless $LOAD_PATH.include?(bundler_lib)
16
+ end
@@ -0,0 +1,167 @@
1
+ require 'pathname'
2
+ require 'rubygems'
3
+ Gem.source_index # ensure Rubygems is fully loaded in Ruby 1.9
4
+
5
+ module Gem
6
+ class Dependency
7
+ if !instance_methods.map { |m| m.to_s }.include?("requirement")
8
+ def requirement
9
+ version_requirements
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ module Bundler
16
+ module SharedHelpers
17
+ attr_accessor :gem_loaded
18
+
19
+ def default_gemfile
20
+ gemfile = find_gemfile
21
+ raise GemfileNotFound, "Could not locate Gemfile" unless gemfile
22
+ Pathname.new(gemfile)
23
+ end
24
+
25
+ def default_lockfile
26
+ Pathname.new("#{default_gemfile}.lock")
27
+ end
28
+
29
+ def in_bundle?
30
+ find_gemfile
31
+ end
32
+
33
+ private
34
+
35
+ def find_gemfile
36
+ given = ENV['BUNDLE_GEMFILE']
37
+ return given if given && !given.empty?
38
+
39
+ previous = nil
40
+ current = File.expand_path(Dir.pwd)
41
+
42
+ until !File.directory?(current) || current == previous
43
+ if ENV['BUNDLE_SPEC_RUN']
44
+ # avoid stepping above the tmp directory when testing
45
+ return nil if File.file?(File.join(current, 'bundler.gemspec'))
46
+ end
47
+
48
+ # otherwise return the Gemfile if it's there
49
+ filename = File.join(current, 'Gemfile')
50
+ return filename if File.file?(filename)
51
+ current, previous = File.expand_path("..", current), current
52
+ end
53
+ end
54
+
55
+ def clean_load_path
56
+ # handle 1.9 where system gems are always on the load path
57
+ if defined?(::Gem)
58
+ me = File.expand_path("../../", __FILE__)
59
+ $LOAD_PATH.reject! do |p|
60
+ next if File.expand_path(p) =~ /^#{me}/
61
+ p != File.dirname(__FILE__) &&
62
+ Gem.path.any?{|gp| p =~ /^#{gp}/ }
63
+ end
64
+ $LOAD_PATH.uniq!
65
+ end
66
+ end
67
+
68
+ def reverse_rubygems_kernel_mixin
69
+ # Disable rubygems' gem activation system
70
+ ::Kernel.class_eval do
71
+ if private_method_defined?(:gem_original_require)
72
+ alias rubygems_require require
73
+ alias require gem_original_require
74
+ end
75
+
76
+ undef gem
77
+ end
78
+ end
79
+
80
+ def cripple_rubygems(specs)
81
+ reverse_rubygems_kernel_mixin
82
+
83
+ executables = specs.map { |s| s.executables }.flatten
84
+ Gem.source_index # ensure RubyGems is fully loaded
85
+
86
+ ::Kernel.send(:define_method, :gem) do |dep, *reqs|
87
+ if executables.include? File.basename(caller.first.split(':').first)
88
+ return
89
+ end
90
+ opts = reqs.last.is_a?(Hash) ? reqs.pop : {}
91
+
92
+ unless dep.respond_to?(:name) && dep.respond_to?(:requirement)
93
+ dep = Gem::Dependency.new(dep, reqs)
94
+ end
95
+
96
+ spec = specs.find { |s| s.name == dep.name }
97
+
98
+ if spec.nil?
99
+
100
+ e = Gem::LoadError.new "#{dep.name} is not part of the bundle. Add it to Gemfile."
101
+ e.name = dep.name
102
+ if e.respond_to?(:requirement=)
103
+ e.requirement = dep.requirement
104
+ else
105
+ e.version_requirement = dep.requirement
106
+ end
107
+ raise e
108
+ elsif dep !~ spec
109
+ e = Gem::LoadError.new "can't activate #{dep}, already activated #{spec.full_name}. " \
110
+ "Make sure all dependencies are added to Gemfile."
111
+ e.name = dep.name
112
+ if e.respond_to?(:requirement=)
113
+ e.requirement = dep.requirement
114
+ else
115
+ e.version_requirement = dep.requirement
116
+ end
117
+ raise e
118
+ end
119
+
120
+ true
121
+ end
122
+
123
+ # === Following hacks are to improve on the generated bin wrappers ===
124
+
125
+ # Yeah, talk about a hack
126
+ source_index_class = (class << Gem::SourceIndex ; self ; end)
127
+ source_index_class.send(:remove_method, :from_gems_in)
128
+ source_index_class.send(:define_method, :from_gems_in) do |*args|
129
+ source_index = Gem::SourceIndex.new
130
+ source_index.spec_dirs = *args
131
+ source_index.add_specs(*specs)
132
+ source_index
133
+ end
134
+
135
+ # OMG more hacks
136
+ gem_class = (class << Gem ; self ; end)
137
+ gem_class.send(:remove_method, :refresh)
138
+ gem_class.send(:define_method, :refresh) { }
139
+ gem_class.send(:remove_method, :bin_path)
140
+ gem_class.send(:define_method, :bin_path) do |name, *args|
141
+ exec_name, *reqs = args
142
+
143
+ if exec_name == 'bundle'
144
+ return ENV['BUNDLE_BIN_PATH']
145
+ end
146
+
147
+ spec = nil
148
+
149
+ if exec_name
150
+ spec = specs.find { |s| s.executables.include?(exec_name) }
151
+ spec or raise Gem::Exception, "can't find executable #{exec_name}"
152
+ else
153
+ spec = specs.find { |s| s.name == name }
154
+ exec_name = spec.default_executable or raise Gem::Exception, "no default executable for #{spec.full_name}"
155
+ end
156
+
157
+ gem_bin = File.join(spec.full_gem_path, spec.bindir, exec_name)
158
+ gem_from_path_bin = File.join(File.dirname(spec.loaded_from), spec.bindir, exec_name)
159
+ File.exist?(gem_bin) ? gem_bin : gem_from_path_bin
160
+ end
161
+
162
+ Gem.clear_paths
163
+ end
164
+
165
+ extend self
166
+ end
167
+ end
@@ -0,0 +1,675 @@
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
+ @fetchers = {}
19
+ @allow_remote = false
20
+ @allow_cached = false
21
+
22
+ @caches = [ Bundler.app_cache ] + Gem.path.map{ |p| File.expand_path("#{p}/cache") }
23
+ @spec_fetch_map = {}
24
+ end
25
+
26
+ def remote!
27
+ @allow_remote = true
28
+ end
29
+
30
+ def cached!
31
+ @allow_cached = true
32
+ end
33
+
34
+ def hash
35
+ Rubygems.hash
36
+ end
37
+
38
+ def eql?(o)
39
+ Rubygems === o
40
+ end
41
+
42
+ alias == eql?
43
+
44
+ def options
45
+ { "remotes" => @remotes.map { |r| r.to_s } }
46
+ end
47
+
48
+ def self.from_lock(options)
49
+ s = new(options)
50
+ Array(options["remote"]).each { |r| s.add_remote(r) }
51
+ s
52
+ end
53
+
54
+ def to_lock
55
+ out = "GEM\n"
56
+ out << remotes.map {|r| " remote: #{r}\n" }.join
57
+ out << " specs:\n"
58
+ end
59
+
60
+ def to_s
61
+ remote_names = self.remotes.map { |r| r.to_s }.join(', ')
62
+ "rubygems repository #{remote_names}"
63
+ end
64
+ alias_method :name, :to_s
65
+
66
+ def specs(dependencies = nil)
67
+ @specs ||= fetch_specs(dependencies)
68
+ end
69
+
70
+ def fetch(spec)
71
+ spec, uri = @spec_fetch_map[spec.full_name]
72
+ if spec
73
+ path = download_gem_from_uri(spec, uri)
74
+ s = Gem::Format.from_file_by_path(path).spec
75
+ spec.__swap__(s)
76
+ end
77
+ end
78
+
79
+ def install(spec)
80
+ path = cached_gem(spec)
81
+
82
+ if installed_specs[spec].any?
83
+ Bundler.ui.info "Using #{spec.name} (#{spec.version}) "
84
+ return
85
+ end
86
+
87
+ Bundler.ui.info "Installing #{spec.name} (#{spec.version}) "
88
+
89
+ install_path = Bundler.requires_sudo? ? Bundler.tmp : Gem.dir
90
+ options = { :install_dir => install_path,
91
+ :ignore_dependencies => true,
92
+ :wrappers => true,
93
+ :env_shebang => true }
94
+ options.merge!(:bin_dir => "#{install_path}/bin") unless spec.executables.nil? || spec.executables.empty?
95
+
96
+ installer = Gem::Installer.new path, options
97
+ installer.install
98
+
99
+ # SUDO HAX
100
+ if Bundler.requires_sudo?
101
+ sudo "mkdir -p #{Gem.dir}/gems #{Gem.dir}/specifications"
102
+ sudo "cp -R #{Bundler.tmp}/gems/#{spec.full_name} #{Gem.dir}/gems/"
103
+ sudo "cp -R #{Bundler.tmp}/specifications/#{spec.full_name}.gemspec #{Gem.dir}/specifications/"
104
+ spec.executables.each do |exe|
105
+ sudo "mkdir -p #{Gem.bindir}"
106
+ sudo "cp -R #{Bundler.tmp}/bin/#{exe} #{Gem.bindir}"
107
+ end
108
+ end
109
+
110
+ spec.loaded_from = "#{Gem.dir}/specifications/#{spec.full_name}.gemspec"
111
+ end
112
+
113
+ def sudo(str)
114
+ Bundler.sudo(str)
115
+ end
116
+
117
+ def cache(spec)
118
+ cached_path = cached_gem(spec)
119
+ raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
120
+ return if File.dirname(cached_path) == Bundler.app_cache.to_s
121
+ Bundler.ui.info " * #{File.basename(cached_path)}"
122
+ FileUtils.cp(cached_path, Bundler.app_cache)
123
+ end
124
+
125
+ def add_remote(source)
126
+ @remotes << normalize_uri(source)
127
+ end
128
+
129
+ def merge_remotes(source)
130
+ @remotes = []
131
+ source.remotes.each do |r|
132
+ add_remote r.to_s
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def cached_gem(spec)
139
+ possibilities = @caches.map { |p| "#{p}/#{spec.file_name}" }
140
+ possibilities.find { |p| File.exist?(p) }
141
+ end
142
+
143
+ def normalize_uri(uri)
144
+ uri = uri.to_s
145
+ uri = "#{uri}/" unless uri =~ %r'/$'
146
+ uri = URI(uri)
147
+ raise ArgumentError, "The source must be an absolute URI" unless uri.absolute?
148
+ uri
149
+ end
150
+
151
+ def fetch_specs(dependencies = nil)
152
+ Index.build do |idx|
153
+ idx.use installed_specs
154
+ idx.use cached_specs if @allow_cached || @allow_remote
155
+ idx.use remote_specs(dependencies) if @allow_remote
156
+ end
157
+ end
158
+
159
+ def installed_specs
160
+ @installed_specs ||= begin
161
+ idx = Index.new
162
+ have_bundler = false
163
+ Gem.source_index.to_a.reverse.each do |dont_use_this_var, spec|
164
+ next if spec.name == 'bundler' && spec.version.to_s != VERSION
165
+ have_bundler = true if spec.name == 'bundler'
166
+ spec.source = self
167
+ idx << spec
168
+ end
169
+
170
+ # Always have bundler locally
171
+ unless have_bundler
172
+ # We're running bundler directly from the source
173
+ # so, let's create a fake gemspec for it (it's a path)
174
+ # gemspec
175
+ bundler = Gem::Specification.new do |s|
176
+ s.name = 'bundler'
177
+ s.version = VERSION
178
+ s.platform = Gem::Platform::RUBY
179
+ s.source = self
180
+ s.loaded_from = File.expand_path("..", __FILE__)
181
+ end
182
+ idx << bundler
183
+ end
184
+ idx
185
+ end
186
+ end
187
+
188
+ def cached_specs
189
+ @cached_specs ||= begin
190
+ idx = installed_specs.dup
191
+
192
+ path = Bundler.app_cache
193
+ Dir["#{path}/*.gem"].each do |gemfile|
194
+ next if gemfile =~ /bundler\-[\d\.]+?\.gem/
195
+
196
+ begin
197
+ s ||= Gem::Format.from_file_by_path(gemfile).spec
198
+ rescue Gem::Package::FormatError
199
+ raise GemspecError, "Could not read gem at #{gemfile}. It may be corrupted."
200
+ end
201
+
202
+ s.source = self
203
+ idx << s
204
+ end
205
+ end
206
+
207
+ idx
208
+ end
209
+
210
+ def remote_specs(dependencies = nil)
211
+ @remote_specs ||= begin
212
+ idx = Index.new
213
+ old = Gem.sources
214
+
215
+ remotes.each do |uri|
216
+ Bundler.ui.info "Fetching source index for #{uri}"
217
+
218
+ @fetchers[uri] = Bundler::Fetcher.new(uri)
219
+ gem_names =
220
+ if dependencies
221
+ dependencies.map {|d| d.name }
222
+ end
223
+ @fetchers[uri].fetch_remote_specs(gem_names) do |n,v|
224
+ v.each do |name, version, platform|
225
+ next if name == 'bundler'
226
+ spec = RemoteSpecification.new(name, version, platform, @fetchers[uri])
227
+ spec.source = self
228
+ @spec_fetch_map[spec.full_name] = [spec, uri]
229
+ idx << spec
230
+ end
231
+ end
232
+ end
233
+ idx
234
+ ensure
235
+ Gem.sources = old
236
+ end
237
+ end
238
+
239
+ def download_gem_from_uri(spec, uri)
240
+ spec.fetch_platform
241
+
242
+ download_path = Bundler.requires_sudo? ? Bundler.tmp : Gem.dir
243
+ gem_path = "#{Gem.dir}/cache/#{spec.full_name}.gem"
244
+
245
+ FileUtils.mkdir_p("#{download_path}/cache")
246
+ Gem::RemoteFetcher.fetcher.download(spec, uri, download_path)
247
+
248
+ if Bundler.requires_sudo?
249
+ sudo "mkdir -p #{Gem.dir}/cache"
250
+ sudo "mv #{Bundler.tmp}/cache/#{spec.full_name}.gem #{gem_path}"
251
+ end
252
+
253
+ gem_path
254
+ end
255
+ end
256
+
257
+ class Path
258
+ attr_reader :path, :options
259
+ # Kind of a hack, but needed for the lock file parser
260
+ attr_writer :name
261
+ attr_accessor :version
262
+
263
+ DEFAULT_GLOB = "{,*/}*.gemspec"
264
+
265
+ def initialize(options)
266
+ @options = options
267
+ @glob = options["glob"] || DEFAULT_GLOB
268
+
269
+ @allow_cached = false
270
+ @allow_remote = false
271
+
272
+ if options["path"]
273
+ @path = Pathname.new(options["path"])
274
+ @path = @path.expand_path(Bundler.root) unless @path.relative?
275
+ end
276
+
277
+ @name = options["name"]
278
+ @version = options["version"]
279
+ end
280
+
281
+ def remote!
282
+ @allow_remote = true
283
+ end
284
+
285
+ def cached!
286
+ @allow_cached = true
287
+ end
288
+
289
+ def self.from_lock(options)
290
+ new(options.merge("path" => options.delete("remote")))
291
+ end
292
+
293
+ def to_lock
294
+ out = "PATH\n"
295
+ out << " remote: #{relative_path}\n"
296
+ out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
297
+ out << " specs:\n"
298
+ end
299
+
300
+ def to_s
301
+ "source at #{@path}"
302
+ end
303
+
304
+ def hash
305
+ self.class.hash
306
+ end
307
+
308
+ def eql?(o)
309
+ o.instance_of?(Path) &&
310
+ path.expand_path(Bundler.root) == o.path.expand_path(Bundler.root) &&
311
+ name == o.name &&
312
+ version == o.version
313
+ end
314
+
315
+ alias == eql?
316
+
317
+ def name
318
+ File.basename(@path.to_s)
319
+ end
320
+
321
+ def load_spec_files
322
+ index = Index.new
323
+
324
+ expanded_path = path.expand_path(Bundler.root)
325
+
326
+ if File.directory?(expanded_path)
327
+ Dir["#{expanded_path}/#{@glob}"].each do |file|
328
+ spec = Bundler.load_gemspec(file)
329
+ if spec
330
+ spec.loaded_from = file.to_s
331
+ spec.source = self
332
+ index << spec
333
+ end
334
+ end
335
+
336
+ if index.empty? && @name && @version
337
+ index << Gem::Specification.new do |s|
338
+ s.name = @name
339
+ s.source = self
340
+ s.version = Gem::Version.new(@version)
341
+ s.platform = Gem::Platform::RUBY
342
+ s.summary = "Fake gemspec for #{@name}"
343
+ s.relative_loaded_from = "#{@name}.gemspec"
344
+ if expanded_path.join("bin").exist?
345
+ binaries = expanded_path.join("bin").children.map{|c| c.basename.to_s }
346
+ s.executables = binaries
347
+ end
348
+ end
349
+ end
350
+ else
351
+ raise PathError, "The path `#{expanded_path}` does not exist."
352
+ end
353
+
354
+ index
355
+ end
356
+
357
+ def local_specs(*)
358
+ @local_specs ||= load_spec_files
359
+ end
360
+
361
+ class Installer < Gem::Installer
362
+ def initialize(spec, options = {})
363
+ @spec = spec
364
+ @bin_dir = Bundler.requires_sudo? ? "#{Bundler.tmp}/bin" : "#{Gem.dir}/bin"
365
+ @gem_dir = spec.full_gem_path
366
+ @wrappers = options[:wrappers] || true
367
+ @env_shebang = options[:env_shebang] || true
368
+ @format_executable = options[:format_executable] || false
369
+ end
370
+
371
+ def generate_bin
372
+ return if spec.executables.nil? || spec.executables.empty?
373
+
374
+ if Bundler.requires_sudo?
375
+ FileUtils.mkdir_p("#{Bundler.tmp}/bin") unless File.exist?("#{Bundler.tmp}/bin")
376
+ end
377
+ super
378
+ if Bundler.requires_sudo?
379
+ Bundler.mkdir_p "#{Gem.dir}/bin"
380
+ spec.executables.each do |exe|
381
+ Bundler.sudo "cp -R #{Bundler.tmp}/bin/#{exe} #{Gem.dir}/bin/"
382
+ end
383
+ end
384
+ end
385
+ end
386
+
387
+ def install(spec)
388
+ Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
389
+ # Let's be honest, when we're working from a path, we can't
390
+ # really expect native extensions to work because the whole point
391
+ # is to just be able to modify what's in that path and go. So, let's
392
+ # not put ourselves through the pain of actually trying to generate
393
+ # the full gem.
394
+ Installer.new(spec).generate_bin
395
+ end
396
+
397
+ alias specs local_specs
398
+
399
+ def cache(spec)
400
+ unless path.expand_path(Bundler.root).to_s.index(Bundler.root.to_s) == 0
401
+ Bundler.ui.warn " * #{spec.name} at `#{path}` will not be cached."
402
+ end
403
+ end
404
+
405
+ private
406
+
407
+ def relative_path
408
+ if path.to_s.include?(Bundler.root.to_s)
409
+ return path.relative_path_from(Bundler.root)
410
+ end
411
+
412
+ path
413
+ end
414
+
415
+ def generate_bin(spec)
416
+ gem_dir = Pathname.new(spec.full_gem_path)
417
+
418
+ # Some gem authors put absolute paths in their gemspec
419
+ # and we have to save them from themselves
420
+ spec.files = spec.files.map do |p|
421
+ next if File.directory?(p)
422
+ begin
423
+ Pathname.new(p).relative_path_from(gem_dir).to_s
424
+ rescue ArgumentError
425
+ p
426
+ end
427
+ end.compact
428
+
429
+ gem_file = Dir.chdir(gem_dir){ Gem::Builder.new(spec).build }
430
+
431
+ installer = Installer.new(spec, :env_shebang => false)
432
+ installer.build_extensions
433
+ installer.generate_bin
434
+ rescue Gem::InvalidSpecificationException => e
435
+ Bundler.ui.warn "\n#{spec.name} at #{spec.full_gem_path} did not have a valid gemspec.\n" \
436
+ "This prevents bundler from installing bins or native extensions, but " \
437
+ "that may not affect its functionality."
438
+
439
+ if !spec.extensions.empty? && !spec.email.empty?
440
+ Bundler.ui.warn "If you need to use this package without installing it from a gem " \
441
+ "repository, please contact #{spec.email} and ask them " \
442
+ "to modify their .gemspec so it can work with `gem build`."
443
+ end
444
+
445
+ Bundler.ui.warn "The validation message from Rubygems was:\n #{e.message}"
446
+ ensure
447
+ Dir.chdir(gem_dir){ FileUtils.rm_rf(gem_file) if gem_file && File.exist?(gem_file) }
448
+ end
449
+
450
+ end
451
+
452
+ class Git < Path
453
+ attr_reader :uri, :ref, :options, :submodules
454
+
455
+ def initialize(options)
456
+ super
457
+
458
+ # stringify options that could be set as symbols
459
+ %w(ref branch tag revision).each{|k| options[k] = options[k].to_s if options[k] }
460
+
461
+ @uri = options["uri"]
462
+ @ref = options["ref"] || options["branch"] || options["tag"] || 'master'
463
+ @revision = options["revision"]
464
+ @submodules = options["submodules"]
465
+ @update = false
466
+ end
467
+
468
+ def self.from_lock(options)
469
+ new(options.merge("uri" => options.delete("remote")))
470
+ end
471
+
472
+ def cache(spec)
473
+ raise GemNotFound, "Missing git repository for '#{spec.full_name}': '#{cache_path}'." unless cache_path
474
+ return if File.directory?(File.join(Bundler.app_cache, "#{base_name}-#{uri_hash}"))
475
+ Bundler.ui.info " * #{spec.full_name} revision #{revision}"
476
+ FileUtils.cp_r cache_path, Bundler.app_cache
477
+ end
478
+
479
+ def to_lock
480
+ out = "GIT\n"
481
+ out << " remote: #{@uri}\n"
482
+ out << " revision: #{revision}\n"
483
+ %w(ref branch tag submodules).each do |opt|
484
+ out << " #{opt}: #{options[opt]}\n" if options[opt]
485
+ end
486
+ out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
487
+ out << " specs:\n"
488
+ end
489
+
490
+ def eql?(o)
491
+ Git === o &&
492
+ uri == o.uri &&
493
+ ref == o.ref &&
494
+ name == o.name &&
495
+ version == o.version &&
496
+ submodules == o.submodules
497
+ end
498
+
499
+ alias == eql?
500
+
501
+ def to_s
502
+ sref = options["ref"] ? shortref_for_display(options["ref"]) : ref
503
+ "#{uri} (at #{sref})"
504
+ end
505
+
506
+ def name
507
+ File.basename(@uri, '.git')
508
+ end
509
+
510
+ def path
511
+ @install_path ||= begin
512
+ git_scope = "#{base_name}-#{shortref_for_path(revision)}"
513
+
514
+ if Bundler.requires_sudo?
515
+ Bundler.user_bundle_path.join(Bundler.ruby_scope).join(git_scope)
516
+ else
517
+ Bundler.install_path.join(git_scope)
518
+ end
519
+ end
520
+ end
521
+
522
+ def unlock!
523
+ @revision = nil
524
+ end
525
+
526
+ # TODO: actually cache git specs
527
+ def specs(*)
528
+ if allow_git_ops? && !@update
529
+ # Start by making sure the git cache is up to date
530
+ repository_cache
531
+ checkout
532
+ @update = true
533
+ end
534
+ local_specs
535
+ end
536
+
537
+ def install(spec)
538
+ Bundler.ui.info "Using #{spec.name} (#{spec.version}) from #{to_s} "
539
+
540
+ unless @installed
541
+ Bundler.ui.debug " * Checking out revision: #{ref}"
542
+ checkout if allow_git_ops?
543
+ @installed = true
544
+ end
545
+ generate_bin(spec)
546
+ end
547
+
548
+ def load_spec_files
549
+ super
550
+ rescue PathError, GitError
551
+ raise GitError, "#{to_s} is not checked out. Please run `bundle install`"
552
+ end
553
+
554
+ private
555
+
556
+ def git(command)
557
+ if allow_git_ops?
558
+ out = %x{git #{command}}
559
+
560
+ if $?.exitstatus != 0
561
+ raise GitError, "An error has occurred in git when running `git #{command}`. Cannot complete bundling."
562
+ end
563
+ out
564
+ else
565
+ raise GitError, "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, " \
566
+ "this error message could probably be more useful. Please submit a ticket at http://github.com/carlhuda/bundler/issues " \
567
+ "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
568
+ end
569
+ end
570
+
571
+ def base_name
572
+ File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)},''), ".git")
573
+ end
574
+
575
+ def shortref_for_display(ref)
576
+ ref[0..6]
577
+ end
578
+
579
+ def shortref_for_path(ref)
580
+ ref[0..11]
581
+ end
582
+
583
+ def uri_hash
584
+ if uri.to_s =~ %r{^\w+://(\w+@)?}
585
+ # Downcase the domain component of the URI
586
+ # and strip off a trailing slash, if one is present
587
+ input = URI.parse(uri).normalize.to_s.sub(%r{/$},'')
588
+ else
589
+ # If there is no URI scheme, assume it is an ssh/git URI
590
+ input = uri.to_s
591
+ end
592
+ Digest::SHA1.hexdigest(input)
593
+ end
594
+
595
+ def cache_path_scope
596
+ "#{base_name}-#{uri_hash}"
597
+ end
598
+
599
+ def cache_path
600
+ @cache_path ||= begin
601
+ git_scope = cache_path_scope
602
+
603
+ if Bundler.requires_sudo?
604
+ Bundler.user_bundle_path.join("cache/git", git_scope)
605
+ else
606
+ Bundler.cache.join("git", git_scope)
607
+ end
608
+ end
609
+ end
610
+
611
+ def repository_cache
612
+ if cached?
613
+ return if has_revision_cached?
614
+ Bundler.ui.info "Updating #{uri}"
615
+ in_cache do
616
+ git %|fetch --force --quiet --tags "#{uri}" refs/heads/*:refs/heads/*|
617
+ end
618
+ else
619
+ Bundler.ui.info "Fetching #{uri}"
620
+ FileUtils.mkdir_p(cache_path.dirname)
621
+ git %|clone "#{uri}" "#{cache_path}" --bare --no-hardlinks|
622
+ end
623
+ end
624
+
625
+ def checkout
626
+ unless File.exist?(path.join(".git"))
627
+ FileUtils.mkdir_p(path.dirname)
628
+ FileUtils.rm_rf(path)
629
+ git %|clone --no-checkout "#{cache_path}" "#{path}"|
630
+ end
631
+ Dir.chdir(path) do
632
+ git %|fetch --force --quiet --tags "#{cache_path}"|
633
+ git "reset --hard #{revision}"
634
+
635
+ if @submodules
636
+ git "submodule init"
637
+ git "submodule update"
638
+ end
639
+ end
640
+ end
641
+
642
+ def has_revision_cached?
643
+ return unless @revision
644
+ in_cache { git %|cat-file -e #{@revision}| }
645
+ true
646
+ rescue GitError
647
+ false
648
+ end
649
+
650
+ def allow_git_ops?
651
+ @allow_remote || @allow_cached
652
+ end
653
+
654
+ def revision
655
+ @revision ||= begin
656
+ if allow_git_ops?
657
+ in_cache { git("rev-parse #{ref}").strip }
658
+ else
659
+ raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application"
660
+ end
661
+ end
662
+ end
663
+
664
+ def cached?
665
+ cache_path.exist?
666
+ end
667
+
668
+ def in_cache(&blk)
669
+ cache unless cached?
670
+ Dir.chdir(cache_path, &blk)
671
+ end
672
+ end
673
+
674
+ end
675
+ end