Permalink
Browse files

Extend Bundler to accommodate Chef use cases (Configure app servers)

closes #324  Gracefully handling missing git sources for gem dependencies
closes #875  ENV['GEM_PATH'] respected but not ENV['BUNDLE_PATH']
closes #880  Can we get a :system option for :git gems?
closes #898  Add :decorate and :overwrite subcommands to git (:git_* for gem)
closes #979  Problem specifying alternate library name with :git scheme
closes #1043 Remove Git dependency from bundler.gemspec

Generally fixes up many spec's that were falsely passing.

## Closed issues notes:
 Extend Bundler to accommodate Chef use cases (Config app servers)

324  Gracefully handling missing git sources for gem dependencies
     - In the test case I cite in that bug report, the message is:
       Could not find gem 'couchdb', required by 'chef', in any of the sources
875  ENV['GEM_PATH'] respected but not ENV['BUNDLE_PATH']
     - I've resolved this for Git sources by respecting ENV['BUNDLE_INSTALL_PATH'] in the Gemfile
880  Can we get a :system option for :git gems?
     - I've added `ENV[BUNDLE_INSTALL_PATH]` and bundle `--install-path`, which
     is the fallback the OP requests..."maybe if BUNDLE_PATH could be BUNDLE_PATH without the ruby_scope appended, that might give me a fallback"
898  Add :decorate and :overwrite subcommands to git (:git_* for gem)
     `git ... :decorate=><boolean>`
     `gem ... :git_decorate=><boolean>`
     - Hash decoration of installed folder name is off by default.
     - Hash decoration of local Git cache is still decorated.
979  Problem specifying alternate library name with :git scheme
1043 Remove Git dependency from bundler.gemspec

## Related issues:
issue #1013 Problem resolving deps from a git repo
issue #1016
issue #1017 Resolver not working correctly
issue #1027 Path bug
issue #396 Add :dest and :path sub-options to :git
    - While not configurable in the Gemfile, i.e. at the method level (git, and gem)
      You can direct Bundler to install to a path without any Bundler adding
      to the path: `bundle install --install-path '/install/exactly/here'`

Signed-off-by: Hedgehog <hedgehogshiatus@gmail.com>
  • Loading branch information...
1 parent 3152238 commit 23ef7cbcc1028a8fe7ee257d120c97ceeffad6ba @hedgehog hedgehog committed Feb 16, 2011
Showing with 1,464 additions and 450 deletions.
  1. +1 −0 .gitignore
  2. +3 −4 bundler.gemspec
  3. +26 −6 lib/bundler.rb
  4. +19 −6 lib/bundler/cli.rb
  5. +4 −2 lib/bundler/definition.rb
  6. +2 −0 lib/bundler/dependency.rb
  7. +27 −2 lib/bundler/dsl.rb
  8. +6 −0 lib/bundler/installer.rb
  9. +2 −0 lib/bundler/lazy_specification.rb
  10. +1 −1 lib/bundler/lockfile_parser.rb
  11. +10 −2 lib/bundler/resolver.rb
  12. +35 −4 lib/bundler/settings.rb
  13. +154 −24 lib/bundler/source.rb
  14. +9 −1 lib/bundler/spec_set.rb
  15. +20 −9 man/bundle-install.ronn
  16. +3 −4 spec/cache/gems_spec.rb
  17. +36 −4 spec/cache/git_spec.rb
  18. +1 −1 spec/cache/path_spec.rb
  19. +7 −7 spec/install/deploy_spec.rb
  20. +1 −1 spec/install/gems/c_ext_spec.rb
  21. +3 −3 spec/install/gems/dependency_api_spec.rb
  22. +7 −7 spec/install/gems/flex_spec.rb
  23. +14 −14 spec/install/gems/groups_spec.rb
  24. +1 −1 spec/install/gems/packed_spec.rb
  25. +10 −10 spec/install/gems/platform_spec.rb
  26. +8 −4 spec/install/gems/resolving_spec.rb
  27. +29 −30 spec/install/gems/simple_case_spec.rb
  28. +3 −3 spec/install/gems/standalone_spec.rb
  29. +8 −8 spec/install/gemspec_spec.rb
  30. +321 −83 spec/install/git_spec.rb
  31. +14 −16 spec/install/path_spec.rb
  32. +2 −2 spec/lock/git_spec.rb
  33. +18 −13 spec/lock/lockfile_spec.rb
  34. +5 −5 spec/other/check_spec.rb
  35. +12 −9 spec/other/clean_spec.rb
  36. +16 −16 spec/other/exec_spec.rb
  37. +76 −0 spec/other/git_sources_spec.rb
  38. +241 −0 spec/other/install_path_spec.rb
  39. +5 −5 spec/other/open_spec.rb
  40. +5 −5 spec/other/show_spec.rb
  41. +5 −5 spec/runtime/executable_spec.rb
  42. +1 −1 spec/runtime/load_spec.rb
  43. +1 −1 spec/runtime/platform_spec.rb
  44. +41 −35 spec/runtime/require_spec.rb
  45. +30 −29 spec/runtime/setup_spec.rb
  46. +15 −1 spec/spec_helper.rb
  47. +65 −15 spec/support/builders.rb
  48. +24 −4 spec/support/helpers.rb
  49. +51 −7 spec/support/matchers.rb
  50. +23 −0 spec/support/path.rb
  51. +3 −3 spec/update/gems_spec.rb
  52. +34 −33 spec/update/git_spec.rb
  53. +6 −4 spec/update/source_spec.rb
View
@@ -1,6 +1,7 @@
# system crap
.DS_Store
.*.swp
+.idea
# files created by running the specs
tmp
View
@@ -20,10 +20,9 @@ Gem::Specification.new do |s|
s.add_development_dependency "ronn"
s.add_development_dependency "rspec", "2.0.0.rc"
- # Man files are required because they are ignored by git
- man_files = Dir.glob("lib/bundler/man/**/*")
- s.files = `git ls-files`.split("\n") + man_files
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ misc_files = %w(bundler.gemspec CHANGELOG.md ISSUES.md LICENSE Rakefile README.md UPGRADING.md)
+ s.files = Dir.glob('{bin,lib,man}/**/*') + misc_files
+ s.test_files = Dir.glob('test,spec,features/**/*')
s.executables = %w(bundle)
s.default_executable = "bundle"
s.require_paths = ["lib"]
View
@@ -87,10 +87,27 @@ def ui
end
def bundle_path
- # STDERR.puts settings.path
@bundle_path ||= Pathname.new(settings.path).expand_path(root)
end
+ def bundle_install_path
+ if install_path?
+ @bundle_install_path = install_path
+ else
+ @bundle_install_path ||= Pathname.new(@settings.install_path).expand_path(root)
+ end
+ end
+
+ def install_path
+ install_path? ?
+ Pathname.new(configure_install_path) :
+ home.join("gems")
+ end
+
+ def install_path?
+ @settings.install_path?
+ end
+
def bin_path
@bin_path ||= begin
path = settings[:bin] || "bin"
@@ -151,10 +168,6 @@ def home
bundle_path.join("bundler")
end
- def install_path
- home.join("gems")
- end
-
def specs_path
bundle_path.join("specifications")
end
@@ -278,6 +291,13 @@ def upgrade_lockfile
lockfile.rmtree
end
end
-
+ def configure_install_path
+ if @settings.install_path?
+ @settings[:install_path] = @settings.install_path
+ else
+ @settings[:install_path] = home.join("gems")
+ end
+ @settings[:install_path]
+ end
end
end
View
@@ -132,7 +132,7 @@ def check
method_option "without", :type => :array, :banner =>
"Exclude gems that are part of the specified named group."
method_option "gemfile", :type => :string, :banner =>
- "Use the specified gemfile instead of Gemfile"
+ "Specify a different path than the current folder, and/or use the specified gemfile name instead of Gemfile."
method_option "no-prune", :type => :boolean, :banner =>
"Don't remove stale gems from the cache."
method_option "no-cache", :type => :boolean, :banner =>
@@ -145,6 +145,8 @@ def check
"Generate bin stubs for bundled gems to ./bin"
method_option "path", :type => :string, :banner =>
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine"
+ method_option "install-path", :type => :string, :banner =>
+ "Specify a different install path than the system default ($BUNDLE_PATH/ruby/1.9.1/gems or $GEM_HOME/...). Bundler will remember this value for future installs on this machine"
method_option "system", :type => :boolean, :banner =>
"Install to the system location ($BUNDLE_PATH or $GEM_HOME) even if the bundle was previously installed somewhere else for this application"
method_option "frozen", :type => :boolean, :banner =>
@@ -162,6 +164,11 @@ def install
end
opts[:without].map!{|g| g.to_sym }
+ if opts['install-path']
+ opts[:install_path] = opts.delete('install-path')
+ ENV['BUNDLE_INSTALL_PATH'] = File.expand_path(opts[:install_path])
+ end
+
ENV['BUNDLE_GEMFILE'] = File.expand_path(opts[:gemfile]) if opts[:gemfile]
ENV['RB_USER_INSTALL'] = '1' if Bundler::FREEBSD
@@ -174,6 +181,12 @@ def install
exit 1
end
+ if (opts[:install_path] || opts[:deployment]) && opts[:system]
+ Bundler.ui.error "You have specified both a install path to install your gems to, \n" \
+ "as well as --system. Please choose."
+ exit 1
+ end
+
if opts[:deployment] || opts[:frozen]
unless Bundler.default_lockfile.exist?
flag = opts[:deployment] ? '--deployment' : '--frozen'
@@ -188,12 +201,12 @@ def install
Bundler.settings[:frozen] = '1'
end
-
# Can't use Bundler.settings for this because settings needs gemfile.dirname
- Bundler.settings[:path] = nil if opts[:system]
- Bundler.settings[:path] = "vendor/bundle" if opts[:deployment]
- Bundler.settings[:path] = opts[:path] if opts[:path]
+ Bundler.settings[:path] = nil if opts[:system]
+ Bundler.settings[:path] = "vendor/bundle" if opts[:deployment]
+ Bundler.settings[:path] = opts[:path] if opts[:path]
Bundler.settings[:path] ||= "bundle" if opts[:standalone]
+ Bundler.settings[:install_path] = opts[:install_path] if opts[:install_path] || ENV['BUNDLE_INSTALL_PATH']
Bundler.settings[:bin] = opts["binstubs"] if opts[:binstubs]
Bundler.settings[:disable_shared_gems] = '1' if Bundler.settings[:path]
Bundler.settings.without = opts[:without] unless opts[:without].empty?
@@ -203,7 +216,7 @@ def install
Bundler.load.cache if Bundler.root.join("vendor/cache").exist? && !options["no-cache"]
if Bundler.settings[:path]
- relative_path = Bundler.settings[:path]
+ relative_path = Bundler.settings[:install_path] ? Bundler.settings[:install_path] : Bundler.settings[:path]
relative_path = "./" + relative_path unless relative_path[0] == ?/
Bundler.ui.confirm "Your bundle is complete! " +
"It was installed into #{relative_path}"
@@ -4,12 +4,12 @@ module Bundler
class Definition
include GemHelpers
- attr_reader :dependencies, :platforms, :sources
+ attr_reader :dependencies, :platforms, :sources, :folders
def self.build(gemfile, lockfile, unlock)
unlock ||= {}
gemfile = Pathname.new(gemfile).expand_path
-
+ gemfile = gemfile + 'Gemfile' unless gemfile.file?
unless gemfile.file?
raise GemfileNotFound, "#{gemfile} not found"
end
@@ -33,11 +33,13 @@ def initialize(lockfile, dependencies, sources, unlock)
@dependencies, @sources, @unlock = dependencies, sources, unlock
@remote = false
@specs = nil
+ @folders = []
@lockfile_contents = ""
if lockfile && File.exists?(lockfile)
@lockfile_contents = Bundler.read_file(lockfile)
locked = LockfileParser.new(@lockfile_contents)
+ @folders << @lockfile_contents.scan(/ folder: (.*)$/).flatten if @lockfile_contents[/ folder: (.*)/,1]
@platforms = locked.platforms
if unlock != true
@@ -7,6 +7,7 @@ class Dependency < Gem::Dependency
attr_reader :autorequire
attr_reader :groups
attr_reader :platforms
+ attr_reader :decorate
PLATFORM_MAP = {
:ruby => Gem::Platform::RUBY,
@@ -30,6 +31,7 @@ def initialize(name, version, options = {}, &blk)
@source = options["source"]
@platforms = Array(options["platforms"])
@env = options["env"]
+ @decorate = options["decorate"].kind_of?(FalseClass) || options["git_decorate"].kind_of?(FalseClass) ? false : true
if options.key?('require')
@autorequire = Array(options['require'] || [])
View
@@ -18,6 +18,7 @@ def initialize
@groups = []
@platforms = []
@env = nil
+ @decorate = nil
end
def gemspec(opts = nil)
@@ -97,6 +98,7 @@ def path(path, options = {}, source_options = {}, &blk)
end
def git(uri, options = {}, source_options = {}, &blk)
+ @uri = uri
unless block_given?
msg = "You can no longer specify a git source by itself. Instead, \n" \
"either use the :git option on a gem, or specify the gems that \n" \
@@ -107,7 +109,15 @@ def git(uri, options = {}, source_options = {}, &blk)
" end"
raise DeprecatedError, msg
end
-
+ unless git_url?
+ msg = "You can no longer give the URI\n" \
+ "#{@uri}\n" \
+ "Instead, either use the git:// protocol, or if some other protocol, specify the trailing .git like: \n\n" \
+ " git 'file:///rails/.git' do\n" \
+ " gem 'rails'\n" \
+ " end"
+ raise DeprecatedError, msg
+ end
source Source::Git.new(_normalize_hash(options).merge("uri" => uri)), source_options, &blk
end
@@ -183,7 +193,7 @@ def _normalize_hash(opts)
def _normalize_options(name, version, opts)
_normalize_hash(opts)
- invalid_keys = opts.keys - %w(group groups git path name branch ref tag require submodules platform platforms)
+ invalid_keys = opts.keys - %w(group groups git git_decorate decorate path name branch ref tag require submodules platform platforms)
if invalid_keys.any?
plural = invalid_keys.size > 1
message = "You passed #{invalid_keys.map{|k| ':'+k }.join(", ")} "
@@ -226,9 +236,12 @@ def _normalize_options(name, version, opts)
opts["env"] ||= @env
opts["platforms"] = platforms.dup
opts["group"] = groups
+ opts["decorate"] ||= true
+ opts["git_decorate"] ||= true
end
def _deprecated_options(options)
+ @uri = options[:git] if options[:git]
if options.include?(:require_as)
raise DeprecatedError, "Please replace :require_as with :require"
elsif options.include?(:vendored_at)
@@ -237,7 +250,19 @@ def _deprecated_options(options)
raise DeprecatedError, "Please replace :only with :group"
elsif options.include?(:except)
raise DeprecatedError, "The :except option is no longer supported"
+ elsif !git_url? && !@uri.nil?
+ msg = "You can no longer give the URI\n" \
+ "#{@uri}\n" \
+ "Instead, either use the git:// protocol, or if some other protocol, specify the trailing .git like: \n\n" \
+ " git 'file:///rails/.git' do\n" \
+ " gem 'rails'\n" \
+ " end"
+ raise DeprecatedError, msg
end
end
+
+ def git_url?
+ @uri =~ /^git:\/\// || @uri =~ /\.git$/ || @uri =~ /\.git\/$/
+ end
end
end
View
@@ -29,6 +29,10 @@ def run(options)
# Since we are installing, we can resolve the definition
# using remote specs
+ if @definition.sources[0].kind_of?(::Bundler::Source::Git) && Dir.exists?(@definition.sources[0].uri)
+ local = false
+ options["local"] = true
+ end
unless local
options["local"] ?
@definition.resolve_with_cache! :
@@ -37,6 +41,8 @@ def run(options)
# Ensure that BUNDLE_PATH exists
Bundler.mkdir_p(Bundler.bundle_path) unless File.exist?(Bundler.bundle_path)
+ # Ensure that user defined BUNDLE_INSTALL_PATH exists
+ Bundler.mkdir_p(Bundler.bundle_install_path) unless File.exist?(Bundler.bundle_install_path)
# Must install gems in the order that the resolver provides
# as dependencies might actually affect the installation of
@@ -15,6 +15,7 @@ def initialize(name, version, platform, source = nil)
@platform = platform
@source = source
@specification = nil
+ @decorate = source.nil? ? true : source.decorate? # source ? source.decorate? : true # (source.kind_of?(::Bundler::Source::Git) ? source.decorate? : true)
end
def full_name
@@ -46,6 +47,7 @@ def to_lock
def __materialize__
@specification = source.specs.search(Gem::Dependency.new(name, version)).last
+ @specification
end
def respond_to?(*args)
@@ -87,7 +87,7 @@ def parse_spec(line)
if line =~ %r{^ {4}#{NAME_VERSION}$}
name, version = $1, Gem::Version.new($2)
platform = $3 ? Gem::Platform.new($3) : Gem::Platform::RUBY
- @current_spec = LazySpecification.new(name, version, platform)
+ @current_spec = @current_source ? LazySpecification.new(name, version, platform, @current_source) : LazySpecification.new(name, version, platform)
@current_spec.source = @current_source
@specs << @current_spec
elsif line =~ %r{^ {6}#{NAME_VERSION}$}
View
@@ -145,7 +145,7 @@ def initialize(index, source_requirements, base)
def debug
if ENV['DEBUG_RESOLVER']
debug_info = yield
- debug_info = debug_info.inpsect unless debug_info.is_a?(String)
+ debug_info = debug_info.inspect unless debug_info.is_a?(String)
$stderr.puts debug_info
end
end
@@ -193,9 +193,11 @@ def resolve(reqs, activated)
# that the currently activated gem satisfies the requirement.
existing = activated[current.name]
if existing || current.name == 'bundler'
+ debug { " Gem has already been activated:\n #{current.name} (#{existing})"}
# Force the current
if current.name == 'bundler' && !existing
existing = search(DepProxy.new(Gem::Dependency.new('bundler', VERSION), Gem::Platform::RUBY)).first
+ debug { " Force the current:\n #{current.name} (#{existing})"}
raise GemNotFound, %Q{Bundler could not find gem "bundler" (#{VERSION})} unless existing
existing.required_by << existing
activated['bundler'] = existing
@@ -253,26 +255,31 @@ def resolve(reqs, activated)
matching_versions = search(current)
if matching_versions.empty?
+ debug { " No matching versions:\n #{current.inspect}"}
if current.required_by.empty?
+ debug { " No required_by:\n #{@base.inspect}"}
if base = @base[current.name] and !base.empty?
version = base.first.version
message = "You have requested:\n" \
" #{current.name} #{current.requirement}\n\n" \
"The bundle currently has #{current.name} locked at #{version}.\n" \
"Try running `bundle update #{current.name}`"
elsif current.source
+ debug { " Current source:\n #{current.source} (#{current.name})"}
name = current.name
versions = @source_requirements[name][name].map { |s| s.version }
message = "Could not find gem '#{current}' in #{current.source}.\n"
if versions.any?
+ debug { " Versions:\n #{current.source} (#{current.name})"}
message << "Source contains '#{name}' at: #{versions.join(', ')}"
else
+ debug { " No versions"}
message << "Source does not contain any versions of '#{current}'"
end
else
message = "Could not find gem '#{current}' "
if @index.sources.include?(Bundler::Source::Rubygems)
- message << "in any of the gem sources listed in your Gemfile."
+ message << "in any of the gem sources listed in your Gemfile (or Gemfile.lock)."
else
message << "in the gems available on this machine."
end
@@ -320,6 +327,7 @@ def resolve_requirement(spec_group, requirement, reqs, activated)
activated[spec_group.name] = spec_group
debug { " Activating: #{spec_group.name} (#{spec_group.version})" }
+ debug { " #{spec_group.inspect}\n #{requirement.inspect}\n #{reqs.inspect}" }
debug { spec_group.required_by.map { |d| " * #{d.name} (#{d.requirement})" }.join("\n") }
dependencies = spec_group.activate_platform(requirement.__platform)
Oops, something went wrong.

0 comments on commit 23ef7cb

Please sign in to comment.