gergich 0.2.2 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +2 -2
  3. data/README.md +0 -1
  4. data/bin/check_coverage +2 -1
  5. data/bin/gergich +1 -0
  6. data/bin/master_bouncer +1 -1
  7. data/bin/run_tests.sh +8 -43
  8. data/lib/gergich.rb +20 -24
  9. data/lib/gergich/capture.rb +8 -4
  10. data/lib/gergich/capture/androidlint_capture.rb +5 -1
  11. data/lib/gergich/capture/brakeman_capture.rb +4 -2
  12. data/lib/gergich/capture/eslint_capture.rb +2 -0
  13. data/lib/gergich/capture/flake8_capture.rb +2 -0
  14. data/lib/gergich/capture/i18nliner_capture.rb +2 -0
  15. data/lib/gergich/capture/rubocop_capture.rb +2 -0
  16. data/lib/gergich/capture/shellcheck_capture.rb +2 -0
  17. data/lib/gergich/capture/stylelint_capture.rb +8 -5
  18. data/lib/gergich/capture/swiftlint_capture.rb +5 -1
  19. data/lib/gergich/cli.rb +4 -2
  20. data/lib/gergich/cli/gergich.rb +119 -118
  21. data/lib/gergich/cli/master_bouncer.rb +16 -14
  22. data/spec/gergich/capture/androidlint_capture_spec.rb +15 -10
  23. data/spec/gergich/capture/brakeman_capture_spec.rb +43 -41
  24. data/spec/gergich/capture/custom_capture_spec.rb +4 -2
  25. data/spec/gergich/capture/eslint_capture_spec.rb +6 -4
  26. data/spec/gergich/capture/flake8_capture_spec.rb +4 -2
  27. data/spec/gergich/capture/i18nliner_capture_spec.rb +6 -4
  28. data/spec/gergich/capture/rubocop_capture_spec.rb +24 -22
  29. data/spec/gergich/capture/shellcheck_capture_spec.rb +45 -43
  30. data/spec/gergich/capture/stylelint_capture_spec.rb +16 -7
  31. data/spec/gergich/capture/swiftlint_capture_spec.rb +8 -5
  32. data/spec/gergich/capture_spec.rb +6 -4
  33. data/spec/gergich_spec.rb +58 -4
  34. data/spec/spec_helper.rb +2 -0
  35. data/spec/support/capture_shared_examples.rb +2 -0
  36. metadata +39 -40
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: dc6e202255732bf23a6df2c75e18250897744e99
4
- data.tar.gz: 1942f228ae335675740fa7ae626a92204ce7e7a5
2
+ SHA256:
3
+ metadata.gz: 9f81bdfdf24629e16b6af3486388f244e73aa43721febfb8e959af52cba8efbe
4
+ data.tar.gz: f13157b630762c91edfcdeb342aac476f62af114cad2edbb40b18a0814060b7e
5
5
  SHA512:
6
- metadata.gz: 88c9113246e2e97fda409346032d88de432c910c360ec72c28b368e33e9bb34606663f2588601ef2be3e4bdab18644eac6498cc94749404dc941a373b0865238
7
- data.tar.gz: d3266350ace476809d572e191275a7a18dab184d48954f8e3407680f8ec2b3e5840524c3c626d98ea6f7bd970ca21fa7a46108c67894d6e75153de430ef9ce28
6
+ metadata.gz: 81e0ce4960ce3338693de120d489d7e045ddac8c57c5c20331ca6f6e30daf68dc162dcadd158e0c39838f0b3a6a6bc906f837ab9e55da0e68185b9ed999c3516
7
+ data.tar.gz: '0687478859b3e572d5f0e888c93cd8d5e0965cc546f9dd6a688a1050d4eb4174b189214dd9f3f581f9c3df8392bcb903b81101de76f85b53c3c387a72b6523e8'
data/LICENSE CHANGED
@@ -13,8 +13,8 @@ included in all copies or substantial portions of the Software.
13
13
 
14
14
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
17
- NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
18
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
19
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # Gergich
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/gergich.svg)](https://rubygems.org/gems/gergich)
4
- [![Dependency Status](https://gemnasium.com/badges/a2946a7849cd94f5ec0f4c3173a968f4.svg)](https://gemnasium.com/cc6fb44edee9fcf855cec82d3b6aed0f)
5
4
  [![Build Status](https://travis-ci.org/instructure/gergich.svg?branch=master)](https://travis-ci.org/instructure/gergich)
6
5
 
7
6
  Gergich is a command-line tool (and ruby lib) for easily posting comments
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "simplecov"
4
5
 
5
6
  SimpleCov.command_name "check_coverage"
6
- SimpleCov.minimum_coverage 90
7
+ SimpleCov.minimum_coverage 85
7
8
  SimpleCov.at_exit { SimpleCov.result.format! }
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  if ENV["COVERAGE"]
4
5
  require "simplecov"
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- # encoding=utf-8
2
+ # frozen_string_literal: true
3
3
 
4
4
  if ENV["COVERAGE"]
5
5
  require "simplecov"
@@ -2,56 +2,21 @@
2
2
 
3
3
  set -e
4
4
 
5
- function run_command {
6
- echo -e "\n[$@] STARTING $(date)"
7
- last_status=0
8
- "$@" || last_status=$?
9
- if [[ $last_status == 0 ]]; then
10
- echo -e "[$@] \033[32mOK\033[0m"
11
- else
12
- echo -e "[$@] \033[31mFAILED!\033[0m"
13
- fi
14
- echo -e "[$@] FINISHED $(date)\n"
5
+ bundle exec rubocop --fail-level autocorrect
15
6
 
16
- [[ $last_status == 0 ]] || clean_up_and_exit
17
- }
7
+ ruby -v | egrep "^ruby 2\.4" && export COVERAGE=1
18
8
 
19
- function clean_up_and_exit {
20
- end_timestamp=$(date +%s)
21
- duration=$((end_timestamp-start_timestamp))
22
-
23
- if [[ $last_status != 0 ]]; then
24
- echo -e "\033[31mBUILD FAILED\033[0m in $duration seconds\n"
25
- else
26
- echo "BUILD PASSED in $duration seconds"
27
- fi
28
- exit $last_status
29
- }
30
-
31
- start_timestamp=$(date +%s)
32
-
33
- run_command bundle exec rubocop
34
-
35
- export COVERAGE=1
36
-
37
- run_command bundle exec rspec
9
+ bundle exec rspec
38
10
 
39
11
  # these actually hit gerrit; only run them in CI land (you can do it
40
12
  # locally if you set all the docker-compose env vars)
41
13
  if [[ "$GERRIT_PATCHSET_REVISION" ]]; then
42
- run_command bin/gergich citest
43
- run_command bin/master_bouncer check
44
- DRY_RUN=1 run_command bin/master_bouncer check_all
14
+ bundle exec bin/gergich citest
15
+ bundle exec bin/master_bouncer check
16
+ DRY_RUN=1 bundle exec bin/master_bouncer check_all
45
17
  # ensure gergich works without .git directories
46
18
  rm -rf .git
47
- run_command bin/gergich status
48
- fi
49
-
50
- run_command bin/check_coverage
51
-
52
- if [[ "$GEMNASIUM_TOKEN" && "$GEMNASIUM_ENABLED" ]]; then
53
- # Push our dependency specification files to gemnasium for analysis
54
- run_command gemnasium dependency_files push -f=gergich.gemspec
19
+ bundle exec bin/gergich status
55
20
  fi
56
21
 
57
- clean_up_and_exit
22
+ bundle exec bin/check_coverage
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "erb"
2
4
  require "sqlite3"
3
5
  require "json"
@@ -105,18 +107,9 @@ module Gergich
105
107
 
106
108
  API.post(generate_url, generate_payload)
107
109
 
108
- # because why not
109
- if change_name?
110
- API.put("/accounts/self/name", { name: whats_his_face }.to_json)
111
- end
112
-
113
110
  review_info
114
111
  end
115
112
 
116
- def change_name?
117
- ENV["GERGICH_CHANGE_NAME"] != "0" && rand < 0.01 && GERGICH_USER == "gergich"
118
- end
119
-
120
113
  def anything_to_publish?
121
114
  !review_info[:comments].empty? ||
122
115
  !review_info[:cover_message_parts].empty? ||
@@ -202,7 +195,7 @@ module Gergich
202
195
  end
203
196
 
204
197
  def my_messages
205
- @messages ||= API.get("/changes/#{commit.change_id}/detail")["messages"]
198
+ @my_messages ||= API.get("/changes/#{commit.change_id}/detail")["messages"]
206
199
  .select { |message| message["author"] && message["author"]["username"] == GERGICH_USER }
207
200
  end
208
201
 
@@ -227,6 +220,7 @@ module Gergich
227
220
  # then grab the comment's revision.
228
221
  def current_label_revision
229
222
  return nil if my_messages.empty?
223
+
230
224
  @current_label_revision ||= begin
231
225
  date = current_label_date
232
226
  comment_for_current_label = my_messages.find { |message| message["date"] == date } ||
@@ -254,17 +248,13 @@ module Gergich
254
248
  score = upcoming_score
255
249
  prefix_parts = []
256
250
  prefix_parts << unique_comment_prefix if multi_build_setup?
257
- prefix_parts << score if score < 0
251
+ prefix_parts << score if score.negative?
258
252
  prefix_parts.join(":")
259
253
  # [].join(":") => ""
260
254
  # [-2].join(":") => "-2"
261
255
  # ["some build prefix", -2].join(":") => "some build prefix:-2"
262
256
  end
263
257
 
264
- def whats_his_face
265
- "#{%w[Garry Larry Terry Jerry].sample} Gergich (Bot)"
266
- end
267
-
268
258
  def review_info
269
259
  @review_info ||= draft.info
270
260
  end
@@ -306,7 +296,8 @@ module Gergich
306
296
  ret = HTTParty.send(method, url, options).body
307
297
  return ret if options[:raw]
308
298
 
309
- if ret.sub!(/\A\)\]\}'\n/, "") && ret =~ /\A("|\[|\{)/
299
+ ret = ret.sub(/\A\)\]\}'\n/, "")
300
+ if ret && ret =~ /\A("|\[|\{)/
310
301
  JSON.parse("[#{ret}]")[0] # array hack so we can parse a string literal
311
302
  elsif ret =~ /Not found: (?<change_id>.*)/i
312
303
  raise("Cannot find Change-Id: #{Regexp.last_match[:change_id]} at #{url}.\n"\
@@ -321,7 +312,7 @@ module Gergich
321
312
  end
322
313
 
323
314
  def base_uri
324
- @base_url ||= \
315
+ @base_uri ||= \
325
316
  ENV["GERRIT_BASE_URL"] ||
326
317
  ENV.key?("GERRIT_HOST") && "https://#{ENV['GERRIT_HOST']}" ||
327
318
  raise(GergichError, "need to set GERRIT_BASE_URL or GERRIT_HOST")
@@ -372,7 +363,9 @@ module Gergich
372
363
  end
373
364
 
374
365
  def db_file
375
- @db_file ||= File.expand_path("/tmp/#{GERGICH_USER}-#{commit.revision_id}.sqlite3")
366
+ @db_file ||= File.expand_path(
367
+ "#{ENV.fetch('GERGICH_DB_PATH', '/tmp')}/#{GERGICH_USER}-#{commit.revision_id}.sqlite3"
368
+ )
376
369
  end
377
370
 
378
371
  def db
@@ -452,13 +445,16 @@ module Gergich
452
445
  # severe comment will be used to determine the overall
453
446
  # Code-Review score (0, -1, or -2 respectively)
454
447
  def add_comment(path, position, message, severity)
448
+ stripped_path = path.strip
449
+
455
450
  raise GergichError, "invalid position `#{position}`" unless valid_position?(position)
451
+
456
452
  position = position.to_json if position.is_a?(Hash)
457
453
  raise GergichError, "invalid severity `#{severity}`" unless SEVERITY_MAP.key?(severity)
458
454
  raise GergichError, "no message specified" unless message.is_a?(String) && !message.empty?
459
455
 
460
456
  db.execute "INSERT INTO comments (path, position, message, severity) VALUES (?, ?, ?, ?)",
461
- [path, position, message, severity]
457
+ [stripped_path, position, message, severity]
462
458
  end
463
459
 
464
460
  POSITION_KEYS = %w[end_character end_line start_character start_line].freeze
@@ -478,7 +474,7 @@ module Gergich
478
474
  labels[row["name"]] = row["score"]
479
475
  end
480
476
  score = min_comment_score
481
- labels[GERGICH_REVIEW_LABEL] = score if score < 0 && score < labels[GERGICH_REVIEW_LABEL]
477
+ labels[GERGICH_REVIEW_LABEL] = score if score < [0, labels[GERGICH_REVIEW_LABEL]].min
482
478
  labels
483
479
  end
484
480
  end
@@ -535,21 +531,21 @@ module Gergich
535
531
  end
536
532
 
537
533
  def orphaned_message
538
- message = "NOTE: I couldn't create inline comments for everything. " \
534
+ messages = ["NOTE: I couldn't create inline comments for everything. " \
539
535
  "Although this isn't technically part of your commit, you " \
540
536
  "should still check it out (i.e. side effects or auto-" \
541
- "generated from stuff you *did* change):"
537
+ "generated from stuff you *did* change):"]
542
538
 
543
539
  other_comments.each do |file|
544
540
  file.comments.each do |position, comments|
545
541
  comments.each do |comment|
546
542
  line = position.is_a?(Integer) ? position : position["start_line"]
547
- message << "\n\n#{file.path}:#{line}: #{comment}"
543
+ messages << "#{file.path}:#{line}: #{comment}"
548
544
  end
549
545
  end
550
546
  end
551
547
 
552
- message
548
+ messages.join("\n\n")
553
549
  end
554
550
 
555
551
  def cover_message_parts
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "../gergich"
2
4
  require "English"
3
5
 
@@ -10,7 +12,7 @@ module Gergich
10
12
  end
11
13
 
12
14
  def self.normalize_captor_class_name(subclass)
13
- name = subclass.name
15
+ name = subclass.name.dup
14
16
  # borrowed from AS underscore, since we may not have it
15
17
  name.gsub!(/.*::|Capture\z/, "")
16
18
  name.gsub!(/([A-Z\d]+)([A-Z][a-z])/, "\\1_\\2")
@@ -35,6 +37,7 @@ module Gergich
35
37
  if add_comments
36
38
  comments.each do |comment|
37
39
  next if skip_paths.any? { |path| comment[:path].start_with?(path) }
40
+
38
41
  draft.add_comment comment[:path], comment[:position],
39
42
  comment[:message], comment[:severity]
40
43
  end
@@ -67,12 +70,12 @@ module Gergich
67
70
  end
68
71
 
69
72
  def wiretap(io, suppress_output)
70
- output = ""
73
+ output = []
71
74
  io.each do |line|
72
75
  $stdout.puts line unless suppress_output
73
76
  output << line
74
77
  end
75
- output
78
+ output.join("")
76
79
  end
77
80
 
78
81
  def load_captor(format)
@@ -81,6 +84,7 @@ module Gergich
81
84
  else
82
85
  captor = captors[format]
83
86
  raise GergichError, "Unrecognized format `#{format}`" unless captor
87
+
84
88
  captor
85
89
  end
86
90
  end
@@ -105,4 +109,4 @@ module Gergich
105
109
  end
106
110
  end
107
111
 
108
- Dir[File.dirname(__FILE__) + "/capture/*.rb"].each { |file| require file }
112
+ Dir[File.dirname(__FILE__) + "/capture/*.rb"].sort.each { |file| require file }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gergich
2
4
  module Capture
3
5
  class AndroidlintCapture < BaseCapture
@@ -9,7 +11,7 @@ module Gergich
9
11
  }.freeze
10
12
 
11
13
  def run(output)
12
- # rubocop:disable Metrics/LineLength
14
+ # rubocop:disable Layout/LineLength
13
15
  #
14
16
  # Example:
15
17
  # /path/to/some.xml:27: Warning: Consider adding android:drawableStart="@drawable/a_media" to better support right-to-left layouts [RtlHardcoded]
@@ -23,6 +25,8 @@ module Gergich
23
25
  # /path/to/values.xml:5: Warning: For language "fr" (French) the following quantities are not relevant: few, zero [UnusedQuantity]
24
26
  # <plurals name="number">
25
27
  # ^
28
+ #
29
+ # rubocop:enable Layout/LineLength
26
30
  pattern = /
27
31
  ^([^:\n]+):(\d+)?:?\s(\w+):\s(.*?)\n
28
32
  ([^\n]+\n
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gergich
2
4
  module Capture
3
5
  class BrakemanCapture < BaseCapture
4
6
  # Map Brakeman "confidence level" to severity.
5
7
  # http://brakemanscanner.org/docs/confidence/
6
8
  SEVERITY_MAP = {
7
- "Weak" => "warn",
9
+ "Weak" => "warn",
8
10
  "Medium" => "warn",
9
- "High" => "error"
11
+ "High" => "error"
10
12
  }.freeze
11
13
 
12
14
  def run(output)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gergich
2
4
  module Capture
3
5
  class EslintCapture < BaseCapture
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gergich
2
4
  module Capture
3
5
  class Flake8Capture < BaseCapture
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gergich
2
4
  module Capture
3
5
  class I18nlinerCapture < BaseCapture
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gergich
2
4
  module Capture
3
5
  class RubocopCapture < BaseCapture
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "json"
2
4
 
3
5
  module Gergich
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gergich
2
4
  module Capture
3
5
  class StylelintCapture < BaseCapture
@@ -14,23 +16,24 @@ module Gergich
14
16
  # 2:15 ✖ Unexpected invalid hex color "#2D3B4" color-no-invalid-hex
15
17
  # 30:15 ⚠ Expected "#2d3b4a" to be "#2D3B4A" color-hex-case
16
18
 
17
- MESSAGE_PREFIX = "[stylelint]".freeze
19
+ MESSAGE_PREFIX = "[stylelint]"
18
20
 
19
21
  SEVERITY_MAP = {
20
22
  "✖" => "error",
21
- "⚠" => "warn"
23
+ "⚠" => "warn",
24
+ "ℹ" => "info"
22
25
  }.freeze
23
26
 
24
27
  # example file line:
25
28
  # app/stylesheets/base/_variables.scss
26
- FILE_PATH_PATTERN = /([^\n]+)\n/
29
+ FILE_PATH_PATTERN = /([^\n]+)\n/.freeze
27
30
 
28
31
  # example error line:
29
32
  # 1:15 ✖ Unexpected invalid hex color "#2D3B4" color-no-invalid-hex
30
- ERROR_PATTERN = /^\s+(\d+):\d+\s+(✖|⚠)\s+(.*?)\s\s+[^\n]+\n/
33
+ ERROR_PATTERN = /^\s+(\d+):\d+\s+(✖|⚠|ℹ)\s+(.*?)\s\s+[^\n]+\n/.freeze
31
34
  # rubocop:enable Style/AsciiComments
32
35
 
33
- PATTERN = /#{FILE_PATH_PATTERN}((#{ERROR_PATTERN})+)/
36
+ PATTERN = /#{FILE_PATH_PATTERN}((#{ERROR_PATTERN})+)/.freeze
34
37
 
35
38
  def run(output)
36
39
  output.scan(PATTERN).map { |file, errors|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gergich
2
4
  module Capture
3
5
  class SwiftlintCapture < BaseCapture
@@ -10,11 +12,13 @@ module Gergich
10
12
  }.freeze
11
13
 
12
14
  def run(output)
13
- # rubocop:disable Metrics/LineLength
15
+ # rubocop:disable Layout/LineLength
14
16
  #
15
17
  # Example:
16
18
  # /path/to/My.swift:13:22: warning: Colon Violation: Colons should be next to the identifier when specifying a type. (colon)
17
19
  # /path/to/Fail.swift:80: warning: Line Length Violation: Line should be 100 characters or less: currently 108 characters (line_length)
20
+ #
21
+ # rubocop:enable Layout/LineLength
18
22
  pattern = /
19
23
  ^([^:\n]+):(\d+)(?::\d+)?:\s(\w+):\s(.*?)\n
20
24
  /mx