class Bundler::CLI
def self.source_root
def self.source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates')) end
def binstubs(*gems)
def binstubs(*gems) Bundler.definition.validate_ruby! Bundler.settings[:bin] = options["path"] if options["path"] Bundler.settings[:bin] = nil if options["path"] && options["path"].empty? installer = Installer.new(Bundler.root, Bundler.definition) if gems.empty? Bundler.ui.error "`bundle binstubs` needs at least one gem to run." exit 1 end gems.each do |gem_name| spec = installer.specs.find{|s| s.name == gem_name } raise GemNotFound, not_found_message(gem_name, Bundler.definition.specs) unless spec if spec.name == "bundler" Bundler.ui.warn "Sorry, Bundler can only be run via Rubygems." else installer.generate_bundler_executable_stubs(spec, :force => options[:force], :binstubs_cmd => true) end end end
def cache
def cache Bundler.definition.validate_ruby! Bundler.definition.resolve_with_cache! setup_cache_all Bundler.load.cache Bundler.settings[:no_prune] = true if options["no-prune"] Bundler.load.lock rescue GemNotFound => e Bundler.ui.error(e.message) Bundler.ui.warn "Run `bundle install` to install missing gems." exit 1 end
def check
def check ENV['BUNDLE_GEMFILE'] = File.expand_path(options[:gemfile]) if options[:gemfile] Bundler.settings[:path] = File.expand_path(options[:path]) if options[:path] begin definition = Bundler.definition definition.validate_ruby! not_installed = definition.missing_specs rescue GemNotFound, VersionConflict Bundler.ui.error "Bundler can't satisfy your Gemfile's dependencies." Bundler.ui.warn "Install missing gems with `bundle install`." exit 1 end 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 elsif !Bundler.default_lockfile.exist? && Bundler.settings[:frozen] Bundler.ui.error "This bundle has been frozen, but there is no Gemfile.lock present" exit 1 else Bundler.load.lock unless options[:"dry-run"] Bundler.ui.info "The Gemfile's dependencies are satisfied" end end
def clean
def clean if Bundler.settings[:path] || options[:force] Bundler.load.clean(options[:"dry-run"]) else Bundler.ui.error "Can only use bundle clean when --path is set or --force is set" exit 1 end end
def config(*args)
def config(*args) peek = args.shift if peek && peek =~ /^\-\-/ name, scope = args.shift, $' else name, scope = peek, "global" end unless name Bundler.ui.confirm "Settings are listed in order of priority. The top value will be used.\n" Bundler.settings.all.each do |setting| Bundler.ui.confirm "#{setting}" with_padding do Bundler.settings.pretty_values_for(setting).each do |line| Bundler.ui.info line end end Bundler.ui.confirm "" end return end case scope when "delete" Bundler.settings.set_local(name, nil) Bundler.settings.set_global(name, nil) when "local", "global" if args.empty? Bundler.ui.confirm "Settings for `#{name}` in order of priority. The top value will be used" with_padding do Bundler.settings.pretty_values_for(name).each { |line| Bundler.ui.info line } end return end locations = Bundler.settings.locations(name) if scope == "global" if local = locations[:local] Bundler.ui.info "Your application has set #{name} to #{local.inspect}. This will override the " \ "global value you are currently setting" end if env = locations[:env] Bundler.ui.info "You have a bundler environment variable for #{name} set to #{env.inspect}. " \ "This will take precedence over the global value you are setting" end if global = locations[:global] Bundler.ui.info "You are replacing the current global value of #{name}, which is currently #{global.inspect}" end end if scope == "local" && local = locations[:local] Bundler.ui.info "You are replacing the current local value of #{name}, which is currently #{local.inspect}" end if name.match(/\Alocal\./) pathname = Pathname.new(args.join(" ")) args = [pathname.expand_path.to_s] if pathname.directory? end Bundler.settings.send("set_#{scope}", name, args.join(" ")) else Bundler.ui.error "Invalid scope --#{scope} given. Please use --local or --global." exit 1 end end
def console(group = nil)
def console(group = nil) group ? Bundler.require(:default, *(group.split.map! {|g| g.to_sym })) : Bundler.require ARGV.clear require 'irb' IRB.start end
def env
def env Env.new.write($stdout) end
def exec(*args)
def exec(*args) Bundler.definition.validate_ruby! Bundler.load.setup_environment begin # Run Kernel.exec(*args) rescue Errno::EACCES Bundler.ui.error "bundler: not executable: #{args.first}" exit 126 rescue Errno::ENOENT Bundler.ui.error "bundler: command not found: #{args.first}" Bundler.ui.warn "Install missing gem executables with `bundle install`" exit 127 rescue ArgumentError Bundler.ui.error "bundler: exec needs a command to run" exit 128 end end
def gem(name)
def gem(name) name = name.chomp("/") # remove trailing slash if present namespaced_path = name.tr('-', '/') target = File.join(Dir.pwd, name) constant_name = name.split('_').map{|p| p[0..0].upcase + p[1..-1] }.join constant_name = constant_name.split('-').map{|q| q[0..0].upcase + q[1..-1] }.join('::') if constant_name =~ /-/ constant_array = constant_name.split('::') git_user_name = `git config user.name`.chomp git_user_email = `git config user.email`.chomp opts = { :name => name, :namespaced_path => namespaced_path, :constant_name => constant_name, :constant_array => constant_array, :author => git_user_name.empty? ? "TODO: Write your name" : git_user_name, :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email, :test => options[:test] } gemspec_dest = File.join(target, "#{name}.gemspec") template(File.join("newgem/Gemfile.tt"), File.join(target, "Gemfile"), opts) template(File.join("newgem/Rakefile.tt"), File.join(target, "Rakefile"), opts) template(File.join("newgem/LICENSE.txt.tt"), File.join(target, "LICENSE.txt"), opts) template(File.join("newgem/README.md.tt"), File.join(target, "README.md"), opts) template(File.join("newgem/gitignore.tt"), File.join(target, ".gitignore"), opts) template(File.join("newgem/newgem.gemspec.tt"), gemspec_dest, opts) template(File.join("newgem/lib/newgem.rb.tt"), File.join(target, "lib/#{namespaced_path}.rb"), opts) template(File.join("newgem/lib/newgem/version.rb.tt"), File.join(target, "lib/#{namespaced_path}/version.rb"), opts) if options[:bin] template(File.join("newgem/bin/newgem.tt"), File.join(target, 'bin', name), opts) end case options[:test] when 'rspec' template(File.join("newgem/rspec.tt"), File.join(target, ".rspec"), opts) template(File.join("newgem/spec/spec_helper.rb.tt"), File.join(target, "spec/spec_helper.rb"), opts) template(File.join("newgem/spec/newgem_spec.rb.tt"), File.join(target, "spec/#{namespaced_path}_spec.rb"), opts) when 'minitest' template(File.join("newgem/test/minitest_helper.rb.tt"), File.join(target, "test/minitest_helper.rb"), opts) template(File.join("newgem/test/test_newgem.rb.tt"), File.join(target, "test/test_#{namespaced_path}.rb"), opts) end if options[:test] template(File.join("newgem/.travis.yml.tt"), File.join(target, ".travis.yml"), opts) end Bundler.ui.info "Initializing git repo in #{target}" Dir.chdir(target) { `git init`; `git add .` } if options[:edit] run("#{options["edit"]} \"#{gemspec_dest}\"") # Open gemspec in editor end end
def help(cli = nil)
def help(cli = nil) case cli when "gemfile" then command = "gemfile.5" when nil then command = "bundle" else command = "bundle-#{cli}" end manpages = %w( bundle bundle-config bundle-exec bundle-install bundle-package bundle-update bundle-platform gemfile.5) if manpages.include?(command) root = File.expand_path("../man", __FILE__) if Bundler.which("man") && root !~ %r{^file:/.+!/META-INF/jruby.home/.+} Kernel.exec "man #{root}/#{command}" else puts File.read("#{root}/#{command}.txt") end else super end end
def init
def init opts = options.dup if File.exist?("Gemfile") Bundler.ui.error "Gemfile already exists at #{Dir.pwd}/Gemfile" exit 1 end if opts[:gemspec] gemspec = File.expand_path(opts[:gemspec]) unless File.exist?(gemspec) Bundler.ui.error "Gem specification #{gemspec} doesn't exist" exit 1 end spec = Gem::Specification.load(gemspec) puts "Writing new Gemfile to #{Dir.pwd}/Gemfile" File.open('Gemfile', 'wb') do |file| file << "# Generated from #{gemspec}\n" file << spec.to_gemfile end else puts "Writing new Gemfile to #{Dir.pwd}/Gemfile" FileUtils.cp(File.expand_path('../templates/Gemfile', __FILE__), 'Gemfile') end end
def initialize(*)
def initialize(*) super Bundler.rubygems.ui = UI::RGProxy.new(Bundler.ui) rescue UnknownArgumentError => e raise InvalidOption, e.message ensure options ||= {} Bundler.ui = UI::Shell.new(options) Bundler.ui.level = "debug" if options["verbose"] end
def inject(name, version, *gems)
def inject(name, version, *gems) # The required arguments allow Thor to give useful feedback when the arguments # are incorrect. This adds those first two arguments onto the list as a whole. gems.unshift(version).unshift(name) # Build an array of Dependency objects out of the arguments deps = [] gems.each_slice(2) do |gem_name, gem_version| deps << Bundler::Dependency.new(gem_name, gem_version) end added = Injector.inject(deps) if added.any? Bundler.ui.confirm "Added to Gemfile:" Bundler.ui.confirm added.map{ |g| " #{g}" }.join("\n") else Bundler.ui.confirm "All injected gems were already present in the Gemfile" end end
def install
def install opts = options.dup if opts[:without] opts[:without] = opts[:without].map{|g| g.tr(' ', ':') } end # Can't use Bundler.settings for this because settings needs gemfile.dirname ENV['BUNDLE_GEMFILE'] = File.expand_path(opts[:gemfile]) if opts[:gemfile] ENV['RB_USER_INSTALL'] = '1' if Bundler::FREEBSD # Just disable color in deployment mode Bundler.ui.shell = Thor::Shell::Basic.new if opts[:deployment] if (opts[:path] || opts[:deployment]) && opts[:system] Bundler.ui.error "You have specified both a path to install your gems to, \n" \ "as well as --system. Please choose." exit 1 end if (opts["trust-policy"]) unless (Bundler.rubygems.security_policies.keys.include?(opts["trust-policy"])) Bundler.ui.error "Rubygems doesn't know about trust policy '#{opts["trust-policy"]}'. " \ "The known policies are: #{Bundler.rubygems.security_policies.keys.join(', ')}." exit 1 end Bundler.settings["trust-policy"] = opts["trust-policy"] else Bundler.settings["trust-policy"] = nil if Bundler.settings["trust-policy"] end if opts[:deployment] || opts[:frozen] unless Bundler.default_lockfile.exist? flag = opts[:deployment] ? '--deployment' : '--frozen' raise ProductionError, "The #{flag} flag requires a Gemfile.lock. Please make " \ "sure you have checked your Gemfile.lock into version control " \ "before deploying." end if Bundler.root.join("vendor/cache").exist? opts[:local] = true end Bundler.settings[:frozen] = '1' end # When install is called with --no-deployment, disable deployment mode if opts[:deployment] == false Bundler.settings.delete(:frozen) opts[:system] = true end opts["no-cache"] ||= opts[:local] # 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] ||= "bundle" if opts[:standalone] Bundler.settings[:bin] = opts["binstubs"] if opts["binstubs"] Bundler.settings[:bin] = nil if opts["binstubs"] && opts["binstubs"].empty? Bundler.settings[:shebang] = opts["shebang"] if opts[:shebang] Bundler.settings[:no_prune] = true if opts["no-prune"] Bundler.settings[:clean] = opts[:clean] if opts[:clean] Bundler.settings.without = opts[:without] Bundler.ui.level = "warn" if opts[:quiet] Bundler::Fetcher.disable_endpoint = opts["full-index"] Bundler.settings[:disable_shared_gems] = Bundler.settings[:path] ? '1' : nil # rubygems plugins sometimes hook into the gem install process Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins) definition = Bundler.definition definition.validate_ruby! Installer.install(Bundler.root, definition, opts) Bundler.load.cache if Bundler.root.join("vendor/cache").exist? && !opts["no-cache"] if Bundler.settings[:path] absolute_path = File.expand_path(Bundler.settings[:path]) relative_path = absolute_path.sub(File.expand_path('.'), '.') Bundler.ui.confirm "Your bundle is complete!" Bundler.ui.confirm without_groups_message if Bundler.settings.without.any? Bundler.ui.confirm "It was installed into #{relative_path}" else Bundler.ui.confirm "Your bundle is complete!" Bundler.ui.confirm without_groups_message if Bundler.settings.without.any? Bundler.ui.confirm "Use `bundle show [gemname]` to see where a bundled gem is installed." end Installer.post_install_messages.to_a.each do |name, msg| Bundler.ui.confirm "Post-install message from #{name}:" Bundler.ui.info msg end clean if Bundler.settings[:clean] && Bundler.settings[:path] rescue GemNotFound, VersionConflict => e if opts[:local] && Bundler.app_cache.exist? Bundler.ui.warn "Some gems seem to be missing from your vendor/cache directory." end if Bundler.definition.rubygems_remotes.empty? Bundler.ui.warn <<-WARN, :wrap => true Your Gemfile has no gem server sources. If you need gems that are \ not already on your machine, add a line like this to your Gemfile: source 'https://rubygems.org' WARN end raise e end
def licenses
def licenses Bundler.load.specs.sort_by { |s| s.license.to_s }.reverse.each do |s| gem_name = s.name license = s.license || s.licenses if license.empty? Bundler.ui.warn "#{gem_name}: Unknown" else Bundler.ui.info "#{gem_name}: #{license}" end end end
def not_found_message(missing_gem_name, alternatives)
def not_found_message(missing_gem_name, alternatives) message = "Could not find gem '#{missing_gem_name}'." # This is called as the result of a GemNotFound, let's see if # there's any similarly named ones we can propose instead alternate_names = alternatives.map { |a| a.respond_to?(:name) ? a.name : a } suggestions = SimilarityDetector.new(alternate_names).similar_word_list(missing_gem_name) message += "\nDid you mean #{suggestions}?" if suggestions message end
def open(name)
def open(name) editor = [ENV['BUNDLER_EDITOR'], ENV['VISUAL'], ENV['EDITOR']].find{|e| !e.nil? && !e.empty? } return Bundler.ui.info("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR") unless editor spec = select_spec(name, :regex_match) return unless spec Dir.chdir(spec.full_gem_path) do command = "#{editor} #{spec.full_gem_path}" success = system(command) Bundler.ui.info "Could not run '#{command}'" unless success end end
def outdated(*gems)
def outdated(*gems) sources = Array(options[:source]) Bundler.definition.validate_ruby! current_specs = Bundler.ui.silence { Bundler.load.specs } current_dependencies = {} Bundler.ui.silence { Bundler.load.dependencies.each { |dep| current_dependencies[dep.name] = dep } } if gems.empty? && sources.empty? # We're doing a full update definition = Bundler.definition(true) else definition = Bundler.definition(:gems => gems, :sources => sources) end options["local"] ? definition.resolve_with_cache! : definition.resolve_remotely! Bundler.ui.info "" out_count = 0 # Loop through the current specs gemfile_specs, dependency_specs = current_specs.partition { |spec| current_dependencies.has_key? spec.name } [gemfile_specs.sort_by(&:name), dependency_specs.sort_by(&:name)].flatten.each do |current_spec| next if !gems.empty? && !gems.include?(current_spec.name) active_spec = definition.index[current_spec.name].sort_by { |b| b.version } if !current_spec.version.prerelease? && !options[:pre] && active_spec.size > 1 active_spec = active_spec.delete_if { |b| b.respond_to?(:version) && b.version.prerelease? } end active_spec = active_spec.last next if active_spec.nil? gem_outdated = Gem::Version.new(active_spec.version) > Gem::Version.new(current_spec.version) git_outdated = current_spec.git_version != active_spec.git_version if gem_outdated || git_outdated if out_count == 0 if options["pre"] Bundler.ui.info "Outdated gems included in the bundle (including pre-releases):" else Bundler.ui.info "Outdated gems included in the bundle:" end end spec_version = "#{active_spec.version}#{active_spec.git_version}" current_version = "#{current_spec.version}#{current_spec.git_version}" dependency = current_dependencies[current_spec.name] dependency_version = %|Gemfile specifies "#{dependency.requirement}"| if dependency && dependency.specific? Bundler.ui.info " * #{active_spec.name} (#{spec_version} > #{current_version}) #{dependency_version}".rstrip out_count += 1 end Bundler.ui.debug "from #{active_spec.loaded_from}" end if out_count.zero? Bundler.ui.info "Your bundle is up to date!\n" else exit 1 end end
def package
def package Bundler.ui.level = "warn" if options[:quiet] setup_cache_all install # TODO: move cache contents here now that all bundles are locked Bundler.load.cache end
def platform
def platform platforms, ruby_version = Bundler.ui.silence do [ Bundler.definition.platforms.map {|p| "* #{p}" }, Bundler.definition.ruby_version ] end output = [] if options[:ruby] if ruby_version output << ruby_version else output << "No ruby version specified" end else output << "Your platform is: #{RUBY_PLATFORM}" output << "Your app has gems that work on these platforms:\n#{platforms.join("\n")}" if ruby_version output << "Your Gemfile specifies a Ruby version requirement:\n* #{ruby_version}" begin Bundler.definition.validate_ruby! output << "Your current platform satisfies the Ruby version requirement." rescue RubyVersionMismatch => e output << e.message end else output << "Your Gemfile does not specify a Ruby version requirement." end end Bundler.ui.info output.join("\n\n") end
def select_spec(name, regex_match = nil)
def select_spec(name, regex_match = nil) specs = [] regexp = Regexp.new(name) if regex_match Bundler.definition.specs.each do |spec| return spec if spec.name == name specs << spec if regexp && spec.name =~ regexp end case specs.count when 0 raise GemNotFound, not_found_message(name, Bundler.definition.dependencies) when 1 specs.first else specs.each_with_index do |spec, index| Bundler.ui.info "#{index.succ} : #{spec.name}", true end Bundler.ui.info '0 : - exit -', true input = Bundler.ui.ask('> ') (num = input.to_i) > 0 ? specs[num - 1] : nil end end
def setup_cache_all
def setup_cache_all Bundler.settings[:cache_all] = options[:all] if options.key?("all") if Bundler.definition.sources.any? { |s| !s.is_a?(Source::Rubygems) } && !Bundler.settings[:cache_all] Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \ "to package them as well, please pass the --all flag. This will be the default " \ "on Bundler 2.0." end end
def show(gem_name = nil)
def show(gem_name = nil) Bundler.ui.silence do Bundler.definition.validate_ruby! Bundler.load.lock end if gem_name if gem_name == "bundler" path = File.expand_path("../../..", __FILE__) else spec = select_spec(gem_name, :regex_match) return unless spec path = spec.full_gem_path if !File.directory?(path) Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at:" end end return Bundler.ui.info(path) end if options[:paths] Bundler.load.specs.sort_by { |s| s.name }.map do |s| Bundler.ui.info s.full_gem_path end else Bundler.ui.info "Gems included by the bundle:" Bundler.load.specs.sort_by { |s| s.name }.each do |s| Bundler.ui.info " * #{s.name} (#{s.version}#{s.git_version})" end end end
def update(*gems)
def update(*gems) sources = Array(options[:source]) Bundler.ui.level = "warn" if options[:quiet] if gems.empty? && sources.empty? # We're doing a full update Bundler.definition(true) else # cycle through the requested gems, just to make sure they exist lock = Bundler.read_file(Bundler.default_lockfile) names = LockfileParser.new(lock).specs.map{ |s| s.name } gems.each do |g| next if names.include?(g) raise GemNotFound, not_found_message(g, names) end Bundler.definition(:gems => gems, :sources => sources) end Bundler::Fetcher.disable_endpoint = options["full-index"] opts = {"update" => true, "local" => options[:local]} # rubygems plugins sometimes hook into the gem install process Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins) Bundler.definition.validate_ruby! Installer.install Bundler.root, Bundler.definition, opts Bundler.load.cache if Bundler.root.join("vendor/cache").exist? clean if Bundler.settings[:clean] && Bundler.settings[:path] Bundler.ui.confirm "Your bundle is updated!" Bundler.ui.confirm without_groups_message if Bundler.settings.without.any? end
def version
def version Bundler.ui.info "Bundler version #{Bundler::VERSION}" end
def viz
def viz require 'graphviz' output_file = File.expand_path(options[:file]) graph = Graph.new(Bundler.load, output_file, options[:version], options[:requirements], options[:format]) graph.viz 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 "Please install GraphViz. On a Mac with homebrew, you can run `brew install graphviz`." else raise end end
def without_groups_message
def without_groups_message groups = Bundler.settings.without group_list = [groups[0...-1].join(", "), groups[-1..-1]]. reject{|s| s.to_s.empty? }.join(" and ") group_str = (groups.size == 1) ? "group" : "groups" "Gems in the #{group_str} #{group_list} were not installed." end