gergich 0.2.2 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/LICENSE +2 -2
- data/README.md +0 -1
- data/bin/check_coverage +2 -1
- data/bin/gergich +1 -0
- data/bin/master_bouncer +1 -1
- data/bin/run_tests.sh +8 -43
- data/lib/gergich.rb +20 -24
- data/lib/gergich/capture.rb +8 -4
- data/lib/gergich/capture/androidlint_capture.rb +5 -1
- data/lib/gergich/capture/brakeman_capture.rb +4 -2
- data/lib/gergich/capture/eslint_capture.rb +2 -0
- data/lib/gergich/capture/flake8_capture.rb +2 -0
- data/lib/gergich/capture/i18nliner_capture.rb +2 -0
- data/lib/gergich/capture/rubocop_capture.rb +2 -0
- data/lib/gergich/capture/shellcheck_capture.rb +2 -0
- data/lib/gergich/capture/stylelint_capture.rb +8 -5
- data/lib/gergich/capture/swiftlint_capture.rb +5 -1
- data/lib/gergich/cli.rb +4 -2
- data/lib/gergich/cli/gergich.rb +119 -118
- data/lib/gergich/cli/master_bouncer.rb +16 -14
- data/spec/gergich/capture/androidlint_capture_spec.rb +15 -10
- data/spec/gergich/capture/brakeman_capture_spec.rb +43 -41
- data/spec/gergich/capture/custom_capture_spec.rb +4 -2
- data/spec/gergich/capture/eslint_capture_spec.rb +6 -4
- data/spec/gergich/capture/flake8_capture_spec.rb +4 -2
- data/spec/gergich/capture/i18nliner_capture_spec.rb +6 -4
- data/spec/gergich/capture/rubocop_capture_spec.rb +24 -22
- data/spec/gergich/capture/shellcheck_capture_spec.rb +45 -43
- data/spec/gergich/capture/stylelint_capture_spec.rb +16 -7
- data/spec/gergich/capture/swiftlint_capture_spec.rb +8 -5
- data/spec/gergich/capture_spec.rb +6 -4
- data/spec/gergich_spec.rb +58 -4
- data/spec/spec_helper.rb +2 -0
- data/spec/support/capture_shared_examples.rb +2 -0
- metadata +39 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9f81bdfdf24629e16b6af3486388f244e73aa43721febfb8e959af52cba8efbe
|
4
|
+
data.tar.gz: f13157b630762c91edfcdeb342aac476f62af114cad2edbb40b18a0814060b7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
17
|
-
NONINFRINGEMENT. IN NO EVENT
|
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
|
data/bin/check_coverage
CHANGED
data/bin/gergich
CHANGED
data/bin/master_bouncer
CHANGED
data/bin/run_tests.sh
CHANGED
@@ -2,56 +2,21 @@
|
|
2
2
|
|
3
3
|
set -e
|
4
4
|
|
5
|
-
|
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
|
-
|
17
|
-
}
|
7
|
+
ruby -v | egrep "^ruby 2\.4" && export COVERAGE=1
|
18
8
|
|
19
|
-
|
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
|
-
|
43
|
-
|
44
|
-
DRY_RUN=1
|
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
|
-
|
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
|
-
|
22
|
+
bundle exec bin/check_coverage
|
data/lib/gergich.rb
CHANGED
@@ -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
|
-
@
|
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
|
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
|
-
|
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
|
-
@
|
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(
|
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
|
-
[
|
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
|
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
|
-
|
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
|
-
|
543
|
+
messages << "#{file.path}:#{line}: #{comment}"
|
548
544
|
end
|
549
545
|
end
|
550
546
|
end
|
551
547
|
|
552
|
-
|
548
|
+
messages.join("\n\n")
|
553
549
|
end
|
554
550
|
|
555
551
|
def cover_message_parts
|
data/lib/gergich/capture.rb
CHANGED
@@ -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
|
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"
|
9
|
+
"Weak" => "warn",
|
8
10
|
"Medium" => "warn",
|
9
|
-
"High"
|
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 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]"
|
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+(
|
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
|
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
|