bundler-maglev- 1.0.21

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