16
16
17
17
package org .scalasteward .core .forge .gitlab
18
18
19
- import cats .{MonadThrow , Parallel }
20
19
import cats .syntax .all ._
20
+ import cats .{MonadThrow , Parallel }
21
21
import io .circe ._
22
22
import io .circe .generic .semiauto ._
23
23
import io .circe .syntax ._
24
24
import org .http4s .{Request , Status , Uri }
25
- import org .scalasteward .core .application .Config .{ForgeCfg , GitLabCfg }
25
+ import org .scalasteward .core .application .Config .{ForgeCfg , GitLabCfg , MergeRequestApprovalsConfig }
26
26
import org .scalasteward .core .data .Repo
27
27
import org .scalasteward .core .forge .ForgeApiAlg
28
28
import org .scalasteward .core .forge .data ._
29
29
import org .scalasteward .core .git .{Branch , Sha1 }
30
30
import org .scalasteward .core .util .uri .uriDecoder
31
- import org .scalasteward .core .util .{intellijThisImportIsUsed , HttpJsonClient , UnexpectedResponse }
31
+ import org .scalasteward .core .util .{
32
+ intellijThisImportIsUsed ,
33
+ HttpJsonClient ,
34
+ Nel ,
35
+ UnexpectedResponse
36
+ }
32
37
import org .typelevel .log4cats .Logger
33
38
34
39
final private [gitlab] case class ForkPayload (id : String , namespace : String )
@@ -44,6 +49,8 @@ final private[gitlab] case class MergeRequestPayload(
44
49
target_branch : Branch
45
50
)
46
51
52
+ final private [gitlab] case class UpdateMergeRequestLevelApprovalRulePayload (approvals_required : Int )
53
+
47
54
private [gitlab] object MergeRequestPayload {
48
55
def apply (
49
56
id : String ,
@@ -81,6 +88,11 @@ final private[gitlab] case class MergeRequestApprovalsOut(
81
88
approvalsRequired : Int
82
89
)
83
90
91
+ final private [gitlab] case class MergeRequestLevelApprovalRuleOut (
92
+ id : Int ,
93
+ name : String
94
+ )
95
+
84
96
final private [gitlab] case class CommitId (id : Sha1 ) {
85
97
val commitOut : CommitOut = CommitOut (id)
86
98
}
@@ -96,6 +108,8 @@ private[gitlab] object GitLabJsonCodec {
96
108
intellijThisImportIsUsed(uriDecoder)
97
109
98
110
implicit val forkPayloadEncoder : Encoder [ForkPayload ] = deriveEncoder
111
+ implicit val updateMergeRequestLevelApprovalRulePayloadEncoder
112
+ : Encoder [UpdateMergeRequestLevelApprovalRulePayload ] = deriveEncoder
99
113
implicit val userOutDecoder : Decoder [UserOut ] = Decoder .instance {
100
114
_.downField(" username" ).as[String ].map(UserOut (_))
101
115
}
@@ -134,6 +148,14 @@ private[gitlab] object GitLabJsonCodec {
134
148
} yield MergeRequestApprovalsOut (requiredReviewers)
135
149
}
136
150
151
+ implicit val mergeRequestLevelApprovalRuleOutDecoder : Decoder [MergeRequestLevelApprovalRuleOut ] =
152
+ Decoder .instance { c =>
153
+ for {
154
+ id <- c.downField(" id" ).as[Int ]
155
+ name <- c.downField(" string" ).as[String ]
156
+ } yield MergeRequestLevelApprovalRuleOut (id, name)
157
+ }
158
+
137
159
implicit val projectIdDecoder : Decoder [ProjectId ] = deriveDecoder
138
160
implicit val mergeRequestPayloadEncoder : Encoder [MergeRequestPayload ] = deriveEncoder
139
161
implicit val updateStateEncoder : Encoder [UpdateState ] = Encoder .instance { newState =>
@@ -216,7 +238,13 @@ final class GitLabApiAlg[F[_]: Parallel](
216
238
for {
217
239
mr <- mergeRequest
218
240
mrWithStatus <- waitForMergeRequestStatus(mr.iid)
219
- _ <- maybeSetReviewers(repo, mrWithStatus)
241
+ _ <- gitLabCfg.requiredReviewers match {
242
+ case Some (Right (approvalRules)) =>
243
+ setApprovalRules(repo, mrWithStatus, approvalRules)
244
+ case Some (Left (requiredReviewers)) =>
245
+ setReviewers(repo, mrWithStatus, requiredReviewers)
246
+ case None => F .unit
247
+ }
220
248
mergedUponSuccess <- mergePipelineUponSuccess(repo, mrWithStatus)
221
249
} yield mergedUponSuccess
222
250
}
@@ -246,29 +274,74 @@ final class GitLabApiAlg[F[_]: Parallel](
246
274
case mr =>
247
275
logger.info(s " Unable to automatically merge ${mr.webUrl}" ).map(_ => mr)
248
276
}
277
+ import cats .implicits ._
249
278
250
- private def maybeSetReviewers (repo : Repo , mrOut : MergeRequestOut ): F [MergeRequestOut ] =
251
- gitLabCfg.requiredReviewers match {
252
- case Some (requiredReviewers) =>
253
- for {
254
- _ <- logger.info(
255
- s " Setting number of required reviewers on ${mrOut.webUrl} to $requiredReviewers"
279
+ private def setReviewers (
280
+ repo : Repo ,
281
+ mrOut : MergeRequestOut ,
282
+ requiredReviewers : Int
283
+ ): F [MergeRequestOut ] =
284
+ for {
285
+ _ <- logger.info(
286
+ s " Setting number of required reviewers on ${mrOut.webUrl} to $requiredReviewers"
287
+ )
288
+ _ <-
289
+ client
290
+ .put[MergeRequestApprovalsOut ](
291
+ url.requiredApprovals(repo, mrOut.iid, requiredReviewers),
292
+ modify(repo)
256
293
)
257
- _ <-
258
- client
259
- .put[MergeRequestApprovalsOut ](
260
- url.requiredApprovals(repo, mrOut.iid, requiredReviewers),
261
- modify(repo)
262
- )
263
- .map(_ => ())
264
- .recoverWith { case UnexpectedResponse (_, _, _, status, body) =>
265
- logger
266
- .warn(s " Unexpected response setting required reviewers: $status: $body" )
267
- .as(())
268
- }
269
- } yield mrOut
270
- case None => F .pure(mrOut)
271
- }
294
+ .map(_ => ())
295
+ .recoverWith { case UnexpectedResponse (_, _, _, status, body) =>
296
+ logger
297
+ .warn(s " Unexpected response setting required reviewers: $status: $body" )
298
+ .as(())
299
+ }
300
+ } yield mrOut
301
+
302
+ private def setApprovalRules (
303
+ repo : Repo ,
304
+ mrOut : MergeRequestOut ,
305
+ approvalsConfig : Nel [MergeRequestApprovalsConfig ]
306
+ ): F [MergeRequestOut ] =
307
+ for {
308
+ _ <- logger.info(
309
+ s " Adjusting merge request approvals rules on ${mrOut.webUrl} with following config: $approvalsConfig"
310
+ )
311
+ activeApprovalRules <-
312
+ client
313
+ .get[List [MergeRequestLevelApprovalRuleOut ]](
314
+ url.listMergeRequestLevelApprovalRules(repo, mrOut.iid),
315
+ modify(repo)
316
+ )
317
+ .recoverWith { case UnexpectedResponse (_, _, _, status, body) =>
318
+ // ToDo better log
319
+ logger
320
+ .warn(s " Unexpected response setting required reviewers: $status: $body" )
321
+ .as(List .empty)
322
+ }
323
+ approvalRuleNamesFromConfig = approvalsConfig.map(_.approvalRuleName)
324
+ approvalRulesToUpdate = activeApprovalRules.intersect(approvalRuleNamesFromConfig.toList)
325
+ _ <-
326
+ approvalRulesToUpdate.map { mergeRequestApprovalConfig =>
327
+ client
328
+ .putWithBody[Unit , UpdateMergeRequestLevelApprovalRulePayload ](
329
+ url.updateMergeRequestLevelApprovalRule(
330
+ repo,
331
+ mrOut.iid,
332
+ mergeRequestApprovalConfig.id
333
+ ),
334
+ UpdateMergeRequestLevelApprovalRulePayload (mergeRequestApprovalConfig.id),
335
+ modify(repo)
336
+ )
337
+ .recoverWith { case UnexpectedResponse (_, _, _, status, body) =>
338
+ // ToDo better log
339
+ logger
340
+ .warn(s " Unexpected response setting required reviewers: $status: $body" )
341
+ .as(List .empty)
342
+ }
343
+ }.sequence
344
+ } yield mrOut
272
345
273
346
private def getUsernameToUserIdsMapping (repo : Repo , usernames : Set [String ]): F [Map [String , Int ]] =
274
347
usernames.toList
0 commit comments