require 'optparse' require 'stove' module Stove class Cli def initialize(argv, stdin=STDIN, stdout=STDOUT, stderr=STDERR, kernel=Kernel) @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel end def execute! $stdout, $stderr = @stdout, @stderr # Parse the options hash option_parser.parse!(@argv) # Login command if @argv.first == 'login' if options[:username].nil? || options[:username].to_s.strip.empty? raise "Missing argument `--username'!" end if options[:key].nil? || options[:key].to_s.strip.empty? raise "Missing argument `--key'!" end Config.username = options[:username] Config.key = options[:key] Config.endpoint = options[:endpoint] unless options[:endpoint].nil? Config.save @stdout.puts "Successfully saved config to `#{Config.__path__}'!" @kernel.exit(0) return end # Override configs Config.endpoint = options[:endpoint] if options[:endpoint] Config.username = options[:username] if options[:username] Config.key = options[:key] if options[:key] Config.artifactory = options[:artifactory] if options[:artifactory] Config.artifactory_key = options[:artifactory_key] if options[:artifactory_key] Config.ssl_verify = options[:ssl_verify] # Set the log level Stove.log_level = options[:log_level] # Useful debugging output for when people type the wrong fucking command # and then open an issue like it's somehow my fault Stove::Log.info("Options: #{options.inspect}") Stove::Log.info("ARGV: #{@argv.inspect}") # Make a new cookbook object - this will raise an exception if there is # no cookbook at the given path cookbook = Cookbook.new(options[:path]) # Now execute the actual runners (validations and errors might occur) runner = Runner.new(cookbook, options) runner.run # If we got this far, everything was successful :) @kernel.exit(0) rescue => e Stove::Log.init($stderr) Stove::Log.error('Stove experienced an error!') Stove::Log.error(e.class.name) Stove::Log.error(e.message) Stove::Log.error(e.backtrace.join("\n")) @kernel.exit(e.respond_to?(:exit_code) ? e.exit_code : 500) ensure $stdout, $stderr = STDOUT, STDERR end private # # The option parser for handling command line flags. # # @return [OptionParser] # def option_parser @option_parser ||= OptionParser.new do |opts| opts.banner = 'Usage: stove [OPTIONS]' opts.separator '' opts.separator 'Plugins:' opts.on('--no-git', 'Do not use the git plugin. Skips tagging if specified.') do options[:no_git] = true end opts.separator '' opts.separator 'Upload Options:' opts.on('--endpoint [URL]', 'Supermarket endpoint') do |v| options[:endpoint] = v end opts.on('--username [USERNAME]', 'Username to authenticate with') do |v| options[:username] = v end opts.on('--key [PATH]', 'Path to the private key on disk') do |v| options[:key] = v end opts.on('--[no-]extended-metadata', 'Include non-backwards compatible metadata keys such as `issues_url`') do |v| options[:extended_metadata] = v end opts.on('--no-ssl-verify', 'Turn off ssl verify') do options[:ssl_verify] = false end opts.separator '' opts.separator 'Git Options:' opts.on('--remote [REMOTE]', 'Name of the git remote') do |v| options[:remote] = v end opts.on('--branch [BRANCH]', 'Name of the git branch') do |v| options[:branch] = v end opts.on('--sign', 'Sign git tags') do options[:sign] = true end opts.separator '' opts.separator 'Artifactory Options:' opts.on('--artifactory [URL]', 'URL for the Artifactory repository') do |v| options[:artifactory] = v end opts.on('--artifactory-key [KEY]', 'Artifactory API key to use') do |v| options[:artifactory_key] = if v[0] == '@' # If passed a key looking like @foo, read it as a file. This allows # passing in the key securely. IO.read(File.expand_path(v[1..-1])) else v end end opts.separator '' opts.separator 'Global Options:' opts.on('--log-level [LEVEL]', 'Set the log verbosity') do |v| options[:log_level] = v end opts.on('--path [PATH]', 'Change the path to a cookbook') do |v| options[:path] = v end opts.on_tail('-h', '--help', 'Show this message') do puts opts exit end opts.on_tail('-v', '--version', 'Show version') do puts Stove::VERSION exit(0) end end end # The options to pass to the cookbook. Includes default values # that are manipulated by the option parser. # # @return [Hash] def options @options ||= { # Upload options :endpoint => nil, :username => Config.username, :key => Config.key, :extended_metadata => true, :ssl_verify => true, # Git options :remote => 'origin', :branch => 'master', :sign => false, # Artifactory options :artifactory => false, :artifactory_key => ENV['ARTIFACTORY_API_KEY'], # Global options :log_level => :warn, :path => Dir.pwd, } end end end