class ForemanMaintain::Reporter::CLIReporter

Attributes

last_line[R]
max_length[R]
select_option_counter[RW]

Public Class Methods

new(stdout = $stdout, stdin = $stdin, options = {}) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 75
def initialize(stdout = $stdout, stdin = $stdin, options = {})
  @stdout = stdout
  @stdin = stdin
  options.validate_options!(:assumeyes)
  @assumeyes = options.fetch(:assumeyes, false)
  @plaintext = options.fetch(:plaintext, false)
  @hl = HighLine.new(@stdin, @stdout)
  @max_length = 80
  @line_char = '-'
  @cell_char = '|'
  @spinner = Spinner.new(self)
  @spinner.start_spinner if @stdout.tty?
  @last_line = ''
  @select_option_counter = 0
end

Public Instance Methods

after_execution_finishes(execution) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 152
def after_execution_finishes(execution)
  puts_status(execution.status)
  puts(execution.output) unless execution.output.empty?
  puts(already_run_msg) if execution.status == :already_run
  hline
  new_line_if_needed
  logger.info("--- Execution step '#{execution.name}' finished ---")
end
after_scenario_finishes(scenario) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 161
def after_scenario_finishes(scenario)
  scenario_failure_message(scenario)
  puts "\n"
  logger.info("=== Scenario '#{scenario.description || scenario.class}' finished ===")
end
ask(message, options = {}) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 122
def ask(message, options = {})
  new_line_if_needed
  options.validate_options!(:password)
  # the answer is confirmed by ENTER which will emit a new line
  @new_line_next_time = false
  @last_line = ''
  # add space at the end as otherwise highline would add new line there :/
  message = "#{message} " unless /\s\Z/.match?(message)
  answer = @hl.ask(message) { |q| q.echo = false if options[:password] }
  answer&.to_s&.chomp
end
ask_decision(message, actions_msg: 'y(yes), n(no), q(quit)', ignore_assumeyes: false, run_strategy: :fail_fast) click to toggle source

rubocop:disable Metrics/LineLength

# File lib/foreman_maintain/reporter/cli_reporter.rb, line 198
def ask_decision(message, actions_msg: 'y(yes), n(no), q(quit)', ignore_assumeyes: false, run_strategy: :fail_fast)
  actions_msg = 'y(yes), n(no)' if run_strategy == :fail_slow
  if !ignore_assumeyes && assumeyes?
    print("#{message} (assuming yes)\n")
    return :yes
  end
  until_valid_decision do
    filter_decision(ask("#{message}, [#{actions_msg}]"))
  end
ensure
  clear_line
end
ask_to_select(message, steps, run_strategy) click to toggle source

rubocop:disable Metrics/MethodLength

# File lib/foreman_maintain/reporter/cli_reporter.rb, line 222
def ask_to_select(message, steps, run_strategy)
  if assumeyes?
    step = steps[@select_option_counter]
    @select_option_counter += 1
    puts("(assuming option #{@select_option_counter})")
    return step
  end

  until_valid_decision do
    actions = (run_strategy == :fail_slow) ? 'n(next)' : 'n(next), q(quit)'
    answer = ask("#{message}, [#{actions}]")
    if answer =~ /^\d+$/ && (answer.to_i - 1) < steps.size
      steps[answer.to_i - 1]
    else
      decision = filter_decision(answer)
      if decision == :yes
        steps.first
      else
        decision
      end
    end
  end
ensure
  clear_line
end
assumeyes?() click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 175
def assumeyes?
  @assumeyes
end
before_execution_starts(execution) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 97
def before_execution_starts(execution)
  label = execution.step.label.to_s.tr('_', '-')
  logger.info("--- Execution step '#{execution.name}' [#{label}] started ---")
  puts(execution_info(execution, ''))
end
before_scenario_starts(scenario) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 91
def before_scenario_starts(scenario)
  logger.info("=== Scenario '#{scenario.description || scenario.class}' started ===")
  puts "Running #{scenario.description || scenario.class}"
  hline('=')
end
clear_line() click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 167
def clear_line
  if plaintext?
    print "\n"
  else
    print "\r" + ' ' * @max_length + "\r"
  end
end
execution_info(execution, text) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 256
def execution_info(execution, text)
  prefix = "#{execution.name}:"
  "#{prefix} #{text}"
end
filter_decision(answer) click to toggle source

rubocop:enable Metrics/LineLength

# File lib/foreman_maintain/reporter/cli_reporter.rb, line 212
def filter_decision(answer)
  decision = nil
  answer   = answer.downcase
  DECISION_MAPPER.each do |options, decision_label|
    decision = decision_label if options.include?(answer)
  end
  decision
end
hline(line_char = @line_char) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 289
def hline(line_char = @line_char)
  puts line_char * @max_length
end
multiple_steps_decision(steps, run_strategy) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 189
def multiple_steps_decision(steps, run_strategy)
  puts 'There are multiple steps to proceed:'
  steps.each_with_index do |step, index|
    puts "#{index + 1}) #{step.runtime_message}"
  end
  ask_to_select('Select step to continue', steps, run_strategy)
end
new_line_if_needed() click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 134
def new_line_if_needed
  if @new_line_next_time
    @stdout.print("\n")
    @stdout.flush
    @new_line_next_time = false
  end
end
print(string) click to toggle source
puts(string) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 110
def puts(string)
  # we don't print the new line right away, as we want to be able to put
  # the status label at the end of the last line, if possible.
  # Therefore, we just mark that we need to print the new line next time
  # we are printing something.
  new_line_if_needed
  @stdout.print(string)
  @stdout.flush
  @new_line_next_time = true
  record_last_line(string)
end
puts_status(status) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 261
def puts_status(status)
  label_offset = 10
  padding = @max_length - @last_line.to_s.size - label_offset
  if padding < 0
    new_line_if_needed
    padding = @max_length - label_offset
  end
  @stdout.print(' ' * padding + status_label(status))
  @new_line_next_time = true
end
record_last_line(string) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 293
def record_last_line(string)
  @last_line = string.lines.to_a.last
end
single_step_decision(step, run_strategy) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 179
def single_step_decision(step, run_strategy)
  answer = ask_decision("Continue with step [#{step.runtime_message}]?",
    run_strategy: run_strategy)
  if answer == :yes
    step
  else
    answer
  end
end
status_label(status) click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 272
def status_label(status)
  mapping = { :success => { :label => '[OK]', :color => :green },
              :fail => { :label => '[FAIL]', :color => :red },
              :abort => { :label => '[ABORTED]', :color => :red },
              :running => { :label => '[RUNNING]', :color => :blue },
              :skipped => { :label => '[SKIPPED]', :color => :yellow },
              :already_run => { :label => '[ALREADY RUN]', :color => :yellow },
              :warning => { :label => '[WARNING]', :color => :yellow },
              :info_warning => { :label => '[WARNING]', :color => :yellow } }
  properties = mapping[status]
  if @plaintext
    properties[:label]
  else
    @hl.color(properties[:label], properties[:color], :bold)
  end
end
until_valid_decision() { |until decision| ... } click to toggle source

loop over the block until it returns some non-false value

# File lib/foreman_maintain/reporter/cli_reporter.rb, line 250
def until_valid_decision
  decision = nil
  decision = yield until decision
  decision
end
with_spinner(message) { |spinner| ... } click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 142
def with_spinner(message)
  new_line_if_needed
  @spinner.activate
  @spinner.update(message)
  yield @spinner
ensure
  @spinner.deactivate
  @new_line_next_time = true
end

Private Instance Methods

already_run_msg() click to toggle source
# File lib/foreman_maintain/reporter/cli_reporter.rb, line 374
def already_run_msg
  'The step was skipped as it was already run and it is marked' \
  ' as run_once. Use --force to enforce the execution.'
end
format_steps(steps, join_with = ', ', indent = 0) click to toggle source

rubocop:enable Metrics/MethodLength, Metrics/AbcSize

# File lib/foreman_maintain/reporter/cli_reporter.rb, line 370
def format_steps(steps, join_with = ', ', indent = 0)
  steps.map { |s| "#{' ' * indent}[#{s.label_dashed}]" }.join(join_with)
end
scenario_failure_message(scenario) click to toggle source

rubocop:disable Metrics/MethodLength, Metrics/AbcSize

# File lib/foreman_maintain/reporter/cli_reporter.rb, line 300
      def scenario_failure_message(scenario)
        return if scenario.passed? && !scenario.warning?

        message = []
        message << <<~MESSAGE
          Scenario [#{scenario.description}] failed.
        MESSAGE
        recommend = []

        steps_with_abort = scenario.steps_with_abort(:whitelisted => false)
        unless steps_with_abort.empty?
          message << format(<<~MESSAGE, format_steps(steps_with_abort, "\n", 2))
            The processing was aborted by user during the following steps:

            %s
          MESSAGE
        end

        steps_with_error = scenario.steps_with_error(:whitelisted => false)
        steps_with_skipped = scenario.steps_with_skipped(:whitelisted => true)
        not_skippable_steps = scenario.steps_with_error.select do |step|
          step.metadata[:do_not_whitelist] == true
        end

        steps_to_whitelist = steps_with_error + steps_with_skipped - not_skippable_steps
        unless steps_with_error.empty?
          message << format(<<~MESSAGE, format_steps(steps_with_error, "\n", 2))
            The following steps ended up in failing state:

            %s
          MESSAGE
          whitelist_labels = steps_to_whitelist.map(&:label_dashed).join(',')
          unless whitelist_labels.empty?
            recommend << if scenario.detector.feature(:instance).downstream
                           format(<<~MESSAGE)
                             Resolve the failed steps and rerun the command.

                             If the situation persists and, you are unclear what to do next,
                             contact #{scenario.detector.feature(:instance).project_support_entity}.

                             In case the failures are false positives, use
                             --whitelist="#{whitelist_labels}"
                           MESSAGE
                         else
                           format(<<~MESSAGE)
                             Resolve the failed steps and rerun the command.
                             In case the failures are false positives, use
                             --whitelist="#{whitelist_labels}"
                           MESSAGE
                         end
          end
        end

        steps_with_warning = scenario.steps_with_warning(:whitelisted => false)
        unless steps_with_warning.empty?
          message << format(<<~MESSAGE, format_steps(steps_with_warning, "\n", 2))
            The following steps ended up in warning state:

            %s
          MESSAGE

          recommend << <<~MESSAGE
            The steps in warning state itself might not mean there is an error,
            but it should be reviewed to ensure the behavior is expected
          MESSAGE
        end
        puts((message + recommend).join("\n"))
      end