reviewer 0.1.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.alexignore +1 -0
  3. data/.github/FUNDING.yml +3 -0
  4. data/.github/workflows/main.yml +81 -11
  5. data/.github/workflows/release.yml +98 -0
  6. data/.gitignore +1 -1
  7. data/.inch.yml +3 -1
  8. data/.reek.yml +175 -0
  9. data/.reviewer.example.yml +27 -12
  10. data/.reviewer.future.yml +221 -0
  11. data/.reviewer.yml +191 -28
  12. data/.reviewer_stdout +0 -0
  13. data/.rubocop.yml +34 -1
  14. data/CHANGELOG.md +42 -2
  15. data/Gemfile +39 -1
  16. data/Gemfile.lock +294 -72
  17. data/README.md +315 -7
  18. data/RELEASING.md +190 -0
  19. data/Rakefile +117 -0
  20. data/dependency_decisions.yml +61 -0
  21. data/exe/fmt +1 -1
  22. data/exe/rvw +1 -1
  23. data/lib/reviewer/arguments/files.rb +60 -27
  24. data/lib/reviewer/arguments/keywords.rb +39 -43
  25. data/lib/reviewer/arguments/tags.rb +21 -14
  26. data/lib/reviewer/arguments.rb +107 -29
  27. data/lib/reviewer/batch/formatter.rb +87 -0
  28. data/lib/reviewer/batch.rb +46 -35
  29. data/lib/reviewer/capabilities.rb +81 -0
  30. data/lib/reviewer/command/string/env.rb +16 -6
  31. data/lib/reviewer/command/string/flags.rb +14 -5
  32. data/lib/reviewer/command/string.rb +53 -24
  33. data/lib/reviewer/command.rb +69 -39
  34. data/lib/reviewer/configuration/loader.rb +70 -0
  35. data/lib/reviewer/configuration.rb +14 -4
  36. data/lib/reviewer/context.rb +15 -0
  37. data/lib/reviewer/doctor/config_check.rb +46 -0
  38. data/lib/reviewer/doctor/environment_check.rb +58 -0
  39. data/lib/reviewer/doctor/formatter.rb +75 -0
  40. data/lib/reviewer/doctor/keyword_check.rb +85 -0
  41. data/lib/reviewer/doctor/opportunity_check.rb +88 -0
  42. data/lib/reviewer/doctor/report.rb +63 -0
  43. data/lib/reviewer/doctor/tool_inventory.rb +41 -0
  44. data/lib/reviewer/doctor.rb +28 -0
  45. data/lib/reviewer/history.rb +36 -12
  46. data/lib/reviewer/output/formatting.rb +40 -0
  47. data/lib/reviewer/output/printer.rb +105 -0
  48. data/lib/reviewer/output.rb +54 -65
  49. data/lib/reviewer/prompt.rb +38 -0
  50. data/lib/reviewer/report/formatter.rb +124 -0
  51. data/lib/reviewer/report.rb +100 -0
  52. data/lib/reviewer/runner/failed_files.rb +66 -0
  53. data/lib/reviewer/runner/formatter.rb +103 -0
  54. data/lib/reviewer/runner/guidance.rb +79 -0
  55. data/lib/reviewer/runner/result.rb +150 -0
  56. data/lib/reviewer/runner/strategies/captured.rb +232 -0
  57. data/lib/reviewer/runner/strategies/{verbose.rb → passthrough.rb} +15 -24
  58. data/lib/reviewer/runner.rb +179 -35
  59. data/lib/reviewer/session/formatter.rb +87 -0
  60. data/lib/reviewer/session.rb +208 -0
  61. data/lib/reviewer/setup/catalog.rb +233 -0
  62. data/lib/reviewer/setup/detector.rb +61 -0
  63. data/lib/reviewer/setup/formatter.rb +94 -0
  64. data/lib/reviewer/setup/gemfile_lock.rb +55 -0
  65. data/lib/reviewer/setup/generator.rb +54 -0
  66. data/lib/reviewer/setup/tool_block.rb +112 -0
  67. data/lib/reviewer/setup.rb +41 -0
  68. data/lib/reviewer/shell/result.rb +25 -11
  69. data/lib/reviewer/shell/timer.rb +47 -27
  70. data/lib/reviewer/shell.rb +46 -21
  71. data/lib/reviewer/tool/conversions.rb +20 -0
  72. data/lib/reviewer/tool/file_resolver.rb +54 -0
  73. data/lib/reviewer/tool/settings.rb +107 -56
  74. data/lib/reviewer/tool/test_file_mapper.rb +73 -0
  75. data/lib/reviewer/tool/timing.rb +78 -0
  76. data/lib/reviewer/tool.rb +88 -47
  77. data/lib/reviewer/tools.rb +47 -33
  78. data/lib/reviewer/version.rb +1 -1
  79. data/lib/reviewer.rb +114 -54
  80. data/reviewer.gemspec +21 -20
  81. data/structure.svg +1 -0
  82. metadata +113 -148
  83. data/.ruby-version +0 -1
  84. data/lib/reviewer/command/string/verbosity.rb +0 -51
  85. data/lib/reviewer/command/verbosity.rb +0 -65
  86. data/lib/reviewer/conversions.rb +0 -27
  87. data/lib/reviewer/guidance.rb +0 -73
  88. data/lib/reviewer/keywords/git/staged.rb +0 -48
  89. data/lib/reviewer/keywords/git.rb +0 -14
  90. data/lib/reviewer/keywords.rb +0 -9
  91. data/lib/reviewer/loader.rb +0 -59
  92. data/lib/reviewer/printer.rb +0 -25
  93. data/lib/reviewer/runner/strategies/quiet.rb +0 -90
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reviewer
4
+ class Runner
5
+ # Handles the logic around what to display after a command has been run
6
+ class Guidance
7
+ attr_reader :command, :result, :formatter, :context
8
+ private :context
9
+
10
+ # Create an instance of guidance for suggesting recovery steps after errors
11
+ # @param command [Command] the command that was run and needs recovery guidance
12
+ # @param result [Result] the result of the command
13
+ # @param context [Context] the shared runtime dependencies
14
+ #
15
+ # @return [Guidance] the guidance class to suggest relevant recovery steps
16
+ def initialize(command:, result:, context:)
17
+ @command = command
18
+ @result = result
19
+ @context = context
20
+ @formatter = Runner::Formatter.new(context.output)
21
+ end
22
+
23
+ # Prints the relevant guidance based on the command and result context
24
+ #
25
+ # @return [void] prints the relevant guidance to the stream
26
+ def show
27
+ case result
28
+ when executable_not_found? then show_missing_executable_guidance
29
+ when cannot_execute? then show_unrecoverable_guidance
30
+ else show_syntax_guidance
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ # Conditional check for when the command result was that the executable couldn't be found
37
+ #
38
+ # @return [Boolean] true if the result indicates the command couldn't be found
39
+ def executable_not_found?
40
+ lambda(&:executable_not_found?)
41
+ end
42
+
43
+ # Conditional check for when the command result was that it was unable to be executed
44
+ #
45
+ # @return [Boolean] true if the result indicates the command couldn't be executed
46
+ def cannot_execute?
47
+ lambda(&:cannot_execute?)
48
+ end
49
+
50
+ # Shows the recovery guidance for when a command is missing
51
+ #
52
+ # @return [void] prints missing executable guidance
53
+ def show_missing_executable_guidance
54
+ tool = command.tool
55
+ installation_command = Command.new(tool, :install, context: context).string if tool.installable?
56
+ install_link = tool.install_link
57
+
58
+ formatter.failure("Missing executable for '#{tool}'", command: command)
59
+ formatter.guidance('Try installing the tool:', installation_command)
60
+ formatter.guidance('Read the installation guidance:', install_link)
61
+ end
62
+
63
+ # Shows the recovery guidance for when a command generates an unrecoverable error
64
+ #
65
+ # @return [void] prints unrecoverable error guidance
66
+ def show_unrecoverable_guidance
67
+ formatter.unrecoverable(result.stderr)
68
+ end
69
+
70
+ # Shows suggestions for ignoring or disable rules when a command fails after reviewing code
71
+ #
72
+ # @return [void] prints syntax guidance
73
+ def show_syntax_guidance
74
+ formatter.guidance('Selectively Ignore a Rule:', command.tool.links[:ignore_syntax])
75
+ formatter.guidance('Fully Disable a Rule:', command.tool.links[:disable_syntax])
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reviewer
4
+ class Runner
5
+ # Immutable value object representing the result of running a single tool
6
+ #
7
+ # @!attribute [r] tool_key
8
+ # @return [Symbol] the unique identifier for the tool
9
+ # @!attribute [r] tool_name
10
+ # @return [String] the human-readable name of the tool
11
+ # @!attribute [r] command_type
12
+ # @return [Symbol] the type of command run (:review, :format, etc.)
13
+ # @!attribute [r] command_string
14
+ # @return [String] the full command string that was executed
15
+ # @!attribute [r] success
16
+ # @return [Boolean] whether the command completed successfully
17
+ # @!attribute [r] exit_status
18
+ # @return [Integer] the exit status code from the command
19
+ # @!attribute [r] duration
20
+ # @return [Float] the execution time in seconds
21
+ # @!attribute [r] stdout
22
+ # @return [String, nil] the standard output from the command
23
+ # @!attribute [r] stderr
24
+ # @return [String, nil] the standard error from the command
25
+ # @!attribute [r] skipped
26
+ # @return [Boolean] whether the tool was skipped
27
+ # @!attribute [r] missing
28
+ # @return [Boolean] whether the tool's executable was not found
29
+ Result = Struct.new(
30
+ :tool_key,
31
+ :tool_name,
32
+ :command_type,
33
+ :command_string,
34
+ :success,
35
+ :exit_status,
36
+ :duration,
37
+ :stdout,
38
+ :stderr,
39
+ :skipped,
40
+ :missing,
41
+ :summary_pattern,
42
+ :summary_label,
43
+ keyword_init: true
44
+ ) do
45
+ # Freeze on initialization to maintain immutability like Data.define
46
+ def initialize(...)
47
+ super
48
+ freeze
49
+ end
50
+
51
+ # Builds an immutable Result from a runner's current state.
52
+ # @param runner [Runner] the runner after command execution
53
+ #
54
+ # @return [Result] an immutable result for reporting
55
+ def self.from_runner(runner)
56
+ if runner.skipped?
57
+ build_skipped(runner)
58
+ elsif runner.missing?
59
+ build_missing(runner)
60
+ else
61
+ build_executed(runner)
62
+ end
63
+ end
64
+
65
+ def self.base_attributes(runner)
66
+ tool = runner.tool
67
+ {
68
+ tool_key: tool.key,
69
+ tool_name: tool.name,
70
+ command_type: runner.command.type,
71
+ command_string: runner.command.string
72
+ }
73
+ end
74
+
75
+ def self.build_skipped(runner)
76
+ new(
77
+ **base_attributes(runner),
78
+ command_string: nil,
79
+ success: true, exit_status: 0, duration: 0,
80
+ stdout: nil, stderr: nil, skipped: true
81
+ )
82
+ end
83
+
84
+ def self.build_missing(runner)
85
+ new(
86
+ **base_attributes(runner),
87
+ success: false, exit_status: runner.shell.result.exit_status, duration: 0,
88
+ stdout: nil, stderr: nil, skipped: nil, missing: true
89
+ )
90
+ end
91
+
92
+ def self.build_executed(runner)
93
+ shell = runner.shell
94
+ shell_result = shell.result
95
+ settings = runner.tool.settings
96
+ new(
97
+ **base_attributes(runner),
98
+ success: runner.success?, exit_status: shell_result.exit_status,
99
+ duration: shell.timer.total_seconds,
100
+ stdout: shell_result.stdout, stderr: shell_result.stderr, skipped: nil,
101
+ summary_pattern: settings.summary_pattern,
102
+ summary_label: settings.summary_label
103
+ )
104
+ end
105
+
106
+ private_class_method :base_attributes, :build_skipped, :build_missing, :build_executed
107
+
108
+ alias_method :success?, :success
109
+ alias_method :skipped?, :skipped
110
+ alias_method :missing?, :missing
111
+
112
+ # Whether this result represents a tool that actually ran (not skipped or missing)
113
+ #
114
+ # @return [Boolean] true if the tool was executed
115
+ def executed? = !skipped? && !missing?
116
+
117
+ # Extracts a short summary detail from stdout for display purposes.
118
+ # Each tool type may have its own summary format (test count, offense count, etc.)
119
+ #
120
+ # @return [String, nil] a brief summary or nil if no detail can be extracted
121
+ def detail_summary
122
+ return nil unless summary_pattern
123
+
124
+ match = stdout&.match(/#{summary_pattern}/i)
125
+ return nil unless match
126
+
127
+ summary_label.gsub(/\\(\d+)/) { match[Regexp.last_match(1).to_i] }
128
+ end
129
+
130
+ # Converts the result to a hash suitable for serialization
131
+ #
132
+ # @return [Hash] hash representation with nil values removed
133
+ def to_h
134
+ {
135
+ tool: tool_key,
136
+ name: tool_name,
137
+ command_type: command_type,
138
+ command: command_string,
139
+ success: success,
140
+ exit_status: exit_status,
141
+ duration: duration,
142
+ stdout: stdout,
143
+ stderr: stderr,
144
+ skipped: skipped,
145
+ missing: missing
146
+ }.compact # Excludes summary_pattern/summary_label (config, not results)
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ruby-progressbar'
4
+
5
+ module Reviewer
6
+ class Runner
7
+ module Strategies
8
+ # Execution strategy for a runner to run a command quietly by capturing the output and only
9
+ # displaying it if there's a failure that justifies it
10
+ # @attr_reader runner [Runner] the instance of the runner that will be executed with this strategy
11
+ # @attr_reader start_time [Time] the start time for the strategy_for timing purposes
12
+ class Captured
13
+ # 256-color ANSI codes for progress bar styling (not in AnsiStyles' 16-color palette)
14
+ PROGRESS_GRAY = "\e[38;5;245m"
15
+ PROGRESS_DARK_GRAY = "\e[38;5;240m"
16
+ ANSI_RESET = "\e[0m"
17
+ ERASE_LINE = "\r\e[2K"
18
+
19
+ attr_reader :runner, :start_time
20
+
21
+ # Create an instance of the captured strategy for a command runner so that any output is
22
+ # fully suppressed so as to not create too much noise when running multiple commands.
23
+ # @param runner [Runner] the instance of the runner to apply the strategy to
24
+ #
25
+ # @return [self]
26
+ def initialize(runner)
27
+ @runner = runner
28
+ @start_time = Time.now
29
+ end
30
+
31
+ # The prepare command strategy when running a command and capturing the results
32
+ #
33
+ # @return [void]
34
+ def prepare
35
+ command = runner.prepare_command
36
+
37
+ display_progress(command) { runner.shell.capture_prep(command) }
38
+
39
+ # Erase the prep progress bar — the run step will show its own
40
+ stream.print(ERASE_LINE) if style_enabled? && runner.streaming?
41
+
42
+ # Running the prepare command, so make sure the timestamp is updated
43
+ runner.update_last_prepared_at
44
+ end
45
+
46
+ # The run command strategy when running a command and capturing the results
47
+ #
48
+ # @return [void]
49
+ def run
50
+ command = runner.command
51
+
52
+ display_progress(command) { runner.shell.capture_main(command) }
53
+
54
+ # Skip output for non-streaming modes - results are formatted at the end
55
+ return unless runner.streaming?
56
+
57
+ # Missing tools are handled by the Runner (shows "Skipped (not installed)")
58
+ return if runner.shell.result.executable_not_found?
59
+
60
+ # If it's successful, show that it was a success and how long it took to run, otherwise,
61
+ # it wasn't successful and we got some explaining to do...
62
+ runner.success? ? show_timing_result : show_command_output
63
+ end
64
+
65
+ private
66
+
67
+ # Displays the progress of the current command since the output is captured/suppressed.
68
+ # Helps people know that the sub-command is running within expectations.
69
+ # @param command [String] the precise command string generated by Reviewer. Serves as the
70
+ # key for looking up the command's history.
71
+ # @param block [Block] the runner for the command that's being timed and having its
72
+ # progress updated and printed
73
+ #
74
+ # @return [void]
75
+ def display_progress(command)
76
+ average_time = runner.tool.average_time(command)
77
+ start_time = Time.now
78
+ thread = Thread.new { yield }
79
+
80
+ # Skip progress output for non-streaming modes
81
+ return thread.join unless runner.streaming?
82
+
83
+ print_progress(thread, start_time, average_time)
84
+ end
85
+
86
+ def print_progress(thread, start_time, average_time)
87
+ bar = create_progress_bar(average_time)
88
+
89
+ while thread.alive?
90
+ update_progress(bar, start_time, average_time)
91
+ sleep 0.1
92
+ end
93
+
94
+ thread.join
95
+
96
+ # Erase the progress bar line if the executable wasn't found
97
+ if runner.shell.result.executable_not_found?
98
+ stream.print(ERASE_LINE) if style_enabled?
99
+ return
100
+ end
101
+
102
+ finish_progress_bar(bar, average_time.positive?)
103
+ end
104
+
105
+ def finish_progress_bar(bar, timed)
106
+ if timed
107
+ bar.format = style_enabled? ? "#{PROGRESS_GRAY}%b#{PROGRESS_DARK_GRAY}%i %p%%#{ANSI_RESET}" : '%b%i %p%%'
108
+ bar.finish
109
+ else
110
+ bar.stop
111
+ end
112
+ end
113
+
114
+ def create_progress_bar(average_time)
115
+ shared = {
116
+ output: stream,
117
+ title: '',
118
+ progress_mark: "\u2501",
119
+ remainder_mark: "\u2500"
120
+ }
121
+
122
+ if average_time.positive?
123
+ eta = average_time >= 3 ? ' %e' : ''
124
+ format = style_enabled? ? "#{PROGRESS_GRAY}%b#{PROGRESS_DARK_GRAY}%i %p%%#{eta}#{ANSI_RESET}" : "%b%i %p%%#{eta}"
125
+ ProgressBar.create(**shared, total: 100, format: format)
126
+ else
127
+ format = style_enabled? ? "#{PROGRESS_GRAY}%B#{ANSI_RESET}" : '%B'
128
+ ProgressBar.create(**shared, total: nil, format: format)
129
+ end
130
+ end
131
+
132
+ def update_progress(bar, start_time, average_time)
133
+ if average_time.positive?
134
+ elapsed = Time.now - start_time
135
+ percent = [(elapsed / average_time * 100).round, 99].min
136
+ bar.progress = percent if percent > bar.progress
137
+ else
138
+ bar.increment
139
+ end
140
+ end
141
+
142
+ # The output stream from the runner's printer
143
+ # @return [IO]
144
+ def stream = runner.output.printer.stream
145
+
146
+ # Whether ANSI styling is enabled on the output stream
147
+ # @return [Boolean]
148
+ def style_enabled? = runner.output.printer.style_enabled?
149
+
150
+ # Determines if stdout or stderr captured any useful output that can be displayed in order
151
+ # to more rapidly display output when a command fails. As long as both aren't nil or
152
+ # otherwise 'blank' strings, then that's enough.
153
+ #
154
+ # @return [Boolean] true if either stdout or stderr contain printable content
155
+ def usable_output_captured?
156
+ [runner.stdout, runner.stderr].reject { |value| value.to_s.strip.empty? }.any?
157
+ end
158
+
159
+ # Prints "Success" and the resulting timing details before moving on to the next tool
160
+ #
161
+ # @return [void]
162
+ def show_timing_result
163
+ runner.record_timing
164
+ runner.formatter.success(runner.timer)
165
+ end
166
+
167
+ # Prints "Failure" and the resulting exit status. Shows the precise command that led to the
168
+ # failure for easier copy and paste or making it easier to see any incorrect syntax or
169
+ # options that could be corrected.
170
+ #
171
+ # @return [void]
172
+ def show_command_output
173
+ # If there's a failure, clear the successful command output to focus on the issues
174
+ runner.output.clear
175
+
176
+ # Show the exit status and failed command
177
+ runner.formatter.failure("Exit Status #{runner.exit_status}", command: runner.command)
178
+
179
+ # If it can't be rerun, then don't try
180
+ usable_output_captured? ? show_captured_output : rerun_via_passthrough
181
+ end
182
+
183
+ # If the command sent output to stdout/stderr as most will, simply display what was captured
184
+ #
185
+ # @return [void]
186
+ def show_captured_output
187
+ show_captured_stdout
188
+ show_captured_stderr
189
+ end
190
+
191
+ # If there's a useful stdout value, display it with a divider to visually separate it.
192
+ #
193
+ # @return [void]
194
+ def show_captured_stdout
195
+ stdout = runner.stdout.to_s
196
+ return if stdout.empty?
197
+
198
+ runner.output.divider
199
+ runner.output.newline
200
+ runner.output.unfiltered(stdout)
201
+ end
202
+
203
+ # If there's a useful stderr value, display it with a divider to visually separate it.
204
+ #
205
+ # @return [void]
206
+ def show_captured_stderr
207
+ stderr = runner.stderr.to_s
208
+ return if stderr.empty?
209
+
210
+ scrubbed_stderr = Output.scrub(stderr)
211
+
212
+ runner.output.divider
213
+ runner.output.newline
214
+ runner.formatter.guidance('Runtime Errors:', scrubbed_stderr)
215
+ end
216
+
217
+ # If for some reason, the command didn't send anything to stdout/stderr, the only option to
218
+ # show results is to rerun it via the passthrough strategy
219
+ #
220
+ # @return [void]
221
+ def rerun_via_passthrough
222
+ return unless runner.rerunnable?
223
+
224
+ runner.strategy = Strategies::Passthrough
225
+
226
+ runner.output.divider
227
+ runner.run
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
@@ -3,22 +3,24 @@
3
3
  module Reviewer
4
4
  class Runner
5
5
  module Strategies
6
- # Execution strategy to run a command verbosely
7
- class Verbose
8
- attr_accessor :runner
9
-
10
- # Create an instance of the verbose strategy for a command runner. This strategy ensures
11
- # that when a command is run, the output isn't suppressed. Essentially, it's a pass-through
12
- # wrapper for running a command and displaying the results.
6
+ # Execution strategy for a runner to run a command transparently-displaying the command's
7
+ # output in realtime as it runs (as opposed to capturing and suppressing the output unless
8
+ # the command fails)
9
+ # @attr_reader runner [Runner] the instance of the runner that will be executed with this strategy
10
+ class Passthrough
11
+ attr_reader :runner
12
+
13
+ # Create an instance of the passthrough strategy for a command runner. This strategy ensures
14
+ # that when a command is run, the output isn't suppressed. Essentially, it's a transparent
15
+ # wrapper for running a command and displaying the results realtime.
13
16
  # @param runner [Runner] the instance of the runner to apply the strategy to
14
17
  #
15
- # @return [Runner::Strategies::Verbose] an instance of the relevant verbose strategy
18
+ # @return [self]
16
19
  def initialize(runner)
17
20
  @runner = runner
18
- @runner.command.verbosity = Reviewer::Command::Verbosity::NO_SILENCE
19
21
  end
20
22
 
21
- # The prepare command strategy when running a command verbosely
23
+ # The prepare command strategy when running a command transparently
22
24
  #
23
25
  # @return [void]
24
26
  def prepare
@@ -28,34 +30,23 @@ module Reviewer
28
30
  # Display the exact command syntax that's being run. This can come in handy if there's an
29
31
  # issue and the command can be copied/pasted or if the generated command somehow has some
30
32
  # incorrect syntax or options that need to be corrected.
31
- runner.output.current_command(runner.prepare_command)
32
-
33
- # Add a divider to visually delineate the results
34
- runner.output.divider
33
+ runner.formatter.current_command(runner.prepare_command)
35
34
 
36
35
  # Run the command through the shell directly so no output is suppressed
37
36
  runner.shell.direct(runner.prepare_command)
38
37
  end
39
38
 
40
- # The run command strategy when running a command verbosely
39
+ # The run command strategy when running a command transparently
41
40
  #
42
41
  # @return [void]
43
42
  def run
44
- # Display the exact command that's being run
45
-
46
43
  # Display the exact command syntax that's being run. This can come in handy if there's an
47
44
  # issue and the command can be copied/pasted or if the generated command somehow has some
48
45
  # incorrect syntax or options that need to be corrected.
49
- runner.output.current_command(runner.command)
50
-
51
- # Add a divider to visually delineate the results
52
- runner.output.divider
46
+ runner.formatter.current_command(runner.command)
53
47
 
54
48
  # Run the command through the shell directly so no output is suppressed
55
49
  runner.shell.direct(runner.command)
56
-
57
- # Add a final divider to visually delineate the results
58
- runner.output.divider
59
50
  end
60
51
  end
61
52
  end