reviewer 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.flayignore +1 -0
  3. data/.github/workflows/main.yml +11 -3
  4. data/.gitignore +5 -0
  5. data/.reviewer.example.yml +27 -23
  6. data/.reviewer.yml +58 -5
  7. data/.rubocop.yml +2 -0
  8. data/.ruby-version +1 -1
  9. data/CHANGELOG.md +14 -0
  10. data/Gemfile +0 -3
  11. data/Gemfile.lock +54 -29
  12. data/README.md +5 -50
  13. data/exe/fmt +1 -1
  14. data/exe/rvw +1 -1
  15. data/lib/reviewer.rb +39 -26
  16. data/lib/reviewer/arguments.rb +25 -9
  17. data/lib/reviewer/arguments/files.rb +37 -5
  18. data/lib/reviewer/arguments/keywords.rb +23 -9
  19. data/lib/reviewer/arguments/tags.rb +26 -3
  20. data/lib/reviewer/batch.rb +64 -0
  21. data/lib/reviewer/command.rb +100 -0
  22. data/lib/reviewer/command/string.rb +72 -0
  23. data/lib/reviewer/command/string/env.rb +40 -0
  24. data/lib/reviewer/command/string/flags.rb +40 -0
  25. data/lib/reviewer/command/string/verbosity.rb +51 -0
  26. data/lib/reviewer/command/verbosity.rb +65 -0
  27. data/lib/reviewer/configuration.rb +24 -4
  28. data/lib/reviewer/conversions.rb +27 -0
  29. data/lib/reviewer/guidance.rb +73 -0
  30. data/lib/reviewer/history.rb +38 -0
  31. data/lib/reviewer/keywords.rb +9 -0
  32. data/lib/reviewer/keywords/git.rb +14 -0
  33. data/lib/reviewer/keywords/git/staged.rb +48 -0
  34. data/lib/reviewer/loader.rb +2 -3
  35. data/lib/reviewer/output.rb +92 -0
  36. data/lib/reviewer/printer.rb +25 -0
  37. data/lib/reviewer/runner.rb +43 -72
  38. data/lib/reviewer/runner/strategies/quiet.rb +90 -0
  39. data/lib/reviewer/runner/strategies/verbose.rb +63 -0
  40. data/lib/reviewer/shell.rb +58 -0
  41. data/lib/reviewer/shell/result.rb +69 -0
  42. data/lib/reviewer/shell/timer.rb +57 -0
  43. data/lib/reviewer/tool.rb +109 -40
  44. data/lib/reviewer/tool/settings.rb +18 -32
  45. data/lib/reviewer/tools.rb +38 -3
  46. data/lib/reviewer/version.rb +1 -1
  47. data/reviewer.gemspec +10 -2
  48. metadata +143 -16
  49. data/lib/reviewer/arguments/keywords/git.rb +0 -16
  50. data/lib/reviewer/arguments/keywords/git/staged.rb +0 -64
  51. data/lib/reviewer/logger.rb +0 -62
  52. data/lib/reviewer/tool/command.rb +0 -80
  53. data/lib/reviewer/tool/env.rb +0 -38
  54. data/lib/reviewer/tool/flags.rb +0 -38
  55. data/lib/reviewer/tool/verbosity.rb +0 -39
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'command/string'
4
+ require_relative 'command/verbosity'
5
+
6
+ module Reviewer
7
+ # The core funtionality to translate a tool, command type, and verbosity into a runnable command
8
+ class Command
9
+ include Conversions
10
+
11
+ SEED_SUBSTITUTION_VALUE = '$SEED'
12
+
13
+ attr_reader :tool, :type
14
+
15
+ # Creates an instance of the Command class to synthesize a command string using the tool,
16
+ # command type, and verbosity.
17
+ # @param tool [Tool, Symbol] a tool or tool key to use to look up the command and options
18
+ # @param type [Symbol] the desired command type (:install, :prepare, :review, :format)
19
+ # @param verbosity = Verbosity::TOTAL_SILENCE [Symbol] the desired verbosity for the command
20
+ #
21
+ # @return [Command] the intersection of a tool, command type, and verbosity
22
+ def initialize(tool, type, verbosity = Verbosity::TOTAL_SILENCE)
23
+ @tool = Tool(tool)
24
+ @type = type.to_sym
25
+ @verbosity = Verbosity(verbosity)
26
+ end
27
+
28
+ # The final command string with all of the conditions appled
29
+ #
30
+ # @return [String] the final, valid command string to run
31
+ def string
32
+ @string ||= seed_substitution? ? seeded_string : raw_string
33
+ end
34
+ alias to_s string
35
+
36
+ # Getter for @verbosity. Since the setter is custom, the getter needs to be explicitly declared.
37
+ # Otherwise, using `attr_accessor` and then overriding the setter muddies the waters.
38
+ #
39
+ # @return [Verbosity] the current verbosity setting for the command
40
+ def verbosity # rubocop:disable Style/TrivialAccessors
41
+ @verbosity
42
+ end
43
+
44
+ # Override verbosity assignment to clear the related memoized values when verbosity changes
45
+ # @param verbosity [Verbosity, Symbol] the desired verbosity for the command
46
+ #
47
+ # @return [Verbosity] the updated verbosity level for the command
48
+ def verbosity=(verbosity)
49
+ # Unmemoize string since the verbosity has been changed
50
+ @raw_string = nil
51
+ @string = nil
52
+
53
+ @verbosity = Verbosity(verbosity)
54
+ end
55
+
56
+ # Generates a seed that can be re-used across runs so that the results are consistent across
57
+ # related runs for tools that would otherwise change the seed automatically every run.
58
+ # Since not all tools will use the seed, there's no need to generate it in the initializer.
59
+ # Instead, it's memoized if it's used.
60
+ #
61
+ # @return [Integer] a random integer to pass to tools that use seeds
62
+ def seed
63
+ @seed ||= Random.rand(100_000)
64
+
65
+ # Store the seed for reference
66
+ Reviewer.history.set(tool.key, :last_seed, @seed)
67
+
68
+ @seed
69
+ end
70
+
71
+ private
72
+
73
+ # The raw command string before any substitutions. For example, since seeds need to remain
74
+ # consistent from one run to the next, they're
75
+ #
76
+ # @return [type] [description]
77
+ def raw_string
78
+ @raw_string ||= String.new(
79
+ type,
80
+ tool_settings: tool.settings,
81
+ verbosity: verbosity
82
+ ).to_s
83
+ end
84
+
85
+ # The version of the command with the SEED_SUBSTITUTION_VALUE replaced
86
+ #
87
+ # @return [String] the command string with the SEED_SUBSTITUTION_VALUE replaced
88
+ def seeded_string
89
+ # Update the string with the memoized seed value
90
+ raw_string.gsub(SEED_SUBSTITUTION_VALUE, seed.to_s)
91
+ end
92
+
93
+ # Determines if the raw command string has a SEED_SUBSTITUTION_VALUE that needs replacing
94
+ #
95
+ # @return [Boolean] true if the raw command string contains the SEED_SUBSTITUTION_VALUE
96
+ def seed_substitution?
97
+ raw_string.include?(SEED_SUBSTITUTION_VALUE)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'string/env'
4
+ require_relative 'string/flags'
5
+ require_relative 'string/verbosity'
6
+
7
+ module Reviewer
8
+ class Command
9
+ # Assembles tool tool_settings into a usable command string for the command type and verbosity
10
+ class String
11
+ include Conversions
12
+
13
+ attr_reader :command_type, :tool_settings, :verbosity
14
+
15
+ def initialize(command_type, tool_settings:, verbosity: nil)
16
+ @command_type = command_type
17
+ @tool_settings = tool_settings
18
+ @verbosity = Verbosity(verbosity)
19
+ end
20
+
21
+ def to_s
22
+ to_a
23
+ .map(&:strip) # Remove extra spaces on the components
24
+ .join(' ') # Merge the components
25
+ .strip # Strip extra spaces from the end result
26
+ end
27
+
28
+ def to_a
29
+ [
30
+ env_variables,
31
+ body,
32
+ flags,
33
+ verbosity_options
34
+ ].compact
35
+ end
36
+
37
+ def env_variables
38
+ Env.new(tool_settings.env).to_s
39
+ end
40
+
41
+ def body
42
+ tool_settings.commands.fetch(command_type)
43
+ end
44
+
45
+ def flags
46
+ # Flags to be used for `review` commands.
47
+ # 1. The `review` commands are the only commands that use flags
48
+ # 2. If no flags are configured, this won't do much
49
+ #
50
+ # Note: Since verbosity is handled separately, flags for 'quiet' are handled separately at a
51
+ # lower level by design and excluded from this check. They are not included with the other
52
+ # configured flags.
53
+ return nil unless flags?
54
+
55
+ Flags.new(tool_settings.flags).to_s
56
+ end
57
+
58
+ def verbosity_options
59
+ Verbosity.new(tool_settings.quiet_option, level: verbosity.level).to_s
60
+ end
61
+
62
+ private
63
+
64
+ # Determines whether the string needs flags added
65
+ #
66
+ # @return [Boolean] true if it's a review command and it has flags configured
67
+ def flags?
68
+ command_type == :review && tool_settings.flags.any?
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reviewer
4
+ class Command
5
+ class String
6
+ # Assembles tool environment variables into a single string or array
7
+ class Env
8
+ attr_reader :env_pairs
9
+
10
+ def initialize(env_pairs)
11
+ @env_pairs = env_pairs
12
+ end
13
+
14
+ def to_s
15
+ to_a.compact.join(' ')
16
+ end
17
+
18
+ def to_a
19
+ env = []
20
+ env_pairs.each { |key, value| env << env(key, value) }
21
+ env
22
+ end
23
+
24
+ private
25
+
26
+ def env(key, value)
27
+ return nil if key.to_s.strip.empty? || value.to_s.strip.empty?
28
+
29
+ value = needs_quotes?(value) ? "'#{value}'" : value
30
+
31
+ "#{key.to_s.strip.upcase}=#{value.to_s.strip}"
32
+ end
33
+
34
+ def needs_quotes?(value)
35
+ value.is_a?(::String) && value.include?(' ')
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reviewer
4
+ class Command
5
+ class String
6
+ # Assembles tool flag settings into a single string or array
7
+ class Flags
8
+ attr_reader :flag_pairs
9
+
10
+ def initialize(flag_pairs)
11
+ @flag_pairs = flag_pairs
12
+ end
13
+
14
+ def to_s
15
+ to_a.join(' ')
16
+ end
17
+
18
+ def to_a
19
+ flags = []
20
+ flag_pairs.each { |key, value| flags << flag(key, value) }
21
+ flags
22
+ end
23
+
24
+ private
25
+
26
+ def flag(key, value)
27
+ dash = key.to_s.size == 1 ? '-' : '--'
28
+
29
+ value = needs_quotes?(value) ? "'#{value}'" : value
30
+
31
+ "#{dash}#{key} #{value}".strip
32
+ end
33
+
34
+ def needs_quotes?(value)
35
+ value.is_a?(::String) && value.include?(' ')
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reviewer
4
+ class Command
5
+ class String
6
+ # Assembles tool settings and provided context for silencing output
7
+ class Verbosity
8
+ include ::Reviewer::Conversions
9
+
10
+ # Even when tools provide a quiet flag, not all treat it as complete silence. In order to
11
+ # ensure no extraneous noise is written to the console in some contexts, command output
12
+ # occasionally needs to be sent to dev null to ensure there's no clutter.
13
+ SEND_TO_DEV_NULL = '> /dev/null'
14
+
15
+ attr_reader :flag, :level
16
+
17
+ # A wrapper for translating a desired verbosity into the correct strings to append to the
18
+ # command so that any output is appropriately silenced for the context under which it's
19
+ # currently being executed.
20
+ # @param flag [String] the tool-level flag to be used for silencing output
21
+ # @param level: Reviewer::Command::Verbosity::TOOL_SILENCE [Symbol] the target level for
22
+ # silence for the the command
23
+ #
24
+ # @return [type] [description]
25
+ def initialize(flag, level: Reviewer::Command::Verbosity::TOOL_SILENCE)
26
+ @flag = String(flag)
27
+ @level = Verbosity(level)
28
+ end
29
+
30
+ # Converts the verbosity to a string that can be appended to a command string
31
+ #
32
+ # @return [String] the string to be appended to commands to ensure the correct verbosity
33
+ def to_s
34
+ to_a.map(&:strip).join(' ').strip
35
+ end
36
+
37
+ # Collection of values to be joined to ensure the correct verbosity
38
+ #
39
+ # @return [Array<String>] the values that need to be joined to ensure the correct verbosity
40
+ # for the context
41
+ def to_a
42
+ case level.key
43
+ when Reviewer::Command::Verbosity::TOTAL_SILENCE then [flag, SEND_TO_DEV_NULL].compact
44
+ when Reviewer::Command::Verbosity::TOOL_SILENCE then [flag].compact
45
+ else []
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reviewer
4
+ class Command
5
+ # Defines the possible verbosity options for running commands
6
+ class Verbosity
7
+ include Comparable
8
+
9
+ class InvalidLevelError < ArgumentError; end
10
+
11
+ # Use the quiet flag and send everything to dev/null.
12
+ # For some tools "quiet" means "less noisy" rather than truly silent.
13
+ # So in those cases, dev/null handles lingering noise.
14
+ TOTAL_SILENCE = :total_silence
15
+
16
+ # Just the quiet flag for the tool. Basically, let the tool determine the useful output.
17
+ TOOL_SILENCE = :tool_silence
18
+
19
+ # Let the output scroll for eternity
20
+ NO_SILENCE = :no_silence
21
+
22
+ # For validation and casting purposes
23
+ LEVELS = [
24
+ TOTAL_SILENCE,
25
+ TOOL_SILENCE,
26
+ NO_SILENCE
27
+ ].freeze
28
+
29
+ attr_accessor :level
30
+
31
+ # Create an instance of verbosity
32
+ # @param level [Symbol] one of the values of verbosity defined by LEVELS
33
+ #
34
+ # @return [Command::Verbosity] an instance of verbosity
35
+ def initialize(level)
36
+ @level = level.to_sym
37
+
38
+ verify_level!
39
+ end
40
+
41
+ def <=>(other)
42
+ level <=> other.level
43
+ end
44
+
45
+ def to_s
46
+ level.to_s
47
+ end
48
+
49
+ def to_i
50
+ LEVELS.index(level)
51
+ end
52
+
53
+ def to_sym
54
+ level
55
+ end
56
+ alias key to_sym
57
+
58
+ private
59
+
60
+ def verify_level!
61
+ raise InvalidLevelError, "Invalid Verbosity Level: '#{level}'" unless LEVELS.include?(level)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,16 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pathname'
4
+
3
5
  module Reviewer
4
6
  # Configuration values container for Reviewer
5
7
  class Configuration
6
8
  DEFAULT_PATH = Dir.pwd.freeze
7
- DEFAULT_FILE_NAME = '.reviewer.yml'
8
- DEFAULT_FILE = "#{DEFAULT_PATH}/#{DEFAULT_FILE_NAME}"
9
9
 
10
- attr_accessor :file
10
+ DEFAULT_CONFIG_FILE_NAME = '.reviewer.yml'
11
+ DEFAULT_HISTORY_FILE_NAME = '.reviewer_history.yml'
12
+
13
+ DEFAULT_CONFIG_LOCATION = "#{DEFAULT_PATH}/#{DEFAULT_CONFIG_FILE_NAME}"
14
+ DEFAULT_HISTORY_LOCATION = "#{DEFAULT_PATH}/#{DEFAULT_HISTORY_FILE_NAME}"
15
+
16
+ attr_accessor :file, :history_file, :printer
11
17
 
12
18
  def initialize
13
- @file = DEFAULT_FILE
19
+ @file = Pathname(DEFAULT_CONFIG_LOCATION)
20
+ @history_file = Pathname(DEFAULT_HISTORY_LOCATION)
21
+ @printer = ::Reviewer::Printer.new
22
+
23
+ # Future Configuration Options:
24
+ # - seed_substitution_value(string): Currently a constant of `$SEED` in Reviewer::Command, but
25
+ # may need to be configurable in case any command-line strings have other legitimate uses
26
+ # for the value such that it may need to be override. Ideally, it woudl be changed to3
27
+ # something obscure enough that conflicts wouldn't happen, but you never know
28
+ # - benchmark_everything(:dev, :optimize): Use the `time_up` gem to measure and show all the results
29
+ # for each tool and step to help identify and reduce bottlenecks. It would mainly be a flag
30
+ # for use in development, but it could also help folks troubleshoot their speed in finer
31
+ # detail than the standard Reviewer output
32
+ # - default_preparation_refresh(integer time): Right now, it's hard-coded at 6 hours, but that may require
33
+ # tuning for individual tools
14
34
  end
15
35
  end
16
36
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reviewer
4
+ # Conversion functions for special types in Reviewer
5
+ module Conversions
6
+ def Tool(value) # rubocop:disable Naming/MethodName
7
+ case value
8
+ when Tool then value
9
+ when Symbol then Tool.new(value)
10
+ when String then Tool.new(value.to_sym)
11
+ else raise TypeError, "Cannot convert #{value} to Tool"
12
+ end
13
+ end
14
+ module_function :Tool
15
+
16
+ def Verbosity(value) # rubocop:disable Naming/MethodName
17
+ case value
18
+ when Command::Verbosity then value
19
+ when Symbol then Command::Verbosity.new(value)
20
+ when String then Command::Verbosity.new(value.to_sym)
21
+ when Integer then Command::Verbosity.new(Command::Verbosity::LEVELS[value])
22
+ else raise TypeError, "Cannot convert #{value} to Verbosity"
23
+ end
24
+ end
25
+ module_function :Verbosity
26
+ end
27
+ end