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
data/lib/gergich/cli.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "shellwords"
|
2
4
|
require "English"
|
3
5
|
|
@@ -38,7 +40,7 @@ def help_command(commands)
|
|
38
40
|
end
|
39
41
|
},
|
40
42
|
help: -> {
|
41
|
-
indentation = commands.keys.map(&:size).
|
43
|
+
indentation = commands.keys.map(&:size).max
|
42
44
|
commands_help = commands
|
43
45
|
.to_a
|
44
46
|
.sort_by(&:first)
|
@@ -73,7 +75,7 @@ def run_app(commands)
|
|
73
75
|
run_command(action)
|
74
76
|
rescue GergichError
|
75
77
|
error $ERROR_INFO.message
|
76
|
-
rescue
|
78
|
+
rescue StandardError
|
77
79
|
error "Unhandled exception: #{$ERROR_INFO}\n#{$ERROR_INFO.backtrace.join("\n")}"
|
78
80
|
end
|
79
81
|
else
|
data/lib/gergich/cli/gergich.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "../cli"
|
4
4
|
require_relative "../../gergich"
|
@@ -14,11 +14,11 @@ CI_TEST_ARGS = {
|
|
14
14
|
],
|
15
15
|
"label" => ["Code-Review", 1],
|
16
16
|
"message" => ["this is a test"],
|
17
|
-
"capture" => ["rubocop", "echo
|
18
|
-
bin/gergich:47:8: C: Prefer double-quoted strings
|
19
|
-
if ENV['DEBUG']
|
20
|
-
|
21
|
-
OUTPUT
|
17
|
+
"capture" => ["rubocop", format("echo %<output>s", output: Shellwords.escape(<<~OUTPUT))]
|
18
|
+
bin/gergich:47:8: C: Prefer double-quoted strings
|
19
|
+
if ENV['DEBUG']
|
20
|
+
^^^^^^^
|
21
|
+
OUTPUT
|
22
22
|
}.freeze
|
23
23
|
|
24
24
|
def run_ci_test!(all_commands)
|
@@ -27,13 +27,14 @@ def run_ci_test!(all_commands)
|
|
27
27
|
commands_to_test << "status" # put it at the end, so we maximize the stuff it tests
|
28
28
|
|
29
29
|
commands = commands_to_test.map { |command| [command, CI_TEST_ARGS[command] || []] }
|
30
|
-
commands.concat
|
30
|
+
commands.concat(all_commands.map { |command| ["help", [command]] })
|
31
31
|
|
32
32
|
# after running our test commands, reset and publish frd:
|
33
33
|
commands << ["reset"]
|
34
|
-
|
35
|
-
|
36
|
-
commands << ["label", ["
|
34
|
+
# Note that while gergich should always be able to vote on these labels, he may be used to
|
35
|
+
# vone on other branches depending on project usage and project-specific permissions
|
36
|
+
commands << ["label", ["Lint-Review", 1]]
|
37
|
+
commands << ["label", ["Code-Review", 1]] # Not all projects have Lint-Review
|
37
38
|
commands << ["message", ["\`gergich citest\` checks out :thumbsup: :mj:"]]
|
38
39
|
commands << ["publish"]
|
39
40
|
|
@@ -50,21 +51,21 @@ commands = {}
|
|
50
51
|
|
51
52
|
commands["reset"] = {
|
52
53
|
summary: "Clear out pending comments/labels/messages for this patchset",
|
53
|
-
action: ->
|
54
|
+
action: -> {
|
54
55
|
Gergich::Draft.new.reset!
|
55
56
|
},
|
56
57
|
help: -> {
|
57
|
-
|
58
|
-
gergich reset
|
58
|
+
<<~TEXT
|
59
|
+
gergich reset
|
59
60
|
|
60
|
-
Clear out the draft for this patchset. Useful for testing.
|
61
|
-
TEXT
|
61
|
+
Clear out the draft for this patchset. Useful for testing.
|
62
|
+
TEXT
|
62
63
|
}
|
63
64
|
}
|
64
65
|
|
65
66
|
commands["publish"] = {
|
66
67
|
summary: "Publish the draft for this patchset",
|
67
|
-
action: ->
|
68
|
+
action: -> {
|
68
69
|
if (data = Gergich::Review.new.publish!)
|
69
70
|
puts "Published #{data[:total_comments]} comments, set score to #{data[:score]}"
|
70
71
|
else
|
@@ -72,33 +73,33 @@ commands["publish"] = {
|
|
72
73
|
end
|
73
74
|
},
|
74
75
|
help: -> {
|
75
|
-
|
76
|
-
gergich publish
|
76
|
+
<<~TEXT
|
77
|
+
gergich publish
|
77
78
|
|
78
|
-
Publish all draft comments/labels/messages for this patchset. no-op if
|
79
|
-
there are none.
|
79
|
+
Publish all draft comments/labels/messages for this patchset. no-op if
|
80
|
+
there are none.
|
80
81
|
|
81
|
-
The cover message and Code-Review label (e.g. -2) are inferred from the
|
82
|
-
comments, but labels and messages may be manually set (via `gergich
|
83
|
-
message` and `gergich labels`)
|
84
|
-
TEXT
|
82
|
+
The cover message and Code-Review label (e.g. -2) are inferred from the
|
83
|
+
comments, but labels and messages may be manually set (via `gergich
|
84
|
+
message` and `gergich labels`)
|
85
|
+
TEXT
|
85
86
|
}
|
86
87
|
}
|
87
88
|
|
88
89
|
commands["status"] = {
|
89
90
|
summary: "Show the current draft for this patchset",
|
90
|
-
action: ->
|
91
|
+
action: -> {
|
91
92
|
Gergich::Review.new.status
|
92
93
|
},
|
93
94
|
help: -> {
|
94
|
-
|
95
|
-
gergich status
|
95
|
+
<<~TEXT
|
96
|
+
gergich status
|
96
97
|
|
97
|
-
Show the current draft for this patchset
|
98
|
+
Show the current draft for this patchset
|
98
99
|
|
99
|
-
Display any labels, cover messages and inline comments that will be set
|
100
|
-
as part of this review.
|
101
|
-
TEXT
|
100
|
+
Display any labels, cover messages and inline comments that will be set
|
101
|
+
as part of this review.
|
102
|
+
TEXT
|
102
103
|
}
|
103
104
|
}
|
104
105
|
|
@@ -107,8 +108,8 @@ commands["comment"] = {
|
|
107
108
|
action: ->(comment_data) {
|
108
109
|
comment_data = begin
|
109
110
|
JSON.parse(comment_data)
|
110
|
-
|
111
|
-
|
111
|
+
rescue JSON::ParserError
|
112
|
+
error("Unable to parse <comment_data> json", "comment")
|
112
113
|
end
|
113
114
|
comment_data = [comment_data] unless comment_data.is_a?(Array)
|
114
115
|
|
@@ -120,36 +121,36 @@ commands["comment"] = {
|
|
120
121
|
comment["severity"]
|
121
122
|
end
|
122
123
|
},
|
123
|
-
help: ->
|
124
|
-
|
125
|
-
gergich comment <comment_data>
|
126
|
-
|
127
|
-
<comment_data> is a JSON object (or array of objects). Each comment object
|
128
|
-
should have the following properties:
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
Note that a cover message and Code-Review score will be inferred from the
|
143
|
-
most severe comment.
|
144
|
-
|
145
|
-
Examples
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
TEXT
|
124
|
+
help: -> {
|
125
|
+
<<~TEXT
|
126
|
+
gergich comment <comment_data>
|
127
|
+
|
128
|
+
<comment_data> is a JSON object (or array of objects). Each comment object
|
129
|
+
should have the following properties:
|
130
|
+
path - the relative file path, e.g. "app/models/user.rb"
|
131
|
+
position - either a number (line) or an object (range). If an object,
|
132
|
+
must have the following numeric properties:
|
133
|
+
* start_line
|
134
|
+
* start_character
|
135
|
+
* end_line
|
136
|
+
* end_character
|
137
|
+
message - the text of the comment
|
138
|
+
severity - "info"|"warn"|"error" - this will automatically prefix the
|
139
|
+
comment (e.g. "[ERROR] message here"), and the most severe
|
140
|
+
comment will be used to determine the overall Code-Review
|
141
|
+
score (0, -1, or -2 respectively)
|
142
|
+
|
143
|
+
Note that a cover message and Code-Review score will be inferred from the
|
144
|
+
most severe comment.
|
145
|
+
|
146
|
+
Examples
|
147
|
+
gergich comment '{"path":"foo.rb","position":3,"severity":"error",
|
148
|
+
"message":"ಠ_ಠ"}'
|
149
|
+
gergich comment '{"path":"bar.rb","severity":"warn",
|
150
|
+
"position":{"start_line":3,"start_character":5,...},
|
151
|
+
"message":"¯\\_(ツ)_/¯"}'
|
152
|
+
gergich comment '[{"path":"baz.rb",...}, {...}, {...}]'
|
153
|
+
TEXT
|
153
154
|
}
|
154
155
|
}
|
155
156
|
|
@@ -159,13 +160,13 @@ commands["message"] = {
|
|
159
160
|
draft = Gergich::Draft.new
|
160
161
|
draft.add_message message
|
161
162
|
},
|
162
|
-
help: ->
|
163
|
-
|
164
|
-
gergich message <message>
|
163
|
+
help: -> {
|
164
|
+
<<~TEXT
|
165
|
+
gergich message <message>
|
165
166
|
|
166
|
-
<message> will be appended to existing cover messages (inferred or manually
|
167
|
-
added) for this patchset.
|
168
|
-
TEXT
|
167
|
+
<message> will be appended to existing cover messages (inferred or manually
|
168
|
+
added) for this patchset.
|
169
|
+
TEXT
|
169
170
|
}
|
170
171
|
}
|
171
172
|
|
@@ -174,16 +175,16 @@ commands["label"] = {
|
|
174
175
|
action: ->(label, score) {
|
175
176
|
Gergich::Draft.new.add_label label, score
|
176
177
|
},
|
177
|
-
help: ->
|
178
|
-
|
179
|
-
gergich label <label> <score>
|
178
|
+
help: -> {
|
179
|
+
<<~TEXT
|
180
|
+
gergich label <label> <score>
|
180
181
|
|
181
|
-
Add a draft label to this patchset. If the same label is set multiple
|
182
|
-
times, the lowest score will win.
|
182
|
+
Add a draft label to this patchset. If the same label is set multiple
|
183
|
+
times, the lowest score will win.
|
183
184
|
|
184
|
-
<label> - a valid label (e.g. "Code-Review")
|
185
|
-
<score> - a valid score (e.g. -1)
|
186
|
-
TEXT
|
185
|
+
<label> - a valid label (e.g. "Code-Review")
|
186
|
+
<score> - a valid score (e.g. -1)
|
187
|
+
TEXT
|
187
188
|
}
|
188
189
|
}
|
189
190
|
|
@@ -194,63 +195,63 @@ commands["capture"] = {
|
|
194
195
|
status, = Gergich::Capture.run(format, command)
|
195
196
|
exit status
|
196
197
|
},
|
197
|
-
help: ->
|
198
|
-
|
199
|
-
gergich capture <format> <command>
|
198
|
+
help: -> {
|
199
|
+
<<~TEXT
|
200
|
+
gergich capture <format> <command>
|
200
201
|
|
201
|
-
For common linting formats, `gergich capture` can be used to automatically
|
202
|
-
do `gergich comment` calls so you don't have to wire it up yourself.
|
202
|
+
For common linting formats, `gergich capture` can be used to automatically
|
203
|
+
do `gergich comment` calls so you don't have to wire it up yourself.
|
203
204
|
|
204
|
-
<format> - One of the following:
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
205
|
+
<format> - One of the following:
|
206
|
+
* brakeman
|
207
|
+
* rubocop
|
208
|
+
* eslint
|
209
|
+
* i18nliner
|
210
|
+
* flake8
|
211
|
+
* stylelint
|
212
|
+
* custom:<path>:<class_name> - file path and ruby
|
213
|
+
class_name of a custom formatter.
|
213
214
|
|
214
|
-
<command> - The command to run whose output conforms to <format>.
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
215
|
+
<command> - The command to run whose output conforms to <format>.
|
216
|
+
Output from the command will still go to STDOUT, and
|
217
|
+
Gergich will preserve its exit status.
|
218
|
+
If command is "-", Gergich will simply read from STDIN
|
219
|
+
and the exit status will always be 0.
|
219
220
|
|
220
|
-
Examples:
|
221
|
-
|
221
|
+
Examples:
|
222
|
+
gergich capture rubocop "bundle exec rubocop"
|
222
223
|
|
223
|
-
|
224
|
+
gergich capture eslint eslint
|
224
225
|
|
225
|
-
|
226
|
+
gergich capture i18nliner "rake i18nliner:check"
|
226
227
|
|
227
|
-
|
228
|
+
gergich capture custom:./gergich/xss:Gergich::XSS "node script/xsslint"
|
228
229
|
|
229
|
-
|
230
|
-
|
231
|
-
TEXT
|
230
|
+
docker-compose run --rm web eslint | gergich capture eslint -
|
231
|
+
# you might be interested in $PIPESTATUS[0]
|
232
|
+
TEXT
|
232
233
|
}
|
233
234
|
}
|
234
235
|
|
235
236
|
commands["citest"] = {
|
236
237
|
summary: "Do a full gergich test based on the current commit",
|
237
|
-
action: ->
|
238
|
+
action: -> {
|
238
239
|
# automagically test any new command that comes along
|
239
240
|
run_ci_test!(commands.keys)
|
240
241
|
},
|
241
|
-
help: ->
|
242
|
-
|
243
|
-
gergich citest
|
244
|
-
|
245
|
-
You shouldn't need to run this locally, it runs on jenkins. It does the
|
246
|
-
following:
|
247
|
-
|
248
|
-
1. runs all the gergich commands (w/ dummy data)
|
249
|
-
2. ensure all `help` commands work
|
250
|
-
3. ensures gergich status is correct
|
251
|
-
4. resets
|
252
|
-
5. posts an actual +1
|
253
|
-
6. publishes
|
242
|
+
help: -> {
|
243
|
+
<<~TEXT
|
244
|
+
gergich citest
|
245
|
+
|
246
|
+
You shouldn't need to run this locally, it runs on jenkins. It does the
|
247
|
+
following:
|
248
|
+
|
249
|
+
1. runs all the gergich commands (w/ dummy data)
|
250
|
+
2. ensure all `help` commands work
|
251
|
+
3. ensures gergich status is correct
|
252
|
+
4. resets
|
253
|
+
5. posts an actual +1
|
254
|
+
6. publishes
|
254
255
|
TEXT
|
255
256
|
}
|
256
257
|
}
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative "../cli"
|
2
4
|
|
3
5
|
ENV["GERGICH_USER"] = ENV.fetch("MASTER_BOUNCER_USER", "master_bouncer")
|
@@ -19,7 +21,7 @@ def potentially_mergeable_changes
|
|
19
21
|
"branch:master" \
|
20
22
|
"&o=CURRENT_REVISION"
|
21
23
|
changes = Gergich::API.get(url)
|
22
|
-
changes.
|
24
|
+
changes.reject { |c| c["subject"] =~ /\Awip($|\W)/i }
|
23
25
|
end
|
24
26
|
|
25
27
|
def maybe_bounce_commit!(commit)
|
@@ -67,22 +69,22 @@ commands = {}
|
|
67
69
|
|
68
70
|
commands["check"] = {
|
69
71
|
summary: "Check the current commit's age",
|
70
|
-
action: ->
|
72
|
+
action: -> {
|
71
73
|
maybe_bounce_commit! Gergich::Commit.new
|
72
74
|
},
|
73
|
-
help: ->
|
74
|
-
|
75
|
-
master_bouncer check
|
75
|
+
help: -> {
|
76
|
+
<<~TEXT
|
77
|
+
master_bouncer check
|
76
78
|
|
77
|
-
Check the current commit's age, and bounce it if it's too old (-1 or -2,
|
78
|
-
depending on the threshold)
|
79
|
-
TEXT
|
79
|
+
Check the current commit's age, and bounce it if it's too old (-1 or -2,
|
80
|
+
depending on the threshold)
|
81
|
+
TEXT
|
80
82
|
}
|
81
83
|
}
|
82
84
|
|
83
85
|
commands["check_all"] = {
|
84
86
|
summary: "Check the age of all potentially mergeable changes",
|
85
|
-
action: ->
|
87
|
+
action: -> {
|
86
88
|
Gergich.git("fetch")
|
87
89
|
gerrit_host = ENV["GERRIT_HOST"] || error("No GERRIT_HOST set")
|
88
90
|
|
@@ -102,12 +104,12 @@ commands["check_all"] = {
|
|
102
104
|
sleep 1
|
103
105
|
end
|
104
106
|
},
|
105
|
-
help: ->
|
106
|
-
|
107
|
-
master_bouncer check_all
|
107
|
+
help: -> {
|
108
|
+
<<~TEXT
|
109
|
+
master_bouncer check_all
|
108
110
|
|
109
|
-
Check all open Verified+1 patchsets and bounce any that are too old.
|
110
|
-
TEXT
|
111
|
+
Check all open Verified+1 patchsets and bounce any that are too old.
|
112
|
+
TEXT
|
111
113
|
}
|
112
114
|
}
|
113
115
|
|
@@ -1,24 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative "../../support/capture_shared_examples"
|
2
4
|
|
3
5
|
RSpec.describe Gergich::Capture::AndroidlintCapture do
|
4
|
-
# rubocop:disable
|
6
|
+
# rubocop:disable Layout/LineLength
|
5
7
|
let(:rtl_hardcoded) { 'Consider adding android:drawableStart="@drawable/a_media" to better support right-to-left layouts [RtlHardcoded]' }
|
6
8
|
let(:rtl_enabled) { "The project references RTL attributes, but does not explicitly enable or disable RTL support with android:supportsRtl in the manifest [RtlEnabled]" }
|
7
9
|
let(:lint_error) { 'No .class files were found in project "0.0.2", so none of the classfile based checks could be run. Does the project need to be built first? [LintError]' }
|
8
10
|
let(:unused_quantity) { 'For language "fr" (French) the following quantities are not relevant: few, zero [UnusedQuantity]' }
|
11
|
+
# rubocop:enable Layout/LineLength
|
9
12
|
let(:output) do
|
10
|
-
|
11
|
-
/path/to/some.xml:27: Warning: #{rtl_hardcoded}
|
12
|
-
|
13
|
-
|
13
|
+
<<~OUTPUT
|
14
|
+
/path/to/some.xml:27: Warning: #{rtl_hardcoded}
|
15
|
+
android:drawableLeft="@drawable/ic_cv_media"/>
|
16
|
+
~~~~~~~~~~~~~~~~~~~~
|
14
17
|
|
15
|
-
/path/to/AndroidManifest.xml: Warning: #{rtl_enabled}
|
18
|
+
/path/to/AndroidManifest.xml: Warning: #{rtl_enabled}
|
16
19
|
|
17
|
-
/path/to/library/0.0.2: Error: #{lint_error}
|
20
|
+
/path/to/library/0.0.2: Error: #{lint_error}
|
18
21
|
|
19
|
-
/path/to/values.xml:5: Warning: #{unused_quantity}
|
20
|
-
|
21
|
-
|
22
|
+
/path/to/values.xml:5: Warning: #{unused_quantity}
|
23
|
+
<plurals name="number">
|
24
|
+
^
|
22
25
|
|
23
26
|
OUTPUT
|
24
27
|
end
|
@@ -28,7 +31,9 @@ RSpec.describe Gergich::Capture::AndroidlintCapture do
|
|
28
31
|
{
|
29
32
|
path: "/path/to/some.xml",
|
30
33
|
position: 27,
|
34
|
+
# rubocop:disable Layout/LineLength
|
31
35
|
message: "[androidlint] #{rtl_hardcoded}\n\n android:drawableLeft=\"@drawable/ic_cv_media\"/>\n ~~~~~~~~~~~~~~~~~~~~",
|
36
|
+
# rubocop:enable Layout/LineLength
|
32
37
|
severity: "warn"
|
33
38
|
},
|
34
39
|
{
|