bundler_package_git 1.1.pre.1

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.
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