honkster-bundler 1.1.pre

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