reviewer 1.1.0 → 1.1.1
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/CHANGELOG.md +7 -0
- data/docs/configuration.md +5 -2
- data/docs/usage.md +32 -6
- data/lib/reviewer/arguments/options.rb +3 -2
- data/lib/reviewer/arguments.rb +64 -2
- data/lib/reviewer/batch.rb +7 -2
- data/lib/reviewer/command.rb +11 -10
- data/lib/reviewer/session/formatter.rb +25 -0
- data/lib/reviewer/session.rb +6 -0
- data/lib/reviewer/version.rb +1 -1
- data/lib/reviewer.rb +7 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f8754795211c60e4aff15d716db73abc4ecbbc55c99afda217ab56180dc74b35
|
|
4
|
+
data.tar.gz: 9d7172360cecb1439d2ae513053211027f064786d72ad59f3a4f8029fbbdb557
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ebbf843d62775a5aa4c7ead5da403f9a368680a503782433e2674df6643a5b9911802e9663200fb39e184d26cffb25c166af6ceef340c5f8926efb5f7cbbec93
|
|
7
|
+
data.tar.gz: e48861c8fe35cf51877298cd97e177705022720f0576e4aa4dc1bd73dd9c4221ca27a676837e888eeff7bf4529e3c5418656ce8b78aba643025cee3a0ad6879e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.1.1] - 2026-09-02
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Keep explicit and Git-derived file-scoped runs focused by skipping tools that cannot accept files,
|
|
8
|
+
while preserving bare runs and failed retries; reject empty file options instead of running broadly.
|
|
9
|
+
|
|
3
10
|
## [1.1.0] - 2026-08-28
|
|
4
11
|
|
|
5
12
|
Reviewer 1.1 makes review outcomes more precise and dependable for people and automated consumers.
|
data/docs/configuration.md
CHANGED
|
@@ -106,8 +106,11 @@ rubocop:
|
|
|
106
106
|
| `files.pattern` | Keeps matching paths before the command runs |
|
|
107
107
|
| `files.map_to_tests` | Maps Ruby source paths to existing `minitest` or `rspec` test paths |
|
|
108
108
|
|
|
109
|
-
Only tools with a `files:` block receive resolved file arguments
|
|
110
|
-
|
|
109
|
+
Only tools with a `files:` block receive resolved file arguments. File-scoped requests skip tools
|
|
110
|
+
without one so `rvw staged`, `rvw modified`, and `rvw -f ...` cannot expand into full-project checks.
|
|
111
|
+
Bare `rvw` still runs every tool in the default batch. See the
|
|
112
|
+
[file-scoped behavior matrix](usage.md#file-scoped-behavior) for explicit scopes, composed Git
|
|
113
|
+
selectors, and failed-run precedence.
|
|
111
114
|
|
|
112
115
|
Slashless patterns match each path's basename. Patterns containing `/` match normalized
|
|
113
116
|
repository-relative paths with pathname semantics. In path patterns, `**/` crosses directory
|
data/docs/usage.md
CHANGED
|
@@ -63,9 +63,10 @@ File selectors resolve paths from Git:
|
|
|
63
63
|
| `untracked` | Untracked, non-ignored files |
|
|
64
64
|
|
|
65
65
|
`failed` selects tools whose last executed review failed and, when available, reuses each tool's
|
|
66
|
-
stored failed file paths.
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
stored failed file paths. File-aware tools retry those paths; tools without file support retry their
|
|
67
|
+
full command. A tool remains selected until an executed review records a pass. Skipped, missing,
|
|
68
|
+
formatted, and not-run tools leave that review history unchanged, so a passing scoped run can coexist
|
|
69
|
+
with tools still selected by `rvw failed`.
|
|
69
70
|
|
|
70
71
|
Selections compose:
|
|
71
72
|
|
|
@@ -75,6 +76,10 @@ rvw -t ruby modified
|
|
|
75
76
|
rvw tests -f test/reviewer_test.rb
|
|
76
77
|
```
|
|
77
78
|
|
|
79
|
+
Explicit `-f`, `staged`, `unstaged`, `modified`, and `untracked` requests skip tools without a
|
|
80
|
+
`files:` configuration instead of running their full-project command. Bare `rvw` remains the full
|
|
81
|
+
configured review.
|
|
82
|
+
|
|
78
83
|
The [configuration reference](configuration.md#file-targeting) describes filtering, file-scoped
|
|
79
84
|
commands, and Minitest/RSpec source-to-test mapping.
|
|
80
85
|
|
|
@@ -117,7 +122,7 @@ State describes the terminal disposition Reviewer acts on. For executed failures
|
|
|
117
122
|
|---|---|---:|---|
|
|
118
123
|
| `passed` | The command executed within its configured threshold | `true` | None |
|
|
119
124
|
| `failed` | The command executed outside its configured threshold | `false` | Exit `1` |
|
|
120
|
-
| `skipped` |
|
|
125
|
+
| `skipped` | The tool cannot accept the file scope, or no requested files matched | `false` | None |
|
|
121
126
|
| `missing` | The executable was unavailable | `false` | None in 1.1 |
|
|
122
127
|
| `not_run` | An earlier tool failed and fail-fast stopped the batch | `false` | None |
|
|
123
128
|
|
|
@@ -161,7 +166,10 @@ Every summary contains `total`, all five state counts, and `duration`; the state
|
|
|
161
166
|
|---|---|---|
|
|
162
167
|
| Nonempty report | `schema_version`, `success`, `summary`, `tools` | `state`, `message`, `error` |
|
|
163
168
|
| Empty report | `schema_version`, `state: "empty"`, `success: true`, `message`, zero summary, `tools: []` | `error` |
|
|
164
|
-
|
|
|
169
|
+
| Usage error | `schema_version`, `state: "error"`, `error`, zero summary, `tools: []` | `success`, `message` |
|
|
170
|
+
|
|
171
|
+
Usage errors include `error.code: "unrecognized_selector"` for an unknown tool, tag, or keyword and
|
|
172
|
+
`error.code: "missing_files"` for an empty or missing `-f`/`--files` value.
|
|
165
173
|
|
|
166
174
|
| Tool state | Execution fields | Compatibility flags |
|
|
167
175
|
|---|---|---|
|
|
@@ -175,13 +183,31 @@ tool now has `success: false`, and its unavailable execution fields are null ins
|
|
|
175
183
|
omitted. Valid 1.x `Runner::Result.new` keyword calls and the legacy true-valued payload flags remain
|
|
176
184
|
supported through the 1.x line.
|
|
177
185
|
|
|
186
|
+
## File-scoped behavior
|
|
187
|
+
|
|
188
|
+
| Request | Tools with `files:` | Tools without `files:` |
|
|
189
|
+
|---|---|---|
|
|
190
|
+
| `rvw` | Run without a file filter | Run without a file filter |
|
|
191
|
+
| `rvw -f app/a.rb` | Run with `app/a.rb` | Skip |
|
|
192
|
+
| `rvw staged untracked` | Run with the deduplicated union | Skip |
|
|
193
|
+
| `rvw staged` with no staged files | Do not run | Do not run |
|
|
194
|
+
| `rvw failed` | Use stored failed paths when available; otherwise retry without a file filter | Retry without a file filter |
|
|
195
|
+
| `rvw failed staged` | Use the newly resolved staged paths instead of stored paths | Skip |
|
|
196
|
+
| `rvw failed -f app/a.rb` | Use `app/a.rb` instead of stored paths | Skip |
|
|
197
|
+
|
|
198
|
+
Repeated `-f` options and Git selectors compose. For review and format execution, every `-f` or
|
|
199
|
+
`--files` occurrence requires at least one path; an empty or missing value exits 2 without running
|
|
200
|
+
any tool. Informational `--help` and `--version` requests, plus the `init` and `doctor` control
|
|
201
|
+
commands, exit before file-scope validation. A valid Git selector that finds no files is instead a
|
|
202
|
+
successful no-op because the selector itself was complete.
|
|
203
|
+
|
|
178
204
|
## Exit statuses
|
|
179
205
|
|
|
180
206
|
| Status | Meaning |
|
|
181
207
|
|---|---|
|
|
182
208
|
| `0` | Every executed tool passed; skipped, missing, and not-run tools do not fail the run |
|
|
183
209
|
| `1` | At least one executed tool failed |
|
|
184
|
-
| `2` | The request used an unrecognized
|
|
210
|
+
| `2` | The request used an unrecognized selector or an empty `-f`/`--files` option |
|
|
185
211
|
|
|
186
212
|
A tool may pass with a nonzero process status when its `commands.max_exit_status` allows it. Reviewer
|
|
187
213
|
returns its own status above instead of forwarding the tool's process status.
|
|
@@ -15,9 +15,10 @@ module Reviewer
|
|
|
15
15
|
#
|
|
16
16
|
# @return [void]
|
|
17
17
|
# :reek:TooManyStatements -- a flag declaration list, not branching logic
|
|
18
|
-
def configure(opts)
|
|
18
|
+
def configure(opts, files_callback: nil)
|
|
19
19
|
# Narrowing what gets reviewed
|
|
20
|
-
opts.array '-f', '--files', 'a list of comma-separated files or paths',
|
|
20
|
+
opts.array '-f', '--files', 'a list of comma-separated files or paths',
|
|
21
|
+
delimiter: ',', default: [], &files_callback
|
|
21
22
|
opts.array '-t', '--tags', 'a list of comma-separated tags', delimiter: ',', default: []
|
|
22
23
|
|
|
23
24
|
# Selecting how results are displayed
|
data/lib/reviewer/arguments.rb
CHANGED
|
@@ -42,7 +42,11 @@ module Reviewer
|
|
|
42
42
|
# @return [self]
|
|
43
43
|
def initialize(options = ARGV, output: Output.new)
|
|
44
44
|
@output = output
|
|
45
|
-
@
|
|
45
|
+
@raw_options = options.to_a.dup
|
|
46
|
+
@invalid_files_option = false
|
|
47
|
+
@parser = Slop::Options.new
|
|
48
|
+
Options.configure(@parser, files_callback: files_validation_callback)
|
|
49
|
+
@options = @parser.parse(normalized_options)
|
|
46
50
|
end
|
|
47
51
|
|
|
48
52
|
private
|
|
@@ -105,6 +109,16 @@ module Reviewer
|
|
|
105
109
|
# @return [Boolean] true if JSON output mode is requested
|
|
106
110
|
def json? = options[:json]
|
|
107
111
|
|
|
112
|
+
# Whether the --capabilities flag was passed
|
|
113
|
+
#
|
|
114
|
+
# @return [Boolean] true if capabilities were requested
|
|
115
|
+
def capabilities? = options[:capabilities]
|
|
116
|
+
|
|
117
|
+
# Whether a files option was supplied without a usable value
|
|
118
|
+
#
|
|
119
|
+
# @return [Boolean] true when any -f/--files occurrence is empty
|
|
120
|
+
def invalid_files_option? = @invalid_files_option
|
|
121
|
+
|
|
108
122
|
# The output format for results
|
|
109
123
|
#
|
|
110
124
|
# @return [Symbol] the output format (:streaming, :summary, or :json)
|
|
@@ -114,7 +128,7 @@ module Reviewer
|
|
|
114
128
|
value = options[:format].to_sym
|
|
115
129
|
return value if KNOWN_FORMATS.include?(value)
|
|
116
130
|
|
|
117
|
-
session_formatter.invalid_format(options[:format], KNOWN_FORMATS)
|
|
131
|
+
session_formatter.invalid_format(options[:format], KNOWN_FORMATS) unless invalid_files_option?
|
|
118
132
|
:streaming
|
|
119
133
|
end
|
|
120
134
|
|
|
@@ -133,5 +147,53 @@ module Reviewer
|
|
|
133
147
|
|
|
134
148
|
multiple_tools ? Runner::Strategies::Captured : Runner::Strategies::Passthrough
|
|
135
149
|
end
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def files_validation_callback
|
|
154
|
+
previous_file_count = 0
|
|
155
|
+
lambda do |files|
|
|
156
|
+
current_files = Array(files)
|
|
157
|
+
file_count = current_files.length
|
|
158
|
+
new_files = current_files.drop(previous_file_count)
|
|
159
|
+
@invalid_files_option = true if file_count <= previous_file_count ||
|
|
160
|
+
new_files.all? { |file| file.strip.empty? }
|
|
161
|
+
previous_file_count = file_count
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def normalized_options
|
|
166
|
+
parsing_options = true
|
|
167
|
+
@raw_options.each_with_index.flat_map do |token, index|
|
|
168
|
+
parsing_options = false if token == '--'
|
|
169
|
+
missing_value = parsing_options && files_option_awaiting_value?(token) &&
|
|
170
|
+
known_option_token?(@raw_options[index + 1])
|
|
171
|
+
normalized_token = parsing_options ? normalize_compact_files_option(token) : token
|
|
172
|
+
|
|
173
|
+
missing_value ? [normalized_token, ''] : [normalized_token]
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def normalize_compact_files_option(token)
|
|
178
|
+
return token unless token.start_with?('-f') && token.length > 2 && !token.start_with?('-f=')
|
|
179
|
+
|
|
180
|
+
"-f=#{token.delete_prefix('-f')}"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def known_option_token?(token)
|
|
184
|
+
return false unless token&.start_with?('-') && token != '--'
|
|
185
|
+
|
|
186
|
+
Slop.parse([token]) { |opts| Options.configure(opts) }
|
|
187
|
+
true
|
|
188
|
+
rescue Slop::UnknownOption
|
|
189
|
+
false
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def files_option_awaiting_value?(token)
|
|
193
|
+
return true if %w[-f --files].include?(token)
|
|
194
|
+
return false if token.start_with?('-f')
|
|
195
|
+
|
|
196
|
+
token.match?(/\A-[^-]+f\z/) && known_option_token?(token)
|
|
197
|
+
end
|
|
136
198
|
end
|
|
137
199
|
end
|
data/lib/reviewer/batch.rb
CHANGED
|
@@ -34,7 +34,7 @@ module Reviewer
|
|
|
34
34
|
runnable_tools = matching_tools
|
|
35
35
|
elapsed_time = Benchmark.realtime do
|
|
36
36
|
failed_index = runnable_tools.find_index { |tool| run_tool(tool).failed? }
|
|
37
|
-
|
|
37
|
+
record_remaining(runnable_tools.drop(failed_index + 1)) if failed_index
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
@report.record_duration(elapsed_time)
|
|
@@ -43,8 +43,13 @@ module Reviewer
|
|
|
43
43
|
|
|
44
44
|
private
|
|
45
45
|
|
|
46
|
-
def
|
|
46
|
+
def record_remaining(tools)
|
|
47
47
|
tools.each do |tool|
|
|
48
|
+
if Command.new(tool, command_type, context: context).skip?
|
|
49
|
+
run_tool(tool)
|
|
50
|
+
next
|
|
51
|
+
end
|
|
52
|
+
|
|
48
53
|
result = Runner::Result.not_run(tool: tool, command_type: command_type)
|
|
49
54
|
@report.add(result)
|
|
50
55
|
end
|
data/lib/reviewer/command.rb
CHANGED
|
@@ -73,10 +73,12 @@ module Reviewer
|
|
|
73
73
|
@target_files ||= tool.resolve_files(requested_files)
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
-
# Determines if this command should be skipped
|
|
76
|
+
# Determines if this command should be skipped for the requested file scope
|
|
77
77
|
#
|
|
78
|
-
# @return [Boolean] true if
|
|
78
|
+
# @return [Boolean] true if the tool cannot accept an explicit file scope or no files match it
|
|
79
79
|
def skip?
|
|
80
|
+
return true if explicit_files.any? && !tool.supports_files?
|
|
81
|
+
|
|
80
82
|
tool.skip_files?(requested_files)
|
|
81
83
|
end
|
|
82
84
|
|
|
@@ -97,16 +99,15 @@ module Reviewer
|
|
|
97
99
|
#
|
|
98
100
|
# @return [Array<String>] files from -f flag, keywords like 'staged', or stored failed files
|
|
99
101
|
def requested_files
|
|
100
|
-
@requested_files ||=
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
explicit
|
|
106
|
-
end
|
|
107
|
-
end
|
|
102
|
+
@requested_files ||= if explicit_files.empty? && arguments.keywords.failed?
|
|
103
|
+
stored_failed_files
|
|
104
|
+
else
|
|
105
|
+
explicit_files
|
|
106
|
+
end
|
|
108
107
|
end
|
|
109
108
|
|
|
109
|
+
def explicit_files = @explicit_files ||= arguments.files.to_a
|
|
110
|
+
|
|
110
111
|
# Retrieves failed files stored from the last executed review for this tool
|
|
111
112
|
#
|
|
112
113
|
# @return [Array<String>] stored failed file paths, or empty array
|
|
@@ -8,6 +8,8 @@ module Reviewer
|
|
|
8
8
|
class Formatter
|
|
9
9
|
include Output::Formatting
|
|
10
10
|
|
|
11
|
+
MISSING_FILES_MESSAGE = 'The --files option requires at least one file or path.'
|
|
12
|
+
|
|
11
13
|
attr_reader :output, :printer
|
|
12
14
|
private :output, :printer
|
|
13
15
|
|
|
@@ -55,6 +57,29 @@ module Reviewer
|
|
|
55
57
|
printer.write_raw("#{JSON.pretty_generate(payload)}\n")
|
|
56
58
|
end
|
|
57
59
|
|
|
60
|
+
# Displays a usage error when -f/--files has no value
|
|
61
|
+
#
|
|
62
|
+
# @return [void]
|
|
63
|
+
def missing_files_option
|
|
64
|
+
printer.puts(:warning, MISSING_FILES_MESSAGE)
|
|
65
|
+
output.newline
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Renders the machine-readable form of the missing files usage error
|
|
69
|
+
#
|
|
70
|
+
# @return [void]
|
|
71
|
+
def missing_files_option_json
|
|
72
|
+
payload = {
|
|
73
|
+
schema_version: Report::SCHEMA_VERSION,
|
|
74
|
+
state: 'error',
|
|
75
|
+
error: { code: 'missing_files', message: MISSING_FILES_MESSAGE },
|
|
76
|
+
summary: Report.empty_summary,
|
|
77
|
+
tools: []
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
printer.write_raw("#{JSON.pretty_generate(payload)}\n")
|
|
81
|
+
end
|
|
82
|
+
|
|
58
83
|
# Displays a warning when an unrecognized output format is requested
|
|
59
84
|
# @param value [String] the invalid format name
|
|
60
85
|
# @param known [Array<Symbol>] the valid format options
|
data/lib/reviewer/session.rb
CHANGED
|
@@ -45,11 +45,17 @@ module Reviewer
|
|
|
45
45
|
def history = context.history
|
|
46
46
|
|
|
47
47
|
def run_tools(command_type)
|
|
48
|
+
return reject_invalid_files_option if arguments.invalid_files_option?
|
|
48
49
|
return reject_unrecognized_selectors if unrecognized_selectors.any?
|
|
49
50
|
|
|
50
51
|
json_output? ? run_json(command_type) : run_text(command_type)
|
|
51
52
|
end
|
|
52
53
|
|
|
54
|
+
def reject_invalid_files_option
|
|
55
|
+
json_output? ? formatter.missing_files_option_json : formatter.missing_files_option
|
|
56
|
+
USAGE_ERROR
|
|
57
|
+
end
|
|
58
|
+
|
|
53
59
|
def run_json(command_type)
|
|
54
60
|
message = json_early_exit_message
|
|
55
61
|
return emit_json_early_exit(message) if message
|
data/lib/reviewer/version.rb
CHANGED
data/lib/reviewer.rb
CHANGED
|
@@ -100,13 +100,18 @@ module Reviewer
|
|
|
100
100
|
return Setup.run(configuration: configuration) if subcommand?(:init)
|
|
101
101
|
return run_doctor if subcommand?(:doctor)
|
|
102
102
|
return run_capabilities if capabilities_flag?
|
|
103
|
-
return run_first_time_setup
|
|
103
|
+
return run_first_time_setup if first_time_setup?
|
|
104
104
|
|
|
105
105
|
yield
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
+
def first_time_setup? = !arguments.invalid_files_option? && !configuration.file.exist?
|
|
109
|
+
|
|
108
110
|
def subcommand?(name) = ARGV.first == name.to_s
|
|
109
|
-
|
|
111
|
+
|
|
112
|
+
def capabilities_flag?
|
|
113
|
+
!arguments.invalid_files_option? && arguments.capabilities?
|
|
114
|
+
end
|
|
110
115
|
|
|
111
116
|
def show_help
|
|
112
117
|
output.help(help_text)
|