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 )
@@ -45,6 +50,8 @@ final private[gitlab] case class MergeRequestPayload(
45
50
target_branch : Branch
46
51
)
47
52
53
+ final private [gitlab] case class UpdateMergeRequestLevelApprovalRulePayload (approvals_required : Int )
54
+
48
55
private [gitlab] object MergeRequestPayload {
49
56
def apply (
50
57
id : String ,
@@ -84,6 +91,11 @@ final private[gitlab] case class MergeRequestApprovalsOut(
84
91
approvalsRequired : Int
85
92
)
86
93
94
+ final private [gitlab] case class MergeRequestLevelApprovalRuleOut (
95
+ id : Int ,
96
+ name : String
97
+ )
98
+
87
99
final private [gitlab] case class CommitId (id : Sha1 ) {
88
100
val commitOut : CommitOut = CommitOut (id)
89
101
}
@@ -99,6 +111,8 @@ private[gitlab] object GitLabJsonCodec {
99
111
intellijThisImportIsUsed(uriDecoder)
100
112
101
113
implicit val forkPayloadEncoder : Encoder [ForkPayload ] = deriveEncoder
114
+ implicit val updateMergeRequestLevelApprovalRulePayloadEncoder
115
+ : Encoder [UpdateMergeRequestLevelApprovalRulePayload ] = deriveEncoder
102
116
implicit val userOutDecoder : Decoder [UserOut ] = Decoder .instance {
103
117
_.downField(" username" ).as[String ].map(UserOut (_))
104
118
}
@@ -137,6 +151,14 @@ private[gitlab] object GitLabJsonCodec {
137
151
} yield MergeRequestApprovalsOut (requiredReviewers)
138
152
}
139
153
154
+ implicit val mergeRequestLevelApprovalRuleOutDecoder : Decoder [MergeRequestLevelApprovalRuleOut ] =
155
+ Decoder .instance { c =>
156
+ for {
157
+ id <- c.downField(" id" ).as[Int ]
158
+ name <- c.downField(" string" ).as[String ]
159
+ } yield MergeRequestLevelApprovalRuleOut (id, name)
160
+ }
161
+
140
162
implicit val projectIdDecoder : Decoder [ProjectId ] = deriveDecoder
141
163
implicit val mergeRequestPayloadEncoder : Encoder [MergeRequestPayload ] =
142
164
deriveEncoder[MergeRequestPayload ].mapJson(_.dropNullValues)
@@ -222,7 +244,13 @@ final class GitLabApiAlg[F[_]: Parallel](
222
244
for {
223
245
mr <- mergeRequest
224
246
mrWithStatus <- waitForMergeRequestStatus(mr.iid)
225
- _ <- maybeSetReviewers(repo, mrWithStatus)
247
+ _ <- gitLabCfg.requiredReviewers match {
248
+ case Some (Right (approvalRules)) =>
249
+ setApprovalRules(repo, mrWithStatus, approvalRules)
250
+ case Some (Left (requiredReviewers)) =>
251
+ setReviewers(repo, mrWithStatus, requiredReviewers)
252
+ case None => F .unit
253
+ }
226
254
mergedUponSuccess <- mergePipelineUponSuccess(repo, mrWithStatus)
227
255
} yield mergedUponSuccess
228
256
}
@@ -252,29 +280,74 @@ final class GitLabApiAlg[F[_]: Parallel](
252
280
case mr =>
253
281
logger.info(s " Unable to automatically merge ${mr.webUrl}" ).map(_ => mr)
254
282
}
283
+ import cats .implicits ._
255
284
256
- private def maybeSetReviewers (repo : Repo , mrOut : MergeRequestOut ): F [MergeRequestOut ] =
257
- gitLabCfg.requiredReviewers match {
258
- case Some (requiredReviewers) =>
259
- for {
260
- _ <- logger.info(
261
- s " Setting number of required reviewers on ${mrOut.webUrl} to $requiredReviewers"
285
+ private def setReviewers (
286
+ repo : Repo ,
287
+ mrOut : MergeRequestOut ,
288
+ requiredReviewers : Int
289
+ ): F [MergeRequestOut ] =
290
+ for {
291
+ _ <- logger.info(
292
+ s " Setting number of required reviewers on ${mrOut.webUrl} to $requiredReviewers"
293
+ )
294
+ _ <-
295
+ client
296
+ .put[MergeRequestApprovalsOut ](
297
+ url.requiredApprovals(repo, mrOut.iid, requiredReviewers),
298
+ modify(repo)
262
299
)
263
- _ <-
264
- client
265
- .put[MergeRequestApprovalsOut ](
266
- url.requiredApprovals(repo, mrOut.iid, requiredReviewers),
267
- modify(repo)
268
- )
269
- .map(_ => ())
270
- .recoverWith { case UnexpectedResponse (_, _, _, status, body) =>
271
- logger
272
- .warn(s " Unexpected response setting required reviewers: $status: $body" )
273
- .as(())
274
- }
275
- } yield mrOut
276
- case None => F .pure(mrOut)
277
- }
300
+ .map(_ => ())
301
+ .recoverWith { case UnexpectedResponse (_, _, _, status, body) =>
302
+ logger
303
+ .warn(s " Unexpected response setting required reviewers: $status: $body" )
304
+ .as(())
305
+ }
306
+ } yield mrOut
307
+
308
+ private def setApprovalRules (
309
+ repo : Repo ,
310
+ mrOut : MergeRequestOut ,
311
+ approvalsConfig : Nel [MergeRequestApprovalsConfig ]
312
+ ): F [MergeRequestOut ] =
313
+ for {
314
+ _ <- logger.info(
315
+ s " Adjusting merge request approvals rules on ${mrOut.webUrl} with following config: $approvalsConfig"
316
+ )
317
+ activeApprovalRules <-
318
+ client
319
+ .get[List [MergeRequestLevelApprovalRuleOut ]](
320
+ url.listMergeRequestLevelApprovalRules(repo, mrOut.iid),
321
+ modify(repo)
322
+ )
323
+ .recoverWith { case UnexpectedResponse (_, _, _, status, body) =>
324
+ // ToDo better log
325
+ logger
326
+ .warn(s " Unexpected response setting required reviewers: $status: $body" )
327
+ .as(List .empty)
328
+ }
329
+ approvalRuleNamesFromConfig = approvalsConfig.map(_.approvalRuleName)
330
+ approvalRulesToUpdate = activeApprovalRules.intersect(approvalRuleNamesFromConfig.toList)
331
+ _ <-
332
+ approvalRulesToUpdate.map { mergeRequestApprovalConfig =>
333
+ client
334
+ .putWithBody[Unit , UpdateMergeRequestLevelApprovalRulePayload ](
335
+ url.updateMergeRequestLevelApprovalRule(
336
+ repo,
337
+ mrOut.iid,
338
+ mergeRequestApprovalConfig.id
339
+ ),
340
+ UpdateMergeRequestLevelApprovalRulePayload (mergeRequestApprovalConfig.id),
341
+ modify(repo)
342
+ )
343
+ .recoverWith { case UnexpectedResponse (_, _, _, status, body) =>
344
+ // ToDo better log
345
+ logger
346
+ .warn(s " Unexpected response setting required reviewers: $status: $body" )
347
+ .as(List .empty)
348
+ }
349
+ }.sequence
350
+ } yield mrOut
278
351
279
352
private def getUsernameToUserIdsMapping (repo : Repo , usernames : Set [String ]): F [Map [String , Int ]] =
280
353
usernames.toList
0 commit comments