reviewer 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.inch.yml +4 -0
- data/.reviewer.example.yml +9 -7
- data/.reviewer.yml +18 -4
- data/CHANGELOG.md +24 -3
- data/Gemfile +2 -1
- data/Gemfile.lock +23 -2
- data/README.md +2 -2
- data/{bin → exe}/fmt +2 -0
- data/{bin → exe}/rvw +2 -0
- data/lib/reviewer.rb +78 -25
- data/lib/reviewer/arguments.rb +29 -8
- data/lib/reviewer/arguments/files.rb +56 -0
- data/lib/reviewer/arguments/keywords.rb +116 -0
- data/lib/reviewer/arguments/keywords/git.rb +16 -0
- data/lib/reviewer/arguments/keywords/git/staged.rb +64 -0
- data/lib/reviewer/arguments/tags.rb +41 -0
- data/lib/reviewer/configuration.rb +5 -8
- data/lib/reviewer/loader.rb +32 -5
- data/lib/reviewer/logger.rb +19 -6
- data/lib/reviewer/runner.rb +18 -18
- data/lib/reviewer/tool.rb +10 -0
- data/lib/reviewer/tool/settings.rb +17 -7
- data/lib/reviewer/tools.rb +56 -8
- data/lib/reviewer/version.rb +1 -1
- data/reviewer.gemspec +1 -0
- metadata +27 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 538f085294cbfbb5ce7c8412a39991534a2251b83978894133537f20e2bbf1ba
|
4
|
+
data.tar.gz: 39bee00497eccb91256b772f5af8a77746374648efd884c9ffb719f9fd7b1e16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 872fbfb03db5c520c6b20d0eedf624ae37c9c9c2452b34bb286a4f9c52eddafe2846f5e401d702424e14dae9a153cefd6e3f8e8bf15a9a3e4c58e76940af64e9
|
7
|
+
data.tar.gz: 4bc2ae679b15339577014e404fb8340c436e0c7ae26f6f46ac2baaf94d14592c42610a343e6eba679928bdcb3d1c0698f0e75d68771f78fa4f1bef9f03ed8dfd
|
data/.inch.yml
ADDED
data/.reviewer.example.yml
CHANGED
@@ -14,14 +14,16 @@
|
|
14
14
|
# prepare: // Optional. Command to run prior to the review phase. ex. 'bundle exec bundle-audit update'
|
15
15
|
# review: // Required. The only truly required field because this is the whole point.
|
16
16
|
# format: // Optional. Command to auto-update rule violations when possible.
|
17
|
-
# quiet_option:
|
17
|
+
# quiet_option: // Optional, but strongly suggested. Helps keep output under control when running multiple tools.
|
18
18
|
# max_exit_status: // Optional, defaults to 0. Some tools like Yarn Audit essentially won't return less than a 3. This specifies the threshold that's still considered passing.
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
19
|
+
# files_flag: // Optional, defaults to '' (empty string). The name of the flag used to pass subsets of files to the command.
|
20
|
+
# files_separator: // Optional, defaults to ' ' (single space). The character used to separate lists of files and directories.
|
21
|
+
# env: // Optional. A way to specify necessary environment variables for the tools commands. The key is the variable name, and the value is, well, the value.
|
22
|
+
# example_one: value // - The names will automatically be capitalized, so you can freely use lower-case here.
|
23
|
+
# example_one: value // - Reviewer is smart enough to handle string values with spaces and automatically quote them.
|
24
|
+
# flags: // Optional. A way to specify flags *only for the review command*. The key is the flag name, and the value is, well, the value.
|
25
|
+
# example_one: value // - Reviewer is smart enough to handle single-letter (-f) and multi-letter (--format) flags.
|
26
|
+
# example_two: value // - It's highly-recommended to use the longer-name format for flags when possible to serve as self-documentation.
|
25
27
|
|
26
28
|
# This is an example for a YAML block for a command-line tool:
|
27
29
|
#
|
data/.reviewer.yml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
bundler_audit:
|
2
|
-
tags: [critical, dependencies, ruby]
|
2
|
+
tags: [critical, dependencies, ruby, dev]
|
3
3
|
name: Bundler Audit
|
4
|
-
description:
|
4
|
+
description: Review Gem Dependencies for Security Issues
|
5
5
|
links:
|
6
6
|
home: https://github.com/rubysec/bundler-audit
|
7
7
|
install: https://github.com/rubysec/bundler-audit#install
|
@@ -14,7 +14,7 @@ bundler_audit:
|
|
14
14
|
tests:
|
15
15
|
name: Minitest
|
16
16
|
description: Unit Tests
|
17
|
-
tags: [ruby, tests]
|
17
|
+
tags: [ruby, tests, dev]
|
18
18
|
links:
|
19
19
|
home:
|
20
20
|
commands:
|
@@ -22,8 +22,9 @@ tests:
|
|
22
22
|
quiet_option: '--silent'
|
23
23
|
|
24
24
|
rubocop:
|
25
|
+
name: Rubocop
|
26
|
+
description: Review Ruby Syntax & Formatting for Consistency
|
25
27
|
tags: [ruby, syntax]
|
26
|
-
description: Review Ruby syntax/formatting for consistency
|
27
28
|
links:
|
28
29
|
home: https://rubocop.org
|
29
30
|
install: https://docs.rubocop.org/rubocop/1.13/installation.html
|
@@ -34,3 +35,16 @@ rubocop:
|
|
34
35
|
review: 'bundle exec rubocop --parallel'
|
35
36
|
format: 'bundle exec rubocop --auto-correct'
|
36
37
|
quiet_option: '--format q'
|
38
|
+
files_flag: ''
|
39
|
+
files_list_separator: ' '
|
40
|
+
|
41
|
+
inch:
|
42
|
+
disabled: true # Inch provides guidance and is generally run solo
|
43
|
+
tags: [docs, ruby, dev]
|
44
|
+
name: Inch
|
45
|
+
description: Review Ruby Documentation
|
46
|
+
links:
|
47
|
+
home: https://rrrene.org/inch/
|
48
|
+
commands:
|
49
|
+
install: 'bundle exec gem install inch'
|
50
|
+
review: 'bundle exec inch'
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,29 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
-
- TODO:
|
4
|
-
- TODO: Add
|
5
|
-
|
3
|
+
- TODO: Improve and streamline installation
|
4
|
+
- TODO: Add support for targeting specific files
|
5
|
+
|
6
|
+
## [0.1.3] - 2021-07-07
|
7
|
+
|
8
|
+
The most significant update to how the core commands work and how the command-line arguments are handled. Most of the overall structure is starting to feel stable enough to begin documenting and adding comments.
|
9
|
+
|
10
|
+
- Commands are now `rvw` and `fmt`
|
11
|
+
- Adds command-line arguments support
|
12
|
+
- Adds support for specifying tags via the command-line
|
13
|
+
- Adds support for specifying files via the command-line
|
14
|
+
- Adds support for handling keywords via the command-line
|
15
|
+
- Improved configuration and loading
|
16
|
+
- Adds Tools class for more convenient filtering of tools based on arguments
|
17
|
+
- Begins the process of adding documentation comments
|
18
|
+
|
19
|
+
## [0.1.2] - 2021-05-04
|
20
|
+
|
21
|
+
The bare minimum works now, but it's not quite there for day-to-day use. It works well enough that it's being used to review this project, but there's much more to do.
|
22
|
+
|
23
|
+
- Add Runner to wrap individual commands
|
24
|
+
- Add benchmark/timing to Runner
|
25
|
+
- Extract Logging to a dedicated class
|
26
|
+
- Intelligently handle non-zero exit statuses
|
6
27
|
|
7
28
|
## [0.1.1] - 2021-04-17
|
8
29
|
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
reviewer (0.1.
|
4
|
+
reviewer (0.1.3)
|
5
5
|
activesupport
|
6
6
|
colorize
|
7
7
|
slop
|
@@ -19,14 +19,26 @@ GEM
|
|
19
19
|
bundler-audit (0.8.0)
|
20
20
|
bundler (>= 1.2.0, < 3)
|
21
21
|
thor (~> 1.0)
|
22
|
+
coderay (1.1.3)
|
22
23
|
colorize (0.8.1)
|
23
24
|
concurrent-ruby (1.1.8)
|
24
25
|
i18n (1.8.10)
|
25
26
|
concurrent-ruby (~> 1.0)
|
27
|
+
inch (0.8.0)
|
28
|
+
pry
|
29
|
+
sparkr (>= 0.2.0)
|
30
|
+
term-ansicolor
|
31
|
+
yard (~> 0.9.12)
|
32
|
+
method_source (1.0.0)
|
26
33
|
minitest (5.14.4)
|
34
|
+
minitest-color (0.0.2)
|
35
|
+
minitest (~> 5)
|
27
36
|
parallel (1.20.1)
|
28
37
|
parser (3.0.1.0)
|
29
38
|
ast (~> 2.4.1)
|
39
|
+
pry (0.14.1)
|
40
|
+
coderay (~> 1.1)
|
41
|
+
method_source (~> 1.0)
|
30
42
|
rainbow (3.0.0)
|
31
43
|
rake (13.0.3)
|
32
44
|
regexp_parser (2.1.1)
|
@@ -48,10 +60,17 @@ GEM
|
|
48
60
|
rubocop
|
49
61
|
ruby-progressbar (1.11.0)
|
50
62
|
slop (4.8.2)
|
63
|
+
sparkr (0.4.1)
|
64
|
+
sync (0.5.0)
|
65
|
+
term-ansicolor (1.7.1)
|
66
|
+
tins (~> 1.0)
|
51
67
|
thor (1.1.0)
|
68
|
+
tins (1.28.0)
|
69
|
+
sync
|
52
70
|
tzinfo (2.0.4)
|
53
71
|
concurrent-ruby (~> 1.0)
|
54
72
|
unicode-display_width (2.0.0)
|
73
|
+
yard (0.9.26)
|
55
74
|
zeitwerk (2.4.2)
|
56
75
|
|
57
76
|
PLATFORMS
|
@@ -61,7 +80,9 @@ PLATFORMS
|
|
61
80
|
|
62
81
|
DEPENDENCIES
|
63
82
|
bundler-audit
|
64
|
-
|
83
|
+
inch
|
84
|
+
minitest (~> 5.14.4)
|
85
|
+
minitest-color (~> 0.0.2)
|
65
86
|
rake (~> 13.0)
|
66
87
|
reviewer!
|
67
88
|
rubocop
|
data/README.md
CHANGED
@@ -17,10 +17,10 @@ bundle exec rake notes
|
|
17
17
|
|
18
18
|
You run...
|
19
19
|
```
|
20
|
-
|
20
|
+
rvw
|
21
21
|
```
|
22
22
|
|
23
|
-
|
23
|
+
But that's just the beginning. It also cleans up the output and lets easily you run subsets of commands for different contexts.
|
24
24
|
|
25
25
|
For more detailed information, take a look at the [Overview](https://github.com/garrettdimon/reviewer/wiki/Overview) and [Usage](https://github.com/garrettdimon/reviewer/wiki/Usage) pages in the wiki.
|
26
26
|
|
data/{bin → exe}/fmt
RENAMED
data/{bin → exe}/rvw
RENAMED
data/lib/reviewer.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
require 'active_support/core_ext/string'
|
4
4
|
require 'benchmark'
|
5
5
|
|
6
|
-
require_relative 'reviewer/configuration'
|
7
6
|
require_relative 'reviewer/arguments'
|
7
|
+
require_relative 'reviewer/configuration'
|
8
8
|
require_relative 'reviewer/loader'
|
9
9
|
require_relative 'reviewer/logger'
|
10
10
|
require_relative 'reviewer/runner'
|
@@ -17,38 +17,91 @@ module Reviewer
|
|
17
17
|
class Error < StandardError; end
|
18
18
|
|
19
19
|
class << self
|
20
|
-
attr_writer :configuration
|
21
|
-
end
|
20
|
+
attr_writer :configuration
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
# Runs the `review` command for the specified tools/files. Reviewer expects all configured
|
23
|
+
# commands that are not disabled to have an entry for the `review` command.
|
24
|
+
#
|
25
|
+
# @return [void] Prints output to the console
|
26
|
+
def review
|
27
|
+
perform(:review)
|
28
|
+
end
|
27
29
|
|
28
|
-
|
30
|
+
# Runs the `format` command for the specified tools/files for which it is configured.
|
31
|
+
#
|
32
|
+
# @return [void] Prints output to the console
|
33
|
+
def format
|
34
|
+
perform(:format)
|
35
|
+
end
|
29
36
|
|
30
|
-
|
31
|
-
|
37
|
+
# The collection of arguments that were passed via the command line.
|
38
|
+
#
|
39
|
+
# @return [Reviewer::Arguments] exposes tags, files, and keywords from arguments
|
40
|
+
def arguments
|
41
|
+
@arguments ||= Arguments.new
|
32
42
|
end
|
33
|
-
puts "\n➤ Total Time: #{elapsed_time.round(3)}s\n"
|
34
|
-
end
|
35
43
|
|
36
|
-
|
37
|
-
#
|
38
|
-
|
39
|
-
|
44
|
+
# An interface for the collection of configured tools for accessing subsets of tools
|
45
|
+
# based on enabled/disabled, tags, keywords, etc.
|
46
|
+
#
|
47
|
+
# @return [Reviewer::Tools] exposes the set of tools to be run in a given context
|
48
|
+
def tools
|
49
|
+
@tools ||= Tools.new
|
40
50
|
end
|
41
|
-
end
|
42
51
|
|
43
|
-
|
44
|
-
|
45
|
-
|
52
|
+
# The primary output method for Reviewer to consistently display success/failure details for a
|
53
|
+
# unique run of each tool and the collective summary when relevant.
|
54
|
+
#
|
55
|
+
# @return [Reviewer::Logger] prints formatted output to the command line.
|
56
|
+
def logger
|
57
|
+
@logger ||= Logger.new
|
58
|
+
end
|
46
59
|
|
47
|
-
|
48
|
-
|
49
|
-
|
60
|
+
# Exposes the configuration options for Reviewer.
|
61
|
+
#
|
62
|
+
# @return [Reviewer::Configuration] configuration settings instance
|
63
|
+
def configuration
|
64
|
+
@configuration ||= Configuration.new
|
65
|
+
end
|
66
|
+
|
67
|
+
# A block approach to configuring Reviewer.
|
68
|
+
#
|
69
|
+
# @example Set configuration file path
|
70
|
+
# Reviewer.configure do |config|
|
71
|
+
# config.file = '~/configuration.yml'
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# @return [Reviewer::Configuration] Reviewer configuration settings
|
75
|
+
def configure
|
76
|
+
yield(configuration)
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
50
80
|
|
51
|
-
|
52
|
-
|
81
|
+
# Provides a consistent approach to running and benchmarking commmands and preventing further
|
82
|
+
# execution of later tools if a command fails.
|
83
|
+
# @param command_type [Symbol] the specific command to run for each tool
|
84
|
+
#
|
85
|
+
# @example Run the `review` command for each relevant tool
|
86
|
+
# perform(:review)
|
87
|
+
#
|
88
|
+
# @return [Hash] the exit status (in integer format) for each command run
|
89
|
+
def perform(command_type)
|
90
|
+
results = {}
|
91
|
+
|
92
|
+
elapsed_time = Benchmark.realtime do
|
93
|
+
tools.current.each do |tool|
|
94
|
+
runner = Runner.new(tool, command_type, logger: logger)
|
95
|
+
exit_status = runner.run
|
96
|
+
results[tool.key] = exit_status
|
97
|
+
|
98
|
+
# If a single tool fails, stop there.
|
99
|
+
break unless exit_status <= tool.max_exit_status
|
100
|
+
end
|
101
|
+
end
|
102
|
+
logger.total_time(elapsed_time)
|
103
|
+
|
104
|
+
results
|
105
|
+
end
|
53
106
|
end
|
54
107
|
end
|
data/lib/reviewer/arguments.rb
CHANGED
@@ -1,9 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'arguments/keywords'
|
4
|
+
require_relative 'arguments/files'
|
5
|
+
require_relative 'arguments/tags'
|
6
|
+
|
3
7
|
require 'slop'
|
4
8
|
|
5
9
|
module Reviewer
|
6
|
-
# Handles option parsing for
|
10
|
+
# Handles option parsing for `rvw` and `fmt` commands
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# `rvw`
|
15
|
+
# `rvw -t ruby`
|
16
|
+
# `rvw -f ./example.rb,./example_test.rb`
|
17
|
+
# `rvw staged`
|
18
|
+
# `rvw --files ./example.rb,./example_test.rb --tags syntax`
|
19
|
+
# `rvw ruby staged`
|
20
|
+
#
|
7
21
|
class Arguments
|
8
22
|
attr_accessor :options
|
9
23
|
|
@@ -24,21 +38,28 @@ module Reviewer
|
|
24
38
|
end
|
25
39
|
end
|
26
40
|
|
27
|
-
def
|
28
|
-
|
41
|
+
def inspect
|
42
|
+
{
|
43
|
+
files: files.raw,
|
44
|
+
tags: tags.raw,
|
45
|
+
keywords: keywords.raw
|
46
|
+
}
|
29
47
|
end
|
30
48
|
|
31
49
|
def tags
|
32
|
-
options[:tags]
|
50
|
+
@tags ||= Arguments::Tags.new(provided: options[:tags])
|
33
51
|
end
|
34
52
|
|
35
|
-
def
|
36
|
-
options
|
53
|
+
def files
|
54
|
+
@files ||= Arguments::Files.new(provided: options[:files])
|
37
55
|
end
|
38
56
|
|
39
57
|
def keywords
|
40
|
-
|
41
|
-
|
58
|
+
@keywords ||= Arguments::Keywords.new(options.arguments)
|
59
|
+
end
|
60
|
+
|
61
|
+
def tool_names
|
62
|
+
@tool_names ||= keywords.for_tool_names.to_a
|
42
63
|
end
|
43
64
|
end
|
44
65
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reviewer
|
4
|
+
class Arguments
|
5
|
+
# Generates the list of files to run the command against
|
6
|
+
class Files
|
7
|
+
attr_reader :provided, :keywords
|
8
|
+
|
9
|
+
alias raw provided
|
10
|
+
|
11
|
+
def initialize(provided: Reviewer.arguments.files.raw, keywords: Reviewer.arguments.keywords)
|
12
|
+
@provided = Array(provided)
|
13
|
+
@keywords = Array(keywords)
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_a
|
17
|
+
file_list
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
to_a.join(',')
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
{
|
26
|
+
provided: provided,
|
27
|
+
from_keywords: from_keywords
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def file_list
|
34
|
+
@file_list ||= [
|
35
|
+
*provided,
|
36
|
+
*from_keywords
|
37
|
+
].compact.sort.uniq
|
38
|
+
end
|
39
|
+
|
40
|
+
def from_keywords
|
41
|
+
return [] unless keywords.any?
|
42
|
+
|
43
|
+
keywords.map do |keyword|
|
44
|
+
next unless respond_to?(keyword.to_sym, true)
|
45
|
+
|
46
|
+
send(keyword.to_sym)
|
47
|
+
end.flatten.uniq
|
48
|
+
end
|
49
|
+
|
50
|
+
def staged
|
51
|
+
# Use git for list of staged fields
|
52
|
+
::Reviewer::Arguments::Keywords::Git::Staged.list
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'keywords/git'
|
4
|
+
|
5
|
+
module Reviewer
|
6
|
+
class Arguments
|
7
|
+
# Handles interpreting all 'leftover' arguments and translating them to file-related,
|
8
|
+
# tag-related, or tool-related arguments
|
9
|
+
class Keywords
|
10
|
+
RESERVED = %w[staged].freeze
|
11
|
+
|
12
|
+
attr_accessor :provided
|
13
|
+
|
14
|
+
alias raw provided
|
15
|
+
|
16
|
+
def initialize(*provided)
|
17
|
+
@provided = Array(provided.flatten)
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_a
|
21
|
+
provided
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
to_a.join(',')
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect
|
29
|
+
{
|
30
|
+
provided: provided,
|
31
|
+
recognized: recognized,
|
32
|
+
unrecognized: unrecognized,
|
33
|
+
reserved: reserved,
|
34
|
+
for_tags: for_tags,
|
35
|
+
for_tool_names: for_tool_names
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
# Extracts reserved keywords from the provided arguments
|
40
|
+
#
|
41
|
+
# @return [Array<String>] intersection of provided arguments and reserved keywords
|
42
|
+
def reserved
|
43
|
+
intersection_with RESERVED
|
44
|
+
end
|
45
|
+
|
46
|
+
# Extracts keywords that match configured tags for enabled tools
|
47
|
+
#
|
48
|
+
# @return [Array<String>] intersection of provided arguments and configured tags for tools
|
49
|
+
def for_tags
|
50
|
+
intersection_with configured_tags
|
51
|
+
end
|
52
|
+
|
53
|
+
# Extracts keywords that match configured tool keys
|
54
|
+
#
|
55
|
+
# @return [Array<String>] intersection of provided arguments and configured tool names
|
56
|
+
def for_tool_names
|
57
|
+
intersection_with configured_tool_names
|
58
|
+
end
|
59
|
+
|
60
|
+
# Extracts keywords that match any possible recognized keyword values
|
61
|
+
#
|
62
|
+
# @return [Array<String>] intersection of provided arguments and recognizable keywords
|
63
|
+
def recognized
|
64
|
+
intersection_with possible
|
65
|
+
end
|
66
|
+
|
67
|
+
# Extracts keywords that don't match any possible recognized keyword values
|
68
|
+
#
|
69
|
+
# @return [Array<String>] leftover keywords that weren't recognized
|
70
|
+
def unrecognized
|
71
|
+
(provided - recognized).uniq.sort
|
72
|
+
end
|
73
|
+
|
74
|
+
# Provides the complete list of all recognized keywords based on configuration
|
75
|
+
#
|
76
|
+
# @return [Array<String>] all keywords that Reviewer can recognized
|
77
|
+
def possible
|
78
|
+
(RESERVED + configured_tags + configured_tool_names).uniq.sort
|
79
|
+
end
|
80
|
+
|
81
|
+
# Provides the complete list of all configured tags for enabled tools
|
82
|
+
#
|
83
|
+
# @return [Array<String>] all unique configured tags
|
84
|
+
def configured_tags
|
85
|
+
enabled_tools.map(&:tags).flatten.uniq.sort
|
86
|
+
end
|
87
|
+
|
88
|
+
# Provides the complete list of all configured tool names for enabled tools
|
89
|
+
#
|
90
|
+
# @return [Array<String>] all unique configured and enabled tools
|
91
|
+
def configured_tool_names
|
92
|
+
# We explicitly don't sort the tool names list because Reviewer uses the configuration order
|
93
|
+
# to determine the execution order. So not sorting maintains the predicted order it will run
|
94
|
+
# in and leaves the option to sort to the consuming code if needed
|
95
|
+
enabled_tools.map { |tool| tool.key.to_s }.flatten.uniq
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# Provides a collection of enabled Tools for convenient access
|
101
|
+
#
|
102
|
+
# @return [Array<Reviewer::Tool>] collection of all currently enabled tools
|
103
|
+
def enabled_tools
|
104
|
+
@enabled_tools ||= Reviewer.tools.enabled
|
105
|
+
end
|
106
|
+
|
107
|
+
# Syntactic sugar for finding intersections with valid keywords
|
108
|
+
# @param values [Array<String>] the collection to use for finding intersecting values
|
109
|
+
#
|
110
|
+
# @return [Array<String>] the list of intersecting values
|
111
|
+
def intersection_with(values)
|
112
|
+
values.intersection(provided).uniq.sort
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reviewer
|
4
|
+
class Arguments
|
5
|
+
class Keywords
|
6
|
+
module Git
|
7
|
+
# Provides a convenient interface to get the list of staged files
|
8
|
+
class Staged
|
9
|
+
OPTIONS = [
|
10
|
+
'diff',
|
11
|
+
'--staged',
|
12
|
+
'--name-only'
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
attr_reader :stdout, :stderr, :status, :exit_status
|
16
|
+
|
17
|
+
def to_a
|
18
|
+
stdout.present? ? stdout.split("\n") : []
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
stdout.present? ? stdout : ''
|
23
|
+
end
|
24
|
+
|
25
|
+
def inspect
|
26
|
+
{
|
27
|
+
command: command,
|
28
|
+
stdout: stdout,
|
29
|
+
stderr: stderr,
|
30
|
+
status: status,
|
31
|
+
results: results
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def list
|
36
|
+
@stdout, @stderr, @status = Open3.capture3(command)
|
37
|
+
@exit_status = @status.exitstatus.to_i
|
38
|
+
|
39
|
+
@status.success? ? to_a : raise_command_line_error
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.list
|
43
|
+
new.list
|
44
|
+
end
|
45
|
+
|
46
|
+
def command
|
47
|
+
command_parts.join(' ')
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def raise_command_line_error
|
53
|
+
message = "Git Error: #{stderr} (#{command})"
|
54
|
+
raise SystemCallError.new(message, exit_status)
|
55
|
+
end
|
56
|
+
|
57
|
+
def command_parts
|
58
|
+
BASE_COMMAND + OPTIONS
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reviewer
|
4
|
+
class Arguments
|
5
|
+
# Handles the logic of translating tag arguments
|
6
|
+
class Tags
|
7
|
+
attr_accessor :provided, :keywords
|
8
|
+
|
9
|
+
alias raw provided
|
10
|
+
|
11
|
+
def initialize(provided: Reviewer.arguments.tags.raw, keywords: Reviewer.arguments.keywords.for_tags)
|
12
|
+
@provided = Array(provided)
|
13
|
+
@keywords = Array(keywords)
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_a
|
17
|
+
tag_list
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
to_a.join(',')
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
{
|
26
|
+
provided: provided,
|
27
|
+
from_keywords: keywords
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def tag_list
|
34
|
+
@tag_list ||= [
|
35
|
+
*provided,
|
36
|
+
*keywords
|
37
|
+
].compact.sort.uniq
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,19 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Reviewer
|
4
|
-
# Configuration for Reviewer
|
4
|
+
# Configuration values container for Reviewer
|
5
5
|
class Configuration
|
6
|
-
|
7
|
-
|
6
|
+
DEFAULT_PATH = Dir.pwd.freeze
|
7
|
+
DEFAULT_FILE_NAME = '.reviewer.yml'
|
8
|
+
DEFAULT_FILE = "#{DEFAULT_PATH}/#{DEFAULT_FILE_NAME}"
|
8
9
|
|
9
10
|
attr_accessor :file
|
10
11
|
|
11
12
|
def initialize
|
12
|
-
@file =
|
13
|
-
end
|
14
|
-
|
15
|
-
def tools
|
16
|
-
@tools ||= Loader.new.to_h
|
13
|
+
@file = DEFAULT_FILE
|
17
14
|
end
|
18
15
|
end
|
19
16
|
end
|
data/lib/reviewer/loader.rb
CHANGED
@@ -10,24 +10,51 @@ module Reviewer
|
|
10
10
|
|
11
11
|
class InvalidConfigurationError < StandardError; end
|
12
12
|
|
13
|
-
|
13
|
+
class MissingReviewCommandError < StandardError; end
|
14
14
|
|
15
|
-
|
15
|
+
attr_reader :configuration, :file
|
16
|
+
|
17
|
+
def initialize(file = Reviewer.configuration.file)
|
18
|
+
@file = file
|
16
19
|
@configuration = HashWithIndifferentAccess.new(configuration_hash)
|
20
|
+
|
21
|
+
validate_configuration!
|
17
22
|
end
|
18
23
|
|
19
24
|
def to_h
|
20
25
|
configuration
|
21
26
|
end
|
22
27
|
|
28
|
+
def self.configuration
|
29
|
+
new.configuration
|
30
|
+
end
|
31
|
+
|
23
32
|
private
|
24
33
|
|
34
|
+
def validate_configuration!
|
35
|
+
# Any additional guidance for configuration issues will live here
|
36
|
+
require_review_commands!
|
37
|
+
end
|
38
|
+
|
39
|
+
def require_review_commands!
|
40
|
+
configuration.each do |key, value|
|
41
|
+
commands = value[:commands]
|
42
|
+
|
43
|
+
next if commands.key?(:review)
|
44
|
+
|
45
|
+
# Ideally, folks would want to fill out everything to receive the most benefit,
|
46
|
+
# but realistically, the 'review' command is the only required value. If the key
|
47
|
+
# is missing, or maybe there was a typo, fail right away.
|
48
|
+
raise MissingReviewCommandError, "'#{key}' does not have a 'review' key under 'commands' in `#{file}`"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
25
52
|
def configuration_hash
|
26
|
-
@configuration_hash ||= YAML.load_file(
|
53
|
+
@configuration_hash ||= YAML.load_file(@file)
|
27
54
|
rescue Errno::ENOENT
|
28
|
-
raise MissingConfigurationError, "Tools configuration file couldn't be found
|
55
|
+
raise MissingConfigurationError, "Tools configuration file couldn't be found at `#{file}`"
|
29
56
|
rescue Psych::SyntaxError => e
|
30
|
-
raise InvalidConfigurationError, "Tools configuration file has a syntax error
|
57
|
+
raise InvalidConfigurationError, "Tools configuration file (#{file}) has a syntax error: #{e.message}"
|
31
58
|
end
|
32
59
|
end
|
33
60
|
end
|
data/lib/reviewer/logger.rb
CHANGED
@@ -5,6 +5,13 @@ require 'colorize'
|
|
5
5
|
module Reviewer
|
6
6
|
# Clean formatter for logging to $stdout
|
7
7
|
class StandardOutFormatter < ::Logger::Formatter
|
8
|
+
# Overrides ::Logger::Formatter `call` to more present output more concisely
|
9
|
+
# @param _severity [Logger::Severity] Unused - Logger severity for etnry
|
10
|
+
# @param _time [DateTime] Unused - Timestamp for entry
|
11
|
+
# @param _progname [String] Unused - Name of the current program for entry
|
12
|
+
# @param message [String] The string to print to $stdout
|
13
|
+
#
|
14
|
+
# @return [type] [description]
|
8
15
|
def call(_severity, _time, _progname, message)
|
9
16
|
"#{message}\n"
|
10
17
|
end
|
@@ -26,11 +33,13 @@ module Reviewer
|
|
26
33
|
end
|
27
34
|
|
28
35
|
def command(cmd)
|
29
|
-
info "
|
36
|
+
info "\nReviewer ran this command:"
|
37
|
+
info cmd.to_s.light_black
|
30
38
|
end
|
31
39
|
|
32
|
-
def rerunning(tool)
|
33
|
-
info "\
|
40
|
+
def rerunning(tool, cmd)
|
41
|
+
info "\nRe-running #{tool.name} verbosely:"
|
42
|
+
info cmd.to_s.light_black
|
34
43
|
end
|
35
44
|
|
36
45
|
def success(elapsed_time)
|
@@ -38,12 +47,16 @@ module Reviewer
|
|
38
47
|
end
|
39
48
|
|
40
49
|
def failure(message)
|
41
|
-
|
50
|
+
error "#{FAILURE} #{message}".red.bold
|
51
|
+
end
|
52
|
+
|
53
|
+
def total_time(elapsed_time)
|
54
|
+
info "\n➤ Total Time: #{elapsed_time.round(3)}s\n"
|
42
55
|
end
|
43
56
|
|
44
57
|
def guidance(summary, details)
|
45
|
-
info "
|
46
|
-
info
|
58
|
+
info "\n#{summary}" if summary
|
59
|
+
info details.to_s.light_black if details
|
47
60
|
end
|
48
61
|
end
|
49
62
|
end
|
data/lib/reviewer/runner.rb
CHANGED
@@ -3,13 +3,19 @@
|
|
3
3
|
require 'open3'
|
4
4
|
|
5
5
|
module Reviewer
|
6
|
-
# Handles running, benchmarking, and printing output for a command
|
6
|
+
# Handles running, benchmarking, and printing output for a single command
|
7
7
|
class Runner
|
8
|
-
|
8
|
+
EXECUTABLE_NOT_FOUND_EXIT_STATUS_CODE = 127
|
9
9
|
|
10
10
|
attr_accessor :tool, :command
|
11
11
|
|
12
|
-
attr_reader :elapsed_time,
|
12
|
+
attr_reader :elapsed_time,
|
13
|
+
:last_command_run,
|
14
|
+
:stdout,
|
15
|
+
:stderr,
|
16
|
+
:status,
|
17
|
+
:exit_status,
|
18
|
+
:logger
|
13
19
|
|
14
20
|
def initialize(tool, command, logger: Logger.new)
|
15
21
|
@tool = tool
|
@@ -21,8 +27,7 @@ module Reviewer
|
|
21
27
|
logger.running(tool)
|
22
28
|
|
23
29
|
@elapsed_time = Benchmark.realtime do
|
24
|
-
|
25
|
-
review
|
30
|
+
tool.format_command? ? run_format : run_review
|
26
31
|
end
|
27
32
|
|
28
33
|
print_result
|
@@ -34,32 +39,26 @@ module Reviewer
|
|
34
39
|
def shell_out(cmd)
|
35
40
|
@stdout, @stderr, @status = Open3.capture3(cmd)
|
36
41
|
@exit_status = status.exitstatus
|
37
|
-
|
38
|
-
logger.command(cmd) unless status.success?
|
42
|
+
@last_command_run = cmd
|
39
43
|
end
|
40
44
|
|
41
|
-
def
|
45
|
+
def run_review
|
42
46
|
shell_out(tool.preparation_command) if tool.prepare_command?
|
43
|
-
end
|
44
|
-
|
45
|
-
def review
|
46
47
|
shell_out(tool.review_command(seed: seed))
|
47
48
|
end
|
48
49
|
|
49
|
-
def
|
50
|
+
def run_format
|
50
51
|
shell_out(tool.format_command) if tool.format_command?
|
51
52
|
end
|
52
53
|
|
53
54
|
def review_verbosely
|
54
55
|
cmd = tool.review_command(:no_silence, seed: seed)
|
55
|
-
logger.rerunning(tool)
|
56
|
-
logger.command(cmd)
|
56
|
+
logger.rerunning(tool, cmd)
|
57
57
|
system(cmd)
|
58
58
|
end
|
59
59
|
|
60
60
|
def print_result
|
61
61
|
if status.success?
|
62
|
-
# Outputs success details
|
63
62
|
logger.success(elapsed_time)
|
64
63
|
else
|
65
64
|
recovery_guidance
|
@@ -68,6 +67,7 @@ module Reviewer
|
|
68
67
|
|
69
68
|
def recovery_guidance
|
70
69
|
logger.failure(error_message)
|
70
|
+
logger.command(last_command_run)
|
71
71
|
if missing_executable?
|
72
72
|
missing_executable_guidance
|
73
73
|
else
|
@@ -84,12 +84,12 @@ module Reviewer
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def missing_executable_guidance
|
87
|
-
logger.guidance('
|
88
|
-
logger.guidance('
|
87
|
+
logger.guidance('Try installing the tool:', tool.installation_command) if tool.install_command?
|
88
|
+
logger.guidance('Read the installation guidance:', tool.settings.links[:install]) if tool.install_link?
|
89
89
|
end
|
90
90
|
|
91
91
|
def missing_executable?
|
92
|
-
(@exit_status ==
|
92
|
+
(@exit_status == EXECUTABLE_NOT_FOUND_EXIT_STATUS_CODE) ||
|
93
93
|
stderr.include?("can't find executable")
|
94
94
|
end
|
95
95
|
|
data/lib/reviewer/tool.rb
CHANGED
@@ -13,6 +13,8 @@ module Reviewer
|
|
13
13
|
|
14
14
|
delegate :name,
|
15
15
|
:description,
|
16
|
+
:tags,
|
17
|
+
:key,
|
16
18
|
:enabled?,
|
17
19
|
:disabled?,
|
18
20
|
:max_exit_status,
|
@@ -30,6 +32,14 @@ module Reviewer
|
|
30
32
|
name
|
31
33
|
end
|
32
34
|
|
35
|
+
def to_sym
|
36
|
+
key
|
37
|
+
end
|
38
|
+
|
39
|
+
def ==(other)
|
40
|
+
settings == other.settings
|
41
|
+
end
|
42
|
+
|
33
43
|
def installation_command(verbosity_level = :no_silence)
|
34
44
|
command_string(:install, verbosity_level: verbosity_level)
|
35
45
|
end
|
@@ -4,18 +4,18 @@ module Reviewer
|
|
4
4
|
class Tool
|
5
5
|
# Converts/casts tool configuration values and provides default values if not set
|
6
6
|
class Settings
|
7
|
-
|
8
|
-
|
9
|
-
attr_reader :tool, :config
|
7
|
+
attr_reader :tool
|
10
8
|
|
11
9
|
def initialize(tool, config: nil)
|
12
10
|
@tool = tool
|
13
|
-
@config = config
|
11
|
+
@config = config
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
def ==(other)
|
15
|
+
self.class == other.class &&
|
16
|
+
state == other.state
|
18
17
|
end
|
18
|
+
alias eql? ==
|
19
19
|
|
20
20
|
def disabled?
|
21
21
|
config.fetch(:disabled, false)
|
@@ -80,6 +80,16 @@ module Reviewer
|
|
80
80
|
def quiet_option
|
81
81
|
commands.fetch(:quiet_option, '')
|
82
82
|
end
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
def config
|
87
|
+
@config || Reviewer.tools.to_h.fetch(tool.to_sym) { {} }
|
88
|
+
end
|
89
|
+
|
90
|
+
def state
|
91
|
+
config.to_hash
|
92
|
+
end
|
83
93
|
end
|
84
94
|
end
|
85
95
|
end
|
data/lib/reviewer/tools.rb
CHANGED
@@ -1,14 +1,62 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Reviewer
|
4
|
-
# Provides
|
5
|
-
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
# Provides convenient access to subsets of configured tools
|
5
|
+
class Tools
|
6
|
+
def initialize(tags: nil, tool_names: nil)
|
7
|
+
@tags = tags
|
8
|
+
@tool_names = tool_names
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_h
|
12
|
+
configured
|
13
|
+
end
|
14
|
+
|
15
|
+
def all
|
16
|
+
configured.keys.map { |tool_name| Tool.new(tool_name) }
|
17
|
+
end
|
18
|
+
alias to_a all
|
19
|
+
|
20
|
+
def enabled
|
21
|
+
@enabled ||= all.keep_if(&:enabled?)
|
22
|
+
end
|
23
|
+
|
24
|
+
def specified
|
25
|
+
all.keep_if { |tool| named?(tool) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def tagged
|
29
|
+
enabled.keep_if { |tool| tagged?(tool) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def current
|
33
|
+
subset? ? (specified + tagged).uniq : enabled
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def subset?
|
39
|
+
tool_names.any? || tags.any?
|
40
|
+
end
|
41
|
+
|
42
|
+
def configured
|
43
|
+
@configured ||= Loader.configuration
|
44
|
+
end
|
45
|
+
|
46
|
+
def tags
|
47
|
+
Array(@tags || Reviewer.arguments.tags)
|
48
|
+
end
|
49
|
+
|
50
|
+
def tool_names
|
51
|
+
Array(@tool_names || Reviewer.arguments.tool_names)
|
52
|
+
end
|
53
|
+
|
54
|
+
def tagged?(tool)
|
55
|
+
tool.enabled? && tags.intersection(tool.tags).any?
|
56
|
+
end
|
57
|
+
|
58
|
+
def named?(tool)
|
59
|
+
tool_names.map(&:to_s).include?(tool.key.to_s)
|
12
60
|
end
|
13
61
|
end
|
14
62
|
end
|
data/lib/reviewer/version.rb
CHANGED
data/reviewer.gemspec
CHANGED
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_dependency 'slop'
|
33
33
|
|
34
34
|
spec.add_development_dependency 'bundler-audit'
|
35
|
+
spec.add_development_dependency 'inch'
|
35
36
|
spec.add_development_dependency 'rubocop'
|
36
37
|
spec.add_development_dependency 'rubocop-minitest'
|
37
38
|
spec.add_development_dependency 'rubocop-rake'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reviewer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Garrett Dimon
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: inch
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: rubocop
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -111,12 +125,15 @@ dependencies:
|
|
111
125
|
description: Provides a unified approach to managing automated code quality tools.
|
112
126
|
email:
|
113
127
|
- email@garrettdimon.com
|
114
|
-
executables:
|
128
|
+
executables:
|
129
|
+
- fmt
|
130
|
+
- rvw
|
115
131
|
extensions: []
|
116
132
|
extra_rdoc_files: []
|
117
133
|
files:
|
118
134
|
- ".github/workflows/main.yml"
|
119
135
|
- ".gitignore"
|
136
|
+
- ".inch.yml"
|
120
137
|
- ".reviewer.example.yml"
|
121
138
|
- ".reviewer.yml"
|
122
139
|
- ".rubocop.yml"
|
@@ -129,11 +146,16 @@ files:
|
|
129
146
|
- README.md
|
130
147
|
- Rakefile
|
131
148
|
- bin/console
|
132
|
-
- bin/fmt
|
133
|
-
- bin/rvw
|
134
149
|
- bin/setup
|
150
|
+
- exe/fmt
|
151
|
+
- exe/rvw
|
135
152
|
- lib/reviewer.rb
|
136
153
|
- lib/reviewer/arguments.rb
|
154
|
+
- lib/reviewer/arguments/files.rb
|
155
|
+
- lib/reviewer/arguments/keywords.rb
|
156
|
+
- lib/reviewer/arguments/keywords/git.rb
|
157
|
+
- lib/reviewer/arguments/keywords/git/staged.rb
|
158
|
+
- lib/reviewer/arguments/tags.rb
|
137
159
|
- lib/reviewer/configuration.rb
|
138
160
|
- lib/reviewer/loader.rb
|
139
161
|
- lib/reviewer/logger.rb
|