gergich 0.1.15 → 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef236cdff9358a9799255dcd4ce0683f89eca6d2
4
- data.tar.gz: 53cf76a1f2b0b490f03883f24b75b0d399b5e0fc
3
+ metadata.gz: 3c315c6166bed8d6a67b00c7356fce017a579139
4
+ data.tar.gz: 4c9825057793fa28ad9970a73ed2c68cae76f41a
5
5
  SHA512:
6
- metadata.gz: fee27eccf19e051b3972975e4531de705ec5a20e517410994d3cc36d088d1e1fb3b9f06fa1a581930b1345b08be3793c01b432bfbf89fd33f608ca519170dd10
7
- data.tar.gz: 90dc1b48b44b42504feb9d26b2aa0ebc39ff7acc7d2af648b818b0b1edcf71d8372ff1d0b2dca78c4ad27d6e7f08f15105f14a046ff08b0270e1338c4ed3bfb2
6
+ metadata.gz: 967db7d75cd976e2aa3ebe6413a9063eba44e7a418892c5565837f0a96b83ba449829fbfff57a9cf53b4bdc521357edf51025280135770e6f535b892c162db62
7
+ data.tar.gz: 7cf0feb214e120c58df7d33468bd27c156182f6f2e72fe142249258f64ca796a92b73fea2d277227e49b134166b56070788fc3ae870fb69bac94de0b2967b335
data/README.md CHANGED
@@ -22,12 +22,14 @@ Gergich publishes the review to Gerrit.
22
22
  ## Limitations
23
23
 
24
24
  Because everything is synchronized/stored in a local sqlite db, you
25
- should only call Gergich from a single box/build per patchset. Gergich
26
- does a check when publishing to ensure he hasn't already posted on this
27
- patchset before; if he has, publish will be a no-op. This protects
28
- against reposts (say, on a retrigger), but it does mean that you shouldn't
29
- have completely different builds posting Gergich comments on the same
30
- revision, unless you set up different credentials for each.
25
+ should only call Gergich from a single box/build per patchset unless you
26
+ have a unique `GERGICH_COMMENT_PREFIX` set for each box/build per patchset.
27
+ Gergich does a check when publishing to ensure he hasn't already posted on
28
+ this patchset before (w/ the same `GERGICH_COMMENT_PREFIX`); if he has,
29
+ publish will be a no-op. This protects against reposts (say, on a retrigger),
30
+ but it does mean that you shouldn't have completely different builds posting
31
+ Gergich comments on the same revision, unless you set up different
32
+ credentials for each.
31
33
 
32
34
  ## Installation
33
35
 
@@ -35,17 +35,21 @@ run_command bundle exec rubocop
35
35
  export COVERAGE=1
36
36
 
37
37
  run_command bundle exec rspec
38
- run_command bin/gergich citest
39
- run_command bin/master_bouncer check
40
- DRY_RUN=1 run_command bin/master_bouncer check_all
41
38
 
42
- # ensure gergich works without .git directories
43
- rm -rf .git
44
- run_command bin/gergich status
39
+ # these actually hit gerrit; only run them in CI land (you can do it
40
+ # locally if you set all the docker-compose env vars)
41
+ 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
45
+ # ensure gergich works without .git directories
46
+ rm -rf .git
47
+ run_command bin/gergich status
48
+ fi
45
49
 
46
50
  run_command bin/check_coverage
47
51
 
48
- if [[ $GEMNASIUM_TOKEN && $GEMNASIUM_ENABLED ]]; then
52
+ if [[ "$GEMNASIUM_TOKEN" && "$GEMNASIUM_ENABLED" ]]; then
49
53
  # Push our dependency specification files to gemnasium for analysis
50
54
  run_command gemnasium dependency_files push -f=gergich.gemspec
51
55
  fi
@@ -42,7 +42,10 @@ module Gergich
42
42
  change_id = ENV["GERRIT_CHANGE_ID"] \
43
43
  || raise(GergichError, "No .git directory, and GERRIT_CHANGE_ID not set")
44
44
  end
45
- { revision_id: revision_id, change_id: change_id }
45
+ project = ENV["GERRIT_PROJECT"]
46
+ branch = ENV["GERRIT_BRANCH"]
47
+
48
+ { revision_id: revision_id, change_id: change_id, project: project, branch: branch }
46
49
  end
47
50
  end
48
51
 
@@ -65,14 +68,22 @@ module Gergich
65
68
 
66
69
  def revision_number
67
70
  @revision_number ||= begin
71
+ patchset_number = ENV["GERRIT_PATCHSET_NUMBER"]
72
+ return patchset_number unless patchset_number.nil?
73
+
68
74
  gerrit_info = API.get("/changes/?q=#{change_id}&o=ALL_REVISIONS")[0]
69
75
  raise GergichError, "Gerrit patchset not found" unless gerrit_info
76
+
70
77
  gerrit_info["revisions"][revision_id]["_number"]
71
78
  end
72
79
  end
73
80
 
74
81
  def change_id
75
- info[:change_id]
82
+ if info[:project] && info[:branch]
83
+ "#{info[:project]}~#{info[:branch]}~#{info[:change_id]}"
84
+ else
85
+ info[:change_id]
86
+ end
76
87
  end
77
88
  end
78
89
 
@@ -87,10 +98,8 @@ module Gergich
87
98
  # Public: publish all draft comments/labels/messages
88
99
  def publish!(allow_repost = false)
89
100
  # only publish if we have something to say or if our last score was negative
90
- return unless anything_to_publish? || previous_score_negative?
101
+ return unless anything_to_publish?
91
102
 
92
- # TODO: rather than just bailing, fetch the comments and only post
93
- # ones that don't exist (if any)
94
103
  return if already_commented? && !allow_repost
95
104
 
96
105
  API.post(generate_url, generate_payload)
@@ -109,8 +118,24 @@ module Gergich
109
118
 
110
119
  def anything_to_publish?
111
120
  !review_info[:comments].empty? ||
112
- !review_info[:cover_message].empty? ||
113
- review_info[:labels].any? { |_, score| score != 0 }
121
+ !review_info[:cover_message_parts].empty? ||
122
+ new_score?
123
+ end
124
+
125
+ def new_score?
126
+ if current_label_is_for_current_revision?
127
+ review_info[:score] < current_score.to_i
128
+ else
129
+ true
130
+ end
131
+ end
132
+
133
+ def upcoming_score
134
+ if current_label_is_for_current_revision?
135
+ [current_score.to_i, review_info[:score]].min
136
+ else
137
+ review_info[:score]
138
+ end
114
139
  end
115
140
 
116
141
  # Public: show the current draft for this patchset
@@ -133,7 +158,7 @@ module Gergich
133
158
 
134
159
  puts
135
160
  puts "Cover Message:"
136
- puts review_info[:cover_message]
161
+ puts cover_message
137
162
 
138
163
  return if review_info[:comments].empty?
139
164
 
@@ -148,24 +173,28 @@ module Gergich
148
173
  end
149
174
  end
150
175
 
151
- def previous_score
152
- last_message = my_messages
153
- .sort_by { |message| message["date"] }
154
- .last
155
-
156
- text = last_message && last_message["message"] || ""
157
- text =~ /^-[12]/
158
-
159
- ($& || "").to_i
176
+ def multi_build_setup?
177
+ # convert to boolean if this variable exists or not
178
+ !ENV["GERGICH_COMMENT_PREFIX"].nil?
160
179
  end
161
180
 
162
- def previous_score_negative?
163
- previous_score < 0
181
+ def unique_comment_prefix
182
+ ENV["GERGICH_COMMENT_PREFIX"]
164
183
  end
165
184
 
166
185
  def already_commented?
186
+ if multi_build_setup?
187
+ my_messages_on_current_revision.any? do |message|
188
+ message["message"] =~ /^#{unique_comment_prefix}/
189
+ end
190
+ else
191
+ my_messages_on_current_revision.any?
192
+ end
193
+ end
194
+
195
+ def my_messages_on_current_revision
167
196
  revision_number = commit.revision_number
168
- my_messages.any? { |message| message["_revision_number"] == revision_number }
197
+ my_messages.select { |message| message["_revision_number"] == revision_number }
169
198
  end
170
199
 
171
200
  def my_messages
@@ -173,6 +202,61 @@ module Gergich
173
202
  .select { |message| message["author"] && message["author"]["username"] == GERGICH_USER }
174
203
  end
175
204
 
205
+ # currently, cover message only supports the GERGICH_REVIEW_LABEL.
206
+ # i.e., even if gergich has "Code-Review: -2"
207
+ def current_label
208
+ @current_label ||= begin
209
+ API.get("/changes/#{commit.change_id}/detail")["labels"]
210
+ .fetch(GERGICH_REVIEW_LABEL, {})
211
+ .fetch("all", [])
212
+ .select { |label| label["username"] == GERGICH_USER }
213
+ .first
214
+ end
215
+ end
216
+
217
+ def current_label_date
218
+ @current_label_date ||= current_label && current_label["date"]
219
+ end
220
+
221
+ # unfortunately, the revision is not a field in the label json.
222
+ # however, we can match the label timestamp w/ one of our comment timestamps,
223
+ # then grab the comment's revision.
224
+ def current_label_revision
225
+ @current_label_revision ||= begin
226
+ date = current_label_date
227
+ comment_for_current_label = my_messages.find { |message| message["date"] == date } ||
228
+ my_messages.last
229
+ comment_for_current_label["_revision_number"]
230
+ end
231
+ end
232
+
233
+ def current_label_is_for_current_revision?
234
+ return false unless current_label
235
+ current_label_revision == commit.revision_number
236
+ end
237
+
238
+ def current_score
239
+ current_label && current_label["value"] || 0
240
+ end
241
+
242
+ def cover_message
243
+ parts = review_info[:cover_message_parts]
244
+ prefix = cover_message_prefix
245
+ parts.unshift prefix if prefix != ""
246
+ parts.join("\n\n")
247
+ end
248
+
249
+ def cover_message_prefix
250
+ score = upcoming_score
251
+ prefix_parts = []
252
+ prefix_parts << unique_comment_prefix if multi_build_setup?
253
+ prefix_parts << score if score < 0
254
+ prefix_parts.join(":")
255
+ # [].join(":") => ""
256
+ # [-2].join(":") => "-2"
257
+ # ["some build prefix", -2].join(":") => "some build prefix:-2"
258
+ end
259
+
176
260
  def whats_his_face
177
261
  "#{%w[Garry Larry Terry Jerry].sample} Gergich (Bot)"
178
262
  end
@@ -187,7 +271,7 @@ module Gergich
187
271
 
188
272
  def generate_payload
189
273
  {
190
- message: review_info[:cover_message],
274
+ message: cover_message,
191
275
  labels: review_info[:labels],
192
276
  comments: review_info[:comments],
193
277
  # we don't want the post to fail if another
@@ -434,7 +518,7 @@ module Gergich
434
518
 
435
519
  {
436
520
  comments: comments,
437
- cover_message: cover_message,
521
+ cover_message_parts: cover_message_parts,
438
522
  total_comments: all_comments.map(&:count).inject(&:+),
439
523
  score: labels[GERGICH_REVIEW_LABEL],
440
524
  labels: labels
@@ -464,13 +548,10 @@ module Gergich
464
548
  message
465
549
  end
466
550
 
467
- def cover_message
468
- score = labels[GERGICH_REVIEW_LABEL]
551
+ def cover_message_parts
469
552
  parts = messages
470
- parts.unshift score.to_s if score < 0
471
-
472
553
  parts << orphaned_message unless other_comments.empty?
473
- parts.join("\n\n")
554
+ parts
474
555
  end
475
556
  end
476
557
 
@@ -42,15 +42,15 @@ def maybe_bounce_commit!(commit)
42
42
  end
43
43
 
44
44
  review = Gergich::Review.new(commit, draft)
45
- previous_score = review.previous_score
45
+ current_score = review.current_score
46
46
 
47
- puts "#{detail}, " + (score == previous_score ?
47
+ puts "#{detail}, " + (score == current_score ?
48
48
  "score still #{score}" :
49
- "changing score from #{previous_score} to #{score}")
49
+ "changing score from #{current_score} to #{score}")
50
50
 
51
51
  # since we run on a daily cron, we might be checking the same patchset
52
52
  # many times, so bail if nothing has changed
53
- return if score == previous_score
53
+ return if score == current_score
54
54
 
55
55
  draft.add_label MASTER_BOUNCER_REVIEW_LABEL, score
56
56
  draft.add_message message if message
@@ -8,6 +8,8 @@ RSpec.describe Gergich::API do
8
8
  end
9
9
 
10
10
  it "provides helpful error when Change-Id not found" do
11
+ # Get ride of CI_TEST_RUN environment variable so the api preforms normally
12
+ ENV["CI_TEST_RUN"] = nil
11
13
  expect { described_class.get("/a/changes/1234") }
12
14
  .to raise_error(/Cannot find Change-Id: 1234/)
13
15
  end
@@ -90,23 +92,29 @@ RSpec.describe Gergich::Draft do
90
92
  end
91
93
  end
92
94
 
93
- describe "[:cover_message]" do
94
- subject { super()[:cover_message] }
95
+ describe "[:cover_message_parts]" do
96
+ subject { super()[:cover_message_parts] }
97
+ let(:message_1) { "this is good" }
98
+ let(:message_2) { "loljk it's terrible" }
95
99
 
96
- it "includes the Code-Review score if negative" do
97
- draft.add_label "Code-Review", -1
98
- expect(subject).to match(/^-1/)
99
- end
100
+ it "includes explicitly added messages" do
101
+ draft.add_message message_1
102
+ draft.add_message message_2
100
103
 
101
- it "doesn't include the score if not negative" do
102
- draft.add_label "Code-Review", 0
103
- expect(subject).to_not match(/^0/)
104
+ expect(subject).to include(message_1)
105
+ expect(subject).to include(message_2)
104
106
  end
105
107
 
106
- it "includes explicitly added messages" do
107
- draft.add_message "this is good"
108
- draft.add_message "loljk it's terrible"
109
- expect(subject).to include("this is good\n\nloljk it's terrible")
108
+ context "orphaned file comments exist" do
109
+ let(:orphaned_comment) { "fix invalid" }
110
+
111
+ before :each do
112
+ draft.add_comment "invalid.rb", 1, orphaned_comment, "info"
113
+ end
114
+
115
+ it "includes orphan file message" do
116
+ expect(subject.first).to match(/#{orphaned_comment}/)
117
+ end
110
118
  end
111
119
  end
112
120
 
@@ -182,3 +190,133 @@ RSpec.describe Gergich::Draft do
182
190
  end
183
191
  end
184
192
  end
193
+
194
+ RSpec.describe Gergich::Review do
195
+ let!(:commit) do
196
+ double(
197
+ :commit,
198
+ files: [
199
+ "foo.rb",
200
+ "bar/baz.lol"
201
+ ],
202
+ revision_id: "test",
203
+ revision_number: 1,
204
+ change_id: "test"
205
+ )
206
+ end
207
+ let!(:draft) do
208
+ Gergich::Draft.new commit
209
+ end
210
+ let!(:review) { described_class.new(commit, draft) }
211
+
212
+ after do
213
+ draft.reset!
214
+ end
215
+
216
+ describe "#publish!" do
217
+ context "nothing to publish" do
218
+ before :each do
219
+ allow(review).to receive(:anything_to_publish?).and_return(false)
220
+ end
221
+
222
+ it "does nothing" do
223
+ expect(Gergich::API).not_to receive(:post)
224
+
225
+ review.publish!
226
+ end
227
+ end
228
+
229
+ context "something to publish" do
230
+ before :each do
231
+ allow(review).to receive(:anything_to_publish?).and_return(true)
232
+ allow(review).to receive(:already_commented?).and_return(false)
233
+ allow(review).to receive(:generate_payload).and_return({})
234
+ end
235
+
236
+ it "publishes via the api" do
237
+ expect(Gergich::API).to receive(:post)
238
+ allow(review).to receive(:change_name?).and_return(false)
239
+ review.publish!
240
+ end
241
+ end
242
+ end
243
+
244
+ describe "#anything_to_publish?" do
245
+ before :each do
246
+ allow(review).to receive(:current_label).and_return("BAHA")
247
+ allow(review).to receive(:current_label_revision).and_return("Revision trash stuff")
248
+ end
249
+
250
+ context "no comments exist" do
251
+ it "returns false" do
252
+ allow(review).to receive(:new_score?).and_return(false)
253
+ expect(review.anything_to_publish?).to eq false
254
+ end
255
+ end
256
+
257
+ context "comments exist" do
258
+ it "returns true" do
259
+ draft.info[:comments] = "Hello there this is a comment"
260
+ expect(review.anything_to_publish?).to eq true
261
+ end
262
+ end
263
+ end
264
+
265
+ describe "#new_score?" do
266
+ before :each do
267
+ allow(review).to receive(:current_label_is_for_current_revision?).and_return(true)
268
+ allow(review).to receive(:current_score).and_return(0)
269
+ end
270
+
271
+ context "score is the same" do
272
+ it "returns false" do
273
+ draft.info[:score] = 0
274
+ expect(review.new_score?).to eq false
275
+ end
276
+ end
277
+
278
+ context "score is different" do
279
+ it "returns true" do
280
+ draft.info[:score] = -1
281
+ expect(review.new_score?).to eq true
282
+ end
283
+ end
284
+ end
285
+
286
+ describe "#upcoming_score" do
287
+ context "current_label_is_for_current_revision? is true" do
288
+ it "Should return the min value of draft.info[:score] and current_score" do
289
+ allow(review).to receive(:current_label_is_for_current_revision?).and_return(true)
290
+ allow(review).to receive(:current_score).and_return(0)
291
+ review.draft.info[:score] = 1
292
+ expect(review.upcoming_score).to eq 0
293
+ end
294
+ end
295
+
296
+ context "current_label_is_for_current_revision? is false" do
297
+ it "Should return the value of draft.info[:score]" do
298
+ allow(review).to receive(:current_label_is_for_current_revision?).and_return(false)
299
+ review.draft.info[:score] = 1
300
+ expect(review.upcoming_score).to eq 1
301
+ end
302
+ end
303
+ end
304
+
305
+ describe "#cover_message" do
306
+ context "score is negative" do
307
+ it "includes the Code-Review score if negative" do
308
+ allow(review).to receive(:upcoming_score).and_return(-1)
309
+ review.draft.add_label "Code-Review", -1
310
+ expect(review.cover_message).to match(/^-1/)
311
+ end
312
+ end
313
+
314
+ context "score is non-negative" do
315
+ it "doesn't include the score if not negative" do
316
+ allow(review).to receive(:upcoming_score).and_return(0)
317
+ draft.add_label "Code-Review", 0
318
+ expect(subject).to_not match(/^0/)
319
+ end
320
+ end
321
+ end
322
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gergich
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.15
4
+ version: 0.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Jensen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-30 00:00:00.000000000 Z
11
+ date: 2017-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sqlite3
@@ -155,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
155
  version: '0'
156
156
  requirements: []
157
157
  rubyforge_project:
158
- rubygems_version: 2.5.1
158
+ rubygems_version: 2.6.11
159
159
  signing_key:
160
160
  specification_version: 4
161
161
  summary: Command-line tool for adding Gerrit comments