gergich 0.2.2 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
[](https://rubygems.org/gems/gergich)
|
4
|
-
[](https://gemnasium.com/cc6fb44edee9fcf855cec82d3b6aed0f)
|
5
4
|
[](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
|