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