From 56c61c962f8d5588a73b559f15f6e4d5ed76665d Mon Sep 17 00:00:00 2001
From: Gabriel Bleu <box.gbleu@gmail.com>
Date: Tue, 6 Sep 2022 09:09:58 +0200
Subject: [PATCH] feat(gitlab): handle merge results pipelines (#17440)
 (#17601)

---
 .../gitlab/__snapshots__/index.spec.ts.snap   |  8 ++
 lib/modules/platform/gitlab/index.spec.ts     | 94 +++++++++++++++++--
 lib/modules/platform/gitlab/index.ts          | 18 +++-
 lib/modules/platform/gitlab/types.ts          |  9 ++
 4 files changed, 118 insertions(+), 11 deletions(-)

diff --git a/lib/modules/platform/gitlab/__snapshots__/index.spec.ts.snap b/lib/modules/platform/gitlab/__snapshots__/index.spec.ts.snap
index 105a60c134..5aa3de2cc3 100644
--- a/lib/modules/platform/gitlab/__snapshots__/index.spec.ts.snap
+++ b/lib/modules/platform/gitlab/__snapshots__/index.spec.ts.snap
@@ -76,6 +76,7 @@ exports[`modules/platform/gitlab/index getBranchPr(branchName) should return the
   "displayNumber": "Merge Request #91",
   "hasAssignees": false,
   "hasReviewers": false,
+  "headPipelineStatus": undefined,
   "labels": undefined,
   "number": 91,
   "sha": undefined,
@@ -94,6 +95,7 @@ exports[`modules/platform/gitlab/index getBranchPr(branchName) should strip depr
   "displayNumber": "Merge Request #91",
   "hasAssignees": false,
   "hasReviewers": false,
+  "headPipelineStatus": undefined,
   "isDraft": true,
   "labels": undefined,
   "number": 91,
@@ -113,6 +115,7 @@ exports[`modules/platform/gitlab/index getBranchPr(branchName) should strip draf
   "displayNumber": "Merge Request #91",
   "hasAssignees": false,
   "hasReviewers": false,
+  "headPipelineStatus": undefined,
   "isDraft": true,
   "labels": undefined,
   "number": 91,
@@ -132,6 +135,7 @@ exports[`modules/platform/gitlab/index getPr(prNo) removes deprecated draft pref
   "displayNumber": "Merge Request #12345",
   "hasAssignees": false,
   "hasReviewers": false,
+  "headPipelineStatus": undefined,
   "isDraft": true,
   "labels": undefined,
   "number": 12345,
@@ -151,6 +155,7 @@ exports[`modules/platform/gitlab/index getPr(prNo) removes draft prefix from ret
   "displayNumber": "Merge Request #12345",
   "hasAssignees": false,
   "hasReviewers": false,
+  "headPipelineStatus": undefined,
   "isDraft": true,
   "labels": undefined,
   "number": 12345,
@@ -170,6 +175,7 @@ exports[`modules/platform/gitlab/index getPr(prNo) returns the PR 1`] = `
   "displayNumber": "Merge Request #12345",
   "hasAssignees": false,
   "hasReviewers": false,
+  "headPipelineStatus": undefined,
   "labels": undefined,
   "number": 12345,
   "sha": undefined,
@@ -188,6 +194,7 @@ exports[`modules/platform/gitlab/index getPr(prNo) returns the PR with nonexisti
   "displayNumber": "Merge Request #12345",
   "hasAssignees": true,
   "hasReviewers": false,
+  "headPipelineStatus": undefined,
   "labels": undefined,
   "number": 12345,
   "sha": undefined,
@@ -206,6 +213,7 @@ exports[`modules/platform/gitlab/index getPr(prNo) returns the mergeable PR 1`]
   "displayNumber": "Merge Request #12345",
   "hasAssignees": true,
   "hasReviewers": false,
+  "headPipelineStatus": undefined,
   "labels": undefined,
   "number": 12345,
   "sha": undefined,
diff --git a/lib/modules/platform/gitlab/index.spec.ts b/lib/modules/platform/gitlab/index.spec.ts
index 74f6fe779c..db0391e924 100644
--- a/lib/modules/platform/gitlab/index.spec.ts
+++ b/lib/modules/platform/gitlab/index.spec.ts
@@ -527,18 +527,68 @@ describe('modules/platform/gitlab/index', () => {
         .get(
           '/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses'
         )
+        .reply(200, [])
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests?per_page=100&scope=created_by_me'
+        )
         .reply(200, []);
       const res = await gitlab.getBranchStatus('somebranch');
       expect(res).toEqual(BranchStatus.yellow);
     });
 
+    it('returns success if no results but head pipeline success', async () => {
+      const scope = await initRepo();
+      scope
+        .get(
+          '/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses'
+        )
+        .reply(200, [])
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests?per_page=100&scope=created_by_me'
+        )
+        .reply(200, [
+          {
+            iid: 91,
+            title: 'some change',
+            source_branch: 'some-branch',
+            target_branch: 'master',
+            state: 'opened',
+          },
+        ])
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests/91?include_diverged_commits_count=1'
+        )
+        .reply(200, {
+          iid: 91,
+          title: 'some change',
+          state: 'opened',
+          additions: 1,
+          deletions: 1,
+          commits: 1,
+          source_branch: 'some-branch',
+          target_branch: 'master',
+          base: {
+            sha: '1234',
+          },
+          head_pipeline: {
+            status: 'success',
+          },
+        });
+      const res = await gitlab.getBranchStatus('some-branch');
+      expect(res).toEqual(BranchStatus.green);
+    });
+
     it('returns success if all are success', async () => {
       const scope = await initRepo();
       scope
         .get(
           '/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses'
         )
-        .reply(200, [{ status: 'success' }, { status: 'success' }]);
+        .reply(200, [{ status: 'success' }, { status: 'success' }])
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests?per_page=100&scope=created_by_me'
+        )
+        .reply(200, []);
       const res = await gitlab.getBranchStatus('somebranch');
       expect(res).toEqual(BranchStatus.green);
     });
@@ -552,7 +602,11 @@ describe('modules/platform/gitlab/index', () => {
         .reply(200, [
           { status: 'success' },
           { status: 'failed', allow_failure: true },
-        ]);
+        ])
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests?per_page=100&scope=created_by_me'
+        )
+        .reply(200, []);
       const res = await gitlab.getBranchStatus('somebranch');
       expect(res).toEqual(BranchStatus.green);
     });
@@ -563,7 +617,11 @@ describe('modules/platform/gitlab/index', () => {
         .get(
           '/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses'
         )
-        .reply(200, [{ status: 'failed', allow_failure: true }]);
+        .reply(200, [{ status: 'failed', allow_failure: true }])
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests?per_page=100&scope=created_by_me'
+        )
+        .reply(200, []);
       const res = await gitlab.getBranchStatus('somebranch');
       expect(res).toEqual(BranchStatus.green);
     });
@@ -574,7 +632,11 @@ describe('modules/platform/gitlab/index', () => {
         .get(
           '/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses'
         )
-        .reply(200, [{ status: 'success' }, { status: 'skipped' }]);
+        .reply(200, [{ status: 'success' }, { status: 'skipped' }])
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests?per_page=100&scope=created_by_me'
+        )
+        .reply(200, []);
       const res = await gitlab.getBranchStatus('somebranch');
       expect(res).toEqual(BranchStatus.green);
     });
@@ -585,7 +647,11 @@ describe('modules/platform/gitlab/index', () => {
         .get(
           '/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses'
         )
-        .reply(200, [{ status: 'skipped' }]);
+        .reply(200, [{ status: 'skipped' }])
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests?per_page=100&scope=created_by_me'
+        )
+        .reply(200, []);
       const res = await gitlab.getBranchStatus('somebranch');
       expect(res).toEqual(BranchStatus.yellow);
     });
@@ -596,7 +662,11 @@ describe('modules/platform/gitlab/index', () => {
         .get(
           '/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses'
         )
-        .reply(200, [{ status: 'skipped' }, { status: 'failed' }]);
+        .reply(200, [{ status: 'skipped' }, { status: 'failed' }])
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests?per_page=100&scope=created_by_me'
+        )
+        .reply(200, []);
       const res = await gitlab.getBranchStatus('somebranch');
       expect(res).toEqual(BranchStatus.red);
     });
@@ -611,7 +681,11 @@ describe('modules/platform/gitlab/index', () => {
           { status: 'success' },
           { status: 'failed', allow_failure: true },
           { status: 'failed' },
-        ]);
+        ])
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests?per_page=100&scope=created_by_me'
+        )
+        .reply(200, []);
       const res = await gitlab.getBranchStatus('somebranch');
       expect(res).toEqual(BranchStatus.red);
     });
@@ -622,7 +696,11 @@ describe('modules/platform/gitlab/index', () => {
         .get(
           '/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses'
         )
-        .reply(200, [{ status: 'success' }, { status: 'foo' }]);
+        .reply(200, [{ status: 'success' }, { status: 'foo' }])
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests?per_page=100&scope=created_by_me'
+        )
+        .reply(200, []);
       const res = await gitlab.getBranchStatus('somebranch');
       expect(res).toEqual(BranchStatus.yellow);
     });
diff --git a/lib/modules/platform/gitlab/index.ts b/lib/modules/platform/gitlab/index.ts
index 94f89233a4..2069b99948 100644
--- a/lib/modules/platform/gitlab/index.ts
+++ b/lib/modules/platform/gitlab/index.ts
@@ -57,6 +57,7 @@ import type {
   GitLabMergeRequest,
   GitlabComment,
   GitlabIssue,
+  GitlabPr,
   MergeMethod,
   RepoResponse,
 } from './types';
@@ -414,6 +415,14 @@ export async function getBranchStatus(
     return BranchStatus.yellow;
   }
   logger.debug(`Got res with ${branchStatuses.length} results`);
+
+  const mrStatus = (await getBranchPr(branchName))?.headPipelineStatus;
+  if (!is.undefined(mrStatus)) {
+    branchStatuses.push({
+      status: mrStatus as BranchState,
+      name: 'head_pipeline',
+    });
+  }
   // ignore all skipped jobs
   const res = branchStatuses.filter((check) => check.status !== 'skipped');
   if (res.length === 0) {
@@ -606,18 +615,19 @@ export async function createPr({
   return massagePr(pr);
 }
 
-export async function getPr(iid: number): Promise<Pr> {
+export async function getPr(iid: number): Promise<GitlabPr> {
   logger.debug(`getPr(${iid})`);
   const mr = await getMR(config.repository, iid);
 
   // Harmonize fields with GitHub
-  const pr: Pr = {
+  const pr: GitlabPr = {
     sourceBranch: mr.source_branch,
     targetBranch: mr.target_branch,
     number: mr.iid,
     displayNumber: `Merge Request #${mr.iid}`,
     bodyStruct: getPrBodyStruct(mr.description),
     state: mr.state === 'opened' ? PrState.Open : mr.state,
+    headPipelineStatus: mr.head_pipeline?.status,
     hasAssignees: !!(mr.assignee?.id ?? mr.assignees?.[0]?.id),
     hasReviewers: !!mr.reviewers?.length,
     title: mr.title,
@@ -734,7 +744,9 @@ export async function findPr({
 }
 
 // Returns the Pull Request for a branch. Null if not exists.
-export async function getBranchPr(branchName: string): Promise<Pr | null> {
+export async function getBranchPr(
+  branchName: string
+): Promise<GitlabPr | null> {
   logger.debug(`getBranchPr(${branchName})`);
   const existingPr = await findPr({
     branchName,
diff --git a/lib/modules/platform/gitlab/types.ts b/lib/modules/platform/gitlab/types.ts
index b7aa89d5f1..011b13d88f 100644
--- a/lib/modules/platform/gitlab/types.ts
+++ b/lib/modules/platform/gitlab/types.ts
@@ -1,3 +1,5 @@
+import type { Pr } from '../types';
+
 export interface GitlabIssue {
   iid: number;
 
@@ -30,6 +32,13 @@ export interface GitLabMergeRequest {
   reviewers?: GitLabUser[];
   labels: string[];
   sha: string;
+  head_pipeline?: {
+    status: string;
+  };
+}
+
+export interface GitlabPr extends Pr {
+  headPipelineStatus?: string;
 }
 
 export interface UpdateMergeRequest {
-- 
GitLab