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.
- checksums.yaml +4 -4
- data/.alexignore +1 -0
- data/.github/FUNDING.yml +3 -0
- data/.github/workflows/main.yml +81 -11
- data/.github/workflows/release.yml +98 -0
- data/.gitignore +1 -1
- data/.inch.yml +3 -1
- data/.reek.yml +175 -0
- data/.reviewer.example.yml +27 -12
- data/.reviewer.future.yml +221 -0
- data/.reviewer.yml +191 -28
- data/.reviewer_stdout +0 -0
- data/.rubocop.yml +34 -1
- data/CHANGELOG.md +42 -2
- data/Gemfile +39 -1
- data/Gemfile.lock +294 -72
- data/README.md +315 -7
- data/RELEASING.md +190 -0
- data/Rakefile +117 -0
- data/dependency_decisions.yml +61 -0
- data/exe/fmt +1 -1
- data/exe/rvw +1 -1
- data/lib/reviewer/arguments/files.rb +60 -27
- data/lib/reviewer/arguments/keywords.rb +39 -43
- data/lib/reviewer/arguments/tags.rb +21 -14
- data/lib/reviewer/arguments.rb +107 -29
- data/lib/reviewer/batch/formatter.rb +87 -0
- data/lib/reviewer/batch.rb +46 -35
- data/lib/reviewer/capabilities.rb +81 -0
- data/lib/reviewer/command/string/env.rb +16 -6
- data/lib/reviewer/command/string/flags.rb +14 -5
- data/lib/reviewer/command/string.rb +53 -24
- data/lib/reviewer/command.rb +69 -39
- data/lib/reviewer/configuration/loader.rb +70 -0
- data/lib/reviewer/configuration.rb +14 -4
- data/lib/reviewer/context.rb +15 -0
- data/lib/reviewer/doctor/config_check.rb +46 -0
- data/lib/reviewer/doctor/environment_check.rb +58 -0
- data/lib/reviewer/doctor/formatter.rb +75 -0
- data/lib/reviewer/doctor/keyword_check.rb +85 -0
- data/lib/reviewer/doctor/opportunity_check.rb +88 -0
- data/lib/reviewer/doctor/report.rb +63 -0
- data/lib/reviewer/doctor/tool_inventory.rb +41 -0
- data/lib/reviewer/doctor.rb +28 -0
- data/lib/reviewer/history.rb +36 -12
- data/lib/reviewer/output/formatting.rb +40 -0
- data/lib/reviewer/output/printer.rb +105 -0
- data/lib/reviewer/output.rb +54 -65
- data/lib/reviewer/prompt.rb +38 -0
- data/lib/reviewer/report/formatter.rb +124 -0
- data/lib/reviewer/report.rb +100 -0
- data/lib/reviewer/runner/failed_files.rb +66 -0
- data/lib/reviewer/runner/formatter.rb +103 -0
- data/lib/reviewer/runner/guidance.rb +79 -0
- data/lib/reviewer/runner/result.rb +150 -0
- data/lib/reviewer/runner/strategies/captured.rb +232 -0
- data/lib/reviewer/runner/strategies/{verbose.rb → passthrough.rb} +15 -24
- data/lib/reviewer/runner.rb +179 -35
- data/lib/reviewer/session/formatter.rb +87 -0
- data/lib/reviewer/session.rb +208 -0
- data/lib/reviewer/setup/catalog.rb +233 -0
- data/lib/reviewer/setup/detector.rb +61 -0
- data/lib/reviewer/setup/formatter.rb +94 -0
- data/lib/reviewer/setup/gemfile_lock.rb +55 -0
- data/lib/reviewer/setup/generator.rb +54 -0
- data/lib/reviewer/setup/tool_block.rb +112 -0
- data/lib/reviewer/setup.rb +41 -0
- data/lib/reviewer/shell/result.rb +25 -11
- data/lib/reviewer/shell/timer.rb +47 -27
- data/lib/reviewer/shell.rb +46 -21
- data/lib/reviewer/tool/conversions.rb +20 -0
- data/lib/reviewer/tool/file_resolver.rb +54 -0
- data/lib/reviewer/tool/settings.rb +107 -56
- data/lib/reviewer/tool/test_file_mapper.rb +73 -0
- data/lib/reviewer/tool/timing.rb +78 -0
- data/lib/reviewer/tool.rb +88 -47
- data/lib/reviewer/tools.rb +47 -33
- data/lib/reviewer/version.rb +1 -1
- data/lib/reviewer.rb +114 -54
- data/reviewer.gemspec +21 -20
- data/structure.svg +1 -0
- metadata +113 -148
- data/.ruby-version +0 -1
- data/lib/reviewer/command/string/verbosity.rb +0 -51
- data/lib/reviewer/command/verbosity.rb +0 -65
- data/lib/reviewer/conversions.rb +0 -27
- data/lib/reviewer/guidance.rb +0 -73
- data/lib/reviewer/keywords/git/staged.rb +0 -48
- data/lib/reviewer/keywords/git.rb +0 -14
- data/lib/reviewer/keywords.rb +0 -9
- data/lib/reviewer/loader.rb +0 -59
- data/lib/reviewer/printer.rb +0 -25
- data/lib/reviewer/runner/strategies/quiet.rb +0 -90
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'date'
|
|
4
|
+
|
|
5
|
+
module Reviewer
|
|
6
|
+
class Tool
|
|
7
|
+
# Manages timing persistence for a tool — recording, retrieving, and averaging
|
|
8
|
+
# execution times, plus tracking when the prepare command was last run.
|
|
9
|
+
class Timing
|
|
10
|
+
SIX_HOURS_IN_SECONDS = 60 * 60 * 6
|
|
11
|
+
|
|
12
|
+
# Creates a timing tracker for a specific tool
|
|
13
|
+
# @param history [History] the persistence store for timing data
|
|
14
|
+
# @param key [Symbol] the tool's unique key
|
|
15
|
+
#
|
|
16
|
+
# @return [Timing]
|
|
17
|
+
def initialize(history, key)
|
|
18
|
+
@history = history
|
|
19
|
+
@key = key
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Specifies when the tool last had its `prepare` command run
|
|
23
|
+
#
|
|
24
|
+
# @return [Time, nil] timestamp of when the `prepare` command was last run
|
|
25
|
+
def last_prepared_at
|
|
26
|
+
date_string = @history.get(@key, :last_prepared_at).to_s
|
|
27
|
+
|
|
28
|
+
date_string.empty? ? nil : DateTime.parse(date_string).to_time
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Sets the timestamp for when the tool last ran its `prepare` command
|
|
32
|
+
# @param timestamp [DateTime, Time] the value to record
|
|
33
|
+
#
|
|
34
|
+
# @return [void]
|
|
35
|
+
def last_prepared_at=(timestamp)
|
|
36
|
+
@history.set(@key, :last_prepared_at, timestamp.to_s)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Calculates the average execution time for a command
|
|
40
|
+
# @param command [Command] the command to get timing for
|
|
41
|
+
#
|
|
42
|
+
# @return [Float] the average time in seconds or 0 if no history
|
|
43
|
+
def average_time(command)
|
|
44
|
+
times = get_timing(command)
|
|
45
|
+
|
|
46
|
+
times.any? ? times.sum / times.size : 0
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Retrieves historical timing data for a command
|
|
50
|
+
# @param command [Command] the command to look up
|
|
51
|
+
#
|
|
52
|
+
# @return [Array<Float>] the last few recorded execution times
|
|
53
|
+
def get_timing(command)
|
|
54
|
+
@history.get(@key, command.raw_string) || []
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Records the execution time for a command to calculate running averages
|
|
58
|
+
# @param command [Command] the command that was run
|
|
59
|
+
# @param time [Float, nil] the execution time in seconds
|
|
60
|
+
#
|
|
61
|
+
# @return [void]
|
|
62
|
+
def record_timing(command, time)
|
|
63
|
+
return unless time
|
|
64
|
+
|
|
65
|
+
timing = get_timing(command).take(4) << time.round(2)
|
|
66
|
+
|
|
67
|
+
@history.set(@key, command.raw_string, timing)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Determines whether the `prepare` command was run recently enough
|
|
71
|
+
#
|
|
72
|
+
# @return [Boolean] true if the timestamp is nil or older than six hours
|
|
73
|
+
def stale?
|
|
74
|
+
!last_prepared_at || last_prepared_at < Time.now - SIX_HOURS_IN_SECONDS
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
data/lib/reviewer/tool.rb
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'tool/conversions'
|
|
4
|
+
require_relative 'tool/file_resolver'
|
|
3
5
|
require_relative 'tool/settings'
|
|
6
|
+
require_relative 'tool/test_file_mapper'
|
|
7
|
+
require_relative 'tool/timing'
|
|
4
8
|
|
|
5
9
|
module Reviewer
|
|
6
10
|
# Provides an instance of a specific tool for accessing its settings and run history
|
|
@@ -8,14 +12,9 @@ module Reviewer
|
|
|
8
12
|
extend Forwardable
|
|
9
13
|
include Comparable
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
# used by some tools to retrieve data, it only runs it occasionally in order to save time.
|
|
13
|
-
# This is the default window that it uses to determine if the tool's preparation step should be
|
|
14
|
-
# considered stale and needs to be rerun. Frequent enough that it shouldn't get stale, but
|
|
15
|
-
# infrequent enough that it's not cumbersome.
|
|
16
|
-
SIX_HOURS_IN_SECONDS = 60 * 60 * 6
|
|
15
|
+
SIX_HOURS_IN_SECONDS = Timing::SIX_HOURS_IN_SECONDS
|
|
17
16
|
|
|
18
|
-
attr_reader :settings
|
|
17
|
+
attr_reader :settings
|
|
19
18
|
|
|
20
19
|
def_delegators :@settings,
|
|
21
20
|
:key,
|
|
@@ -27,78 +26,86 @@ module Reviewer
|
|
|
27
26
|
:links,
|
|
28
27
|
:enabled?,
|
|
29
28
|
:disabled?,
|
|
30
|
-
:
|
|
29
|
+
:skip_in_batch?,
|
|
30
|
+
:max_exit_status,
|
|
31
|
+
:supports_files?
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
# Returns the tool's key as a symbol
|
|
34
|
+
# @return [Symbol] the tool's unique identifier
|
|
35
|
+
def to_sym = key
|
|
36
|
+
|
|
37
|
+
# Returns the tool's name as a string
|
|
38
|
+
# @return [String] the tool's display name
|
|
39
|
+
def to_s = name
|
|
34
40
|
|
|
35
41
|
# Create an instance of a tool
|
|
36
42
|
# @param tool_key [Symbol] the key to the tool from the configuration file
|
|
43
|
+
# @param config [Hash] the tool's configuration hash
|
|
44
|
+
# @param history [History] the history store for timing and state persistence
|
|
37
45
|
#
|
|
38
46
|
# @return [Tool] an instance of tool for accessing settings information and facts about the tool
|
|
39
|
-
def initialize(tool_key)
|
|
40
|
-
@settings = Settings.new(tool_key)
|
|
47
|
+
def initialize(tool_key, config:, history:)
|
|
48
|
+
@settings = Settings.new(tool_key, config: config)
|
|
49
|
+
@history = history
|
|
50
|
+
@timing = Timing.new(history, key)
|
|
41
51
|
end
|
|
42
52
|
|
|
43
|
-
# For determining if the tool should run
|
|
44
|
-
# the tool has a preparation command
|
|
53
|
+
# For determining if the tool should run its preparation command. It will only be run if
|
|
54
|
+
# the tool has a preparation command and it hasn't been run in the last 6 hours
|
|
45
55
|
#
|
|
46
56
|
# @return [Boolean] true if the tool has a configured `prepare` command that hasn't been run in
|
|
47
57
|
# the last 6 hours
|
|
48
|
-
def prepare?
|
|
49
|
-
preparable? && stale?
|
|
50
|
-
end
|
|
58
|
+
def prepare? = preparable? && stale?
|
|
51
59
|
|
|
52
60
|
# Determines whether a tool has a specific command type configured
|
|
53
61
|
# @param command_type [Symbol] one of the available command types defined in Command::TYPES
|
|
54
62
|
#
|
|
55
63
|
# @return [Boolean] true if the command type is configured and not blank
|
|
56
64
|
def command?(command_type)
|
|
57
|
-
commands.key?(command_type) &&
|
|
65
|
+
commands.key?(command_type) && commands[command_type]
|
|
58
66
|
end
|
|
59
67
|
|
|
60
68
|
# Determines if the tool can run a `install` command
|
|
61
69
|
#
|
|
62
70
|
# @return [Boolean] true if there is a non-blank `install` command configured
|
|
63
|
-
def installable?
|
|
64
|
-
|
|
71
|
+
def installable? = command?(:install)
|
|
72
|
+
|
|
73
|
+
# Returns the install command string for this tool
|
|
74
|
+
#
|
|
75
|
+
# @return [String, nil] the install command or nil if not configured
|
|
76
|
+
def install_command
|
|
77
|
+
commands[:install]
|
|
65
78
|
end
|
|
66
79
|
|
|
67
80
|
# Determines if the tool can run a `prepare` command
|
|
68
81
|
#
|
|
69
82
|
# @return [Boolean] true if there is a non-blank `prepare` command configured
|
|
70
|
-
def preparable?
|
|
71
|
-
command?(:prepare)
|
|
72
|
-
end
|
|
83
|
+
def preparable? = command?(:prepare)
|
|
73
84
|
|
|
74
85
|
# Determines if the tool can run a `review` command
|
|
75
86
|
#
|
|
76
87
|
# @return [Boolean] true if there is a non-blank `review` command configured
|
|
77
|
-
def reviewable?
|
|
78
|
-
command?(:review)
|
|
79
|
-
end
|
|
88
|
+
def reviewable? = command?(:review)
|
|
80
89
|
|
|
81
90
|
# Determines if the tool can run a `format` command
|
|
82
91
|
#
|
|
83
92
|
# @return [Boolean] true if there is a non-blank `format` command configured
|
|
84
|
-
def formattable?
|
|
85
|
-
command?(:format)
|
|
86
|
-
end
|
|
93
|
+
def formattable? = command?(:format)
|
|
87
94
|
|
|
88
|
-
#
|
|
95
|
+
# Whether this tool matches any of the given tags and is eligible for batch runs
|
|
89
96
|
#
|
|
90
|
-
# @
|
|
91
|
-
|
|
92
|
-
|
|
97
|
+
# @param tag_list [Array<String, Symbol>] tags to match against
|
|
98
|
+
# @return [Boolean] true if the tool is batch-eligible and shares at least one tag
|
|
99
|
+
def matches_tags?(tag_list)
|
|
100
|
+
!skip_in_batch? && tag_list.intersect?(tags)
|
|
93
101
|
end
|
|
94
102
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
end
|
|
103
|
+
def_delegators :@timing,
|
|
104
|
+
:last_prepared_at,
|
|
105
|
+
:last_prepared_at=,
|
|
106
|
+
:average_time,
|
|
107
|
+
:get_timing,
|
|
108
|
+
:record_timing
|
|
102
109
|
|
|
103
110
|
# Determines whether the `prepare` command was run recently enough
|
|
104
111
|
#
|
|
@@ -107,22 +114,18 @@ module Reviewer
|
|
|
107
114
|
def stale?
|
|
108
115
|
return false unless preparable?
|
|
109
116
|
|
|
110
|
-
|
|
117
|
+
@timing.stale?
|
|
111
118
|
end
|
|
112
119
|
|
|
113
120
|
# Convenience method for determining if a tool has a configured install link
|
|
114
121
|
#
|
|
115
122
|
# @return [Boolean] true if there is an `install` key under links and the value isn't blank
|
|
116
|
-
def install_link?
|
|
117
|
-
links.key?(:install) && !links[:install].nil?
|
|
118
|
-
end
|
|
123
|
+
def install_link? = links.key?(:install) && !!links[:install]
|
|
119
124
|
|
|
120
125
|
# Returns the text for the install link if available
|
|
121
126
|
#
|
|
122
127
|
# @return [String, nil] the link if it exists, nil otherwise
|
|
123
|
-
def install_link
|
|
124
|
-
install_link? ? links.fetch(:install) : nil
|
|
125
|
-
end
|
|
128
|
+
def install_link = install_link? ? links.fetch(:install) : nil
|
|
126
129
|
|
|
127
130
|
# Determines if two tools are equal
|
|
128
131
|
# @param other [Tool] the tool to compare to the current instance
|
|
@@ -132,5 +135,43 @@ module Reviewer
|
|
|
132
135
|
settings == other.settings
|
|
133
136
|
end
|
|
134
137
|
alias :== eql?
|
|
138
|
+
|
|
139
|
+
# Records the pass/fail status and failed files from a result into history
|
|
140
|
+
# @param result [Runner::Result] the result of running this tool
|
|
141
|
+
#
|
|
142
|
+
# @return [void]
|
|
143
|
+
def record_run(result)
|
|
144
|
+
status = result.success? ? :passed : :failed
|
|
145
|
+
@history.set(key, :last_status, status)
|
|
146
|
+
|
|
147
|
+
if result.success?
|
|
148
|
+
@history.set(key, :last_failed_files, nil)
|
|
149
|
+
else
|
|
150
|
+
files = Runner::FailedFiles.new(result.stdout, result.stderr).to_a
|
|
151
|
+
@history.set(key, :last_failed_files, files) if files.any?
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Resolves which files this tool should process
|
|
156
|
+
# @param files [Array<String>] the input files to resolve
|
|
157
|
+
#
|
|
158
|
+
# @return [Array<String>] files after mapping and filtering
|
|
159
|
+
def resolve_files(files)
|
|
160
|
+
file_resolver.resolve(files)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Determines if this tool should be skipped because files were requested but none match
|
|
164
|
+
# @param files [Array<String>] the requested files
|
|
165
|
+
#
|
|
166
|
+
# @return [Boolean] true if files were requested but none match after resolution
|
|
167
|
+
def skip_files?(files)
|
|
168
|
+
file_resolver.skip?(files)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
private
|
|
172
|
+
|
|
173
|
+
def file_resolver
|
|
174
|
+
@file_resolver ||= FileResolver.new(settings)
|
|
175
|
+
end
|
|
135
176
|
end
|
|
136
177
|
end
|
data/lib/reviewer/tools.rb
CHANGED
|
@@ -7,37 +7,39 @@ module Reviewer
|
|
|
7
7
|
include Enumerable
|
|
8
8
|
|
|
9
9
|
# Provides an instance to work with for knowing which tools to run in a given context.
|
|
10
|
-
# @param tags
|
|
11
|
-
# @param tool_names
|
|
10
|
+
# @param tags [Array] the tags to use to filter tools for a run
|
|
11
|
+
# @param tool_names [Array<String>] the explicitly provided tool names to filter tools for a run
|
|
12
|
+
# @param arguments [Arguments] the parsed CLI arguments
|
|
13
|
+
# @param history [History] the history store for status persistence
|
|
14
|
+
# @param config_file [Pathname, nil] path to the .reviewer.yml configuration file
|
|
12
15
|
#
|
|
13
16
|
# @return [Reviewer::Tools] collection of tools based on the current run context
|
|
14
|
-
def initialize(tags: nil, tool_names: nil)
|
|
15
|
-
@tags
|
|
16
|
-
@tool_names
|
|
17
|
+
def initialize(tags: nil, tool_names: nil, arguments: nil, history: nil, config_file: nil)
|
|
18
|
+
@tags = tags
|
|
19
|
+
@tool_names = tool_names
|
|
20
|
+
@arguments = arguments
|
|
21
|
+
@history = history
|
|
22
|
+
@config_file = config_file
|
|
17
23
|
end
|
|
18
24
|
|
|
19
25
|
# The current state of all available configured tools regardless of whether they are disabled
|
|
20
26
|
#
|
|
21
27
|
# @return [Hash] hash representing all of the configured tools
|
|
22
|
-
def to_h
|
|
23
|
-
configured
|
|
24
|
-
end
|
|
28
|
+
def to_h = configured
|
|
25
29
|
alias inspect to_h
|
|
26
30
|
|
|
27
31
|
# Provides a collection of all configured tools instantiated as Tool instances
|
|
28
32
|
#
|
|
29
33
|
# @return [Array<Tool>] the full collection of all Tool instances
|
|
30
34
|
def all
|
|
31
|
-
configured.
|
|
35
|
+
configured.map { |tool_name, config| Tool.new(tool_name, config: config, history: @history) }
|
|
32
36
|
end
|
|
33
37
|
alias to_a all
|
|
34
38
|
|
|
35
|
-
# Provides a collection of all
|
|
39
|
+
# Provides a collection of all tools that run in the default batch
|
|
36
40
|
#
|
|
37
|
-
# @return [Array<Tool>] the full collection of
|
|
38
|
-
def enabled
|
|
39
|
-
@enabled ||= all.keep_if(&:enabled?)
|
|
40
|
-
end
|
|
41
|
+
# @return [Array<Tool>] the full collection of batch-included Tool instances
|
|
42
|
+
def enabled = @enabled ||= all.reject(&:skip_in_batch?)
|
|
41
43
|
|
|
42
44
|
# Provides a collection of all explicitly-specified-via-command-line tools as Tool instances
|
|
43
45
|
#
|
|
@@ -54,13 +56,22 @@ module Reviewer
|
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
# Uses the full context of a run to provide the filtered subset of tools to use. It takes into
|
|
57
|
-
# consideration: tagged tools, explicitly-specified tools,
|
|
58
|
-
# any other relevant details that should influence whether a specific
|
|
59
|
-
# of the current batch being executed.
|
|
59
|
+
# consideration: tagged tools, explicitly-specified tools, failed tools, configuration
|
|
60
|
+
# (enabled/disabled), and any other relevant details that should influence whether a specific
|
|
61
|
+
# tool should be run as part of the current batch being executed.
|
|
60
62
|
#
|
|
61
63
|
# @return [Array<Tool>] the full collection of should-be-used-for-this-run tools
|
|
62
64
|
def current
|
|
63
|
-
|
|
65
|
+
return enabled unless subset? || failed_keyword?
|
|
66
|
+
|
|
67
|
+
(specified + tagged + failed).uniq
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Returns tools that failed in the previous run based on history
|
|
71
|
+
#
|
|
72
|
+
# @return [Array<Tool>] tools with :last_status of :failed in history
|
|
73
|
+
def failed_from_history
|
|
74
|
+
all.select { |tool| @history.get(tool.key, :last_status) == :failed }
|
|
64
75
|
end
|
|
65
76
|
|
|
66
77
|
private
|
|
@@ -70,28 +81,31 @@ module Reviewer
|
|
|
70
81
|
# only a subset of relevant tools.
|
|
71
82
|
#
|
|
72
83
|
# @return [Boolean] true if any tool names or tags are provided via the command line
|
|
73
|
-
def subset?
|
|
74
|
-
tool_names.any? || tags.any?
|
|
75
|
-
end
|
|
84
|
+
def subset? = tool_names.any? || tags.any?
|
|
76
85
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
end
|
|
86
|
+
def failed
|
|
87
|
+
return [] unless failed_keyword?
|
|
80
88
|
|
|
81
|
-
|
|
82
|
-
Array(@tags || Reviewer.arguments.tags)
|
|
89
|
+
failed_from_history
|
|
83
90
|
end
|
|
84
91
|
|
|
85
|
-
def
|
|
86
|
-
|
|
87
|
-
|
|
92
|
+
def failed_keyword? = @arguments&.keywords&.failed? || false
|
|
93
|
+
|
|
94
|
+
def configured = @configured ||= Configuration::Loader.configuration(file: @config_file)
|
|
95
|
+
def tags = Array(@tags || matching_tags)
|
|
96
|
+
def tool_names = Array(@tool_names || matching_tool_names)
|
|
97
|
+
def tagged?(tool) = tool.matches_tags?(tags)
|
|
98
|
+
def named?(tool) = tool_names.map(&:to_s).include?(tool.key.to_s)
|
|
88
99
|
|
|
89
|
-
def
|
|
90
|
-
|
|
100
|
+
def matching_tool_names
|
|
101
|
+
provided = @arguments&.keywords&.provided || []
|
|
102
|
+
provided & configured.keys.map(&:to_s)
|
|
91
103
|
end
|
|
92
104
|
|
|
93
|
-
def
|
|
94
|
-
|
|
105
|
+
def matching_tags
|
|
106
|
+
provided = @arguments&.keywords&.provided || []
|
|
107
|
+
all_tags = enabled.flat_map(&:tags).uniq
|
|
108
|
+
(provided & all_tags) + Array(@arguments&.tags&.raw)
|
|
95
109
|
end
|
|
96
110
|
end
|
|
97
111
|
end
|
data/lib/reviewer/version.rb
CHANGED
data/lib/reviewer.rb
CHANGED
|
@@ -2,89 +2,85 @@
|
|
|
2
2
|
|
|
3
3
|
require 'benchmark'
|
|
4
4
|
require 'forwardable'
|
|
5
|
-
require 'logger'
|
|
5
|
+
# require 'logger'
|
|
6
|
+
require 'rainbow'
|
|
6
7
|
|
|
7
|
-
require_relative 'reviewer/
|
|
8
|
+
require_relative 'reviewer/configuration'
|
|
9
|
+
require_relative 'reviewer/configuration/loader'
|
|
10
|
+
require_relative 'reviewer/context'
|
|
11
|
+
require_relative 'reviewer/history'
|
|
12
|
+
require_relative 'reviewer/tool'
|
|
13
|
+
require_relative 'reviewer/tools'
|
|
8
14
|
|
|
9
15
|
require_relative 'reviewer/arguments'
|
|
10
16
|
require_relative 'reviewer/batch'
|
|
17
|
+
require_relative 'reviewer/capabilities'
|
|
11
18
|
require_relative 'reviewer/command'
|
|
12
|
-
require_relative 'reviewer/configuration'
|
|
13
|
-
require_relative 'reviewer/guidance'
|
|
14
|
-
require_relative 'reviewer/history'
|
|
15
|
-
require_relative 'reviewer/keywords'
|
|
16
|
-
require_relative 'reviewer/loader'
|
|
17
19
|
require_relative 'reviewer/output'
|
|
18
|
-
require_relative 'reviewer/
|
|
20
|
+
require_relative 'reviewer/prompt'
|
|
21
|
+
require_relative 'reviewer/report'
|
|
19
22
|
require_relative 'reviewer/runner'
|
|
23
|
+
require_relative 'reviewer/session'
|
|
20
24
|
require_relative 'reviewer/shell'
|
|
21
|
-
require_relative 'reviewer/
|
|
22
|
-
require_relative 'reviewer/
|
|
25
|
+
require_relative 'reviewer/setup'
|
|
26
|
+
require_relative 'reviewer/doctor'
|
|
23
27
|
require_relative 'reviewer/version'
|
|
24
28
|
|
|
25
29
|
# Primary interface for the reviewer tools
|
|
26
30
|
module Reviewer
|
|
31
|
+
# Base error class for all Reviewer errors
|
|
27
32
|
class Error < StandardError; end
|
|
28
33
|
|
|
29
34
|
class << self
|
|
30
|
-
# Resets the loaded tools
|
|
31
|
-
def reset!
|
|
32
|
-
@tools = nil
|
|
33
|
-
end
|
|
35
|
+
# Resets the loaded tools and arguments
|
|
36
|
+
def reset! = @tools = @arguments = @prompt = @output = @history = @configuration = nil
|
|
34
37
|
|
|
35
38
|
# Runs the `review` command for the specified tools/files. Reviewer expects all configured
|
|
36
|
-
#
|
|
37
|
-
# @param clear_streen [boolean] clears the screen to reduce noise when true
|
|
39
|
+
# commands that are not disabled to have an entry for the `review` command.
|
|
38
40
|
#
|
|
39
41
|
# @return [void] Prints output to the console
|
|
40
|
-
def review
|
|
41
|
-
|
|
42
|
+
def review
|
|
43
|
+
handle_early_exits { exit build_session.review }
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
# Runs the `format` command for the specified tools/files for which it is configured.
|
|
45
|
-
# @param clear_streen [boolean] clears the screen to reduce noise when true
|
|
46
47
|
#
|
|
47
48
|
# @return [void] Prints output to the console
|
|
48
|
-
def format
|
|
49
|
-
|
|
49
|
+
def format
|
|
50
|
+
handle_early_exits { exit build_session.format }
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
# The collection of arguments that were passed via the command line.
|
|
53
54
|
#
|
|
54
55
|
# @return [Reviewer::Arguments] exposes tags, files, and keywords from arguments
|
|
55
|
-
def arguments
|
|
56
|
-
@arguments ||= Arguments.new
|
|
57
|
-
end
|
|
56
|
+
def arguments = @arguments ||= Arguments.new
|
|
58
57
|
|
|
59
58
|
# An interface for the collection of configured tools for accessing subsets of tools
|
|
60
|
-
#
|
|
59
|
+
# based on enabled/disabled, tags, keywords, etc.
|
|
61
60
|
#
|
|
62
61
|
# @return [Reviewer::Tools] exposes the set of tools to be run in a given context
|
|
63
|
-
def tools
|
|
64
|
-
@tools ||= Tools.new
|
|
65
|
-
end
|
|
62
|
+
def tools = @tools ||= Tools.new(arguments: arguments, history: history, config_file: configuration.file)
|
|
66
63
|
|
|
67
64
|
# The primary output method for Reviewer to consistently display success/failure details for a
|
|
68
|
-
#
|
|
65
|
+
# unique run of each tool and the collective summary when relevant.
|
|
69
66
|
#
|
|
70
67
|
# @return [Reviewer::Output] prints formatted output to the console.
|
|
71
|
-
def output
|
|
72
|
-
@output ||= Output.new
|
|
73
|
-
end
|
|
68
|
+
def output = @output ||= Output.new
|
|
74
69
|
|
|
75
70
|
# A file store for sharing information across runs
|
|
76
71
|
#
|
|
77
72
|
# @return [Reviewer::History] a YAML::Store (or Pstore) containing data on tools
|
|
78
|
-
def history
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
def history = @history ||= History.new(file: configuration.history_file)
|
|
74
|
+
|
|
75
|
+
# An interactive prompt for yes/no questions
|
|
76
|
+
#
|
|
77
|
+
# @return [Reviewer::Prompt] prompt instance wrapping stdin/stdout
|
|
78
|
+
def prompt = @prompt ||= Prompt.new
|
|
81
79
|
|
|
82
80
|
# Exposes the configuration options for Reviewer.
|
|
83
81
|
#
|
|
84
82
|
# @return [Reviewer::Configuration] configuration settings instance
|
|
85
|
-
def configuration
|
|
86
|
-
@configuration ||= Configuration.new
|
|
87
|
-
end
|
|
83
|
+
def configuration = @configuration ||= Configuration.new
|
|
88
84
|
|
|
89
85
|
# A block approach to configuring Reviewer.
|
|
90
86
|
#
|
|
@@ -94,27 +90,91 @@ module Reviewer
|
|
|
94
90
|
# end
|
|
95
91
|
#
|
|
96
92
|
# @return [Reviewer::Configuration] Reviewer configuration settings
|
|
97
|
-
def configure
|
|
98
|
-
yield(configuration)
|
|
99
|
-
end
|
|
93
|
+
def configure = yield(configuration)
|
|
100
94
|
|
|
101
95
|
private
|
|
102
96
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
97
|
+
def handle_early_exits
|
|
98
|
+
return show_help if arguments.help?
|
|
99
|
+
return show_version if arguments.version?
|
|
100
|
+
return Setup.run if subcommand?(:init)
|
|
101
|
+
return run_doctor if subcommand?(:doctor)
|
|
102
|
+
return run_capabilities if capabilities_flag?
|
|
103
|
+
return run_first_time_setup unless configuration.file.exist?
|
|
104
|
+
|
|
105
|
+
yield
|
|
106
|
+
end
|
|
113
107
|
|
|
114
|
-
|
|
108
|
+
def subcommand?(name) = ARGV.first == name.to_s
|
|
109
|
+
def capabilities_flag? = ARGV.include?('--capabilities') || ARGV.include?('-c')
|
|
110
|
+
|
|
111
|
+
def show_help
|
|
112
|
+
output.help(help_text)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def help_text
|
|
116
|
+
<<~HELP
|
|
117
|
+
Usage: rvw [tool or tag ...] [keyword ...] [options]
|
|
118
|
+
fmt [tool or tag ...] [keyword ...] [options]
|
|
119
|
+
|
|
120
|
+
Commands:
|
|
121
|
+
rvw Run all enabled tools
|
|
122
|
+
rvw <tool> Run a single tool by its config key
|
|
123
|
+
rvw <tag> Run tools matching a tag
|
|
124
|
+
fmt Auto-fix with format commands
|
|
125
|
+
rvw init Generate .reviewer.yml from Gemfile.lock
|
|
126
|
+
rvw doctor Check configuration and tool health
|
|
127
|
+
|
|
128
|
+
Keywords:
|
|
129
|
+
staged Files staged for commit
|
|
130
|
+
unstaged Files with unstaged changes
|
|
131
|
+
modified All changed files (staged + unstaged)
|
|
132
|
+
untracked New files not yet tracked by git
|
|
133
|
+
failed Re-run only tools that failed last time
|
|
134
|
+
|
|
135
|
+
Options:
|
|
136
|
+
#{slop_options}
|
|
137
|
+
|
|
138
|
+
Examples:
|
|
139
|
+
rvw rubocop Run RuboCop only
|
|
140
|
+
rvw staged Review staged files across all tools
|
|
141
|
+
rvw -t security modified Run security-tagged tools on changed files
|
|
142
|
+
rvw tests -f test/user_test.rb Run tests on a specific file
|
|
143
|
+
rvw failed Re-run what failed last time
|
|
144
|
+
fmt rubocop Auto-fix with RuboCop
|
|
145
|
+
HELP
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def slop_options
|
|
149
|
+
arguments.options.to_s.lines.drop(1).join.rstrip
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def show_version
|
|
153
|
+
output.help(VERSION)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def run_capabilities
|
|
157
|
+
puts Capabilities.new(tools: tools).to_json
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def run_doctor
|
|
161
|
+
report = Doctor.run(configuration: configuration, tools: tools)
|
|
162
|
+
Doctor::Formatter.new(output).print(report)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def run_first_time_setup
|
|
166
|
+
formatter = Setup::Formatter.new(output)
|
|
167
|
+
formatter.first_run_greeting
|
|
168
|
+
if prompt.yes?('Would you like to set it up now?')
|
|
169
|
+
Setup.run(configuration: configuration)
|
|
170
|
+
else
|
|
171
|
+
formatter.first_run_skip
|
|
172
|
+
end
|
|
173
|
+
end
|
|
115
174
|
|
|
116
|
-
|
|
117
|
-
|
|
175
|
+
def build_session
|
|
176
|
+
context = Context.new(arguments: arguments, output: output, history: history)
|
|
177
|
+
Session.new(context: context, tools: tools)
|
|
118
178
|
end
|
|
119
179
|
end
|
|
120
180
|
end
|