bowline-bundler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/LICENSE +20 -0
  2. data/README.markdown +291 -0
  3. data/Rakefile +45 -0
  4. data/VERSION +1 -0
  5. data/bin/bowline-bundle +59 -0
  6. data/lib/bowline/bundler/bundle.rb +323 -0
  7. data/lib/bowline/bundler/cli.rb +87 -0
  8. data/lib/bowline/bundler/dependency.rb +62 -0
  9. data/lib/bowline/bundler/dsl.rb +182 -0
  10. data/lib/bowline/bundler/environment.rb +87 -0
  11. data/lib/bowline/bundler/finder.rb +51 -0
  12. data/lib/bowline/bundler/gem_bundle.rb +11 -0
  13. data/lib/bowline/bundler/gem_ext.rb +34 -0
  14. data/lib/bowline/bundler/remote_specification.rb +53 -0
  15. data/lib/bowline/bundler/resolver.rb +250 -0
  16. data/lib/bowline/bundler/runtime.rb +2 -0
  17. data/lib/bowline/bundler/source.rb +361 -0
  18. data/lib/bowline/bundler/templates/app_script.erb +3 -0
  19. data/lib/bowline/bundler/templates/environment.erb +156 -0
  20. data/lib/bowline/bundler/templates/environment_picker.erb +4 -0
  21. data/lib/bowline/bundler.rb +35 -0
  22. data/spec/bundler/cli_spec.rb +558 -0
  23. data/spec/bundler/directory_spec.rb +255 -0
  24. data/spec/bundler/dsl_spec.rb +126 -0
  25. data/spec/bundler/fetcher_spec.rb +138 -0
  26. data/spec/bundler/git_spec.rb +266 -0
  27. data/spec/bundler/installer_spec.rb +155 -0
  28. data/spec/bundler/manifest_file_spec.rb +105 -0
  29. data/spec/bundler/manifest_spec.rb +257 -0
  30. data/spec/bundler/runtime_spec.rb +141 -0
  31. data/spec/bundler/system_gems_spec.rb +42 -0
  32. data/spec/quality_spec.rb +57 -0
  33. data/spec/resolver/engine_spec.rb +112 -0
  34. data/spec/resolver/error_spec.rb +50 -0
  35. data/spec/resolver/fake_source_index_spec.rb +43 -0
  36. data/spec/resolver/prerelease_spec.rb +113 -0
  37. data/spec/spec_helper.rb +46 -0
  38. data/spec/support/builders.rb +257 -0
  39. data/spec/support/core_ext.rb +18 -0
  40. data/spec/support/helpers.rb +126 -0
  41. data/spec/support/matchers.rb +201 -0
  42. data/spec/support/path_utils.rb +63 -0
  43. metadata +126 -0
@@ -0,0 +1,361 @@
1
+ module Bundler
2
+ class DirectorySourceError < StandardError; end
3
+ class GitSourceError < StandardError ; end
4
+ # Represents a source of rubygems. Initially, this is only gem repositories, but
5
+ # eventually, this will be git, svn, HTTP
6
+ class Source
7
+ attr_accessor :local
8
+ attr_reader :bundle
9
+
10
+ def initialize(bundle, options)
11
+ @bundle = bundle
12
+ end
13
+
14
+ private
15
+
16
+ def process_source_gems(gems)
17
+ new_gems = Hash.new { |h,k| h[k] = [] }
18
+ gems.values.each do |spec|
19
+ spec.source = self
20
+ new_gems[spec.name] << spec
21
+ end
22
+ new_gems
23
+ end
24
+ end
25
+
26
+ class GemSource < Source
27
+ attr_reader :uri
28
+
29
+ def initialize(bundle, options)
30
+ super
31
+ @uri = options[:uri]
32
+ @uri = URI.parse(@uri) unless @uri.is_a?(URI)
33
+ raise ArgumentError, "The source must be an absolute URI" unless @uri.absolute?
34
+ end
35
+
36
+ def can_be_local?
37
+ false
38
+ end
39
+
40
+ def gems
41
+ @specs ||= fetch_specs
42
+ end
43
+
44
+ def ==(other)
45
+ uri == other.uri
46
+ end
47
+
48
+ def to_s
49
+ @uri.to_s
50
+ end
51
+
52
+ class RubygemsRetardation < StandardError; end
53
+
54
+ def download(spec)
55
+ Bundler.logger.info "Downloading #{spec.full_name}.gem"
56
+
57
+ destination = bundle.gem_path
58
+
59
+ unless destination.writable?
60
+ raise RubygemsRetardation, "destination: #{destination} is not writable"
61
+ end
62
+
63
+ # Download the gem
64
+ Gem::RemoteFetcher.fetcher.download(spec, uri, destination)
65
+
66
+ # Re-read the gemspec from the downloaded gem to correct
67
+ # any errors that were present in the Rubyforge specification.
68
+ new_spec = Gem::Format.from_file_by_path(destination.join('cache', "#{spec.full_name}.gem")).spec
69
+ spec.__swap__(new_spec)
70
+ end
71
+
72
+ private
73
+
74
+ def fetch_specs
75
+ Bundler.logger.info "Updating source: #{to_s}"
76
+ build_gem_index(fetch_main_specs + fetch_prerelease_specs)
77
+ end
78
+
79
+ def build_gem_index(index)
80
+ gems = Hash.new { |h,k| h[k] = [] }
81
+ index.each do |name, version, platform|
82
+ spec = RemoteSpecification.new(name, version, platform, @uri)
83
+ spec.source = self
84
+ gems[spec.name] << spec if Gem::Platform.match(spec.platform)
85
+ end
86
+ gems
87
+ end
88
+
89
+ def fetch_main_specs
90
+ Marshal.load(Gem::RemoteFetcher.fetcher.fetch_path("#{uri}/specs.4.8.gz"))
91
+ rescue Gem::RemoteFetcher::FetchError => e
92
+ raise ArgumentError, "#{to_s} is not a valid source: #{e.message}"
93
+ end
94
+
95
+ def fetch_prerelease_specs
96
+ Marshal.load(Gem::RemoteFetcher.fetcher.fetch_path("#{uri}/prerelease_specs.4.8.gz"))
97
+ rescue Gem::RemoteFetcher::FetchError
98
+ Bundler.logger.warn "Source '#{uri}' does not support prerelease gems"
99
+ []
100
+ end
101
+ end
102
+
103
+ class SystemGemSource < Source
104
+
105
+ def self.instance
106
+ @instance
107
+ end
108
+
109
+ def self.new(*args)
110
+ @instance ||= super
111
+ end
112
+
113
+ def initialize(bundle, options = {})
114
+ super
115
+ @source = Gem::SourceIndex.from_installed_gems
116
+ end
117
+
118
+ def can_be_local?
119
+ false
120
+ end
121
+
122
+ def gems
123
+ @gems ||= process_source_gems(@source.gems)
124
+ end
125
+
126
+ def ==(other)
127
+ other.is_a?(SystemGemSource)
128
+ end
129
+
130
+ def to_s
131
+ "system"
132
+ end
133
+
134
+ def download(spec)
135
+ gemfile = Pathname.new(spec.loaded_from)
136
+ gemfile = gemfile.dirname.join('..', 'cache', "#{spec.full_name}.gem")
137
+ bundle.cache(gemfile)
138
+ end
139
+
140
+ private
141
+
142
+ end
143
+
144
+ class GemDirectorySource < Source
145
+ attr_reader :location
146
+
147
+ def initialize(bundle, options)
148
+ super
149
+ @location = options[:location]
150
+ end
151
+
152
+ def can_be_local?
153
+ true
154
+ end
155
+
156
+ def gems
157
+ @specs ||= fetch_specs
158
+ end
159
+
160
+ def ==(other)
161
+ location == other.location
162
+ end
163
+
164
+ def to_s
165
+ location.to_s
166
+ end
167
+
168
+ def download(spec)
169
+ # raise NotImplementedError
170
+ end
171
+
172
+ private
173
+
174
+ def fetch_specs
175
+ specs = Hash.new { |h,k| h[k] = [] }
176
+
177
+ Dir["#{@location}/*.gem"].each do |gemfile|
178
+ spec = Gem::Format.from_file_by_path(gemfile).spec
179
+ spec.source = self
180
+ specs[spec.name] << spec
181
+ end
182
+
183
+ specs
184
+ end
185
+ end
186
+
187
+ class DirectorySource < Source
188
+ attr_reader :location, :specs, :required_specs
189
+
190
+ def initialize(bundle, options)
191
+ super
192
+ if options[:location]
193
+ @location = Pathname.new(options[:location]).expand_path
194
+ end
195
+ @glob = options[:glob] || "**/*.gemspec"
196
+ @specs = {}
197
+ @required_specs = []
198
+ end
199
+
200
+ def add_spec(path, name, version, require_paths = %w(lib))
201
+ raise DirectorySourceError, "already have a gem defined for '#{path}'" if @specs[path.to_s]
202
+ @specs[path.to_s] = Gem::Specification.new do |s|
203
+ s.name = name
204
+ s.version = Gem::Version.new(version)
205
+ end
206
+ end
207
+
208
+ def can_be_local?
209
+ true
210
+ end
211
+
212
+ def gems
213
+ @gems ||= begin
214
+ # Locate all gemspecs from the directory
215
+ specs = locate_gemspecs
216
+ specs = merge_defined_specs(specs)
217
+
218
+ required_specs.each do |required|
219
+ unless specs.any? {|k,v| v.name == required }
220
+ raise DirectorySourceError, "No gemspec for '#{required}' was found in" \
221
+ " '#{location}'. Please explicitly specify a version."
222
+ end
223
+ end
224
+
225
+ process_source_gems(specs)
226
+ end
227
+ end
228
+
229
+ def locate_gemspecs
230
+ Dir["#{location}/#{@glob}"].inject({}) do |specs, file|
231
+ file = Pathname.new(file)
232
+ if spec = eval(File.read(file)) # and validate_gemspec(file.dirname, spec)
233
+ spec.location = file.dirname.expand_path
234
+ specs[spec.full_name] = spec
235
+ end
236
+ specs
237
+ end
238
+ end
239
+
240
+ def merge_defined_specs(specs)
241
+ @specs.each do |path, spec|
242
+ # Set the spec location
243
+ spec.location = "#{location}/#{path}"
244
+
245
+ if existing = specs.values.find { |s| s.name == spec.name }
246
+ if existing.version != spec.version
247
+ raise DirectorySourceError, "The version you specified for #{spec.name}" \
248
+ " is #{spec.version}. The gemspec is #{existing.version}."
249
+ # Not sure if this is needed
250
+ # ====
251
+ # elsif File.expand_path(existing.location) != File.expand_path(spec.location)
252
+ # raise DirectorySourceError, "The location you specified for #{spec.name}" \
253
+ # " is '#{spec.location}'. The gemspec was found at '#{existing.location}'."
254
+ end
255
+ # elsif !validate_gemspec(spec.location, spec)
256
+ # raise "Your gem definition is not valid: #{spec}"
257
+ else
258
+ specs[spec.full_name] = spec
259
+ end
260
+ end
261
+ specs
262
+ end
263
+
264
+ def validate_gemspec(path, spec)
265
+ path = Pathname.new(path)
266
+ msg = "Gemspec for #{spec.name} (#{spec.version}) is invalid:"
267
+ # Check the require_paths
268
+ (spec.require_paths || []).each do |require_path|
269
+ unless path.join(require_path).directory?
270
+ Bundler.logger.warn "#{msg} Missing require path: '#{require_path}'"
271
+ return false
272
+ end
273
+ end
274
+
275
+ # Check the executables
276
+ (spec.executables || []).each do |exec|
277
+ unless path.join(spec.bindir, exec).file?
278
+ Bundler.logger.warn "#{msg} Missing executable: '#{File.join(spec.bindir, exec)}'"
279
+ return false
280
+ end
281
+ end
282
+
283
+ true
284
+ end
285
+
286
+ def ==(other)
287
+ # TMP HAX
288
+ other.is_a?(DirectorySource)
289
+ end
290
+
291
+ def to_s
292
+ "directory: '#{location}'"
293
+ end
294
+
295
+ def download(spec)
296
+ # Nothing needed here
297
+ end
298
+ end
299
+
300
+ class GitSource < DirectorySource
301
+ attr_reader :ref, :uri, :branch
302
+
303
+ def initialize(bundle, options)
304
+ super
305
+ @uri = options[:uri]
306
+ @branch = options[:branch] || 'master'
307
+ @ref = options[:ref] || "origin/#{@branch}"
308
+ end
309
+
310
+ def location
311
+ # TMP HAX to get the *.gemspec reading to work
312
+ bundle.gem_path.join('dirs', File.basename(@uri, '.git'))
313
+ end
314
+
315
+ def gems
316
+ update
317
+ checkout
318
+ super
319
+ end
320
+
321
+ def download(spec)
322
+ # Nothing needed here
323
+ end
324
+
325
+ def to_s
326
+ "git: #{uri}"
327
+ end
328
+
329
+ private
330
+ def update
331
+ if location.directory?
332
+ fetch
333
+ else
334
+ clone
335
+ end
336
+ end
337
+
338
+ def fetch
339
+ unless local
340
+ Bundler.logger.info "Fetching git repository at: #{@uri}"
341
+ Dir.chdir(location) { `git fetch origin` }
342
+ end
343
+ end
344
+
345
+ def clone
346
+ # Raise an error if the source should run in local mode,
347
+ # but it has not been cached yet.
348
+ if local
349
+ raise SourceNotCached, "Git repository '#{@uri}' has not been cloned yet"
350
+ end
351
+
352
+ Bundler.logger.info "Cloning git repository at: #{@uri}"
353
+ FileUtils.mkdir_p(location.dirname)
354
+ `git clone #{@uri} #{location} --no-hardlinks`
355
+ end
356
+
357
+ def checkout
358
+ Dir.chdir(location) { `git checkout --quiet #{@ref}` }
359
+ end
360
+ end
361
+ end
@@ -0,0 +1,3 @@
1
+ <%= shebang bin_file_name %>
2
+ require File.expand_path(File.join(File.dirname(__FILE__), "<%= path.join("environment").relative_path_from(Pathname.new(bin_dir)) %>"))
3
+ load File.expand_path(File.join(File.dirname(__FILE__), "<%= path.join("gems", @spec.full_name, @spec.bindir, bin_file_name).relative_path_from(Pathname.new(bin_dir)) %>"))
@@ -0,0 +1,156 @@
1
+ # DO NOT MODIFY THIS FILE
2
+ module Bundler
3
+ file = File.expand_path(__FILE__)
4
+ dir = File.dirname(file)
5
+
6
+ <% unless system_gems? -%>
7
+ ENV["GEM_HOME"] = dir
8
+ ENV["GEM_PATH"] = dir
9
+
10
+ # handle 1.9 where system gems are always on the load path
11
+ if defined?(::Gem)
12
+ $LOAD_PATH.reject! do |p|
13
+ p != File.dirname(__FILE__) &&
14
+ Gem.path.any? { |gp| p.include?(gp) }
15
+ end
16
+ end
17
+
18
+ <% end -%>
19
+ ENV["PATH"] = "#{dir}/<%= bindir %><%= File::PATH_SEPARATOR %>#{ENV["PATH"]}"
20
+ ENV["RUBYOPT"] = "-r#{file} #{ENV["RUBYOPT"]}"
21
+
22
+ <% load_paths.each do |load_path| -%>
23
+ $LOAD_PATH.unshift File.expand_path("#{dir}/<%= load_path %>")
24
+ <% end -%>
25
+
26
+ @gemfile = "#{dir}/<%= filename %>"
27
+
28
+ <% if rubygems? -%>
29
+ require "rubygems" unless respond_to?(:gem) # 1.9 already has RubyGems loaded
30
+
31
+ @bundled_specs = {}
32
+ <% specs.each do |spec| -%>
33
+ <% if spec.no_bundle? -%>
34
+ gem "<%= spec.name %>", "<%= spec.version %>"
35
+ <% else -%>
36
+ <% path = spec_file_for(spec) -%>
37
+ @bundled_specs["<%= spec.name %>"] = eval(File.read("#{dir}/<%= path %>"))
38
+ @bundled_specs["<%= spec.name %>"].loaded_from = "#{dir}/<%= path %>"
39
+ <% end -%>
40
+ <% end -%>
41
+
42
+ def self.add_specs_to_loaded_specs
43
+ Gem.loaded_specs.merge! @bundled_specs
44
+ end
45
+
46
+ def self.add_specs_to_index
47
+ @bundled_specs.each do |name, spec|
48
+ Gem.source_index.add_spec spec
49
+ end
50
+ end
51
+
52
+ add_specs_to_loaded_specs
53
+ add_specs_to_index
54
+ <% end -%>
55
+
56
+ def self.require_env(env = nil)
57
+ context = Class.new do
58
+ def initialize(env) @env = env && env.to_s ; end
59
+ def method_missing(*) ; yield if block_given? ; end
60
+ def only(*env)
61
+ old, @only = @only, _combine_only(env.flatten)
62
+ yield
63
+ @only = old
64
+ end
65
+ def except(*env)
66
+ old, @except = @except, _combine_except(env.flatten)
67
+ yield
68
+ @except = old
69
+ end
70
+ def gem(name, *args)
71
+ opt = args.last.is_a?(Hash) ? args.pop : {}
72
+ only = _combine_only(opt[:only] || opt["only"])
73
+ except = _combine_except(opt[:except] || opt["except"])
74
+ files = opt[:require_as] || opt["require_as"] || name
75
+ files = [files] unless files.respond_to?(:each)
76
+
77
+ return unless !only || only.any? {|e| e == @env }
78
+ return if except && except.any? {|e| e == @env }
79
+
80
+ if files = opt[:require_as] || opt["require_as"]
81
+ files = Array(files)
82
+ files.each { |f| require f }
83
+ else
84
+ begin
85
+ require name
86
+ rescue LoadError
87
+ # Do nothing
88
+ end
89
+ end
90
+ yield if block_given?
91
+ true
92
+ end
93
+ private
94
+ def _combine_only(only)
95
+ return @only unless only
96
+ only = [only].flatten.compact.uniq.map { |o| o.to_s }
97
+ only &= @only if @only
98
+ only
99
+ end
100
+ def _combine_except(except)
101
+ return @except unless except
102
+ except = [except].flatten.compact.uniq.map { |o| o.to_s }
103
+ except |= @except if @except
104
+ except
105
+ end
106
+ end
107
+ context.new(env && env.to_s).instance_eval(File.read(@gemfile), @gemfile, 1)
108
+ end
109
+ end
110
+
111
+ <% if rubygems? -%>
112
+ module Gem
113
+ @loaded_stacks = Hash.new { |h,k| h[k] = [] }
114
+
115
+ def source_index.refresh!
116
+ super
117
+ Bundler.add_specs_to_index
118
+ end
119
+ end
120
+ <% else -%>
121
+ $" << "rubygems.rb"
122
+
123
+ module Kernel
124
+ def gem(*)
125
+ # Silently ignore calls to gem, since, in theory, everything
126
+ # is activated correctly already.
127
+ end
128
+ end
129
+
130
+ # Define all the Gem errors for gems that reference them.
131
+ module Gem
132
+ def self.ruby ; <%= Gem.ruby.inspect %> ; end
133
+ def self.dir ; @dir ||= File.dirname(File.expand_path(__FILE__)) ; end
134
+ class << self ; alias default_dir dir; alias path dir ; end
135
+ class LoadError < ::LoadError; end
136
+ class Exception < RuntimeError; end
137
+ class CommandLineError < Exception; end
138
+ class DependencyError < Exception; end
139
+ class DependencyRemovalException < Exception; end
140
+ class GemNotInHomeException < Exception ; end
141
+ class DocumentError < Exception; end
142
+ class EndOfYAMLException < Exception; end
143
+ class FilePermissionError < Exception; end
144
+ class FormatException < Exception; end
145
+ class GemNotFoundException < Exception; end
146
+ class InstallError < Exception; end
147
+ class InvalidSpecificationException < Exception; end
148
+ class OperationNotSupportedError < Exception; end
149
+ class RemoteError < Exception; end
150
+ class RemoteInstallationCancelled < Exception; end
151
+ class RemoteInstallationSkipped < Exception; end
152
+ class RemoteSourceException < Exception; end
153
+ class VerificationError < Exception; end
154
+ class SystemExitException < SystemExit; end
155
+ end
156
+ <% end -%>
@@ -0,0 +1,4 @@
1
+ require 'rbconfig'
2
+ engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
3
+ version = '1.9.1'
4
+ require File.expand_path("../#{engine}/#{version}/environment", __FILE__)
@@ -0,0 +1,35 @@
1
+ require 'pathname'
2
+ require 'logger'
3
+ require 'set'
4
+ require 'erb'
5
+ # Required elements of rubygems
6
+ require "rubygems/remote_fetcher"
7
+ require "rubygems/installer"
8
+
9
+ require "bowline/bundler/gem_bundle"
10
+ require "bowline/bundler/source"
11
+ require "bowline/bundler/finder"
12
+ require "bowline/bundler/gem_ext"
13
+ require "bowline/bundler/resolver"
14
+ require "bowline/bundler/environment"
15
+ require "bowline/bundler/dsl"
16
+ require "bowline/bundler/cli"
17
+ require "bowline/bundler/bundle"
18
+ require "bowline/bundler/dependency"
19
+ require "bowline/bundler/remote_specification"
20
+
21
+ module Bundler
22
+ VERSION = "0.0.1"
23
+
24
+ class << self
25
+ attr_writer :logger
26
+
27
+ def logger
28
+ @logger ||= begin
29
+ logger = Logger.new(STDOUT, Logger::INFO)
30
+ logger.formatter = proc {|_,_,_,msg| "#{msg}\n" }
31
+ logger
32
+ end
33
+ end
34
+ end
35
+ end