gergich 0.1.15 → 0.1.16

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 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