1616
1717package org .scalasteward .core .forge .gitlab
1818
19- import cats .{MonadThrow , Parallel }
2019import cats .syntax .all ._
20+ import cats .{MonadThrow , Parallel }
2121import io .circe ._
2222import io .circe .generic .semiauto ._
2323import io .circe .syntax ._
2424import org .http4s .{Request , Status , Uri }
25- import org .scalasteward .core .application .Config .{ForgeCfg , GitLabCfg }
25+ import org .scalasteward .core .application .Config .{ForgeCfg , GitLabCfg , MergeRequestApprovalsConfig }
2626import org .scalasteward .core .data .Repo
2727import org .scalasteward .core .forge .ForgeApiAlg
2828import org .scalasteward .core .forge .data ._
2929import org .scalasteward .core .git .{Branch , Sha1 }
3030import 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+ }
3237import org .typelevel .log4cats .Logger
3338
3439final private [gitlab] case class ForkPayload (id : String , namespace : String )
@@ -45,6 +50,8 @@ final private[gitlab] case class MergeRequestPayload(
4550 target_branch : Branch
4651)
4752
53+ final private [gitlab] case class UpdateMergeRequestLevelApprovalRulePayload (approvals_required : Int )
54+
4855private [gitlab] object MergeRequestPayload {
4956 def apply (
5057 id : String ,
@@ -84,6 +91,11 @@ final private[gitlab] case class MergeRequestApprovalsOut(
8491 approvalsRequired : Int
8592)
8693
94+ final private [gitlab] case class MergeRequestLevelApprovalRuleOut (
95+ id : Int ,
96+ name : String
97+ )
98+
8799final private [gitlab] case class CommitId (id : Sha1 ) {
88100 val commitOut : CommitOut = CommitOut (id)
89101}
@@ -99,6 +111,8 @@ private[gitlab] object GitLabJsonCodec {
99111 intellijThisImportIsUsed(uriDecoder)
100112
101113 implicit val forkPayloadEncoder : Encoder [ForkPayload ] = deriveEncoder
114+ implicit val updateMergeRequestLevelApprovalRulePayloadEncoder
115+ : Encoder [UpdateMergeRequestLevelApprovalRulePayload ] = deriveEncoder
102116 implicit val userOutDecoder : Decoder [UserOut ] = Decoder .instance {
103117 _.downField(" username" ).as[String ].map(UserOut (_))
104118 }
@@ -137,6 +151,14 @@ private[gitlab] object GitLabJsonCodec {
137151 } yield MergeRequestApprovalsOut (requiredReviewers)
138152 }
139153
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+
140162 implicit val projectIdDecoder : Decoder [ProjectId ] = deriveDecoder
141163 implicit val mergeRequestPayloadEncoder : Encoder [MergeRequestPayload ] =
142164 deriveEncoder[MergeRequestPayload ].mapJson(_.dropNullValues)
@@ -222,7 +244,13 @@ final class GitLabApiAlg[F[_]: Parallel](
222244 for {
223245 mr <- mergeRequest
224246 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+ }
226254 mergedUponSuccess <- mergePipelineUponSuccess(repo, mrWithStatus)
227255 } yield mergedUponSuccess
228256 }
@@ -252,29 +280,74 @@ final class GitLabApiAlg[F[_]: Parallel](
252280 case mr =>
253281 logger.info(s " Unable to automatically merge ${mr.webUrl}" ).map(_ => mr)
254282 }
283+ import cats .implicits ._
255284
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)
262299 )
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
278351
279352 private def getUsernameToUserIdsMapping (repo : Repo , usernames : Set [String ]): F [Map [String , Int ]] =
280353 usernames.toList
0 commit comments