@@ -2,9 +2,11 @@ use std::sync::Arc;
2
2
3
3
use crate :: bors:: command:: Approver ;
4
4
use crate :: bors:: event:: PullRequestEdited ;
5
+ use crate :: bors:: event:: PullRequestPushed ;
5
6
use crate :: bors:: handlers:: labels:: handle_label_trigger;
6
7
use crate :: bors:: Comment ;
7
8
use crate :: bors:: RepositoryState ;
9
+ use crate :: github:: CommitSha ;
8
10
use crate :: github:: GithubUser ;
9
11
use crate :: github:: LabelTrigger ;
10
12
use crate :: github:: PullRequest ;
@@ -67,7 +69,7 @@ pub(super) async fn handle_pull_request_edited(
67
69
let pr_model = db
68
70
. get_or_create_pull_request ( repo_state. repository ( ) , payload. pull_request . number )
69
71
. await ?;
70
- if pr_model. approved_by . is_none ( ) {
72
+ if ! pr_model. is_approved ( ) {
71
73
return Ok ( ( ) ) ;
72
74
}
73
75
@@ -77,6 +79,26 @@ pub(super) async fn handle_pull_request_edited(
77
79
notify_of_edited_pr ( & repo_state, pr_number, & payload. pull_request . base . name ) . await
78
80
}
79
81
82
+ pub ( super ) async fn handle_push_to_pull_request (
83
+ repo_state : Arc < RepositoryState > ,
84
+ db : Arc < PgDbClient > ,
85
+ payload : PullRequestPushed ,
86
+ ) -> anyhow:: Result < ( ) > {
87
+ let pr = & payload. pull_request ;
88
+ let pr_model = db
89
+ . get_or_create_pull_request ( repo_state. repository ( ) , pr. number )
90
+ . await ?;
91
+
92
+ if !pr_model. is_approved ( ) {
93
+ return Ok ( ( ) ) ;
94
+ }
95
+
96
+ let pr_number = pr_model. number ;
97
+ db. unapprove ( repo_state. repository ( ) , pr_number) . await ?;
98
+ handle_label_trigger ( & repo_state, pr_number, LabelTrigger :: Unapproved ) . await ?;
99
+ notify_of_pushed_pr ( & repo_state, pr_number, pr. head . sha . clone ( ) ) . await
100
+ }
101
+
80
102
fn sufficient_approve_permission ( repo : Arc < RepositoryState > , author : & GithubUser ) -> bool {
81
103
repo. permissions
82
104
. load ( )
@@ -176,27 +198,37 @@ PR will need to be re-approved."#,
176
198
. await
177
199
}
178
200
201
+ async fn notify_of_pushed_pr (
202
+ repo : & RepositoryState ,
203
+ pr_number : PullRequestNumber ,
204
+ head_sha : CommitSha ,
205
+ ) -> anyhow:: Result < ( ) > {
206
+ repo. client
207
+ . post_comment (
208
+ pr_number,
209
+ Comment :: new ( format ! (
210
+ r#":warning: A new commit `{}` was pushed to the branch, the
211
+ PR will need to be re-approved."# ,
212
+ head_sha
213
+ ) ) ,
214
+ )
215
+ . await
216
+ }
217
+
179
218
#[ cfg( test) ]
180
219
mod tests {
181
220
use crate :: {
182
221
github:: PullRequestNumber ,
183
222
tests:: mocks:: {
184
- default_pr_number, default_repo_name, BorsBuilder , BorsTester , Permissions ,
223
+ default_pr_number, default_repo_name, run_test , BorsBuilder , BorsTester , Permissions ,
185
224
PullRequestChangeEvent , User , World ,
186
225
} ,
187
226
} ;
188
227
189
228
#[ sqlx:: test]
190
229
async fn default_approve ( pool : sqlx:: PgPool ) {
191
- let world = World :: default ( ) ;
192
- world. default_repo ( ) . lock ( ) . set_config (
193
- r#"
194
- [labels]
195
- approve = ["+approved"]
196
- "# ,
197
- ) ;
198
230
BorsBuilder :: new ( pool)
199
- . world ( world )
231
+ . world ( create_world_with_approve_config ( ) )
200
232
. run_test ( |mut tester| async {
201
233
tester. post_comment ( "@bors r+" ) . await ?;
202
234
assert_eq ! (
@@ -221,15 +253,8 @@ approve = ["+approved"]
221
253
222
254
#[ sqlx:: test]
223
255
async fn approve_on_behalf ( pool : sqlx:: PgPool ) {
224
- let world = World :: default ( ) ;
225
- world. default_repo ( ) . lock ( ) . set_config (
226
- r#"
227
- [labels]
228
- approve = ["+approved"]
229
- "# ,
230
- ) ;
231
256
BorsBuilder :: new ( pool)
232
- . world ( world )
257
+ . world ( create_world_with_approve_config ( ) )
233
258
. run_test ( |mut tester| async {
234
259
let approve_user = "user1" ;
235
260
tester
@@ -269,15 +294,8 @@ approve = ["+approved"]
269
294
270
295
#[ sqlx:: test]
271
296
async fn unapprove ( pool : sqlx:: PgPool ) {
272
- let world = World :: default ( ) ;
273
- world. default_repo ( ) . lock ( ) . set_config (
274
- r#"
275
- [labels]
276
- approve = ["+approved"]
277
- "# ,
278
- ) ;
279
297
BorsBuilder :: new ( pool)
280
- . world ( world )
298
+ . world ( create_world_with_approve_config ( ) )
281
299
. run_test ( |mut tester| async {
282
300
tester. post_comment ( "@bors r+" ) . await ?;
283
301
assert_eq ! (
@@ -307,15 +325,8 @@ approve = ["+approved"]
307
325
308
326
#[ sqlx:: test]
309
327
async fn unapprove_on_base_edited ( pool : sqlx:: PgPool ) {
310
- let world = World :: default ( ) ;
311
- world. default_repo ( ) . lock ( ) . set_config (
312
- r#"
313
- [labels]
314
- approve = ["+approved"]
315
- "# ,
316
- ) ;
317
328
BorsBuilder :: new ( pool)
318
- . world ( world )
329
+ . world ( create_world_with_approve_config ( ) )
319
330
. run_test ( |mut tester| async {
320
331
tester. post_comment ( "@bors r+" ) . await ?;
321
332
assert_eq ! (
@@ -348,15 +359,8 @@ PR will need to be re-approved."#,
348
359
349
360
#[ sqlx:: test]
350
361
async fn edit_pr_do_nothing_when_base_not_edited ( pool : sqlx:: PgPool ) {
351
- let world = World :: default ( ) ;
352
- world. default_repo ( ) . lock ( ) . set_config (
353
- r#"
354
- [labels]
355
- approve = ["+approved"]
356
- "# ,
357
- ) ;
358
362
BorsBuilder :: new ( pool)
359
- . world ( world )
363
+ . world ( create_world_with_approve_config ( ) )
360
364
. run_test ( |mut tester| async {
361
365
tester. post_comment ( "@bors r+" ) . await ?;
362
366
assert_eq ! (
@@ -389,31 +393,74 @@ approve = ["+approved"]
389
393
390
394
#[ sqlx:: test]
391
395
async fn edit_pr_do_nothing_when_not_approved ( pool : sqlx:: PgPool ) {
392
- let world = World :: default ( ) ;
393
- world. default_repo ( ) . lock ( ) . set_config (
394
- r#"
395
- [labels]
396
- approve = ["+approved"]
397
- "# ,
398
- ) ;
396
+ run_test ( pool, |mut tester| async {
397
+ tester
398
+ . edit_pull_request (
399
+ default_pr_number ( ) ,
400
+ PullRequestChangeEvent {
401
+ from_base_sha : Some ( "main-sha" . to_string ( ) ) ,
402
+ } ,
403
+ )
404
+ . await ?;
405
+
406
+ // No comment should be posted
407
+ Ok ( tester)
408
+ } )
409
+ . await ;
410
+ }
411
+
412
+ #[ sqlx:: test]
413
+ async fn unapprove_on_push ( pool : sqlx:: PgPool ) {
399
414
BorsBuilder :: new ( pool)
400
- . world ( world )
415
+ . world ( create_world_with_approve_config ( ) )
401
416
. run_test ( |mut tester| async {
402
- tester
403
- . edit_pull_request (
417
+ tester. post_comment ( "@bors r+" ) . await ?;
418
+ assert_eq ! (
419
+ tester. get_comment( ) . await ?,
420
+ format!(
421
+ "Commit pr-{}-sha has been approved by `{}`" ,
404
422
default_pr_number( ) ,
405
- PullRequestChangeEvent {
406
- from_base_sha : Some ( "main-sha" . to_string ( ) ) ,
407
- } ,
408
- )
409
- . await ?;
423
+ User :: default_user( ) . name
424
+ ) ,
425
+ ) ;
426
+ tester. push_to_pull_request ( default_pr_number ( ) ) . await ?;
410
427
411
- // No comment should be posted
428
+ assert_eq ! (
429
+ tester. get_comment( ) . await ?,
430
+ format!(
431
+ r#":warning: A new commit `pr-{}-sha` was pushed to the branch, the
432
+ PR will need to be re-approved."# ,
433
+ default_pr_number( )
434
+ )
435
+ ) ;
436
+ check_pr_unapproved ( & tester, default_pr_number ( ) . into ( ) ) . await ;
412
437
Ok ( tester)
413
438
} )
414
439
. await ;
415
440
}
416
441
442
+ #[ sqlx:: test]
443
+ async fn push_to_pr_do_nothing_when_not_approved ( pool : sqlx:: PgPool ) {
444
+ run_test ( pool, |mut tester| async {
445
+ tester. push_to_pull_request ( default_pr_number ( ) ) . await ?;
446
+
447
+ // No comment should be posted
448
+ Ok ( tester)
449
+ } )
450
+ . await ;
451
+ }
452
+
453
+ fn create_world_with_approve_config ( ) -> World {
454
+ let world = World :: default ( ) ;
455
+ world. default_repo ( ) . lock ( ) . set_config (
456
+ r#"
457
+ [labels]
458
+ approve = ["+approved"]
459
+ "# ,
460
+ ) ;
461
+ world
462
+ }
463
+
417
464
async fn check_pr_approved_by (
418
465
tester : & BorsTester ,
419
466
pr_number : PullRequestNumber ,
0 commit comments