lib/bundler/cli.rb in bundler-0.9.26 vs lib/bundler/cli.rb in bundler-1.0.0.beta.1
- old
+ new
@@ -5,13 +5,26 @@
# Work around a RubyGems bug
Gem.configuration
module Bundler
class CLI < Thor
+ def initialize(*)
+ super
+ Bundler.ui = UI::Shell.new(shell)
+ Gem::DefaultUserInteraction.ui = UI::RGProxy.new(Bundler.ui)
+ end
+
check_unknown_options! unless ARGV.include?("exec")
+ default_task :install
+
desc "init", "Generates a Gemfile into the current working directory"
+ long_desc <<-D
+ Init generates a default Gemfile in the current working directory. When adding a
+ Gemfile to a gem with a gemspec, the --gemspec option will automatically add each
+ dependency listed in the gemspec file to the newly created Gemfile.
+ D
method_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile"
def init
opts = options.dup
if File.exist?("Gemfile")
Bundler.ui.error "Gemfile already exists at #{Dir.pwd}/Gemfile"
@@ -34,138 +47,151 @@
puts "Writing new Gemfile to #{Dir.pwd}/Gemfile"
FileUtils.cp(File.expand_path('../templates/Gemfile', __FILE__), 'Gemfile')
end
end
- def initialize(*)
- super
- Bundler.ui = UI::Shell.new(shell)
- Gem::DefaultUserInteraction.ui = UI::RGProxy.new(Bundler.ui)
- end
-
desc "check", "Checks if the dependencies listed in Gemfile are satisfied by currently installed gems"
+ long_desc <<-D
+ Check searches the local machine for each of the gems requested in the Gemfile. If
+ all gems are found, Bundler prints a success message and exits with a status of 0.
+ If not, the first missing gem is listed and Bundler exits status 1.
+ D
def check
- env = Bundler.runtime
- # Check top level dependencies
- missing = env.dependencies.select { |d| env.index.search(d).empty? }
- if missing.any?
- Bundler.ui.error "The following dependencies are missing"
- missing.each do |d|
- Bundler.ui.error " * #{d}"
- end
+ not_installed = Bundler.definition.missing_specs
+
+ if not_installed.any?
+ Bundler.ui.error "The following gems are missing"
+ not_installed.each { |s| Bundler.ui.error " * #{s.name} (#{s.version})" }
Bundler.ui.warn "Install missing gems with `bundle install`"
exit 1
else
- not_installed = env.requested_specs.select { |spec| !spec.loaded_from }
-
- if not_installed.any?
- not_installed.each { |s| Bundler.ui.error "#{s.name} (#{s.version}) is cached, but not installed" }
- Bundler.ui.warn "Install missing gems with `bundle install`"
- exit 1
- else
- Bundler.ui.info "The Gemfile's dependencies are satisfied"
- end
+ Bundler.ui.info "The Gemfile's dependencies are satisfied"
end
end
desc "install", "Install the current environment to the system"
- method_option "without", :type => :array, :banner => "Exclude gems that are part of the specified named group."
- method_option "relock", :type => :boolean, :banner => "Unlock, install the gems, and relock."
- method_option "disable-shared-gems", :type => :boolean, :banner => "Do not use any shared gems, such as the system gem repository."
- method_option "gemfile", :type => :string, :banner => "Use the specified gemfile instead of Gemfile"
- method_option "no-cache", :type => :boolean, :banner => "Don't update the existing gem cache."
- method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
+ long_desc <<-D
+ Install will install all of the gems in the current bundle, making them available
+ for use. In a freshly checked out repository, this command will give you the same
+ gem versions as the last person who updated the Gemfile and ran `bundle update`.
+
+ Passing [DIR] to install (e.g. vendor) will cause the unpacked gems to be installed
+ into the [DIR] directory rather than into system gems.
+
+ If the bundle has already been installed, bundler will tell you so and then exit.
+ D
+ method_option "without", :type => :array, :banner =>
+ "Exclude gems that are part of the specified named group."
+ method_option "disable-shared-gems", :type => :boolean, :banner =>
+ "Do not use any shared gems, such as the system gem repository."
+ method_option "gemfile", :type => :string, :banner =>
+ "Use the specified gemfile 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 =>
+ "Don't update the existing gem cache."
def install(path = nil)
opts = options.dup
opts[:without] ||= []
opts[:without].map! { |g| g.to_sym }
# Can't use Bundler.settings for this because settings needs gemfile.dirname
ENV['BUNDLE_GEMFILE'] = opts[:gemfile] if opts[:gemfile]
Bundler.settings[:path] = path if path
- Bundler.settings[:disable_shared_gems] = '1' if options["disable-shared-gems"]
+ Bundler.settings[:disable_shared_gems] = '1' if options["disable-shared-gems"] || path
Bundler.settings.without = opts[:without]
- remove_lockfiles if options[:relock]
-
- begin
- Installer.install(Bundler.root, Bundler.definition, opts)
- rescue GemfileChanged
- raise GemfileChanged, "You changed your Gemfile after locking. " +
- "Please run `bundle install --relock`."
- end
-
- lock if options[:relock]
- cache if Bundler.root.join("vendor/cache").exist? && !options[:no_cache]
+ Installer.install(Bundler.root, Bundler.definition, opts)
+ cache if Bundler.root.join("vendor/cache").exist?
Bundler.ui.confirm "Your bundle is complete! " +
"Use `bundle show [gemname]` to see where a bundled gem is installed."
rescue GemNotFound => e
- if Bundler.definition.sources.empty?
+ if Bundler.definition.no_sources?
Bundler.ui.warn "Your Gemfile doesn't have any sources. You can add one with a line like 'source :gemcutter'"
end
raise e
end
- desc "lock", "Locks the bundle to the current set of dependencies, including all child dependencies."
- def lock
- if locked?
- Bundler.ui.info("Your bundle is already locked, relocking.")
- remove_lockfiles
+ desc "update", "update the current environment"
+ long_desc <<-D
+ Update will install the newest versions of the gems listed in the Gemfile. Use
+ update when you have changed the Gemfile, or if you want to get the newest
+ possible versions of the gems in the bundle.
+ D
+ method_option "source", :type => :array, :banner => "Update a specific source (and all gems associated with it)"
+ def update(*gems)
+ sources = Array(options[:source])
+
+ if gems.empty? && sources.empty?
+ # We're doing a full update
+ FileUtils.rm Bundler.root.join("Gemfile.lock")
+ else
+ Bundler.definition(:gems => gems, :sources => sources)
end
- Bundler.runtime.lock
- Bundler.ui.confirm("Your bundle is now locked. " +
- "Use `bundle show [gemname]` to list the gems in the environment.")
- rescue GemNotFound, VersionConflict => e
- Bundler.ui.error(e.message)
- Bundler.ui.warn "Run `bundle install` to install missing gems."
- exit 128
+ Installer.install Bundler.root, Bundler.definition
end
+ desc "lock", "Locks the bundle to the current set of dependencies, including all child dependencies."
+ def lock
+ Bundler.ui.warn "Lock is deprecated. Your bundle is now locked whenever you run `bundle install`."
+ end
+
desc "unlock", "Unlock the bundle. This allows gem versions to be changed."
def unlock
- if locked?
- remove_lockfiles
- Bundler.ui.info("Your bundle is now unlocked. The dependencies may be changed.")
- else
- Bundler.ui.info("Your bundle is not currently locked.")
- end
+ Bundler.ui.warn "Unlock is deprecated. To update to newer gem versions, use `bundle update`."
end
desc "show [GEM]", "Shows all gems that are part of the bundle, or the path to a given gem"
+ long_desc <<-D
+ Show lists the names and versions of all gems that are required by your Gemfile.
+ Calling show with [GEM] will list the exact location of that gem on your machine.
+ D
def show(gem_name = nil)
if gem_name
Bundler.ui.info locate_gem(gem_name)
else
Bundler.ui.info "Gems included by the bundle:"
- Bundler.runtime.specs.sort_by { |s| s.name }.each do |s|
+ Bundler.load.specs.sort_by { |s| s.name }.each do |s|
Bundler.ui.info " * #{s.name} (#{s.version}#{s.git_version})"
end
end
end
map %w(list) => "show"
desc "cache", "Cache all the gems to vendor/cache"
method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
def cache
- Bundler.runtime.cache
- Bundler.runtime.prune_cache unless options[:no_prune]
+ Bundler.load.cache
+ Bundler.load.prune_cache unless options[:no_prune]
rescue GemNotFound => e
Bundler.ui.error(e.message)
Bundler.ui.warn "Run `bundle install` to install missing gems."
exit 128
end
desc "package", "Locks and then caches all of the gems into vendor/cache"
method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
+ long_desc <<-D
+ The package command will copy the .gem files for every gem in the bundle into the
+ directory ./vendor/cache. If you then check that directory into your source
+ control repository, others who check out your source will be able to install the
+ bundle without having to download any additional gems.
+ D
def package
- lock
+ install
+ # TODO: move cache contents here now that all bundles are locked
cache
end
map %w(pack) => :package
desc "exec", "Run the command in context of the bundle"
+ long_desc <<-D
+ Exec runs a command, providing it access to the gems in the bundle. While using
+ bundle exec you can require and call the bundled gems as if they were installed
+ into the systemwide Rubygems repository.
+ D
def exec(*)
ARGV.delete("exec")
# Set PATH
paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
@@ -183,11 +209,11 @@
ENV["RUBYOPT"] = rubyopt.join(' ')
end
begin
# Run
- Kernel.exec *ARGV
+ Kernel.exec(*ARGV)
rescue Errno::EACCES
Bundler.ui.error "bundler: not executable: #{ARGV.first}"
rescue Errno::ENOENT
Bundler.ui.error "bundler: command not found: #{ARGV.first}"
Bundler.ui.warn "Install missing gem binaries with `bundle install`"
@@ -220,22 +246,38 @@
def version
Bundler.ui.info "Bundler version #{Bundler::VERSION}"
end
map %w(-v --version) => :version
- private
+ desc 'viz', "Generates a visual dependency graph"
+ method_option :file, :type => :string, :default => 'gem_graph.png', :aliases => '-f', :banner => "Show the version with each gem name."
+ method_option :version, :type => :boolean, :default => false, :aliases => '-v', :banner => "Show the version with each gem name."
+ method_option :requirements, :type => :boolean, :default => false, :aliases => '-r', :banner => "Show the requirement for each dependency."
+ def viz
+ output_file = File.expand_path(options[:file])
+ graph = Graph.new( Bundler.load )
- def locked?
- File.exist?("#{Bundler.root}/Gemfile.lock") || File.exist?("#{Bundler.root}/.bundle/environment.rb")
+ begin
+ graph.viz(output_file, options[:version], options[:requirements])
+ Bundler.ui.info output_file
+ rescue LoadError => e
+ Bundler.ui.error e.inspect
+ Bundler.ui.warn "Make sure you have the graphviz ruby gem. You can install it with:"
+ Bundler.ui.warn "`gem install ruby-graphviz`"
+ rescue StandardError => e
+ if e.message =~ /GraphViz not installed or dot not in PATH/
+ Bundler.ui.error e.message
+ Bundler.ui.warn "The ruby graphviz gem requires GraphViz to be installed"
+ else
+ raise
+ end
+ end
end
- def remove_lockfiles
- FileUtils.rm_f "#{Bundler.root}/Gemfile.lock"
- FileUtils.rm_f "#{Bundler.root}/.bundle/environment.rb"
- end
+ private
def locate_gem(name)
- spec = Bundler.runtime.specs.find{|s| s.name == name }
+ spec = Bundler.load.specs.find{|s| s.name == name }
raise GemNotFound, "Could not find gem '#{name}' in the current bundle." unless spec
spec.full_gem_path
end
def self.printable_tasks