lib/berkshelf/cli.rb in berkshelf-2.0.18 vs lib/berkshelf/cli.rb in berkshelf-3.0.0.beta1

- old
+ new

@@ -5,10 +5,41 @@ require_relative 'commands/shelf' require_relative 'commands/test_command' module Berkshelf class Cli < Thor + # This is the main entry point for the CLI. It exposes the method {#execute!} to + # start the CLI. + # + # @note the arity of {#initialize} and {#execute!} are extremely important for testing purposes. It + # is a requirement to perform in-process testing with Aruba. In process testing is much faster + # than spawning a new Ruby process for each test. + class Runner + def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel) + @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel + end + + def execute! + begin + $stdin = @stdin + $stdout = @stdout + $stderr = @stderr + + Berkshelf::Cli.start(@argv) + @kernel.exit(0) + rescue Berkshelf::BerkshelfError => e + Berkshelf.ui.error e + Berkshelf.ui.error "\t" + e.backtrace.join("\n\t") if ENV['BERKSHELF_DEBUG'] + @kernel.exit(e.status_code) + rescue Ridley::Errors::RidleyError => e + Berkshelf.ui.error "#{e.class} #{e}" + Berkshelf.ui.error "\t" + e.backtrace.join("\n\t") if ENV['BERKSHELF_DEBUG'] + @kernel.exit(47) + end + end + end + class << self def dispatch(meth, given_args, given_opts, config) unless (given_args & ['-h', '--help']).empty? if given_args.length == 1 # berks --help @@ -29,11 +60,12 @@ if @options[:config] unless File.exist?(@options[:config]) raise ConfigNotFound.new(:berkshelf, @options[:config]) end - Berkshelf::Config.set_path(@options[:config]) + + Berkshelf.config = Berkshelf::Config.from_file(@options[:config]) end if @options[:debug] Berkshelf.logger.level = ::Logger::DEBUG end @@ -83,49 +115,50 @@ type: :boolean, default: false, desc: 'create a new configuration file even if one already exists.' method_option :path, type: :string, - default: Berkshelf::Config.path, + default: Berkshelf.config.path, desc: 'The path to save the configuration file' desc 'configure', 'Create a new Berkshelf configuration file' def configure path = File.expand_path(options[:path]) if File.exist?(path) && !options[:force] raise Berkshelf::ConfigExists, 'A configuration file already exists. Re-run with the --force flag if you wish to overwrite it.' end - @config = Berkshelf::Config.new(path) + config = Berkshelf::Config.new(path) [ 'chef.chef_server_url', 'chef.node_name', 'chef.client_key', 'chef.validation_client_name', 'chef.validation_key_path', 'vagrant.vm.box', 'vagrant.vm.box_url', ].each do |attribute| - default = @config.get_attribute(attribute) + default = config.get_attribute(attribute) message = "Enter value for #{attribute}" message << " (default: '#{default}')" if default message << ": " input = Berkshelf.ui.ask(message) if input.present? - @config.set_attribute(attribute, input) + config.set_attribute(attribute, input) end end - unless @config.valid? - raise InvalidConfiguration.new(@config.errors) + unless config.valid? + raise InvalidConfiguration.new(config.errors) end - @config.save + config.save + Berkshelf.config = config Berkshelf.formatter.msg "Config written to: '#{path}'" end method_option :except, @@ -153,11 +186,11 @@ berksfile.install(options) end method_option :berksfile, type: :string, - default: File.join(Dir.pwd, Berkshelf::DEFAULT_FILENAME), + default: Berkshelf::DEFAULT_FILENAME, desc: 'Path to a Berksfile to operate off of.', aliases: '-b', banner: 'PATH' method_option :except, type: :array, @@ -178,11 +211,11 @@ berksfile.update(update_options) end method_option :berksfile, type: :string, - default: File.join(Dir.pwd, Berkshelf::DEFAULT_FILENAME), + default: Berkshelf::DEFAULT_FILENAME, desc: 'Path to a Berksfile to operate off of.', aliases: '-b', banner: 'PATH' method_option :except, type: :array, @@ -207,15 +240,10 @@ method_option :skip_syntax_check, type: :boolean, default: false, desc: 'Skip Ruby syntax check when uploading cookbooks.', aliases: '-s' - method_option :skip_dependencies, - type: :boolean, - desc: 'Skip uploading dependent cookbook(s).', - default: false, - aliases: '-D' method_option :halt_on_frozen, type: :boolean, default: false, desc: 'Halt uploading and exit if the Chef Server has a frozen version of the cookbook(s).' desc 'upload [COOKBOOKS]', 'Upload the cookbook specified in the Berksfile to the Chef Server' @@ -223,41 +251,35 @@ berksfile = ::Berkshelf::Berksfile.from_file(options[:berksfile]) upload_options = Hash[options.except(:no_freeze, :berksfile)].symbolize_keys upload_options[:cookbooks] = cookbook_names upload_options[:freeze] = false if options[:no_freeze] - upload_options[:validate] = false if options[:skip_syntax_check] berksfile.upload(upload_options) end method_option :berksfile, type: :string, - default: File.join(Dir.pwd, Berkshelf::DEFAULT_FILENAME), + default: Berkshelf::DEFAULT_FILENAME, desc: 'Path to a Berksfile to operate off of.', aliases: '-b', banner: 'PATH' method_option :ssl_verify, type: :boolean, default: nil, desc: 'Disable/Enable SSL verification when locking cookbooks.' - method_option :from_file, - type: :string, - desc: 'overwrite environment attributes when using apply using local environment file', - aliases: '-f', - banner: 'PATH' desc 'apply ENVIRONMENT', 'Apply the cookbook version locks from Berksfile.lock to a Chef environment' def apply(environment_name) berksfile = ::Berkshelf::Berksfile.from_file(options[:berksfile]) lock_options = Hash[options].symbolize_keys berksfile.apply(environment_name, lock_options) end method_option :berksfile, type: :string, - default: File.join(Dir.pwd, Berkshelf::DEFAULT_FILENAME), + default: Berkshelf::DEFAULT_FILENAME, desc: 'Path to a Berksfile to operate off of.', aliases: '-b', banner: 'PATH' method_option :except, type: :array, @@ -265,40 +287,26 @@ aliases: '-e' method_option :only, type: :array, desc: 'Only cookbooks that are in these groups.', aliases: '-o' - desc 'outdated [COOKBOOKS]', 'Show outdated cookbooks (from the community site)' + desc 'outdated [COOKBOOKS]', 'List dependencies that have new versions available that satisfy their constraints' def outdated(*cookbook_names) - berksfile = ::Berkshelf::Berksfile.from_file(options[:berksfile]) + berksfile = Berkshelf::Berksfile.from_file(options[:berksfile]) Berkshelf.formatter.msg 'Listing outdated cookbooks with newer versions available...' - Berkshelf.formatter.msg 'BETA: this feature will only pull differences from the community site and will' - Berkshelf.formatter.msg 'BETA: ignore all other sources you may have defined' - Berkshelf.formatter.msg '' - outdated_options = { - cookbooks: cookbook_names - }.merge(options).symbolize_keys - - outdated = berksfile.outdated(outdated_options) - - if outdated.empty? - Berkshelf.formatter.msg 'All cookbooks up to date' - else - outdated.each do |cookbook, latest_version| - Berkshelf.formatter.msg "Cookbook '#{cookbook.name} (#{cookbook.version_constraint})' is outdated (#{latest_version})" - end - end + outdated_options = { cookbooks: cookbook_names }.merge(options).symbolize_keys + berksfile.outdated(outdated_options) end desc 'init [PATH]', 'Initialize Berkshelf in the given directory' - def init(path = Dir.pwd) + def init(path = '.') Berkshelf.formatter.deprecation '--git is now the default' if options[:git] Berkshelf.formatter.deprecation '--vagrant is now the default' if options[:vagrant] if File.chef_cookbook?(path) - options[:chefignore] = true + options[:chefignore] = true options[:metadata_entry] = true end ::Berkshelf::InitGenerator.new([path], options).invoke_all @@ -309,20 +317,20 @@ type: :string, default: Berkshelf::DEFAULT_FILENAME, desc: 'Path to a Berksfile to operate off of.', aliases: '-b', banner: 'PATH' - desc 'list', 'List all cookbooks (and dependencies) specified in the Berksfile' + desc 'list', 'List all cookbooks and their dependencies specified by your Berksfile' def list - berksfile = Berksfile.from_file(options[:berksfile]) - sources = Berkshelf.ui.mute { berksfile.resolve(berksfile.sources)[:solution] }.sort + berksfile = Berksfile.from_file(options[:berksfile]) + dependencies = Berkshelf.ui.mute { berksfile.install }.sort - if sources.empty? + if dependencies.empty? Berkshelf.formatter.msg 'There are no cookbooks installed by your Berksfile' else Berkshelf.formatter.msg 'Cookbooks installed by your Berksfile:' - print_list(sources) + print_list(dependencies) end end method_option :berksfile, type: :string, @@ -331,17 +339,16 @@ aliases: "-b", banner: "PATH" desc "show [COOKBOOK]", "Display name, author, copyright, and dependency information about a cookbook" def show(name) berksfile = Berksfile.from_file(options[:berksfile]) + cookbook = Berkshelf.ui.mute { berksfile.install(cookbooks: name) }.first - cookbook = Berkshelf.ui.mute { - berksfile.resolve(berksfile.find(name))[:solution].first - } + unless cookbook + raise CookbookNotFound, "Cookbook '#{name}' is not installed by your Berksfile" + end - raise CookbookNotFound, "Cookbook '#{name}' is not installed by your Berksfile" unless cookbook - Berkshelf.formatter.show(cookbook) end method_option :berksfile, type: :string, @@ -349,44 +356,39 @@ desc: 'Path to a Berksfile to operate off of.', aliases: '-b', banner: 'PATH' desc 'contingent COOKBOOK', 'List all cookbooks that depend on the given cookbook' def contingent(name) - berksfile = Berksfile.from_file(options[:berksfile]) + berksfile = Berksfile.from_file(options[:berksfile]) + dependencies = Berkshelf.ui.mute { berksfile.install }.sort + dependencies = dependencies.select { |cookbook| cookbook.dependencies.include?(name) } - sources = Berkshelf.ui.mute { berksfile.resolve(berksfile.sources)[:solution] }.sort - dependencies = sources.select { |cookbook| cookbook.dependencies.include?(name) } - if dependencies.empty? Berkshelf.formatter.msg "There are no cookbooks contingent upon '#{name}' defined in this Berksfile" else Berkshelf.formatter.msg "Cookbooks in this Berksfile contingent upon #{name}:" print_list(dependencies) end end method_option :berksfile, type: :string, - default: File.join(Dir.pwd, Berkshelf::DEFAULT_FILENAME), + default: Berkshelf::DEFAULT_FILENAME, desc: 'Path to a Berksfile to operate off of.', aliases: '-b', banner: 'PATH' method_option :output, type: :string, - default: Dir.pwd, + default: '.', desc: 'Path to output the tarball', aliases: '-o', banner: 'PATH' - method_option :skip_dependencies, - type: :boolean, - desc: 'Skip packaging dependent cookbook(s).', - default: false method_option :ignore_chefignore, type: :boolean, desc: 'Do not apply the chefignore to the packaged contents', default: false - desc 'package [COOKBOOK]', 'Package a cookbook (and dependencies) as a tarball' + desc "package [COOKBOOK]", "Package a cookbook and it's dependencies as a tarball" def package(name = nil) berksfile = Berkshelf::Berksfile.from_file(options[:berksfile]) berksfile.package(name, options) end @@ -399,13 +401,9 @@ desc 'cookbook NAME', 'Create a skeleton for a new cookbook' def cookbook(name) Berkshelf.formatter.deprecation '--git is now the default' if options[:git] Berkshelf.formatter.deprecation '--vagrant is now the default' if options[:vagrant] - - unless Config.instance.valid? - raise InvalidConfiguration.new(Config.instance.errors) - end ::Berkshelf::CookbookGenerator.new([File.join(Dir.pwd, name), name], options).invoke_all end tasks['cookbook'].options = Berkshelf::CookbookGenerator.class_options