class Travis::CLI::Command

Constants

DAY
HOUR
MINUTE
WEEK
YAML_ERROR

Attributes

arguments[RW]
config[RW]
debug[RW]
debug?[RW]
force_interactive[RW]
formatter[RW]
input[R]
output[R]

Public Class Methods

abstract() click to toggle source
# File lib/travis/cli/command.rb, line 58
def self.abstract
  @@abstract << self
end
abstract?() click to toggle source
# File lib/travis/cli/command.rb, line 54
def self.abstract?
  @@abstract.include? self
end
command_name() click to toggle source
# File lib/travis/cli/command.rb, line 49
def self.command_name
  name[/[^:]*$/].split(/(?=[A-Z])/).map(&:downcase).join('-')
end
description(description = nil) click to toggle source
# File lib/travis/cli/command.rb, line 66
def self.description(description = nil)
  @description = description if description
  @description ||= ""
end
new(options = {}) click to toggle source
# File lib/travis/cli/command.rb, line 90
def initialize(options = {})
  @on_signal  = []
  @formatter  = Travis::Tools::Formatter.new
  self.output = $stdout
  self.input  = $stdin
  options.each do |key, value|
    public_send("#{key}=", value) if respond_to? "#{key}="
  end
  @arguments ||= []
end
skip(*names) click to toggle source
# File lib/travis/cli/command.rb, line 62
def self.skip(*names)
  names.each { |n| define_method(n) {} }
end
subcommands(*list) click to toggle source
# File lib/travis/cli/command.rb, line 71
def self.subcommands(*list)
  return @subcommands ||= [] if list.empty?
  @subcommands = list

  define_method :run do |subcommand, *args|
    error "Unknown subcommand. Available: #{list.join(', ')}." unless list.include? subcommand.to_sym
    send(subcommand, *args)
  end

  define_method :usage do
    usages = list.map { |c| color(usage_for("#{command_name} #{c}", c), :command) }
    "\nUsage: #{usages.join("\n       ")}\n\n"
  end
end

Public Instance Methods

check_completion() click to toggle source
# File lib/travis/cli/command.rb, line 168
def check_completion
  return if skip_completion_check? or !interactive?

  if config['checked_completion']
    Tools::Completion.update_completion if config['completion_version'] != Travis::VERSION
  else
    write_to($stderr) do
      next Tools::Completion.update_completion if Tools::Completion.completion_installed?
      next unless agree('Shell completion not installed. Would you like to install it now? ') { |q| q.default = "y" }
      Tools::Completion.install_completion
    end
  end

  config['checked_completion'] = true
  config['completion_version'] = Travis::VERSION
end
check_ruby() click to toggle source
# File lib/travis/cli/command.rb, line 185
def check_ruby
  return if RUBY_VERSION > '1.9.2' or skip_version_check?
  warn "Your Ruby version is outdated, please consider upgrading, as we will drop support for #{RUBY_VERSION} soon!"
end
check_version() click to toggle source
# File lib/travis/cli/command.rb, line 140
def check_version
  last_check.clear if last_check['version'] != Travis::VERSION
  seconds_since = Time.now.to_i - last_check['at'].to_i

  return if skip_version_check?
  return if seconds_since < MINUTE

  case seconds_since
  when MINUTE .. HOUR then timeout = 0.5
  when HOUR   .. DAY  then timeout = 1.0
  when DAY    .. WEEK then timeout = 2.0
  else                     timeout = 10.0
  end

  Timeout.timeout(timeout) do
    response              = Faraday.get('https://rubygems.org/api/v1/gems/travis.json', {}, 'If-None-Match' => last_check['etag'].to_s)
    last_check['etag']    = response.headers['etag']
    last_check['version'] = JSON.parse(response.body)['version'] if response.status == 200
  end

  last_check['at'] = Time.now.to_i
  unless Tools::System.recent_version? Travis::VERSION, last_check['version']
    warn "Outdated CLI version, run `gem install travis`."
  end
rescue Timeout::Error, Faraday::Error::ClientError => error
  debug "#{error.class}: #{error.message}"
end
command_name() click to toggle source
# File lib/travis/cli/command.rb, line 218
def command_name
  self.class.command_name
end
execute() click to toggle source
# File lib/travis/cli/command.rb, line 190
def execute
  setup_trap
  check_ruby
  check_arity(method(:run), *arguments)
  load_config
  check_version
  check_completion
  setup
  run(*arguments)
  clear_error
  store_config
rescue Travis::Client::NotLoggedIn => e
  raise(e) if explode?
  error "#{e.message} - try running #{command("login#{endpoint_option}")}"
rescue Travis::Client::NotFound => e
  raise(e) if explode?
  error "resource not found (#{e.message})"
rescue Travis::Client::Error => e
  raise(e) if explode?
  error e.message
rescue StandardError => e
  raise(e) if explode?
  message = e.message
  message += color("\nfor a full error report, run #{command("report#{endpoint_option}")}", :error) if interactive?
  store_error(e)
  error(message)
end
help(info = "") click to toggle source
# File lib/travis/cli/command.rb, line 242
def help(info = "")
  parser.banner = usage
  self.class.description.sub(/./) { |c| c.upcase } + ".\n" + info + parser.to_s
end
info(line) click to toggle source
# File lib/travis/cli/command.rb, line 267
def info(line)
  write_to($stderr) do
    say color(line, :info)
  end
end
input=(io) click to toggle source
# File lib/travis/cli/command.rb, line 105
def input=(io)
  @terminal = nil
  @input = io
end
last_check() click to toggle source
# File lib/travis/cli/command.rb, line 132
def last_check
  config['last_check'] ||= {
    # migrate from old values
    'at'   => config.delete('last_version_check'),
    'etag' => config.delete('etag')
  }
end
on_signal(&block) click to toggle source
# File lib/travis/cli/command.rb, line 273
def on_signal(&block)
  @on_signal << block
end
output=(io) click to toggle source
# File lib/travis/cli/command.rb, line 110
def output=(io)
  @terminal = nil
  @output = io
end
parse(args) click to toggle source
# File lib/travis/cli/command.rb, line 122
def parse(args)
  rest = parser.parse(args)
  arguments.concat(rest)
rescue OptionParser::ParseError => e
  error e.message
end
say(data, format = nil, style = nil) click to toggle source
# File lib/travis/cli/command.rb, line 247
def say(data, format = nil, style = nil)
  terminal.say format(data, format, style)
end
setup() click to toggle source
# File lib/travis/cli/command.rb, line 129
def setup
end
terminal() click to toggle source
# File lib/travis/cli/command.rb, line 101
def terminal
  @terminal ||= HighLine.new(input, output)
end
time(info, callback = Proc.new) click to toggle source
# File lib/travis/cli/command.rb, line 258
def time(info, callback = Proc.new)
  return callback.call unless debug?
  start = Time.now
  debug(info)
  callback.call
  duration = Time.now - start
  debug("  took %.2g seconds" % duration)
end
usage() click to toggle source
# File lib/travis/cli/command.rb, line 222
def usage
  "Usage: " << color(usage_for(command_name, :run), :command)
end
usage_for(prefix, method) click to toggle source
# File lib/travis/cli/command.rb, line 226
def usage_for(prefix, method)
  usage = "travis #{prefix}"
  method = method(method)
  if method.respond_to? :parameters
    method.parameters.each do |type, name|
      name = name.upcase
      name = "[#{name}]"   if type == :opt
      name = "[#{name}..]" if type == :rest
      usage << " #{name}"
    end
  elsif method.arity != 0
    usage << " ..."
  end
  usage << " [OPTIONS]"
end
write_to(io) { || ... } click to toggle source
# File lib/travis/cli/command.rb, line 115
def write_to(io)
  io_was, self.output = output, io
  yield
ensure
  self.output = io_was if io_was
end

Private Instance Methods

check_arity(method, *args) click to toggle source
# File lib/travis/cli/command.rb, line 387
def check_arity(method, *args)
  return unless method.respond_to? :parameters
  method.parameters.each do |type, name|
    return if type == :rest
    wrong_args("few") unless args.shift or type == :opt or type == :block
  end
  wrong_args("many") if args.any?
end
clear_error() click to toggle source
# File lib/travis/cli/command.rb, line 285
def clear_error
  delete_file("error.log")
end
color(line, style) click to toggle source
# File lib/travis/cli/command.rb, line 310
def color(line, style)
  return line.to_s unless interactive?
  terminal.color(line || '???', Array(style).map(&:to_sym))
end
command(name) click to toggle source
# File lib/travis/cli/command.rb, line 336
def command(name)
  color("#{File.basename($0)} #{name}", :command)
end
config_path(name) click to toggle source
# File lib/travis/cli/command.rb, line 344
def config_path(name)
  path = ENV.fetch('TRAVIS_CONFIG_PATH') { File.expand_path('.travis', Dir.home) }
  Dir.mkdir(path, 0700) unless File.directory? path
  File.join(path, name)
end
danger_zone?(message) click to toggle source
# File lib/travis/cli/command.rb, line 396
def danger_zone?(message)
  agree(color("DANGER ZONE: ", [:red, :bold]) << message << " ") { |q| q.default = "no" }
end
delete_file(name) click to toggle source
# File lib/travis/cli/command.rb, line 356
def delete_file(name)
  return unless path = config_path(name) and File.exist? path
  debug "Deleting %p" % path
  File.delete(path)
end
empty_line() click to toggle source
# File lib/travis/cli/command.rb, line 320
def empty_line
  say "\n"
end
endpoint_option() click to toggle source
# File lib/travis/cli/command.rb, line 417
def endpoint_option
  ""
end
error(message, &block) click to toggle source
# File lib/travis/cli/command.rb, line 331
def error(message, &block)
  warn(message, &block)
  exit 1
end
format(data, format = nil, style = nil) click to toggle source
# File lib/travis/cli/command.rb, line 298
def format(data, format = nil, style = nil)
  style ||= :important
  data = format % color(data, style) if format and interactive?
  data = data.gsub(/<\[\[/, '<%=').gsub(/\]\]>/, '%>')
  data.encode! 'utf-8' if data.respond_to? :encode!
  data
end
interactive?(io = output) click to toggle source
# File lib/travis/cli/command.rb, line 315
def interactive?(io = output)
  return io.tty? if force_interactive.nil?
  force_interactive
end
load_config() click to toggle source
# File lib/travis/cli/command.rb, line 372
def load_config
  @config          = YAML.load load_file('config.yml', '{}')
  @config        ||= {}
  @original_config = @config.dup
rescue YAML_ERROR => error
  raise error if explode?
  warn "Broken config file: #{color config_path('config.yml'), :bold}"
  exit 1 unless interactive? and agree("Remove config file? ") { |q| q.default = "no" }
  @original_config, @config = {}, {}
end
load_file(name, default = nil) click to toggle source
# File lib/travis/cli/command.rb, line 350
def load_file(name, default = nil)
  return default unless path = config_path(name) and File.exist? path
  debug "Loading %p" % path
  File.read(path)
end
save_file(name, content, read_only = false) click to toggle source
# File lib/travis/cli/command.rb, line 362
def save_file(name, content, read_only = false)
  path = config_path(name)
  debug "Storing %p" % path
  File.open(path, 'w') do |file|
    file.write(content.to_s)
    file.chmod(0600) if read_only
  end
end
setup_trap() click to toggle source
# File lib/travis/cli/command.rb, line 289
def setup_trap
  [:INT, :TERM].each do |signal|
    trap signal do
      @on_signal.each { |c| c.call }
      exit 1
    end
  end
end
store_config() click to toggle source
# File lib/travis/cli/command.rb, line 383
def store_config
  save_file('config.yml', @config.to_yaml, true)
end
store_error(exception) click to toggle source
# File lib/travis/cli/command.rb, line 279
def store_error(exception)
  message = "An error occurred running `travis %s%s`:\n    %p: %s\n" % [command_name, endpoint_option, exception.class, exception.message]
  exception.backtrace.each { |l| message << "        from #{l}\n" }
  save_file("error.log", message)
end
success(line) click to toggle source
# File lib/travis/cli/command.rb, line 340
def success(line)
  say color(line, :success) if interactive?
end
template(*args) click to toggle source
# File lib/travis/cli/command.rb, line 306
def template(*args)
  File.read(*args).split('__END__', 2)[1].strip
end
warn(message) { || ... } click to toggle source
# File lib/travis/cli/command.rb, line 324
def warn(message)
  write_to($stderr) do
    say color(message, :error)
    yield if block_given?
  end
end
write_file(file, content, force = false) click to toggle source
# File lib/travis/cli/command.rb, line 400
def write_file(file, content, force = false)
  error "#{file} already exists" unless write_file?(file, force)
  File.write(file, content)
end
write_file?(file, force) click to toggle source
# File lib/travis/cli/command.rb, line 405
def write_file?(file, force)
  return true if force or not File.exist?(file)
  return false unless interactive?
  danger_zone? "Override existing #{color(file, :info)}?"
end
wrong_args(quantity) click to toggle source
# File lib/travis/cli/command.rb, line 411
def wrong_args(quantity)
  error "too #{quantity} arguments" do
    say help
  end
end