Class: Shiba::Review::CLI

Inherits:
Object
  • Object
show all
Defined in:
lib/shiba/review/cli.rb

Overview

Builds options for interacting with the reviewer via the command line. Automatically infers options from environment variables on CI.

Example: cli = CLI.new cli.valid?

> true

cli.options

> { “file” => ‘path/to/explain_log.json’ }

or

cli.valid?

> false

cli.failure

> “An error message with command line help.”

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(out: $stdout, err: $stderr, input: $stdin, options: nil) ⇒ CLI

Options may be provided for testing, in which case the option parser is skipped. When this happens, default options are also skipped.



31
32
33
34
35
36
37
38
39
# File 'lib/shiba/review/cli.rb', line 31

def initialize(out: $stdout, err: $stderr, input: $stdin, options: nil)
  @out = out
  @err = err
  @input = input
  @user_options = options || {}
  @errors = []
  parser.parse! if options.nil?
  @options = options || default_options.merge(@user_options)
end

Instance Attribute Details

#errObject (readonly)

Returns the value of attribute err.



27
28
29
# File 'lib/shiba/review/cli.rb', line 27

def err
  @err
end

#inputObject (readonly)

Returns the value of attribute input.



27
28
29
# File 'lib/shiba/review/cli.rb', line 27

def input
  @input
end

#outObject (readonly)

Returns the value of attribute out.



27
28
29
# File 'lib/shiba/review/cli.rb', line 27

def out
  @out
end

Instance Method Details

#failureObject



152
153
154
155
156
157
158
159
160
161
162
# File 'lib/shiba/review/cli.rb', line 152

def failure
  return nil if @errors.empty?

  message, help = @errors.first
  message += "\n"
  if help
    message += "\n#{parser}"
  end

  message
end

#optionsObject



132
133
134
# File 'lib/shiba/review/cli.rb', line 132

def options
  @options
end

#report_options(*keys) ⇒ Object



164
165
166
167
168
# File 'lib/shiba/review/cli.rb', line 164

def report_options(*keys)
  keys.each do |key|
    report("#{key}: #{options[key]}")
  end
end

#runObject

Generates the review, returning an exit status code. Prints to @out / @err, which default to STDOUT/STDERR.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/shiba/review/cli.rb', line 43

def run
  report_options("diff", "branch", "pull_request")

  if !valid?
    err.puts failure
    return 1
  end

  explain_diff = Shiba::Review::ExplainDiff.new(options["file"], options)

  problems = if explain_diff.diff_requested_by_user?
    result = explain_diff.result

    if result.message
      @err.puts result.message
    end

    if result.status == :pass
      return 0
    end

    explain_diff.problems
  else
    # Find all problem explains
    begin
      explains = explain_file.each_line.map { |json| JSON.parse(json) }
      bad = explains.select { |explain| explain["severity"] && explain["severity"] != 'none' }
      bad = bad.sort_by { |explain| explain["cost"] }
      bad.map { |explain| [ "#{explain["sql"]}:-2", explain ] }
    rescue Interrupt
      @err.puts "SIGINT: Canceled reading from STDIN. To read from an explain log, provide the --file option."
      exit 1
    end
  end

  if problems.empty?
    return 0
  end

  # Dedup
  problems.uniq! { |_,p| p["md5"] }

  # Output problem explains, this can be provided as a file to shiba review for comments.
  if options["raw"]
    pr = options["pull_request"]
    if pr
      problems.each { |_,problem| problem["pull_request"] = pr }
    end


    problems.each { |_,problem| @out.puts JSON.dump(problem) }
    return 2
  end

  # Generate comments for the problem queries
  repo_cmd = "git config --get remote.origin.url"
  repo_url = `#{repo_cmd}`.chomp

  if options["verbose"]
    @err.puts "#{repo_cmd}\t#{repo_url}"
  end

  if repo_url.empty?
    @err.puts "'#{Dir.pwd}' does not appear to be a git repo"
    return 1
  end

  reviewer = Shiba::Reviewer.new(repo_url, problems, options)

  if !options["submit"] || options["verbose"]
    reviewer.comments.each do |c|
      @out.puts "#{c[:path]}:#{c[:line]} (#{c[:position]})"
      @out.puts c[:body]
      @out.puts ""
    end
  end

  if options["submit"]
    if reviewer.repo_host.empty? || reviewer.repo_path.empty?
      @err.puts "Invalid repo url '#{repo_url}' from git config --get remote.origin.url"
      return 1
    end

    reviewer.submit
  end

  return 2
end

#valid?Boolean

Returns:

  • (Boolean)


136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/shiba/review/cli.rb', line 136

def valid?
  return false if @errors.any?

  validate_log_path
  #validate_git_repo if branch || options["submit"]

  if options["submit"]
    require_option("branch") if options["diff"].nil?
    require_option("token", description: "This can be read from the $SHIBA_GITHUB_TOKEN environment variable.")
    require_option("pull_request")
    error("Must specify either 'submit' or 'raw' output option, not both") if options["raw"]
  end

  @errors.empty?
end