honkster-bundler 1.1.pre

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