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 )
@@ -44,6 +49,8 @@ final private[gitlab] case class MergeRequestPayload(
4449 target_branch : Branch
4550)
4651
52+ final private [gitlab] case class UpdateMergeRequestLevelApprovalRulePayload (approvals_required : Int )
53+
4754private [gitlab] object MergeRequestPayload {
4855 def apply (
4956 id : String ,
@@ -81,6 +88,11 @@ final private[gitlab] case class MergeRequestApprovalsOut(
8188 approvalsRequired : Int
8289)
8390
91+ final private [gitlab] case class MergeRequestLevelApprovalRuleOut (
92+ id : Int ,
93+ name : String
94+ )
95+
8496final private [gitlab] case class CommitId (id : Sha1 ) {
8597 val commitOut : CommitOut = CommitOut (id)
8698}
@@ -96,6 +108,8 @@ private[gitlab] object GitLabJsonCodec {
96108 intellijThisImportIsUsed(uriDecoder)
97109
98110 implicit val forkPayloadEncoder : Encoder [ForkPayload ] = deriveEncoder
111+ implicit val updateMergeRequestLevelApprovalRulePayloadEncoder
112+ : Encoder [UpdateMergeRequestLevelApprovalRulePayload ] = deriveEncoder
99113 implicit val userOutDecoder : Decoder [UserOut ] = Decoder .instance {
100114 _.downField(" username" ).as[String ].map(UserOut (_))
101115 }
@@ -134,6 +148,14 @@ private[gitlab] object GitLabJsonCodec {
134148 } yield MergeRequestApprovalsOut (requiredReviewers)
135149 }
136150
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+
137159 implicit val projectIdDecoder : Decoder [ProjectId ] = deriveDecoder
138160 implicit val mergeRequestPayloadEncoder : Encoder [MergeRequestPayload ] = deriveEncoder
139161 implicit val updateStateEncoder : Encoder [UpdateState ] = Encoder .instance { newState =>
@@ -216,7 +238,13 @@ final class GitLabApiAlg[F[_]: Parallel](
216238 for {
217239 mr <- mergeRequest
218240 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+ }
220248 mergedUponSuccess <- mergePipelineUponSuccess(repo, mrWithStatus)
221249 } yield mergedUponSuccess
222250 }
@@ -246,29 +274,74 @@ final class GitLabApiAlg[F[_]: Parallel](
246274 case mr =>
247275 logger.info(s " Unable to automatically merge ${mr.webUrl}" ).map(_ => mr)
248276 }
277+ import cats .implicits ._
249278
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)
256293 )
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
272345
273346 private def getUsernameToUserIdsMapping (repo : Repo , usernames : Set [String ]): F [Map [String , Int ]] =
274347 usernames.toList
0 commit comments