Class: Reviewer::Runner

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/reviewer/runner.rb,
lib/reviewer/runner/result.rb,
lib/reviewer/runner/guidance.rb,
lib/reviewer/runner/formatter.rb,
lib/reviewer/runner/failed_files.rb,
lib/reviewer/runner/strategies/captured.rb,
lib/reviewer/runner/strategies/passthrough.rb

Overview

Wrapper for executng a command and printing the results

Defined Under Namespace

Modules: Strategies Classes: FailedFiles, Formatter, Guidance, Result

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tool, command_type, strategy = Strategies::Captured, context:) ⇒ self

Creates a wrapper for running commands through Reviewer in order to provide a more accessible API for recording execution time and interpreting the results of a command in a more generous way so that non-zero exit statuses can still potentially be passing.

Parameters:

  • the key for the desired tool to run

  • the key for the type of command to run

  • (defaults to: Strategies::Captured)

    how to execute and handle the results of the command

  • the shared runtime dependencies (arguments, output, history)



34
35
36
37
38
39
40
41
# File 'lib/reviewer/runner.rb', line 34

def initialize(tool, command_type, strategy = Strategies::Captured, context:)
  @command = Command.new(tool, command_type, context: context)
  @strategy = strategy
  @shell = Shell.new(stream: context.output.printer.stream)
  @context = context
  @skipped = false
  @missing = false
end

Instance Attribute Details

#commandObject (readonly)

Returns the value of attribute command.



19
20
21
# File 'lib/reviewer/runner.rb', line 19

def command
  @command
end

#shellObject (readonly)

Returns the value of attribute shell.



19
20
21
# File 'lib/reviewer/runner.rb', line 19

def shell
  @shell
end

#strategyClass

Returns the strategy class for running the command (Captured or Passthrough).

Returns:

  • the strategy class for running the command (Captured or Passthrough)



17
18
19
# File 'lib/reviewer/runner.rb', line 17

def strategy
  @strategy
end

Instance Method Details

#execute_strategyvoid

This method returns an undefined value.

Runs the relevant strategy to either capture or pass through command output.



139
140
141
142
143
144
145
# File 'lib/reviewer/runner.rb', line 139

def execute_strategy
  # Run the provided strategy
  strategy.new(self).tap do |run_strategy|
    run_strategy.prepare if run_prepare_step?
    run_strategy.run
  end
end

#failed_filesArray<String>

Extracts file paths from stdout/stderr for failed-file tracking

Returns:

  • file paths found in the command output



109
110
111
# File 'lib/reviewer/runner.rb', line 109

def failed_files
  FailedFiles.new(stdout, stderr).to_a
end

#failure?Boolean

Returns:



104
# File 'lib/reviewer/runner.rb', line 104

def failure? = !success?

#formatterRunner::Formatter

Display formatter for runner-specific output (tool summary, success, failure, etc.) Computed rather than stored to avoid exceeding instance variable threshold.

Returns:



52
# File 'lib/reviewer/runner.rb', line 52

def formatter = @formatter ||= Runner::Formatter.new(output)

#guidanceGuidance

Uses the result of the runner to determine what, if any, guidance to display to help the user get back on track in the event of an unsuccessful run.

Returns:

  • the relevant guidance based on the result of the runner



181
# File 'lib/reviewer/runner.rb', line 181

def guidance = @guidance ||= Guidance.new(command: command, result: result, context: @context)

#identify_toolvoid

This method returns an undefined value.

Prints the tool name and description to the console as a frame of reference. Only displays in streaming mode; non-streaming strategies handle their own output.



117
118
119
120
121
122
123
# File 'lib/reviewer/runner.rb', line 117

def identify_tool
  # If there's an existing result, the runner is being re-run, and identifying the tool would
  # be redundant.
  return if result.exists?

  stream_output { formatter.tool_summary(tool) }
end

#missing?Boolean

Whether this runner's executable was not found (exit status 127)

Returns:

  • true if the tool was missing



88
# File 'lib/reviewer/runner.rb', line 88

def missing? = @missing == true

#outputOutput

The output channel for displaying content, delegated from context.

Returns:



46
# File 'lib/reviewer/runner.rb', line 46

def output = @context.output

#prepare_commandComman

Creates_an instance of the prepare command for a tool

Returns:

  • the current tool's prepare command



157
# File 'lib/reviewer/runner.rb', line 157

def prepare_command = @prepare_command ||= Command.new(tool, :prepare, context: @context)

#record_timingvoid

This method returns an undefined value.

Saves the last 5 elapsed times for the commands used this run by using the raw command as a unique key. This enables the ability to compare times across runs while taking into consideration that different iterations of the command may be running on fewer files. So comparing a full run to the average time for a partial run wouldn't be helpful. By using the raw command string, it will always be apples to apples.



172
173
174
175
# File 'lib/reviewer/runner.rb', line 172

def record_timing
  tool.record_timing(prepare_command, timer.prep)
  tool.record_timing(command, timer.main)
end

#runInteger

Executes the command and returns the exit status

Returns:

  • the exit status from the command



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/reviewer/runner.rb', line 62

def run
  # Skip if files were requested but none match this tool's pattern
  if command.skip?
    @skipped = true
    show_skipped
    return 0
  end

  # Show which tool is running
  identify_tool

  # Use the provided strategy to run the command
  execute_strategy

  # Handle the result based on whether the tool was found
  result.executable_not_found? ? handle_missing : handle_result
end

#run_prepare_step?Boolean

Determines whether a preparation step should be run before the primary command. If/when the primary command is a :prepare command, then it shouldn't run twice. So it skips what would be a superfluous run of the preparation.

Returns:

  • true the primary command is not prepare and the tool needs to be prepare



152
# File 'lib/reviewer/runner.rb', line 152

def run_prepare_step? = command.type != :prepare && tool.prepare?

#show_skippedvoid

This method returns an undefined value.

Shows that a tool was skipped due to no matching files. Only displays in streaming mode; non-streaming modes report skips in the final summary.



129
130
131
132
133
134
# File 'lib/reviewer/runner.rb', line 129

def show_skipped
  stream_output do
    formatter.tool_summary(tool)
    formatter.skipped
  end
end

#skipped?Boolean

Whether this runner was skipped due to no matching files

Returns:

  • true if the tool was skipped



83
# File 'lib/reviewer/runner.rb', line 83

def skipped? = @skipped == true

#streaming?Boolean

Whether this runner is operating in streaming mode

Returns:

  • true if output should be streamed



57
# File 'lib/reviewer/runner.rb', line 57

def streaming? = @context.arguments.streaming?

#success?Boolean

Some review tools return a range of non-zero exit statuses and almost never return 0. (yarn audit is a good example.) Those tools can be configured to accept a non-zero exit status so they aren't constantly considered to be failing over minor issues.

But when other command types (prepare, install, format) are run, they either succeed or they fail. With no shades of gray in those cases, anything other than a 0 is a failure.

Skipped tools are always considered successful.

Returns:



98
99
100
101
102
# File 'lib/reviewer/runner.rb', line 98

def success?
  return true if skipped?

  command.type == :review ? exit_status <= tool.max_exit_status : exit_status.zero?
end

#to_resultRunner::Result

Builds an immutable Result object from the current runner state

Returns:

  • the result of running this tool



186
187
188
# File 'lib/reviewer/runner.rb', line 186

def to_result
  Result.from_runner(self)
end

#update_last_prepared_atTime

Updates the 'last prepared at' timestamp that Reviewer uses to know if a tool's preparation step is stale and needs to be run again.

Returns:

  • the timestamp last_prepared_at is updated to



163
# File 'lib/reviewer/runner.rb', line 163

def update_last_prepared_at = tool.last_prepared_at = Time.now