quiet_quality 1.0.3 → 1.2.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/.github/workflows/dogfood.yml +1 -1
- data/.quiet_quality.ci.yml +6 -0
- data/.quiet_quality.yml +2 -1
- data/CHANGELOG.md +52 -0
- data/README.md +59 -12
- data/lib/quiet_quality/cli/arg_parser.rb +20 -0
- data/lib/quiet_quality/cli/entrypoint.rb +53 -29
- data/lib/quiet_quality/cli/presenter.rb +77 -0
- data/lib/quiet_quality/config/builder.rb +7 -1
- data/lib/quiet_quality/config/logging.rb +23 -0
- data/lib/quiet_quality/config/options.rb +6 -0
- data/lib/quiet_quality/config/parsed_options.rb +6 -2
- data/lib/quiet_quality/config/parser.rb +21 -11
- data/lib/quiet_quality/config/tool_options.rb +8 -2
- data/lib/quiet_quality/executors/base_executor.rb +8 -0
- data/lib/quiet_quality/executors/pipeline.rb +4 -2
- data/lib/quiet_quality/logger.rb +17 -0
- data/lib/quiet_quality/tools/base_runner.rb +49 -0
- data/lib/quiet_quality/tools/brakeman/runner.rb +7 -22
- data/lib/quiet_quality/tools/brakeman.rb +0 -2
- data/lib/quiet_quality/tools/haml_lint/runner.rb +15 -47
- data/lib/quiet_quality/tools/haml_lint.rb +0 -2
- data/lib/quiet_quality/tools/markdown_lint/parser.rb +34 -0
- data/lib/quiet_quality/tools/markdown_lint/runner.rb +28 -0
- data/lib/quiet_quality/tools/markdown_lint.rb +9 -0
- data/lib/quiet_quality/tools/relevant_runner.rb +55 -0
- data/lib/quiet_quality/tools/rspec/runner.rb +9 -43
- data/lib/quiet_quality/tools/rspec.rb +0 -2
- data/lib/quiet_quality/tools/rubocop/runner.rb +9 -53
- data/lib/quiet_quality/tools/rubocop.rb +0 -2
- data/lib/quiet_quality/tools/standardrb/runner.rb +15 -3
- data/lib/quiet_quality/tools/standardrb.rb +0 -2
- data/lib/quiet_quality/tools.rb +6 -0
- data/lib/quiet_quality/version.rb +1 -1
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae3f031f4d627e624ce2ae28a5cf58d8e9e0663822371089d54bda51cb1aefeb
|
4
|
+
data.tar.gz: c9c26fd4d3df19e70162b38ef78622cc569ea492656d9264c4a41a964931934a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6925f90d2f144b9bd59b4f0ca8d9a28e41cebf8570b30e3fa85cee6eefe2961aaaf246f733cd9b62de0d070afb7cca545c650ead15e45a6a39372d34c4240023
|
7
|
+
data.tar.gz: 3589c916dab556a467ab64c974ea91eda6a4f6f229ccda58aa51488b13b3a9a44217206fb3962697570cd176fb1bd282bee6231eccad5b50e1d948f0758eb8fb
|
data/.quiet_quality.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## Release 1.2.0
|
4
|
+
|
5
|
+
* Support `--light`, `--quiet`, and `--logging LEVEL` arguments for less output
|
6
|
+
(#78, resolves #37)
|
7
|
+
* Support the [markdownlint](https://github.com/markdownlint/markdownlint) tool
|
8
|
+
(#79, resolves #58)
|
9
|
+
* Extract BaseRunner (#82) and RelevantRunner (#83) parent classes from the tool
|
10
|
+
runners, to allow new tools to be more easily implemented. (Resolves #81)
|
11
|
+
* Extract a Cli::Presenter from the Entrypoint, to simplify pending work on cli
|
12
|
+
presentation (#84, resolves #42)
|
13
|
+
* Update the docs a bit, and add a changelog (hi!)
|
14
|
+
|
15
|
+
## Release 1.1.0
|
16
|
+
|
17
|
+
* Support a `file_filter` config entry per-tool (without a cli option), to limit
|
18
|
+
what file paths a runner might supply to its tool based on a regex
|
19
|
+
(#74, resolves #68)
|
20
|
+
* When what tools to execute is not specified (by cli or by config file), abort
|
21
|
+
`bin/qq` and explain, rather than assuming "all of them" (#79, resolves #58)
|
22
|
+
* Update the config parser to handle keys named to match the cli options
|
23
|
+
alongside the ones that were (mistakenly) named differently. This is a
|
24
|
+
backwards-compatible change; if we eventually deprecate and simplify some of
|
25
|
+
these option names, you'll have plenty of warning (#77, resolves #75)
|
26
|
+
* Support `--version/-V` flag (#73, resolves #69)
|
27
|
+
|
28
|
+
## Release 1.0.3
|
29
|
+
|
30
|
+
* Fix the printed _output_ for the case where there were some warnings from a
|
31
|
+
tool, but all of them were filtered out (because they targetted lines that
|
32
|
+
were not changed, for example). This situation should tell you that nothing
|
33
|
+
is wrong with your PR, not that there is a problem (#71)
|
34
|
+
|
35
|
+
## Release 1.0.2
|
36
|
+
|
37
|
+
* Fix the _exit status_ for the case where there were some warnings from a
|
38
|
+
tool, but all of them were filtered out (because they targetted lines that
|
39
|
+
were not changed, for example). This situation should produce a successful
|
40
|
+
result, and not fail a CI pipeline (#67)
|
41
|
+
|
42
|
+
## Release 1.0.1
|
43
|
+
|
44
|
+
* Fix the calculation of `changed_files` for the executor - in the migration
|
45
|
+
to Entrypoint, the actual git call to get a ChangedFiles object to pass into
|
46
|
+
other service classes was lost, which had the result that the entire system
|
47
|
+
behaved (outside of tests) as if you were always running with `--all-files`
|
48
|
+
(#65).
|
49
|
+
|
50
|
+
## Release 1.0.0
|
51
|
+
|
52
|
+
Initial functional public release.
|
data/README.md
CHANGED
@@ -14,11 +14,13 @@ that too.
|
|
14
14
|
|
15
15
|
So far, we have support for the following tools:
|
16
16
|
|
17
|
-
* rubocop
|
18
|
-
* standardrb
|
19
|
-
* rspec
|
20
|
-
* haml-lint
|
21
|
-
*
|
17
|
+
* [rubocop](https://github.com/rubocop/rubocop)
|
18
|
+
* [standardrb](https://github.com/standardrb/standard)
|
19
|
+
* [rspec](https://rspec.info/)
|
20
|
+
* [haml-lint](https://github.com/sds/haml-lint)
|
21
|
+
* [markdownlint](https://github.com/markdownlint/markdownlint)
|
22
|
+
* [brakeman](https://brakemanscanner.org/) (though there's no way to run this
|
23
|
+
against only changed files)
|
22
24
|
|
23
25
|
Supporting more tools is relatively straightforward - they're implemented by
|
24
26
|
wrapping cli invocations and parsing output files (which overall seem to be much
|
@@ -126,6 +128,9 @@ And then run `qq -C config/quiet_quality/linters_workflow.yml`
|
|
126
128
|
|
127
129
|
The configuration file supports the following _global_ options (top-level keys):
|
128
130
|
|
131
|
+
* `default_tools`: Which tools should be run when you `qq` without specifying?
|
132
|
+
Valid values are: `rubocop`, `rspec`, `standardrb`, `haml_lint`, `brakeman`,
|
133
|
+
and `markdown_lint`.
|
129
134
|
* `executor`: 'serial' or 'concurrent' (the latter is the default)
|
130
135
|
* `annotator`: none set by default, and `github_stdout` is the only supported
|
131
136
|
value so far.
|
@@ -139,17 +144,59 @@ The configuration file supports the following _global_ options (top-level keys):
|
|
139
144
|
* `filter_messages`: defaults to false - should the resulting messages that do
|
140
145
|
not refer to lines that were changed or added relative to the comparison
|
141
146
|
branch be skipped? Also possible to set for each tool.
|
147
|
+
* `logging`: defaults to full messages printed. The `light` option
|
148
|
+
prints a aggregated result (e.g. "3 tools executed: 1 passed, 2 failed
|
149
|
+
(rubocop, standardrb)"). The `quiet` option will only return a status code,
|
150
|
+
printing nothing.
|
142
151
|
|
143
152
|
And then each tool can have an entry, within which `changed_files` and
|
144
153
|
`filter_messages` can be specified - the tool-specific settings override the
|
145
154
|
global ones.
|
146
155
|
|
147
|
-
|
156
|
+
The tools have one additional setting that is not available at a global level:
|
157
|
+
`file_filter`. This is a string that will be turned into a _ruby regex_, and
|
158
|
+
used to limit what file paths are passed to the tool. For example, if you are
|
159
|
+
working in a rails engine `engines/foo/`, and you touch one of the rspec tests
|
160
|
+
there, you would not want `qq` in the root of the repository to run
|
161
|
+
`rspec engines/foo/spec/foo/thing_spec.rb` - that probably won't work, as your
|
162
|
+
engine will have its own test setup code and Gemfile. This setting is mostly
|
163
|
+
intended to be used like this:
|
148
164
|
|
149
|
-
|
150
|
-
|
165
|
+
```yaml
|
166
|
+
rspec:
|
167
|
+
changed_files: true
|
168
|
+
filter_messages: false
|
169
|
+
file_filter: "^spec/"
|
170
|
+
```
|
151
171
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
172
|
+
### CLI Options
|
173
|
+
|
174
|
+
To specify which _tools_ to run (and if any are specified, the `default_tools`
|
175
|
+
from the configuration file will be ignored), you supply them as positional
|
176
|
+
arguments: `qq rubocop rspec --all-files -L` will run the `rubocop` and `rspec`
|
177
|
+
tools, for example.
|
178
|
+
|
179
|
+
Run `qq --help` for a detailed list of the CLI options, they largely agree with
|
180
|
+
those in the configuration file, but there are some differences. There's no way
|
181
|
+
to specify a `file_filter` for a tool on the command-line, and there are some
|
182
|
+
additional options available focused on managing the interactions with
|
183
|
+
configuration files.
|
184
|
+
|
185
|
+
```text
|
186
|
+
Usage: qq [TOOLS] [GLOBAL_OPTIONS] [TOOL_OPTIONS]
|
187
|
+
-h, --help Prints this help
|
188
|
+
-V, --version Print the current version of the gem
|
189
|
+
-C, --config PATH Load a config file from this path
|
190
|
+
-N, --no-config Do not load a config file, even if present
|
191
|
+
-E, --executor EXECUTOR Which executor to use
|
192
|
+
-A, --annotate ANNOTATOR Annotate with this annotator
|
193
|
+
-G, --annotate-github-stdout Annotate with GitHub Workflow commands
|
194
|
+
-a, --all-files [tool] Use the tool(s) on all files
|
195
|
+
-c, --changed-files [tool] Use the tool(s) only on changed files
|
196
|
+
-B, --comparison-branch BRANCH Specify the branch to compare against
|
197
|
+
-f, --filter-messages [tool] Filter messages from tool(s) based on changed lines
|
198
|
+
-u, --unfiltered [tool] Don't filter messages from tool(s)
|
199
|
+
-l, --light Print aggregated results only
|
200
|
+
-q, --quiet Don't print results, only return a status code
|
201
|
+
-L, --logging LEVEL Specify logging mode that results will be returned in. Valid options: light, quiet
|
202
|
+
```
|
@@ -65,6 +65,7 @@ module QuietQuality
|
|
65
65
|
setup_annotation_options(parser)
|
66
66
|
setup_file_target_options(parser)
|
67
67
|
setup_filter_messages_options(parser)
|
68
|
+
setup_logging_options(parser)
|
68
69
|
end
|
69
70
|
end
|
70
71
|
|
@@ -76,6 +77,10 @@ module QuietQuality
|
|
76
77
|
parser.on("-h", "--help", "Prints this help") do
|
77
78
|
@parsed_options.helping = true
|
78
79
|
end
|
80
|
+
|
81
|
+
parser.on("-V", "--version", "Print the current version of the gem") do
|
82
|
+
@parsed_options.printing_version = true
|
83
|
+
end
|
79
84
|
end
|
80
85
|
|
81
86
|
def setup_config_options(parser)
|
@@ -130,6 +135,21 @@ module QuietQuality
|
|
130
135
|
read_tool_or_global_option(:filter_messages, tool, false)
|
131
136
|
end
|
132
137
|
end
|
138
|
+
|
139
|
+
def setup_logging_options(parser)
|
140
|
+
parser.on("-l", "--light", "Print aggregated results only") do
|
141
|
+
set_global_option(:logging, Config::Logging::LIGHT)
|
142
|
+
end
|
143
|
+
|
144
|
+
parser.on("-q", "--quiet", "Don't print results, only return a status code") do
|
145
|
+
set_global_option(:logging, Config::Logging::QUIET)
|
146
|
+
end
|
147
|
+
|
148
|
+
parser.on("-L", "--logging LEVEL", "Specify logging mode that results will be returned in. Valid options: light, quiet") do |level|
|
149
|
+
validate_value_from("logging level", level, Config::Logging::LEVELS)
|
150
|
+
set_global_option(:logging, level.to_sym)
|
151
|
+
end
|
152
|
+
end
|
133
153
|
end
|
134
154
|
end
|
135
155
|
end
|
@@ -10,10 +10,13 @@ module QuietQuality
|
|
10
10
|
def execute
|
11
11
|
if helping?
|
12
12
|
log_help_text
|
13
|
+
elsif printing_version?
|
14
|
+
log_version_text
|
15
|
+
elsif no_tools?
|
16
|
+
log_no_tools_text
|
13
17
|
else
|
14
18
|
executed
|
15
|
-
|
16
|
-
log_messages
|
19
|
+
log_results
|
17
20
|
annotate_messages
|
18
21
|
end
|
19
22
|
|
@@ -21,13 +24,32 @@ module QuietQuality
|
|
21
24
|
end
|
22
25
|
|
23
26
|
def successful?
|
24
|
-
helping? ||
|
27
|
+
return true if helping? || printing_version?
|
28
|
+
return false if no_tools?
|
29
|
+
!executed.any_failure?
|
25
30
|
end
|
26
31
|
|
27
32
|
private
|
28
33
|
|
29
34
|
attr_reader :argv, :output_stream, :error_stream
|
30
35
|
|
36
|
+
def logger
|
37
|
+
@_logger ||= QuietQuality::Logger.new(stream: error_stream, logging: options.logging)
|
38
|
+
end
|
39
|
+
|
40
|
+
def presenter
|
41
|
+
@_presenter ||= Presenter.new(
|
42
|
+
logger: logger,
|
43
|
+
logging: options.logging,
|
44
|
+
outcomes: executor.outcomes,
|
45
|
+
messages: executor.messages
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def log_results
|
50
|
+
presenter.log_results
|
51
|
+
end
|
52
|
+
|
31
53
|
def arg_parser
|
32
54
|
@_arg_parser ||= ArgParser.new(argv.dup)
|
33
55
|
end
|
@@ -40,8 +62,35 @@ module QuietQuality
|
|
40
62
|
parsed_options.helping?
|
41
63
|
end
|
42
64
|
|
65
|
+
def quiet_logging?
|
66
|
+
options.logging.quiet?
|
67
|
+
end
|
68
|
+
|
69
|
+
def light_logging?
|
70
|
+
options.logging.light?
|
71
|
+
end
|
72
|
+
|
73
|
+
def printing_version?
|
74
|
+
parsed_options.printing_version?
|
75
|
+
end
|
76
|
+
|
77
|
+
def no_tools?
|
78
|
+
options.tools.empty?
|
79
|
+
end
|
80
|
+
|
43
81
|
def log_help_text
|
44
|
-
|
82
|
+
logger.puts(arg_parser.help_text)
|
83
|
+
end
|
84
|
+
|
85
|
+
def log_version_text
|
86
|
+
logger.puts(QuietQuality::VERSION)
|
87
|
+
end
|
88
|
+
|
89
|
+
def log_no_tools_text
|
90
|
+
logger.puts(<<~TEXT)
|
91
|
+
You must specify one or more tools to run, either on the command-line or in the
|
92
|
+
default_tools key in a configuration file.
|
93
|
+
TEXT
|
45
94
|
end
|
46
95
|
|
47
96
|
def options
|
@@ -70,31 +119,6 @@ module QuietQuality
|
|
70
119
|
@_executed = executor
|
71
120
|
end
|
72
121
|
|
73
|
-
def log_outcomes
|
74
|
-
executed.outcomes.each do |outcome|
|
75
|
-
result = outcome.success? ? "Passed" : "Failed"
|
76
|
-
error_stream.puts "--- #{result}: #{outcome.tool}"
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def log_message(msg)
|
81
|
-
line_range =
|
82
|
-
if msg.start_line == msg.stop_line
|
83
|
-
msg.start_line.to_s
|
84
|
-
else
|
85
|
-
"#{msg.start_line}-#{msg.stop_line}"
|
86
|
-
end
|
87
|
-
rule_string = msg.rule ? " [#{msg.rule}]" : ""
|
88
|
-
truncated_body = msg.body.gsub(/ *\n */, "\\n").slice(0, 120)
|
89
|
-
error_stream.puts " #{msg.path}:#{line_range}#{rule_string} #{truncated_body}"
|
90
|
-
end
|
91
|
-
|
92
|
-
def log_messages
|
93
|
-
return unless executed.messages.any?
|
94
|
-
error_stream.puts "\n\n#{executed.messages.count} messages:"
|
95
|
-
executed.messages.each { |msg| log_message(msg) }
|
96
|
-
end
|
97
|
-
|
98
122
|
def annotate_messages
|
99
123
|
return unless options.annotator
|
100
124
|
annotator = options.annotator.new(output_stream: output_stream)
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module QuietQuality
|
2
|
+
module Cli
|
3
|
+
class Presenter
|
4
|
+
def initialize(logger:, logging:, outcomes:, messages:)
|
5
|
+
@logger = logger
|
6
|
+
@logging = logging
|
7
|
+
@outcomes = outcomes
|
8
|
+
@messages = messages
|
9
|
+
end
|
10
|
+
|
11
|
+
def log_results
|
12
|
+
return if logging.quiet?
|
13
|
+
|
14
|
+
if logging.light?
|
15
|
+
log_light_outcomes
|
16
|
+
else
|
17
|
+
log_outcomes
|
18
|
+
log_messages
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :logger, :logging, :outcomes, :messages
|
25
|
+
|
26
|
+
def failed_outcomes
|
27
|
+
@_failed_outcomes ||= outcomes.select(&:failure?)
|
28
|
+
end
|
29
|
+
|
30
|
+
def successful_outcomes
|
31
|
+
@_successful_outcomes ||= outcomes.select(&:success?)
|
32
|
+
end
|
33
|
+
|
34
|
+
def log_light_outcomes
|
35
|
+
line = "%d tools executed: %d passed, %d failed" % [
|
36
|
+
outcomes.count,
|
37
|
+
successful_outcomes.count,
|
38
|
+
failed_outcomes.count
|
39
|
+
]
|
40
|
+
line += " (#{failed_outcomes.map(&:tool).join(", ")})" if failed_outcomes.any?
|
41
|
+
logger.puts line
|
42
|
+
end
|
43
|
+
|
44
|
+
def log_outcomes
|
45
|
+
outcomes.each do |outcome|
|
46
|
+
result = outcome.success? ? "Passed" : "Failed"
|
47
|
+
logger.puts "--- #{result}: #{outcome.tool}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def log_messages
|
52
|
+
return unless messages.any?
|
53
|
+
logger.puts "\n\n#{messages.count} messages:"
|
54
|
+
messages.each { |msg| log_message(msg) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def line_range_for(msg)
|
58
|
+
if msg.start_line == msg.stop_line
|
59
|
+
msg.start_line.to_s
|
60
|
+
else
|
61
|
+
"#{msg.start_line}-#{msg.stop_line}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def reduce_text(s, length)
|
66
|
+
s.gsub(/ *\n */, "\\n").slice(0, length)
|
67
|
+
end
|
68
|
+
|
69
|
+
def log_message(msg)
|
70
|
+
line_range = line_range_for(msg)
|
71
|
+
rule_string = msg.rule ? " [#{msg.rule}]" : ""
|
72
|
+
truncated_body = reduce_text(msg.body, 120)
|
73
|
+
logger.puts " #{msg.path}:#{line_range}#{rule_string} #{truncated_body}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -28,7 +28,7 @@ module QuietQuality
|
|
28
28
|
elsif config_file&.tools&.any?
|
29
29
|
config_file.tools
|
30
30
|
else
|
31
|
-
|
31
|
+
[]
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -85,6 +85,7 @@ module QuietQuality
|
|
85
85
|
update_annotator
|
86
86
|
update_executor
|
87
87
|
update_comparison_branch
|
88
|
+
update_logging
|
88
89
|
end
|
89
90
|
|
90
91
|
def update_annotator
|
@@ -103,12 +104,17 @@ module QuietQuality
|
|
103
104
|
set_unless_nil(options, :comparison_branch, apply.global_option(:comparison_branch))
|
104
105
|
end
|
105
106
|
|
107
|
+
def update_logging
|
108
|
+
set_unless_nil(options, :logging, apply.global_option(:logging))
|
109
|
+
end
|
110
|
+
|
106
111
|
# ---- update the tool options (apply global forms first) -------
|
107
112
|
|
108
113
|
def update_tools
|
109
114
|
options.tools.each do |tool_options|
|
110
115
|
update_tool_option(tool_options, :limit_targets)
|
111
116
|
update_tool_option(tool_options, :filter_messages)
|
117
|
+
update_tool_option(tool_options, :file_filter)
|
112
118
|
end
|
113
119
|
end
|
114
120
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module QuietQuality
|
2
|
+
module Config
|
3
|
+
class Logging
|
4
|
+
LIGHT = :light
|
5
|
+
QUIET = :quiet
|
6
|
+
LEVELS = [LIGHT, QUIET].freeze
|
7
|
+
|
8
|
+
attr_accessor :level
|
9
|
+
|
10
|
+
def initialize(level: nil)
|
11
|
+
@level = level
|
12
|
+
end
|
13
|
+
|
14
|
+
def light?
|
15
|
+
@level == LIGHT
|
16
|
+
end
|
17
|
+
|
18
|
+
def quiet?
|
19
|
+
@level == QUIET
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -6,9 +6,15 @@ module QuietQuality
|
|
6
6
|
@executor = Executors::ConcurrentExecutor
|
7
7
|
@tools = nil
|
8
8
|
@comparison_branch = nil
|
9
|
+
@logging = Logging.new
|
9
10
|
end
|
10
11
|
|
11
12
|
attr_accessor :tools, :comparison_branch, :annotator, :executor
|
13
|
+
attr_reader :logging
|
14
|
+
|
15
|
+
def logging=(level)
|
16
|
+
@logging.level = level
|
17
|
+
end
|
12
18
|
end
|
13
19
|
end
|
14
20
|
end
|
@@ -5,16 +5,20 @@ module QuietQuality
|
|
5
5
|
@tools = []
|
6
6
|
@tool_options = {}
|
7
7
|
@global_options = {}
|
8
|
-
@helping = false
|
8
|
+
@helping = @printing_version = false
|
9
9
|
end
|
10
10
|
|
11
11
|
attr_accessor :tools
|
12
|
-
attr_writer :helping
|
12
|
+
attr_writer :helping, :printing_version
|
13
13
|
|
14
14
|
def helping?
|
15
15
|
@helping
|
16
16
|
end
|
17
17
|
|
18
|
+
def printing_version?
|
19
|
+
@printing_version
|
20
|
+
end
|
21
|
+
|
18
22
|
def set_global_option(name, value)
|
19
23
|
@global_options[name.to_sym] = value
|
20
24
|
end
|
@@ -38,11 +38,15 @@ module QuietQuality
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def store_global_options(opts)
|
41
|
-
read_global_option(opts, :executor, as: :symbol, validate_from: Executors::AVAILABLE)
|
42
|
-
read_global_option(opts, :annotator, as: :symbol, validate_from: Annotators::ANNOTATOR_TYPES)
|
43
|
-
read_global_option(opts, :
|
44
|
-
read_global_option(opts, :
|
45
|
-
read_global_option(opts, :
|
41
|
+
read_global_option(opts, :executor, :executor, as: :symbol, validate_from: Executors::AVAILABLE)
|
42
|
+
read_global_option(opts, :annotator, :annotator, as: :symbol, validate_from: Annotators::ANNOTATOR_TYPES)
|
43
|
+
read_global_option(opts, :annotate, :annotator, as: :symbol, validate_from: Annotators::ANNOTATOR_TYPES)
|
44
|
+
read_global_option(opts, :comparison_branch, :comparison_branch, as: :string)
|
45
|
+
read_global_option(opts, :changed_files, :changed_files, as: :boolean)
|
46
|
+
read_global_option(opts, :all_files, :changed_files, as: :reversed_boolean)
|
47
|
+
read_global_option(opts, :filter_messages, :filter_messages, as: :boolean)
|
48
|
+
read_global_option(opts, :unfiltered, :filter_messages, as: :reversed_boolean)
|
49
|
+
read_global_option(opts, :logging, :logging, as: :symbol, validate_from: Logging::LEVELS)
|
46
50
|
end
|
47
51
|
|
48
52
|
def store_tool_options(opts)
|
@@ -54,8 +58,12 @@ module QuietQuality
|
|
54
58
|
def store_tool_options_for(opts, tool_name)
|
55
59
|
entries = data.fetch(tool_name, nil)
|
56
60
|
return if entries.nil?
|
57
|
-
|
58
|
-
read_tool_option(opts, tool_name, :
|
61
|
+
|
62
|
+
read_tool_option(opts, tool_name, :filter_messages, :filter_messages, as: :boolean)
|
63
|
+
read_tool_option(opts, tool_name, :unfiltered, :filter_messages, as: :reversed_boolean)
|
64
|
+
read_tool_option(opts, tool_name, :changed_files, :changed_files, as: :boolean)
|
65
|
+
read_tool_option(opts, tool_name, :all_files, :changed_files, as: :reversed_boolean)
|
66
|
+
read_tool_option(opts, tool_name, :file_filter, :file_filter, as: :string)
|
59
67
|
end
|
60
68
|
|
61
69
|
def invalid!(message)
|
@@ -70,27 +78,28 @@ module QuietQuality
|
|
70
78
|
[true, false].include?(value)
|
71
79
|
end
|
72
80
|
|
73
|
-
def read_global_option(opts, name, as:, validate_from: nil)
|
81
|
+
def read_global_option(opts, name, into, as:, validate_from: nil)
|
74
82
|
parsed_value = data.fetch(name.to_sym, nil)
|
75
83
|
return if parsed_value.nil?
|
76
84
|
|
77
85
|
validate_value(name, parsed_value, as: as, from: validate_from)
|
78
86
|
coerced_value = coerce_value(parsed_value, as: as)
|
79
|
-
opts.set_global_option(
|
87
|
+
opts.set_global_option(into, coerced_value)
|
80
88
|
end
|
81
89
|
|
82
|
-
def read_tool_option(opts, tool, name, as:)
|
90
|
+
def read_tool_option(opts, tool, name, into, as:)
|
83
91
|
parsed_value = data.dig(tool.to_sym, name.to_sym)
|
84
92
|
return if parsed_value.nil?
|
85
93
|
|
86
94
|
validate_value("#{tool}.#{name}", parsed_value, as: as)
|
87
95
|
coerced_value = coerce_value(parsed_value, as: as)
|
88
|
-
opts.set_tool_option(tool,
|
96
|
+
opts.set_tool_option(tool, into, coerced_value)
|
89
97
|
end
|
90
98
|
|
91
99
|
def validate_value(name, value, as:, from: nil)
|
92
100
|
case as
|
93
101
|
when :boolean then validate_boolean(name, value)
|
102
|
+
when :reversed_boolean then validate_boolean(name, value)
|
94
103
|
when :symbol then validate_symbol(name, value, from: from)
|
95
104
|
when :string then validate_string(name, value)
|
96
105
|
else
|
@@ -123,6 +132,7 @@ module QuietQuality
|
|
123
132
|
def coerce_value(value, as:)
|
124
133
|
case as
|
125
134
|
when :boolean then !!value
|
135
|
+
when :reversed_boolean then !value
|
126
136
|
when :string then value.to_s
|
127
137
|
when :symbol then value.to_sym
|
128
138
|
else
|
@@ -1,14 +1,15 @@
|
|
1
1
|
module QuietQuality
|
2
2
|
module Config
|
3
3
|
class ToolOptions
|
4
|
-
def initialize(tool, limit_targets: true, filter_messages: true)
|
4
|
+
def initialize(tool, limit_targets: true, filter_messages: true, file_filter: nil)
|
5
5
|
@tool_name = tool.to_sym
|
6
6
|
@limit_targets = limit_targets
|
7
7
|
@filter_messages = filter_messages
|
8
|
+
@file_filter = file_filter
|
8
9
|
end
|
9
10
|
|
10
11
|
attr_reader :tool_name
|
11
|
-
attr_writer :limit_targets, :filter_messages
|
12
|
+
attr_writer :limit_targets, :filter_messages, :file_filter
|
12
13
|
|
13
14
|
def limit_targets?
|
14
15
|
@limit_targets
|
@@ -29,6 +30,11 @@ module QuietQuality
|
|
29
30
|
def parser_class
|
30
31
|
tool_namespace::Parser
|
31
32
|
end
|
33
|
+
|
34
|
+
def file_filter
|
35
|
+
return nil if @file_filter.nil?
|
36
|
+
Regexp.new(@file_filter)
|
37
|
+
end
|
32
38
|
end
|
33
39
|
end
|
34
40
|
end
|
@@ -22,6 +22,14 @@ module QuietQuality
|
|
22
22
|
pipelines.any?(&:failure?)
|
23
23
|
end
|
24
24
|
|
25
|
+
def successful_outcomes
|
26
|
+
@_successful_outcomes ||= outcomes.select(&:success?)
|
27
|
+
end
|
28
|
+
|
29
|
+
def failed_outcomes
|
30
|
+
@_failed_outcomes ||= outcomes.select(&:failure?)
|
31
|
+
end
|
32
|
+
|
25
33
|
private
|
26
34
|
|
27
35
|
attr_reader :tools, :changed_files
|
@@ -48,8 +48,10 @@ module QuietQuality
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def runner
|
51
|
-
@_runner ||= tool_options.runner_class
|
52
|
-
|
51
|
+
@_runner ||= tool_options.runner_class.new(
|
52
|
+
changed_files: limit_targets? ? changed_files : nil,
|
53
|
+
file_filter: tool_options.file_filter
|
54
|
+
)
|
53
55
|
end
|
54
56
|
|
55
57
|
def parser
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module QuietQuality
|
2
|
+
class Logger
|
3
|
+
def initialize(stream:, logging:)
|
4
|
+
@stream = stream
|
5
|
+
@logging = logging
|
6
|
+
end
|
7
|
+
|
8
|
+
def puts(s)
|
9
|
+
return if logging.quiet?
|
10
|
+
stream.puts(s)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
attr_reader :stream, :logging
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module QuietQuality
|
2
|
+
module Tools
|
3
|
+
class BaseRunner
|
4
|
+
# In general, we don't want to supply a huge number of arguments to a command-line tool.
|
5
|
+
MAX_FILES = 100
|
6
|
+
|
7
|
+
def initialize(changed_files: nil, file_filter: nil)
|
8
|
+
@changed_files = changed_files
|
9
|
+
@file_filter = file_filter
|
10
|
+
end
|
11
|
+
|
12
|
+
def invoke!
|
13
|
+
@_outcome ||= performed_outcome
|
14
|
+
end
|
15
|
+
|
16
|
+
def tool_name
|
17
|
+
fail(NoMethodError, "BaseRunner subclass must implement `tool_name`")
|
18
|
+
end
|
19
|
+
|
20
|
+
def command
|
21
|
+
fail(NoMethodError, "BaseRunner subclass must implement `command`")
|
22
|
+
end
|
23
|
+
|
24
|
+
def success_status?(stat)
|
25
|
+
stat.success?
|
26
|
+
end
|
27
|
+
|
28
|
+
# distinct from _error_ status - this is asking "does this status represent failures-found?"
|
29
|
+
def failure_status?(stat)
|
30
|
+
stat.exitstatus == 1
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :changed_files, :file_filter
|
36
|
+
|
37
|
+
def performed_outcome
|
38
|
+
out, err, stat = Open3.capture3(*command)
|
39
|
+
if success_status?(stat)
|
40
|
+
Outcome.new(tool: tool_name, output: out, logging: err)
|
41
|
+
elsif failure_status?(stat)
|
42
|
+
Outcome.new(tool: tool_name, output: out, logging: err, failure: true)
|
43
|
+
else
|
44
|
+
fail(ExecutionError, "Execution of #{tool_name} failed with #{stat.exitstatus}")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -1,34 +1,19 @@
|
|
1
1
|
module QuietQuality
|
2
2
|
module Tools
|
3
3
|
module Brakeman
|
4
|
-
class Runner
|
5
|
-
|
6
|
-
|
7
|
-
KNOWN_EXIT_STATUSES = [3, 4, 5, 6, 7, 8].to_set
|
8
|
-
|
9
|
-
def initialize(changed_files: nil)
|
10
|
-
@changed_files = changed_files
|
11
|
-
end
|
12
|
-
|
13
|
-
def invoke!
|
14
|
-
@_outcome ||= performed_outcome
|
4
|
+
class Runner < BaseRunner
|
5
|
+
def tool_name
|
6
|
+
:brakeman
|
15
7
|
end
|
16
8
|
|
17
|
-
private
|
18
|
-
|
19
9
|
def command
|
20
10
|
["brakeman", "-f", "json"]
|
21
11
|
end
|
22
12
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
elsif KNOWN_EXIT_STATUSES.include?(stat.exitstatus)
|
28
|
-
Outcome.new(tool: :brakeman, output: out, logging: err, failure: true)
|
29
|
-
else
|
30
|
-
fail(ExecutionError, "Execution of brakeman failed with #{stat.exitstatus}")
|
31
|
-
end
|
13
|
+
# These are specified in constants at the top of brakeman.rb:
|
14
|
+
# https://github.com/presidentbeef/brakeman/blob/main/lib/brakeman.rb#L6-L25
|
15
|
+
def failure_status?(stat)
|
16
|
+
[3, 4, 5, 6, 7, 8].include?(stat.exitstatus)
|
32
17
|
end
|
33
18
|
end
|
34
19
|
end
|
@@ -1,61 +1,29 @@
|
|
1
1
|
module QuietQuality
|
2
2
|
module Tools
|
3
3
|
module HamlLint
|
4
|
-
class Runner
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# haml-lint uses the `sysexits` gem, and exits with Sysexits::EX_DATAERR for the
|
9
|
-
# failures case here in lib/haml_lint/cli.rb. That's mapped to status 65 - other
|
10
|
-
# statuses have other failure meanings, which we don't want to interpret as "problems
|
11
|
-
# encountered"
|
12
|
-
FAILURE_STATUS = 65
|
13
|
-
|
14
|
-
def initialize(changed_files: nil)
|
15
|
-
@changed_files = changed_files
|
16
|
-
end
|
17
|
-
|
18
|
-
def invoke!
|
19
|
-
@_outcome ||= skip_execution? ? skipped_outcome : performed_outcome
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
attr_reader :changed_files
|
25
|
-
|
26
|
-
def skip_execution?
|
27
|
-
changed_files && relevant_files.empty?
|
28
|
-
end
|
29
|
-
|
30
|
-
def relevant_files
|
31
|
-
return nil if changed_files.nil?
|
32
|
-
changed_files.paths.select { |path| path.end_with?(".haml") }
|
4
|
+
class Runner < RelevantRunner
|
5
|
+
def tool_name
|
6
|
+
:haml_lint
|
33
7
|
end
|
34
8
|
|
35
|
-
def
|
36
|
-
|
37
|
-
return [] if relevant_files.length > MAX_FILES
|
38
|
-
relevant_files
|
9
|
+
def no_files_output
|
10
|
+
%({"files": []})
|
39
11
|
end
|
40
12
|
|
41
|
-
def
|
42
|
-
|
43
|
-
["haml-lint", "--reporter", "json"] + target_files.sort
|
13
|
+
def base_command
|
14
|
+
["haml-lint", "--reporter", "json"]
|
44
15
|
end
|
45
16
|
|
46
|
-
def
|
47
|
-
|
17
|
+
def relevant_path?(path)
|
18
|
+
path.end_with?(".haml")
|
48
19
|
end
|
49
20
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
else
|
57
|
-
fail(ExecutionError, "Execution of haml-lint failed with #{stat.exitstatus}")
|
58
|
-
end
|
21
|
+
# haml-lint uses the `sysexits` gem, and exits with Sysexits::EX_DATAERR for the
|
22
|
+
# failures case here in lib/haml_lint/cli.rb. That's mapped to status 65 - other
|
23
|
+
# statuses have other failure meanings, which we don't want to interpret as "problems
|
24
|
+
# encountered"
|
25
|
+
def failure_status?(stat)
|
26
|
+
stat.exitstatus == 65
|
59
27
|
end
|
60
28
|
end
|
61
29
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module QuietQuality
|
2
|
+
module Tools
|
3
|
+
module MarkdownLint
|
4
|
+
class Parser
|
5
|
+
def initialize(text)
|
6
|
+
@text = text
|
7
|
+
end
|
8
|
+
|
9
|
+
def messages
|
10
|
+
return @_messages if defined?(@_messages)
|
11
|
+
messages = content.map { |entry| message_for_entry(entry) }
|
12
|
+
@_messages = Messages.new(messages)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader :text
|
18
|
+
|
19
|
+
def content
|
20
|
+
@_content ||= JSON.parse(text, symbolize_names: true)
|
21
|
+
end
|
22
|
+
|
23
|
+
def message_for_entry(entry)
|
24
|
+
Message.new(
|
25
|
+
path: entry.fetch(:filename),
|
26
|
+
start_line: entry.fetch(:line),
|
27
|
+
rule: entry.fetch(:description),
|
28
|
+
body: entry.fetch(:docs)
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module QuietQuality
|
2
|
+
module Tools
|
3
|
+
module MarkdownLint
|
4
|
+
class Runner < RelevantRunner
|
5
|
+
def tool_name
|
6
|
+
:markdown_lint
|
7
|
+
end
|
8
|
+
|
9
|
+
def no_files_output
|
10
|
+
"[]"
|
11
|
+
end
|
12
|
+
|
13
|
+
def command
|
14
|
+
return nil if skip_execution?
|
15
|
+
if target_files.any?
|
16
|
+
["mdl", "--json"] + target_files.sort
|
17
|
+
else
|
18
|
+
["mdl", "--json", "."]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def relevant_path?(path)
|
23
|
+
path.end_with?(".md")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative "./base_runner"
|
2
|
+
|
3
|
+
module QuietQuality
|
4
|
+
module Tools
|
5
|
+
class RelevantRunner < BaseRunner
|
6
|
+
# In general, we don't want to supply a huge number of arguments to a command-line tool.
|
7
|
+
# This will probably become configurable later.
|
8
|
+
MAX_FILES = 100
|
9
|
+
|
10
|
+
def invoke!
|
11
|
+
@_outcome ||= skip_execution? ? skipped_outcome : performed_outcome
|
12
|
+
end
|
13
|
+
|
14
|
+
def command
|
15
|
+
return nil if skip_execution?
|
16
|
+
base_command + target_files.sort
|
17
|
+
end
|
18
|
+
|
19
|
+
def relevant_path?(path)
|
20
|
+
fail(NoMethodError, "RelevantRunner subclass must implement `relevant_path?`")
|
21
|
+
end
|
22
|
+
|
23
|
+
def base_command
|
24
|
+
fail(NoMethodError, "RelevantRunner subclass must implement either `command` or `base_command`")
|
25
|
+
end
|
26
|
+
|
27
|
+
def no_files_output
|
28
|
+
fail(NoMethodError, "RelevantRunner subclass must implement `no_files_output`")
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def skip_execution?
|
34
|
+
changed_files && relevant_files.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def relevant_files
|
38
|
+
return nil if changed_files.nil?
|
39
|
+
changed_files.paths
|
40
|
+
.select { |path| relevant_path?(path) }
|
41
|
+
.select { |path| file_filter.nil? || file_filter.match?(path) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def target_files
|
45
|
+
return [] if changed_files.nil?
|
46
|
+
return [] if relevant_files.length > MAX_FILES
|
47
|
+
relevant_files
|
48
|
+
end
|
49
|
+
|
50
|
+
def skipped_outcome
|
51
|
+
Outcome.new(tool: tool_name, output: no_files_output)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -1,55 +1,21 @@
|
|
1
1
|
module QuietQuality
|
2
2
|
module Tools
|
3
3
|
module Rspec
|
4
|
-
class Runner
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(changed_files: nil)
|
9
|
-
@changed_files = changed_files
|
10
|
-
end
|
11
|
-
|
12
|
-
def invoke!
|
13
|
-
@_outcome ||= skip_execution? ? skipped_outcome : performed_outcome
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
attr_reader :changed_files
|
19
|
-
|
20
|
-
def skip_execution?
|
21
|
-
changed_files && relevant_files.empty?
|
22
|
-
end
|
23
|
-
|
24
|
-
def relevant_files
|
25
|
-
return nil if changed_files.nil?
|
26
|
-
changed_files.paths.select { |path| path.end_with?("_spec.rb") }
|
27
|
-
end
|
28
|
-
|
29
|
-
def target_files
|
30
|
-
return [] if changed_files.nil?
|
31
|
-
return [] if relevant_files.length > MAX_FILES
|
32
|
-
relevant_files
|
4
|
+
class Runner < RelevantRunner
|
5
|
+
def tool_name
|
6
|
+
:rspec
|
33
7
|
end
|
34
8
|
|
35
|
-
def
|
36
|
-
|
37
|
-
["rspec", "-f", "json"] + target_files.sort
|
9
|
+
def no_files_output
|
10
|
+
'{"examples": [], "summary": {"failure_count": 0}}'
|
38
11
|
end
|
39
12
|
|
40
|
-
def
|
41
|
-
|
13
|
+
def base_command
|
14
|
+
["rspec", "-f", "json"]
|
42
15
|
end
|
43
16
|
|
44
|
-
def
|
45
|
-
|
46
|
-
if stat.success?
|
47
|
-
Outcome.new(tool: :rspec, output: out, logging: err)
|
48
|
-
elsif stat.exitstatus == 1
|
49
|
-
Outcome.new(tool: :rspec, output: out, logging: err, failure: true)
|
50
|
-
else
|
51
|
-
fail(ExecutionError, "Execution of rspec failed with #{stat.exitstatus}")
|
52
|
-
end
|
17
|
+
def relevant_path?(path)
|
18
|
+
path.end_with?("_spec.rb")
|
53
19
|
end
|
54
20
|
end
|
55
21
|
end
|
@@ -1,65 +1,21 @@
|
|
1
1
|
module QuietQuality
|
2
2
|
module Tools
|
3
3
|
module Rubocop
|
4
|
-
class Runner
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def command_name
|
9
|
-
"rubocop"
|
10
|
-
end
|
11
|
-
|
12
|
-
# Supplying changed_files: nil means "run against all files".
|
13
|
-
def initialize(changed_files: nil)
|
14
|
-
@changed_files = changed_files
|
15
|
-
end
|
16
|
-
|
17
|
-
def invoke!
|
18
|
-
@_outcome ||= skip_execution? ? skipped_outcome : performed_outcome
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
attr_reader :changed_files
|
24
|
-
|
25
|
-
# If we were told that _no files changed_ (which is distinct from not being told that
|
26
|
-
# any files changed - a [] instead of a nil), then we shouldn't run rubocop at all.
|
27
|
-
def skip_execution?
|
28
|
-
changed_files && relevant_files.empty?
|
29
|
-
end
|
30
|
-
|
31
|
-
# Note: if target_files goes over MAX_FILES, it's _empty_ instead - that means that
|
32
|
-
# we run against the full repository instead of the specific files (rubocop's behavior
|
33
|
-
# when no target files are specified)
|
34
|
-
def command
|
35
|
-
return nil if skip_execution?
|
36
|
-
[command_name, "-f", "json"] + target_files.sort
|
37
|
-
end
|
38
|
-
|
39
|
-
def relevant_files
|
40
|
-
return nil if changed_files.nil?
|
41
|
-
changed_files.paths.select { |path| path.end_with?(".rb") }
|
4
|
+
class Runner < RelevantRunner
|
5
|
+
def tool_name
|
6
|
+
:rubocop
|
42
7
|
end
|
43
8
|
|
44
|
-
def
|
45
|
-
|
46
|
-
return [] if relevant_files.length > MAX_FILES
|
47
|
-
relevant_files
|
9
|
+
def no_files_output
|
10
|
+
'{"files": [], "summary": {"offense_count": 0}}'
|
48
11
|
end
|
49
12
|
|
50
|
-
def
|
51
|
-
|
13
|
+
def base_command
|
14
|
+
["rubocop", "-f", "json"]
|
52
15
|
end
|
53
16
|
|
54
|
-
def
|
55
|
-
|
56
|
-
if stat.success?
|
57
|
-
Outcome.new(tool: command_name.to_sym, output: out, logging: err)
|
58
|
-
elsif stat.exitstatus == 1
|
59
|
-
Outcome.new(tool: command_name.to_sym, output: out, logging: err, failure: true)
|
60
|
-
else
|
61
|
-
fail(ExecutionError, "Execution of #{command_name} failed with #{stat.exitstatus}")
|
62
|
-
end
|
17
|
+
def relevant_path?(path)
|
18
|
+
path.end_with?(".rb")
|
63
19
|
end
|
64
20
|
end
|
65
21
|
end
|
@@ -1,9 +1,21 @@
|
|
1
1
|
module QuietQuality
|
2
2
|
module Tools
|
3
3
|
module Standardrb
|
4
|
-
class Runner <
|
5
|
-
def
|
6
|
-
|
4
|
+
class Runner < RelevantRunner
|
5
|
+
def tool_name
|
6
|
+
:standardrb
|
7
|
+
end
|
8
|
+
|
9
|
+
def no_files_output
|
10
|
+
'{"files": [], "summary": {"offense_count": 0}}'
|
11
|
+
end
|
12
|
+
|
13
|
+
def base_command
|
14
|
+
["standardrb", "-f", "json"]
|
15
|
+
end
|
16
|
+
|
17
|
+
def relevant_path?(path)
|
18
|
+
path.end_with?(".rb")
|
7
19
|
end
|
8
20
|
end
|
9
21
|
end
|
data/lib/quiet_quality/tools.rb
CHANGED
@@ -3,9 +3,14 @@ require "open3"
|
|
3
3
|
module QuietQuality
|
4
4
|
module Tools
|
5
5
|
Error = Class.new(::QuietQuality::Error)
|
6
|
+
ExecutionError = Class.new(Error)
|
7
|
+
ParsingError = Class.new(Error)
|
6
8
|
end
|
7
9
|
end
|
8
10
|
|
11
|
+
require_relative "./tools/base_runner"
|
12
|
+
require_relative "./tools/relevant_runner"
|
13
|
+
|
9
14
|
glob = File.expand_path("../tools/*.rb", __FILE__)
|
10
15
|
Dir.glob(glob).sort.each { |f| require f }
|
11
16
|
|
@@ -15,6 +20,7 @@ module QuietQuality
|
|
15
20
|
AVAILABLE = {
|
16
21
|
brakeman: Brakeman,
|
17
22
|
haml_lint: HamlLint,
|
23
|
+
markdown_lint: MarkdownLint,
|
18
24
|
rspec: Rspec,
|
19
25
|
rubocop: Rubocop,
|
20
26
|
standardrb: Standardrb
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quiet_quality
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Mueller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: git
|
@@ -166,9 +166,11 @@ files:
|
|
166
166
|
- ".gitignore"
|
167
167
|
- ".mdl_rules.rb"
|
168
168
|
- ".mdlrc"
|
169
|
+
- ".quiet_quality.ci.yml"
|
169
170
|
- ".quiet_quality.yml"
|
170
171
|
- ".rspec"
|
171
172
|
- ".rubocop.yml"
|
173
|
+
- CHANGELOG.md
|
172
174
|
- Gemfile
|
173
175
|
- LICENSE
|
174
176
|
- README.md
|
@@ -183,9 +185,11 @@ files:
|
|
183
185
|
- lib/quiet_quality/cli.rb
|
184
186
|
- lib/quiet_quality/cli/arg_parser.rb
|
185
187
|
- lib/quiet_quality/cli/entrypoint.rb
|
188
|
+
- lib/quiet_quality/cli/presenter.rb
|
186
189
|
- lib/quiet_quality/config.rb
|
187
190
|
- lib/quiet_quality/config/builder.rb
|
188
191
|
- lib/quiet_quality/config/finder.rb
|
192
|
+
- lib/quiet_quality/config/logging.rb
|
189
193
|
- lib/quiet_quality/config/options.rb
|
190
194
|
- lib/quiet_quality/config/parsed_options.rb
|
191
195
|
- lib/quiet_quality/config/parser.rb
|
@@ -195,17 +199,23 @@ files:
|
|
195
199
|
- lib/quiet_quality/executors/concurrent_executor.rb
|
196
200
|
- lib/quiet_quality/executors/pipeline.rb
|
197
201
|
- lib/quiet_quality/executors/serial_executor.rb
|
202
|
+
- lib/quiet_quality/logger.rb
|
198
203
|
- lib/quiet_quality/message.rb
|
199
204
|
- lib/quiet_quality/message_filter.rb
|
200
205
|
- lib/quiet_quality/messages.rb
|
201
206
|
- lib/quiet_quality/tools.rb
|
207
|
+
- lib/quiet_quality/tools/base_runner.rb
|
202
208
|
- lib/quiet_quality/tools/brakeman.rb
|
203
209
|
- lib/quiet_quality/tools/brakeman/parser.rb
|
204
210
|
- lib/quiet_quality/tools/brakeman/runner.rb
|
205
211
|
- lib/quiet_quality/tools/haml_lint.rb
|
206
212
|
- lib/quiet_quality/tools/haml_lint/parser.rb
|
207
213
|
- lib/quiet_quality/tools/haml_lint/runner.rb
|
214
|
+
- lib/quiet_quality/tools/markdown_lint.rb
|
215
|
+
- lib/quiet_quality/tools/markdown_lint/parser.rb
|
216
|
+
- lib/quiet_quality/tools/markdown_lint/runner.rb
|
208
217
|
- lib/quiet_quality/tools/outcome.rb
|
218
|
+
- lib/quiet_quality/tools/relevant_runner.rb
|
209
219
|
- lib/quiet_quality/tools/rspec.rb
|
210
220
|
- lib/quiet_quality/tools/rspec/parser.rb
|
211
221
|
- lib/quiet_quality/tools/rspec/runner.rb
|