@@ -23,13 +23,18 @@ import io.circe._
2323import io .circe .generic .semiauto ._
2424import io .circe .syntax ._
2525import org .http4s .{Request , Status , Uri }
26- import org .scalasteward .core .application .Config .{ForgeCfg , GitLabCfg }
26+ import org .scalasteward .core .application .Config .{ForgeCfg , GitLabCfg , MergeRequestApprovalsConfig }
2727import org .scalasteward .core .data .Repo
2828import org .scalasteward .core .forge .ForgeApiAlg
2929import org .scalasteward .core .forge .data ._
3030import org .scalasteward .core .git .{Branch , Sha1 }
3131import org .scalasteward .core .util .uri .uriDecoder
32- import org .scalasteward .core .util .{intellijThisImportIsUsed , HttpJsonClient , UnexpectedResponse }
32+ import org .scalasteward .core .util .{
33+ intellijThisImportIsUsed ,
34+ HttpJsonClient ,
35+ Nel ,
36+ UnexpectedResponse
37+ }
3338import org .typelevel .log4cats .Logger
3439
3540import scala .concurrent .duration .{Duration , DurationInt }
@@ -48,6 +53,8 @@ final private[gitlab] case class MergeRequestPayload(
4853 target_branch : Branch
4954)
5055
56+ final private [gitlab] case class UpdateMergeRequestLevelApprovalRulePayload (approvals_required : Int )
57+
5158private [gitlab] object MergeRequestPayload {
5259 def apply (
5360 id : String ,
@@ -87,6 +94,11 @@ final private[gitlab] case class MergeRequestApprovalsOut(
8794 approvalsRequired : Int
8895)
8996
97+ final private [gitlab] case class MergeRequestLevelApprovalRuleOut (
98+ id : Int ,
99+ name : String
100+ )
101+
90102final private [gitlab] case class CommitId (id : Sha1 ) {
91103 val commitOut : CommitOut = CommitOut (id)
92104}
@@ -102,6 +114,8 @@ private[gitlab] object GitLabJsonCodec {
102114 intellijThisImportIsUsed(uriDecoder)
103115
104116 implicit val forkPayloadEncoder : Encoder [ForkPayload ] = deriveEncoder
117+ implicit val updateMergeRequestLevelApprovalRulePayloadEncoder
118+ : Encoder [UpdateMergeRequestLevelApprovalRulePayload ] = deriveEncoder
105119 implicit val userOutDecoder : Decoder [UserOut ] = Decoder .instance {
106120 _.downField(" username" ).as[String ].map(UserOut (_))
107121 }
@@ -140,6 +154,14 @@ private[gitlab] object GitLabJsonCodec {
140154 } yield MergeRequestApprovalsOut (requiredReviewers)
141155 }
142156
157+ implicit val mergeRequestLevelApprovalRuleOutDecoder : Decoder [MergeRequestLevelApprovalRuleOut ] =
158+ Decoder .instance { c =>
159+ for {
160+ id <- c.downField(" id" ).as[Int ]
161+ name <- c.downField(" string" ).as[String ]
162+ } yield MergeRequestLevelApprovalRuleOut (id, name)
163+ }
164+
143165 implicit val projectIdDecoder : Decoder [ProjectId ] = deriveDecoder
144166 implicit val mergeRequestPayloadEncoder : Encoder [MergeRequestPayload ] =
145167 deriveEncoder[MergeRequestPayload ].mapJson(_.dropNullValues)
@@ -240,7 +262,13 @@ final class GitLabApiAlg[F[_]: Parallel](
240262 for {
241263 mr <- mergeRequest
242264 mrWithStatus <- waitForMergeRequestStatus(mr.iid)
243- _ <- maybeSetReviewers(repo, mrWithStatus)
265+ _ <- gitLabCfg.requiredReviewers match {
266+ case Some (Right (approvalRules)) =>
267+ setApprovalRules(repo, mrWithStatus, approvalRules)
268+ case Some (Left (requiredReviewers)) =>
269+ setReviewers(repo, mrWithStatus, requiredReviewers)
270+ case None => F .unit
271+ }
244272 mergedUponSuccess <- mergePipelineUponSuccess(repo, mrWithStatus)
245273 } yield mergedUponSuccess
246274 }
@@ -270,29 +298,74 @@ final class GitLabApiAlg[F[_]: Parallel](
270298 case mr =>
271299 logger.info(s " Unable to automatically merge ${mr.webUrl}" ).map(_ => mr)
272300 }
301+ import cats .implicits ._
273302
274- private def maybeSetReviewers (repo : Repo , mrOut : MergeRequestOut ): F [MergeRequestOut ] =
275- gitLabCfg.requiredReviewers match {
276- case Some (requiredReviewers) =>
277- for {
278- _ <- logger.info(
279- s " Setting number of required reviewers on ${mrOut.webUrl} to $requiredReviewers"
303+ private def setReviewers (
304+ repo : Repo ,
305+ mrOut : MergeRequestOut ,
306+ requiredReviewers : Int
307+ ): F [MergeRequestOut ] =
308+ for {
309+ _ <- logger.info(
310+ s " Setting number of required reviewers on ${mrOut.webUrl} to $requiredReviewers"
311+ )
312+ _ <-
313+ client
314+ .put[MergeRequestApprovalsOut ](
315+ url.requiredApprovals(repo, mrOut.iid, requiredReviewers),
316+ modify(repo)
280317 )
281- _ <-
282- client
283- .put[MergeRequestApprovalsOut ](
284- url.requiredApprovals(repo, mrOut.iid, requiredReviewers),
285- modify(repo)
286- )
287- .map(_ => ())
288- .recoverWith { case UnexpectedResponse (_, _, _, status, body) =>
289- logger
290- .warn(s " Unexpected response setting required reviewers: $status: $body" )
291- .as(())
292- }
293- } yield mrOut
294- case None => F .pure(mrOut)
295- }
318+ .map(_ => ())
319+ .recoverWith { case UnexpectedResponse (_, _, _, status, body) =>
320+ logger
321+ .warn(s " Unexpected response setting required reviewers: $status: $body" )
322+ .as(())
323+ }
324+ } yield mrOut
325+
326+ private def setApprovalRules (
327+ repo : Repo ,
328+ mrOut : MergeRequestOut ,
329+ approvalsConfig : Nel [MergeRequestApprovalsConfig ]
330+ ): F [MergeRequestOut ] =
331+ for {
332+ _ <- logger.info(
333+ s " Adjusting merge request approvals rules on ${mrOut.webUrl} with following config: $approvalsConfig"
334+ )
335+ activeApprovalRules <-
336+ client
337+ .get[List [MergeRequestLevelApprovalRuleOut ]](
338+ url.listMergeRequestLevelApprovalRules(repo, mrOut.iid),
339+ modify(repo)
340+ )
341+ .recoverWith { case UnexpectedResponse (_, _, _, status, body) =>
342+ // ToDo better log
343+ logger
344+ .warn(s " Unexpected response setting required reviewers: $status: $body" )
345+ .as(List .empty)
346+ }
347+ approvalRuleNamesFromConfig = approvalsConfig.map(_.approvalRuleName)
348+ approvalRulesToUpdate = activeApprovalRules.intersect(approvalRuleNamesFromConfig.toList)
349+ _ <-
350+ approvalRulesToUpdate.map { mergeRequestApprovalConfig =>
351+ client
352+ .putWithBody[Unit , UpdateMergeRequestLevelApprovalRulePayload ](
353+ url.updateMergeRequestLevelApprovalRule(
354+ repo,
355+ mrOut.iid,
356+ mergeRequestApprovalConfig.id
357+ ),
358+ UpdateMergeRequestLevelApprovalRulePayload (mergeRequestApprovalConfig.id),
359+ modify(repo)
360+ )
361+ .recoverWith { case UnexpectedResponse (_, _, _, status, body) =>
362+ // ToDo better log
363+ logger
364+ .warn(s " Unexpected response setting required reviewers: $status: $body" )
365+ .as(List .empty)
366+ }
367+ }.sequence
368+ } yield mrOut
296369
297370 private def getUsernameToUserIdsMapping (repo : Repo , usernames : Set [String ]): F [Map [String , Int ]] =
298371 usernames.toList
0 commit comments