quiet_quality 1.1.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dogfood.yml +1 -1
  3. data/.github/workflows/rspec.yml +1 -1
  4. data/.quiet_quality.ci.yml +6 -0
  5. data/.quiet_quality.yml +2 -1
  6. data/CHANGELOG.md +63 -0
  7. data/README.md +43 -13
  8. data/lib/quiet_quality/cli/arg_parser.rb +23 -7
  9. data/lib/quiet_quality/cli/entrypoint.rb +21 -30
  10. data/lib/quiet_quality/cli/presenter.rb +77 -0
  11. data/lib/quiet_quality/config/builder.rb +5 -0
  12. data/lib/quiet_quality/config/finder.rb +0 -4
  13. data/lib/quiet_quality/config/logging.rb +23 -0
  14. data/lib/quiet_quality/config/options.rb +6 -0
  15. data/lib/quiet_quality/config/parsed_options.rb +36 -0
  16. data/lib/quiet_quality/config/parser.rb +5 -8
  17. data/lib/quiet_quality/executors/base_executor.rb +9 -11
  18. data/lib/quiet_quality/logger.rb +17 -0
  19. data/lib/quiet_quality/tools/base_runner.rb +49 -0
  20. data/lib/quiet_quality/tools/brakeman/runner.rb +7 -26
  21. data/lib/quiet_quality/tools/brakeman.rb +0 -2
  22. data/lib/quiet_quality/tools/haml_lint/runner.rb +15 -50
  23. data/lib/quiet_quality/tools/haml_lint.rb +0 -2
  24. data/lib/quiet_quality/tools/markdown_lint/parser.rb +34 -0
  25. data/lib/quiet_quality/tools/markdown_lint/runner.rb +28 -0
  26. data/lib/quiet_quality/tools/markdown_lint.rb +9 -0
  27. data/lib/quiet_quality/tools/relevant_runner.rb +55 -0
  28. data/lib/quiet_quality/tools/rspec/runner.rb +9 -46
  29. data/lib/quiet_quality/tools/rspec.rb +0 -2
  30. data/lib/quiet_quality/tools/rubocop/runner.rb +9 -56
  31. data/lib/quiet_quality/tools/rubocop.rb +0 -2
  32. data/lib/quiet_quality/tools/standardrb/runner.rb +15 -3
  33. data/lib/quiet_quality/tools/standardrb.rb +0 -2
  34. data/lib/quiet_quality/tools.rb +6 -0
  35. data/lib/quiet_quality/version.rb +1 -1
  36. data/lib/quiet_quality/version_control_systems/git.rb +2 -11
  37. metadata +12 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b6aea488d2026da63fdf9483d1df9bd4e2363270d7b441cbca384cd803cab803
4
- data.tar.gz: 0536d6eb0b8cdd1c4678f49058be4d37017b7ff6156d363afa71df8a979d30aa
3
+ metadata.gz: b3847a8d0b996b19f34c32ac587dd1b9e51a2bedbea8e777e6b888294b16b5c0
4
+ data.tar.gz: 742080e4b9193d0d99606f00c68077229ffba805c5b93ffa75c09705e0101b18
5
5
  SHA512:
6
- metadata.gz: 766f24f8211b83610c05e57b0f81d77bbf2d4bf8c206cc5e9af1d4e302adc9fee6425c506b344850a1421b7a30f18b6dfa6a23132aaa64dc3e6fbeb5e59c4c0b
7
- data.tar.gz: 7b72dcf22a6696b2ff88a5efb08fb0338d2d4f0b43f506f147a2d5cb0ed7186328157ff4b8ded7198fff3785f6636b32d8b1ecd1102cd462b341f28c57dfd429
6
+ metadata.gz: b61e91968e6bdd4943809a336d60cf6a7feee9ab58db225a33f8268bd02efc911c3fa812e45491be1cc9f6d461a6db71308e790f1d912fac011c82cf4864e8d7
7
+ data.tar.gz: ace3273fb13dfcd04ae2432ed0cd488195789c75a27251617fc6be851bf865e4c4b689f9c3e3e83203ffb2b1ecd2be11c258405b10a3720c186b79b5e150fe3b
@@ -27,4 +27,4 @@ jobs:
27
27
  run: bundle install --jobs 4 --retry 3
28
28
 
29
29
  - name: Run QuietQuality
30
- run: bundle exec bin/qq standardrb rubocop rspec --all-files --unfiltered --annotate github_stdout --comparison-branch origin/main
30
+ run: bundle exec bin/qq -C .quiet_quality.ci.yml
@@ -30,4 +30,4 @@ jobs:
30
30
  run: bundle install --jobs 4 --retry 3
31
31
 
32
32
  - name: Run RSpec
33
- run: bundle exec rspec
33
+ run: SIMPLECOV=true SIMPLECOV_TEXT=true bundle exec rspec
@@ -0,0 +1,6 @@
1
+ ---
2
+ default_tools: ["standardrb", "rubocop", "markdown_lint", "rspec"]
3
+ executor: concurrent
4
+ comparison_branch: origin/main
5
+ all_files: true
6
+ unfiltered: true
data/.quiet_quality.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  ---
2
- default_tools: ["standardrb", "rubocop", "rspec"]
2
+ default_tools: ["standardrb", "rubocop", "markdown_lint", "rspec"]
3
3
  executor: concurrent
4
4
  comparison_branch: main
5
5
  changed_files: false
6
6
  filter_messages: false
7
+ logging: light
data/CHANGELOG.md ADDED
@@ -0,0 +1,63 @@
1
+ # Changelog
2
+
3
+ ## Release 1.2.1
4
+
5
+ * Fix the handling of the various ways to specify whether tools should limit
6
+ their targets to changed files or run against the entire repository. The
7
+ configuration systems had disagreements on what to call the options in
8
+ question, which resulted in some configuration entries being ignored. We
9
+ enforce a set of validations on reads and writes now to avoid such a problem
10
+ in the future (#89, resolves #88)
11
+ * Add coverage-checking, and then improve test coverage and remove unreferenced
12
+ code as a result. (#87)
13
+
14
+ ## Release 1.2.0
15
+
16
+ * Support `--light`, `--quiet`, and `--logging LEVEL` arguments for less output
17
+ (#78, resolves #37)
18
+ * Support the [markdownlint](https://github.com/markdownlint/markdownlint) tool
19
+ (#79, resolves #58)
20
+ * Extract BaseRunner (#82) and RelevantRunner (#83) parent classes from the tool
21
+ runners, to allow new tools to be more easily implemented. (Resolves #81)
22
+ * Extract a Cli::Presenter from the Entrypoint, to simplify pending work on cli
23
+ presentation (#84, resolves #42)
24
+ * Update the docs a bit, and add a changelog (hi!)
25
+
26
+ ## Release 1.1.0
27
+
28
+ * Support a `file_filter` config entry per-tool (without a cli option), to limit
29
+ what file paths a runner might supply to its tool based on a regex
30
+ (#74, resolves #68)
31
+ * When what tools to execute is not specified (by cli or by config file), abort
32
+ `bin/qq` and explain, rather than assuming "all of them" (#79, resolves #58)
33
+ * Update the config parser to handle keys named to match the cli options
34
+ alongside the ones that were (mistakenly) named differently. This is a
35
+ backwards-compatible change; if we eventually deprecate and simplify some of
36
+ these option names, you'll have plenty of warning (#77, resolves #75)
37
+ * Support `--version/-V` flag (#73, resolves #69)
38
+
39
+ ## Release 1.0.3
40
+
41
+ * Fix the printed _output_ for the case where there were some warnings from a
42
+ tool, but all of them were filtered out (because they targetted lines that
43
+ were not changed, for example). This situation should tell you that nothing
44
+ is wrong with your PR, not that there is a problem (#71)
45
+
46
+ ## Release 1.0.2
47
+
48
+ * Fix the _exit status_ for the case where there were some warnings from a
49
+ tool, but all of them were filtered out (because they targetted lines that
50
+ were not changed, for example). This situation should produce a successful
51
+ result, and not fail a CI pipeline (#67)
52
+
53
+ ## Release 1.0.1
54
+
55
+ * Fix the calculation of `changed_files` for the executor - in the migration
56
+ to Entrypoint, the actual git call to get a ChangedFiles object to pass into
57
+ other service classes was lost, which had the result that the entire system
58
+ behaved (outside of tests) as if you were always running with `--all-files`
59
+ (#65).
60
+
61
+ ## Release 1.0.0
62
+
63
+ 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
- * brakeman (though there's no way to run this against only changed files)
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,6 +144,10 @@ 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
@@ -162,11 +171,32 @@ rspec:
162
171
 
163
172
  ### CLI Options
164
173
 
165
- The same options are all available on the CLI, plus some additional ones - run
166
- `qq --help` for a detailed list of the options, but the notable additions are:
167
-
168
- * `--help/-H`: See a list of the options
169
- * `--no-config/-N`: Do _not_ load a config file, even if present.
170
- * `--config/-C`: load the supplied config file (instead of the detected one, if
171
- found)
172
- * `--version/-V`: what version of the gem are you using?
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
+ ```
@@ -45,12 +45,12 @@ module QuietQuality
45
45
  # options; if they don't, they are global options. (optparse allows an optional argument
46
46
  # to a flag if the string representing it is not a 'string in all caps'. So `[FOO]` or `foo`
47
47
  # would be optional, but `FOO` would be required. This helper simplifies handling those.
48
- def read_tool_or_global_option(name, tool, value)
48
+ def read_tool_or_global_option(name:, into:, tool:, value:)
49
49
  if tool
50
50
  validate_value_from("tool", tool, Tools::AVAILABLE)
51
- set_tool_option(tool, name, value)
51
+ set_tool_option(tool, into, value)
52
52
  else
53
- set_global_option(name, value)
53
+ set_global_option(into, value)
54
54
  end
55
55
  end
56
56
 
@@ -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
 
@@ -113,11 +114,11 @@ module QuietQuality
113
114
 
114
115
  def setup_file_target_options(parser)
115
116
  parser.on("-a", "--all-files [tool]", "Use the tool(s) on all files") do |tool|
116
- read_tool_or_global_option(:all_files, tool, true)
117
+ read_tool_or_global_option(name: :all_files, into: :limit_targets, tool: tool, value: false)
117
118
  end
118
119
 
119
120
  parser.on("-c", "--changed-files [tool]", "Use the tool(s) only on changed files") do |tool|
120
- read_tool_or_global_option(:all_files, tool, false)
121
+ read_tool_or_global_option(name: :all_files, into: :limit_targets, tool: tool, value: true)
121
122
  end
122
123
 
123
124
  parser.on("-B", "--comparison-branch BRANCH", "Specify the branch to compare against") do |branch|
@@ -127,11 +128,26 @@ module QuietQuality
127
128
 
128
129
  def setup_filter_messages_options(parser)
129
130
  parser.on("-f", "--filter-messages [tool]", "Filter messages from tool(s) based on changed lines") do |tool|
130
- read_tool_or_global_option(:filter_messages, tool, true)
131
+ read_tool_or_global_option(name: :filter_messages, into: :filter_messages, tool: tool, value: true)
131
132
  end
132
133
 
133
134
  parser.on("-u", "--unfiltered [tool]", "Don't filter messages from tool(s)") do |tool|
134
- read_tool_or_global_option(:filter_messages, tool, false)
135
+ read_tool_or_global_option(name: :filter_messages, into: :filter_messages, tool: tool, value: false)
136
+ end
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)
135
151
  end
136
152
  end
137
153
  end
@@ -16,8 +16,7 @@ module QuietQuality
16
16
  log_no_tools_text
17
17
  else
18
18
  executed
19
- log_outcomes
20
- log_messages
19
+ log_results
21
20
  annotate_messages
22
21
  end
23
22
 
@@ -34,6 +33,23 @@ module QuietQuality
34
33
 
35
34
  attr_reader :argv, :output_stream, :error_stream
36
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
+
37
53
  def arg_parser
38
54
  @_arg_parser ||= ArgParser.new(argv.dup)
39
55
  end
@@ -55,15 +71,15 @@ module QuietQuality
55
71
  end
56
72
 
57
73
  def log_help_text
58
- error_stream.puts(arg_parser.help_text)
74
+ logger.puts(arg_parser.help_text)
59
75
  end
60
76
 
61
77
  def log_version_text
62
- error_stream.puts(QuietQuality::VERSION)
78
+ logger.puts(QuietQuality::VERSION)
63
79
  end
64
80
 
65
81
  def log_no_tools_text
66
- error_stream.puts(<<~TEXT)
82
+ logger.puts(<<~TEXT)
67
83
  You must specify one or more tools to run, either on the command-line or in the
68
84
  default_tools key in a configuration file.
69
85
  TEXT
@@ -95,31 +111,6 @@ module QuietQuality
95
111
  @_executed = executor
96
112
  end
97
113
 
98
- def log_outcomes
99
- executed.outcomes.each do |outcome|
100
- result = outcome.success? ? "Passed" : "Failed"
101
- error_stream.puts "--- #{result}: #{outcome.tool}"
102
- end
103
- end
104
-
105
- def log_message(msg)
106
- line_range =
107
- if msg.start_line == msg.stop_line
108
- msg.start_line.to_s
109
- else
110
- "#{msg.start_line}-#{msg.stop_line}"
111
- end
112
- rule_string = msg.rule ? " [#{msg.rule}]" : ""
113
- truncated_body = msg.body.gsub(/ *\n */, "\\n").slice(0, 120)
114
- error_stream.puts " #{msg.path}:#{line_range}#{rule_string} #{truncated_body}"
115
- end
116
-
117
- def log_messages
118
- return unless executed.messages.any?
119
- error_stream.puts "\n\n#{executed.messages.count} messages:"
120
- executed.messages.each { |msg| log_message(msg) }
121
- end
122
-
123
114
  def annotate_messages
124
115
  return unless options.annotator
125
116
  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
@@ -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,6 +104,10 @@ 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
@@ -25,10 +25,6 @@ module QuietQuality
25
25
 
26
26
  attr_reader :from
27
27
 
28
- def config_path_within(dir)
29
- File.join(dir, CONFIG_FILENAME)
30
- end
31
-
32
28
  def each_successive_enclosing_directory(max_depth: 100, &block)
33
29
  d = Pathname.new(from)
34
30
  depth = 0
@@ -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
@@ -1,6 +1,26 @@
1
1
  module QuietQuality
2
2
  module Config
3
3
  class ParsedOptions
4
+ InvalidOptionName = Class.new(Error)
5
+
6
+ GLOBAL_OPTIONS = [
7
+ :no_config,
8
+ :config_path,
9
+ :annotator,
10
+ :executor,
11
+ :comparison_branch,
12
+ :logging,
13
+ :limit_targets,
14
+ :filter_messages,
15
+ :file_filter
16
+ ].to_set
17
+
18
+ TOOL_OPTIONS = [
19
+ :limit_targets,
20
+ :filter_messages,
21
+ :file_filter
22
+ ].to_set
23
+
4
24
  def initialize
5
25
  @tools = []
6
26
  @tool_options = {}
@@ -20,21 +40,37 @@ module QuietQuality
20
40
  end
21
41
 
22
42
  def set_global_option(name, value)
43
+ validate_global_option(name)
23
44
  @global_options[name.to_sym] = value
24
45
  end
25
46
 
26
47
  def global_option(name)
48
+ validate_global_option(name)
27
49
  @global_options.fetch(name.to_sym, nil)
28
50
  end
29
51
 
30
52
  def set_tool_option(tool, name, value)
53
+ validate_tool_option(name)
31
54
  @tool_options[tool.to_sym] ||= {}
32
55
  @tool_options[tool.to_sym][name.to_sym] = value
33
56
  end
34
57
 
35
58
  def tool_option(tool, name)
59
+ validate_tool_option(name)
36
60
  @tool_options.dig(tool.to_sym, name.to_sym)
37
61
  end
62
+
63
+ private
64
+
65
+ def validate_global_option(name)
66
+ return if GLOBAL_OPTIONS.include?(name.to_sym)
67
+ fail(InvalidOptionName, "Option name #{name} is not a recognized global ParsedOption")
68
+ end
69
+
70
+ def validate_tool_option(name)
71
+ return if TOOL_OPTIONS.include?(name.to_sym)
72
+ fail(InvalidOptionName, "Option name #{name} is not a recognized tool ParsedOption")
73
+ end
38
74
  end
39
75
  end
40
76
  end
@@ -42,10 +42,11 @@ module QuietQuality
42
42
  read_global_option(opts, :annotator, :annotator, as: :symbol, validate_from: Annotators::ANNOTATOR_TYPES)
43
43
  read_global_option(opts, :annotate, :annotator, as: :symbol, validate_from: Annotators::ANNOTATOR_TYPES)
44
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)
45
+ read_global_option(opts, :changed_files, :limit_targets, as: :boolean)
46
+ read_global_option(opts, :all_files, :limit_targets, as: :reversed_boolean)
47
47
  read_global_option(opts, :filter_messages, :filter_messages, as: :boolean)
48
48
  read_global_option(opts, :unfiltered, :filter_messages, as: :reversed_boolean)
49
+ read_global_option(opts, :logging, :logging, as: :symbol, validate_from: Logging::LEVELS)
49
50
  end
50
51
 
51
52
  def store_tool_options(opts)
@@ -60,8 +61,8 @@ module QuietQuality
60
61
 
61
62
  read_tool_option(opts, tool_name, :filter_messages, :filter_messages, as: :boolean)
62
63
  read_tool_option(opts, tool_name, :unfiltered, :filter_messages, as: :reversed_boolean)
63
- read_tool_option(opts, tool_name, :changed_files, :changed_files, as: :boolean)
64
- read_tool_option(opts, tool_name, :all_files, :changed_files, as: :reversed_boolean)
64
+ read_tool_option(opts, tool_name, :changed_files, :limit_targets, as: :boolean)
65
+ read_tool_option(opts, tool_name, :all_files, :limit_targets, as: :reversed_boolean)
65
66
  read_tool_option(opts, tool_name, :file_filter, :file_filter, as: :string)
66
67
  end
67
68
 
@@ -101,8 +102,6 @@ module QuietQuality
101
102
  when :reversed_boolean then validate_boolean(name, value)
102
103
  when :symbol then validate_symbol(name, value, from: from)
103
104
  when :string then validate_string(name, value)
104
- else
105
- fail ArgumentError, "validate_value does not handle type #{as}"
106
105
  end
107
106
  end
108
107
 
@@ -134,8 +133,6 @@ module QuietQuality
134
133
  when :reversed_boolean then !value
135
134
  when :string then value.to_s
136
135
  when :symbol then value.to_sym
137
- else
138
- fail ArgumentError, "coerce_value does not handle type #{as}"
139
136
  end
140
137
  end
141
138
  end
@@ -7,7 +7,7 @@ module QuietQuality
7
7
  end
8
8
 
9
9
  def execute!
10
- fail NoMethodError, "execute! should be implemented by the subclass of BaseExecutor"
10
+ pipelines.none?(&:failure?)
11
11
  end
12
12
 
13
13
  def outcomes
@@ -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
@@ -31,16 +39,6 @@ module QuietQuality
31
39
  Pipeline.new(tool_options: topts, changed_files: changed_files)
32
40
  end
33
41
  end
34
-
35
- def pipeline_by_tool
36
- @_pipeline_by_tool ||= pipelines
37
- .map { |p| [p.tool_name, p] }
38
- .to_h
39
- end
40
-
41
- def pipeline_for(tool)
42
- pipeline_by_tool.fetch(tool.to_sym)
43
- end
44
42
  end
45
43
  end
46
44
  end
@@ -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