From 4fb8df095311aab7216875f55b5b8e8d5f9fe0c6 Mon Sep 17 00:00:00 2001
From: Rich DiCroce <Rich.DiCroce@scientificgames.com>
Date: Thu, 29 Oct 2020 05:25:55 -0400
Subject: [PATCH] feat(gitlab): Implement draftPR for GitLab (#7357)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
---
 docs/usage/configuration-options.md           |   6 +-
 lib/platform/common.ts                        |   1 +
 .../gitlab/__snapshots__/index.spec.ts.snap   | 634 +++++++++++++++++-
 lib/platform/gitlab/index.spec.ts             | 319 ++++++++-
 lib/platform/gitlab/index.ts                  | 125 ++--
 5 files changed, 1031 insertions(+), 54 deletions(-)

diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index f348aabb5d..14bcbb9129 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -318,7 +318,7 @@ Add config here if you wish it to apply to Docker package managers Dockerfile an
 
 ## draftPR
 
-If you want the PRs created by Renovate to be considered as drafts rather than normal PRs in Github you could add this property to your `renovate.json`:
+If you want the PRs created by Renovate to be considered as drafts rather than normal PRs, you could add this property to your `renovate.json`:
 
 ```json
 {
@@ -326,7 +326,9 @@ If you want the PRs created by Renovate to be considered as drafts rather than n
 }
 ```
 
-Please see [draft pull requests on Github](https://github.blog/2019-02-14-introducing-draft-pull-requests/) for more information about draft PRs.
+This option is evaluated at PR/MR creation time and is only supported on the following platforms: GitHub, GitLab, Azure.
+
+Note that GitLab implements draft status by checking whether the PR's title starts with certain strings. Therefore, draftPR on GitLab is incompatible with the legacy method of triggering Renovate to rebase a PR by renaming the PR to start with `rebase!`.
 
 ## enabled
 
diff --git a/lib/platform/common.ts b/lib/platform/common.ts
index a858aa15ed..782514eb39 100644
--- a/lib/platform/common.ts
+++ b/lib/platform/common.ts
@@ -63,6 +63,7 @@ export interface Pr {
   state: string;
   targetBranch?: string;
   title: string;
+  isDraft?: boolean;
 }
 
 /**
diff --git a/lib/platform/gitlab/__snapshots__/index.spec.ts.snap b/lib/platform/gitlab/__snapshots__/index.spec.ts.snap
index 22c4924140..c37b40f0e9 100644
--- a/lib/platform/gitlab/__snapshots__/index.spec.ts.snap
+++ b/lib/platform/gitlab/__snapshots__/index.spec.ts.snap
@@ -145,6 +145,28 @@ exports[`platform/gitlab addReviewers(issueNo, reviewers) should add the given r
 
 exports[`platform/gitlab createPr(branchName, title, body) auto-accepts the MR when requested 1`] = `
 Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/user",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/version",
+  },
   Object {
     "body": "{\\"source_branch\\":\\"some-branch\\",\\"target_branch\\":\\"master\\",\\"remove_source_branch\\":true,\\"title\\":\\"some-title\\",\\"description\\":\\"the-body\\",\\"labels\\":\\"\\"}",
     "headers": Object {
@@ -205,11 +227,34 @@ Object {
   "iid": 12345,
   "number": 12345,
   "sourceBranch": "some-branch",
+  "title": "some title",
 }
 `;
 
 exports[`platform/gitlab createPr(branchName, title, body) returns the PR 2`] = `
 Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/user",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/version",
+  },
   Object {
     "body": "{\\"source_branch\\":\\"some-branch\\",\\"target_branch\\":\\"master\\",\\"remove_source_branch\\":true,\\"title\\":\\"some-title\\",\\"description\\":\\"the-body\\",\\"labels\\":null}",
     "headers": Object {
@@ -227,6 +272,112 @@ Array [
 ]
 `;
 
+exports[`platform/gitlab createPr(branchName, title, body) supports draftPR on < 13.2 1`] = `
+Object {
+  "displayNumber": "Merge Request #12345",
+  "id": 1,
+  "iid": 12345,
+  "isDraft": true,
+  "number": 12345,
+  "sourceBranch": "some-branch",
+  "title": "some title",
+}
+`;
+
+exports[`platform/gitlab createPr(branchName, title, body) supports draftPR on < 13.2 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/user",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/version",
+  },
+  Object {
+    "body": "{\\"source_branch\\":\\"some-branch\\",\\"target_branch\\":\\"master\\",\\"remove_source_branch\\":true,\\"title\\":\\"WIP: some-title\\",\\"description\\":\\"the-body\\",\\"labels\\":null}",
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "content-length": "149",
+      "content-type": "application/json",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "POST",
+    "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests",
+  },
+]
+`;
+
+exports[`platform/gitlab createPr(branchName, title, body) supports draftPR on >= 13.2 1`] = `
+Object {
+  "displayNumber": "Merge Request #12345",
+  "id": 1,
+  "iid": 12345,
+  "isDraft": true,
+  "number": 12345,
+  "sourceBranch": "some-branch",
+  "title": "some title",
+}
+`;
+
+exports[`platform/gitlab createPr(branchName, title, body) supports draftPR on >= 13.2 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/user",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/version",
+  },
+  Object {
+    "body": "{\\"source_branch\\":\\"some-branch\\",\\"target_branch\\":\\"master\\",\\"remove_source_branch\\":true,\\"title\\":\\"Draft: some-title\\",\\"description\\":\\"the-body\\",\\"labels\\":null}",
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "content-length": "151",
+      "content-type": "application/json",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "POST",
+    "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests",
+  },
+]
+`;
+
 exports[`platform/gitlab createPr(branchName, title, body) uses default branch 1`] = `
 Object {
   "displayNumber": "Merge Request #12345",
@@ -234,11 +385,34 @@ Object {
   "iid": 12345,
   "number": 12345,
   "sourceBranch": "some-branch",
+  "title": "some title",
 }
 `;
 
 exports[`platform/gitlab createPr(branchName, title, body) uses default branch 2`] = `
 Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/user",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/version",
+  },
   Object {
     "body": "{\\"source_branch\\":\\"some-branch\\",\\"target_branch\\":\\"master\\",\\"remove_source_branch\\":true,\\"title\\":\\"some-title\\",\\"description\\":\\"the-body\\",\\"labels\\":\\"\\"}",
     "headers": Object {
@@ -717,6 +891,38 @@ Array [
 ]
 `;
 
+exports[`platform/gitlab findPr(branchName, prTitle, state) returns true with deprecated draft prefix title 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined",
+  },
+]
+`;
+
+exports[`platform/gitlab findPr(branchName, prTitle, state) returns true with draft prefix title 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined",
+  },
+]
+`;
+
 exports[`platform/gitlab findPr(branchName, prTitle, state) returns true with title 1`] = `
 Array [
   Object {
@@ -755,12 +961,157 @@ Array [
       "user-agent": "https://github.com/renovatebot/renovate",
     },
     "method": "GET",
-    "url": "https://gitlab.com/api/v4/projects/some%2Frepo/merge_requests?per_page=100&state=opened&source_branch=some-branch",
+    "url": "https://gitlab.com/api/v4/projects/some%2Frepo/merge_requests?per_page=100&state=opened&source_branch=some-branch",
+  },
+]
+`;
+
+exports[`platform/gitlab getBranchPr(branchName) should return the PR object 1`] = `
+Object {
+  "additions": 1,
+  "base": Object {
+    "sha": "1234",
+  },
+  "body": undefined,
+  "commits": 1,
+  "deletions": 1,
+  "displayNumber": "Merge Request #91",
+  "hasAssignees": false,
+  "hasReviewers": false,
+  "iid": 91,
+  "number": 91,
+  "sourceBranch": "some-branch",
+  "source_branch": "some-branch",
+  "state": "open",
+  "targetBranch": "master",
+  "target_branch": "master",
+  "title": "some change",
+}
+`;
+
+exports[`platform/gitlab getBranchPr(branchName) should return the PR object 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/some%2Frepo",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/some%2Frepo/merge_requests?per_page=100&state=opened&source_branch=some-branch",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/some%2Frepo/merge_requests/91?include_diverged_commits_count=1",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses",
+  },
+]
+`;
+
+exports[`platform/gitlab getBranchPr(branchName) should strip deprecated draft prefix from title 1`] = `
+Object {
+  "additions": 1,
+  "base": Object {
+    "sha": "1234",
+  },
+  "body": undefined,
+  "commits": 1,
+  "deletions": 1,
+  "displayNumber": "Merge Request #91",
+  "hasAssignees": false,
+  "hasReviewers": false,
+  "iid": 91,
+  "isDraft": true,
+  "number": 91,
+  "sourceBranch": "some-branch",
+  "source_branch": "some-branch",
+  "state": "open",
+  "targetBranch": "master",
+  "target_branch": "master",
+  "title": "some change",
+}
+`;
+
+exports[`platform/gitlab getBranchPr(branchName) should strip deprecated draft prefix from title 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/some%2Frepo",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/some%2Frepo/merge_requests?per_page=100&state=opened&source_branch=some-branch",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/some%2Frepo/merge_requests/91?include_diverged_commits_count=1",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses",
   },
 ]
 `;
 
-exports[`platform/gitlab getBranchPr(branchName) should return the PR object 1`] = `
+exports[`platform/gitlab getBranchPr(branchName) should strip draft prefix from title 1`] = `
 Object {
   "additions": 1,
   "base": Object {
@@ -773,16 +1124,18 @@ Object {
   "hasAssignees": false,
   "hasReviewers": false,
   "iid": 91,
+  "isDraft": true,
   "number": 91,
   "sourceBranch": "some-branch",
   "source_branch": "some-branch",
   "state": "open",
   "targetBranch": "master",
   "target_branch": "master",
+  "title": "some change",
 }
 `;
 
-exports[`platform/gitlab getBranchPr(branchName) should return the PR object 2`] = `
+exports[`platform/gitlab getBranchPr(branchName) should strip draft prefix from title 2`] = `
 Array [
   Object {
     "headers": Object {
@@ -1144,6 +1497,86 @@ Array [
 ]
 `;
 
+exports[`platform/gitlab getPr(prNo) removes deprecated draft prefix from returned title 1`] = `
+Object {
+  "body": "a merge request",
+  "canMerge": false,
+  "description": "a merge request",
+  "displayNumber": "Merge Request #12345",
+  "diverged_commits_count": 5,
+  "hasAssignees": false,
+  "hasReviewers": false,
+  "id": 1,
+  "iid": 12345,
+  "isConflicted": true,
+  "isDraft": true,
+  "merge_status": "cannot_be_merged",
+  "number": 12345,
+  "sourceBranch": "some-branch",
+  "source_branch": "some-branch",
+  "state": "merged",
+  "targetBranch": "master",
+  "target_branch": "master",
+  "title": "do something",
+}
+`;
+
+exports[`platform/gitlab getPr(prNo) removes deprecated draft prefix from returned title 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests/12345?include_diverged_commits_count=1",
+  },
+]
+`;
+
+exports[`platform/gitlab getPr(prNo) removes draft prefix from returned title 1`] = `
+Object {
+  "body": "a merge request",
+  "canMerge": false,
+  "description": "a merge request",
+  "displayNumber": "Merge Request #12345",
+  "diverged_commits_count": 5,
+  "hasAssignees": false,
+  "hasReviewers": false,
+  "id": 1,
+  "iid": 12345,
+  "isConflicted": true,
+  "isDraft": true,
+  "merge_status": "cannot_be_merged",
+  "number": 12345,
+  "sourceBranch": "some-branch",
+  "source_branch": "some-branch",
+  "state": "merged",
+  "targetBranch": "master",
+  "target_branch": "master",
+  "title": "do something",
+}
+`;
+
+exports[`platform/gitlab getPr(prNo) removes draft prefix from returned title 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests/12345?include_diverged_commits_count=1",
+  },
+]
+`;
+
 exports[`platform/gitlab getPr(prNo) returns the PR 1`] = `
 Object {
   "body": "a merge request",
@@ -1163,6 +1596,7 @@ Object {
   "state": "merged",
   "targetBranch": "master",
   "target_branch": "master",
+  "title": "do something",
 }
 `;
 
@@ -1201,6 +1635,7 @@ Object {
   "state": "open",
   "targetBranch": "master",
   "target_branch": "master",
+  "title": "do something",
 }
 `;
 
@@ -1237,6 +1672,7 @@ Object {
   "state": "open",
   "targetBranch": "master",
   "target_branch": "master",
+  "title": "do something",
 }
 `;
 
@@ -1382,6 +1818,17 @@ Array [
     "method": "GET",
     "url": "https://gitlab.renovatebot.com/user",
   },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.renovatebot.com/version",
+  },
 ]
 `;
 
@@ -1405,6 +1852,17 @@ Array [
     "method": "GET",
     "url": "https://gitlab.com/api/v4/user",
   },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/version",
+  },
 ]
 `;
 
@@ -1712,6 +2170,39 @@ Array [
 
 exports[`platform/gitlab updatePr(prNo, title, body) closes the PR 1`] = `
 Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/user",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/version",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined",
+  },
   Object {
     "body": "{\\"title\\":\\"title\\",\\"description\\":\\"body\\",\\"state_event\\":\\"close\\"}",
     "headers": Object {
@@ -1729,8 +2220,145 @@ Array [
 ]
 `;
 
+exports[`platform/gitlab updatePr(prNo, title, body) retains draft status when draft uses current prefix 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/user",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/version",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined",
+  },
+  Object {
+    "body": "{\\"title\\":\\"Draft: title\\",\\"description\\":\\"body\\"}",
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "content-length": "45",
+      "content-type": "application/json",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "PUT",
+    "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests/1",
+  },
+]
+`;
+
+exports[`platform/gitlab updatePr(prNo, title, body) retains draft status when draft uses deprecated prefix 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/user",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/version",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined",
+  },
+  Object {
+    "body": "{\\"title\\":\\"Draft: title\\",\\"description\\":\\"body\\"}",
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "content-length": "45",
+      "content-type": "application/json",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "PUT",
+    "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests/1",
+  },
+]
+`;
+
 exports[`platform/gitlab updatePr(prNo, title, body) updates the PR 1`] = `
 Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/user",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer some-token",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/version",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer abc123",
+      "host": "gitlab.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://gitlab.com/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined",
+  },
   Object {
     "body": "{\\"title\\":\\"title\\",\\"description\\":\\"body\\"}",
     "headers": Object {
diff --git a/lib/platform/gitlab/index.spec.ts b/lib/platform/gitlab/index.spec.ts
index bb85f841e4..b353d05025 100644
--- a/lib/platform/gitlab/index.spec.ts
+++ b/lib/platform/gitlab/index.spec.ts
@@ -60,6 +60,9 @@ describe('platform/gitlab', () => {
         email: 'a@b.com',
         name: 'Renovate Bot',
       });
+      httpMock.scope(gitlabApiHost).get('/api/v4/version').reply(200, {
+        version: '13.3.6-ee',
+      });
       expect(
         await gitlab.initPlatform({
           token: 'some-token',
@@ -69,13 +72,21 @@ describe('platform/gitlab', () => {
       expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it(`should accept custom endpoint`, async () => {
-      httpMock.scope('https://gitlab.renovatebot.com').get('/user').reply(200, {
-        email: 'a@b.com',
-        name: 'Renovate Bot',
-      });
+      const endpoint = 'https://gitlab.renovatebot.com';
+      httpMock
+        .scope(endpoint)
+        .get('/user')
+        .reply(200, {
+          email: 'a@b.com',
+          name: 'Renovate Bot',
+        })
+        .get('/version')
+        .reply(200, {
+          version: '13.3.6-ee',
+        });
       expect(
         await gitlab.initPlatform({
-          endpoint: 'https://gitlab.renovatebot.com',
+          endpoint,
           token: 'some-token',
         })
       ).toMatchSnapshot();
@@ -326,6 +337,83 @@ describe('platform/gitlab', () => {
         )
         .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',
+          },
+        })
+        .get(
+          '/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses'
+        )
+        .reply(200, []);
+      const pr = await gitlab.getBranchPr('some-branch');
+      expect(pr).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
+    });
+    it('should strip draft prefix from title', async () => {
+      const scope = await initRepo();
+      scope
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests?per_page=100&state=opened&source_branch=some-branch'
+        )
+        .reply(200, [
+          {
+            iid: 91,
+            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: 'Draft: some change',
+          state: 'opened',
+          additions: 1,
+          deletions: 1,
+          commits: 1,
+          source_branch: 'some-branch',
+          target_branch: 'master',
+          base: {
+            sha: '1234',
+          },
+        })
+        .get(
+          '/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses'
+        )
+        .reply(200, []);
+      const pr = await gitlab.getBranchPr('some-branch');
+      expect(pr).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
+    });
+    it('should strip deprecated draft prefix from title', async () => {
+      const scope = await initRepo();
+      scope
+        .get(
+          '/api/v4/projects/some%2Frepo/merge_requests?per_page=100&state=opened&source_branch=some-branch'
+        )
+        .reply(200, [
+          {
+            iid: 91,
+            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: 'WIP: some change',
           state: 'opened',
           additions: 1,
           deletions: 1,
@@ -881,15 +969,78 @@ describe('platform/gitlab', () => {
       expect(res).toBeDefined();
       expect(httpMock.getTrace()).toMatchSnapshot();
     });
+    it('returns true with draft prefix title', async () => {
+      httpMock
+        .scope(gitlabApiHost)
+        .get(
+          '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined'
+        )
+        .reply(200, [
+          {
+            iid: 1,
+            source_branch: 'branch-a',
+            title: 'Draft: branch a pr',
+            state: 'opened',
+          },
+        ]);
+      const res = await gitlab.findPr({
+        branchName: 'branch-a',
+        prTitle: 'branch a pr',
+      });
+      expect(res).toBeDefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
+    });
+    it('returns true with deprecated draft prefix title', async () => {
+      httpMock
+        .scope(gitlabApiHost)
+        .get(
+          '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined'
+        )
+        .reply(200, [
+          {
+            iid: 1,
+            source_branch: 'branch-a',
+            title: 'WIP: branch a pr',
+            state: 'opened',
+          },
+        ]);
+      const res = await gitlab.findPr({
+        branchName: 'branch-a',
+        prTitle: 'branch a pr',
+      });
+      expect(res).toBeDefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
+    });
   });
+
+  async function initPlatform(gitlabVersion: string) {
+    httpMock
+      .scope(gitlabApiHost)
+      .get('/api/v4/user')
+      .reply(200, {
+        email: 'a@b.com',
+        name: 'Renovate Bot',
+      })
+      .get('/api/v4/version')
+      .reply(200, {
+        version: gitlabVersion,
+      });
+    await gitlab.initPlatform({
+      token: 'some-token',
+      endpoint: undefined,
+    });
+  }
+
   describe('createPr(branchName, title, body)', () => {
     it('returns the PR', async () => {
+      await initPlatform('13.3.6-ee');
       httpMock
         .scope(gitlabApiHost)
         .post('/api/v4/projects/undefined/merge_requests')
         .reply(200, {
           id: 1,
           iid: 12345,
+          title: 'some title',
         });
       const pr = await gitlab.createPr({
         sourceBranch: 'some-branch',
@@ -902,12 +1053,14 @@ describe('platform/gitlab', () => {
       expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('uses default branch', async () => {
+      await initPlatform('13.3.6-ee');
       httpMock
         .scope(gitlabApiHost)
         .post('/api/v4/projects/undefined/merge_requests')
         .reply(200, {
           id: 1,
           iid: 12345,
+          title: 'some title',
         });
       const pr = await gitlab.createPr({
         sourceBranch: 'some-branch',
@@ -919,13 +1072,55 @@ describe('platform/gitlab', () => {
       expect(pr).toMatchSnapshot();
       expect(httpMock.getTrace()).toMatchSnapshot();
     });
+    it('supports draftPR on < 13.2', async () => {
+      await initPlatform('13.1.0-ee');
+      httpMock
+        .scope(gitlabApiHost)
+        .post('/api/v4/projects/undefined/merge_requests')
+        .reply(200, {
+          id: 1,
+          iid: 12345,
+          title: 'WIP: some title',
+        });
+      const pr = await gitlab.createPr({
+        sourceBranch: 'some-branch',
+        targetBranch: 'master',
+        prTitle: 'some-title',
+        prBody: 'the-body',
+        draftPR: true,
+      });
+      expect(pr).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
+    });
+    it('supports draftPR on >= 13.2', async () => {
+      await initPlatform('13.2.0-ee');
+      httpMock
+        .scope(gitlabApiHost)
+        .post('/api/v4/projects/undefined/merge_requests')
+        .reply(200, {
+          id: 1,
+          iid: 12345,
+          title: 'Draft: some title',
+        });
+      const pr = await gitlab.createPr({
+        sourceBranch: 'some-branch',
+        targetBranch: 'master',
+        prTitle: 'some-title',
+        prBody: 'the-body',
+        draftPR: true,
+      });
+      expect(pr).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
+    });
     it('auto-accepts the MR when requested', async () => {
+      await initPlatform('13.3.6-ee');
       httpMock
         .scope(gitlabApiHost)
         .post('/api/v4/projects/undefined/merge_requests')
         .reply(200, {
           id: 1,
           iid: 12345,
+          title: 'some title',
         })
         .get('/api/v4/projects/undefined/merge_requests/12345')
         .reply(200)
@@ -965,6 +1160,7 @@ describe('platform/gitlab', () => {
         .reply(200, {
           id: 1,
           iid: 12345,
+          title: 'do something',
           description: 'a merge request',
           state: PrState.Merged,
           merge_status: 'cannot_be_merged',
@@ -978,6 +1174,52 @@ describe('platform/gitlab', () => {
       expect(pr.hasAssignees).toBe(false);
       expect(httpMock.getTrace()).toMatchSnapshot();
     });
+    it('removes draft prefix from returned title', async () => {
+      httpMock
+        .scope(gitlabApiHost)
+        .get(
+          '/api/v4/projects/undefined/merge_requests/12345?include_diverged_commits_count=1'
+        )
+        .reply(200, {
+          id: 1,
+          iid: 12345,
+          title: 'Draft: do something',
+          description: 'a merge request',
+          state: PrState.Merged,
+          merge_status: 'cannot_be_merged',
+          diverged_commits_count: 5,
+          source_branch: 'some-branch',
+          target_branch: 'master',
+          assignees: [],
+        });
+      const pr = await gitlab.getPr(12345);
+      expect(pr).toMatchSnapshot();
+      expect(pr.title).toBe('do something');
+      expect(httpMock.getTrace()).toMatchSnapshot();
+    });
+    it('removes deprecated draft prefix from returned title', async () => {
+      httpMock
+        .scope(gitlabApiHost)
+        .get(
+          '/api/v4/projects/undefined/merge_requests/12345?include_diverged_commits_count=1'
+        )
+        .reply(200, {
+          id: 1,
+          iid: 12345,
+          title: 'WIP: do something',
+          description: 'a merge request',
+          state: PrState.Merged,
+          merge_status: 'cannot_be_merged',
+          diverged_commits_count: 5,
+          source_branch: 'some-branch',
+          target_branch: 'master',
+          assignees: [],
+        });
+      const pr = await gitlab.getPr(12345);
+      expect(pr).toMatchSnapshot();
+      expect(pr.title).toBe('do something');
+      expect(httpMock.getTrace()).toMatchSnapshot();
+    });
     it('returns the mergeable PR', async () => {
       const scope = await initRepo();
       scope
@@ -987,6 +1229,7 @@ describe('platform/gitlab', () => {
         .reply(200, {
           id: 1,
           iid: 12345,
+          title: 'do something',
           description: 'a merge request',
           state: PrState.Open,
           diverged_commits_count: 5,
@@ -1014,6 +1257,7 @@ describe('platform/gitlab', () => {
         .reply(200, {
           id: 1,
           iid: 12345,
+          title: 'do something',
           description: 'a merge request',
           state: PrState.Open,
           merge_status: 'cannot_be_merged',
@@ -1035,16 +1279,80 @@ describe('platform/gitlab', () => {
   describe('updatePr(prNo, title, body)', () => {
     jest.resetAllMocks();
     it('updates the PR', async () => {
+      await initPlatform('13.3.6-ee');
+      httpMock
+        .scope(gitlabApiHost)
+        .get(
+          '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined'
+        )
+        .reply(200, [
+          {
+            iid: 1,
+            source_branch: 'branch-a',
+            title: 'branch a pr',
+            state: PrState.Open,
+          },
+        ])
+        .put('/api/v4/projects/undefined/merge_requests/1')
+        .reply(200);
+      await gitlab.updatePr({ number: 1, prTitle: 'title', prBody: 'body' });
+      expect(httpMock.getTrace()).toMatchSnapshot();
+    });
+    it('retains draft status when draft uses current prefix', async () => {
+      await initPlatform('13.3.6-ee');
+      httpMock
+        .scope(gitlabApiHost)
+        .get(
+          '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined'
+        )
+        .reply(200, [
+          {
+            iid: 1,
+            source_branch: 'branch-a',
+            title: 'Draft: foo',
+            state: PrState.Open,
+          },
+        ])
+        .put('/api/v4/projects/undefined/merge_requests/1')
+        .reply(200);
+      await gitlab.updatePr({ number: 1, prTitle: 'title', prBody: 'body' });
+      expect(httpMock.getTrace()).toMatchSnapshot();
+    });
+    it('retains draft status when draft uses deprecated prefix', async () => {
+      await initPlatform('13.3.6-ee');
       httpMock
         .scope(gitlabApiHost)
+        .get(
+          '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined'
+        )
+        .reply(200, [
+          {
+            iid: 1,
+            source_branch: 'branch-a',
+            title: 'WIP: foo',
+            state: PrState.Open,
+          },
+        ])
         .put('/api/v4/projects/undefined/merge_requests/1')
         .reply(200);
       await gitlab.updatePr({ number: 1, prTitle: 'title', prBody: 'body' });
       expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('closes the PR', async () => {
+      await initPlatform('13.3.6-ee');
       httpMock
         .scope(gitlabApiHost)
+        .get(
+          '/api/v4/projects/undefined/merge_requests?per_page=100&author_id=undefined'
+        )
+        .reply(200, [
+          {
+            iid: 1,
+            source_branch: 'branch-a',
+            title: 'branch a pr',
+            state: PrState.Open,
+          },
+        ])
         .put('/api/v4/projects/undefined/merge_requests/1')
         .reply(200);
       await gitlab.updatePr({
@@ -1098,6 +1406,7 @@ These updates have all been created already. Click a checkbox below to force a r
         .reply(200, {
           id: 1,
           iid: 12345,
+          title: 'some change',
           description: 'a merge request',
           state: PrState.Merged,
           merge_status: 'cannot_be_merged',
diff --git a/lib/platform/gitlab/index.ts b/lib/platform/gitlab/index.ts
index f854e2310a..5bebabb596 100644
--- a/lib/platform/gitlab/index.ts
+++ b/lib/platform/gitlab/index.ts
@@ -1,6 +1,7 @@
 import URL, { URLSearchParams } from 'url';
 import is from '@sindresorhus/is';
 import delay from 'delay';
+import semver from 'semver';
 import {
   PLATFORM_AUTHENTICATION_ERROR,
   REPOSITORY_ACCESS_FORBIDDEN,
@@ -56,7 +57,11 @@ const defaults = {
   endpoint: 'https://gitlab.com/api/v4/',
 };
 
+const DRAFT_PREFIX = 'Draft: ';
+const DRAFT_PREFIX_DEPRECATED = 'WIP: ';
+
 let authorId: number;
+let draftPrefix = DRAFT_PREFIX;
 
 export async function initPlatform({
   endpoint,
@@ -72,6 +77,7 @@ export async function initPlatform({
     logger.debug('Using default GitLab endpoint: ' + defaults.endpoint);
   }
   let gitAuthor: string;
+  let gitlabVersion: string;
   try {
     const user = (
       await gitlabApi.getJson<{ email: string; name: string; id: number }>(
@@ -81,13 +87,21 @@ export async function initPlatform({
     ).body;
     gitAuthor = `${user.name} <${user.email}>`;
     authorId = user.id;
+    // version is 'x.y.z-edition', so not strictly semver; need to strip edition
+    gitlabVersion = (
+      await gitlabApi.getJson<{ version: string }>('version', { token })
+    ).body.version.split('-')[0];
+    logger.debug('GitLab version is: ' + gitlabVersion);
   } catch (err) {
     logger.debug(
       { err },
-      'Error authenticating with GitLab. Check that your token includes "user" permissions'
+      'Error authenticating with GitLab. Check that your token includes "api" permissions'
     );
     throw new Error('Init: Authentication failure');
   }
+  draftPrefix = semver.lt(gitlabVersion, '13.2.0')
+    ? DRAFT_PREFIX_DEPRECATED
+    : DRAFT_PREFIX;
   const platformConfig: PlatformResult = {
     endpoint: defaults.endpoint,
     gitAuthor,
@@ -334,14 +348,72 @@ export async function getBranchStatus(
 
 // Pull Request
 
+function massagePr(prToModify: Pr): Pr {
+  const pr = prToModify;
+  if (pr.title.startsWith(DRAFT_PREFIX)) {
+    pr.title = pr.title.substring(DRAFT_PREFIX.length);
+    pr.isDraft = true;
+  } else if (pr.title.startsWith(DRAFT_PREFIX_DEPRECATED)) {
+    pr.title = pr.title.substring(DRAFT_PREFIX_DEPRECATED.length);
+    pr.isDraft = true;
+  }
+  return pr;
+}
+
+async function fetchPrList(): Promise<Pr[]> {
+  const query = new URLSearchParams({
+    per_page: '100',
+    author_id: `${authorId}`,
+  }).toString();
+  const urlString = `projects/${config.repository}/merge_requests?${query}`;
+  try {
+    const res = await gitlabApi.getJson<
+      {
+        iid: number;
+        source_branch: string;
+        title: string;
+        state: string;
+        created_at: string;
+      }[]
+    >(urlString, { paginate: true });
+    return res.body.map((pr) =>
+      massagePr({
+        number: pr.iid,
+        sourceBranch: pr.source_branch,
+        title: pr.title,
+        state: pr.state === 'opened' ? PrState.Open : pr.state,
+        createdAt: pr.created_at,
+      })
+    );
+  } catch (err) /* istanbul ignore next */ {
+    logger.debug({ err }, 'Error fetching PR list');
+    if (err.statusCode === 403) {
+      throw new Error(PLATFORM_AUTHENTICATION_ERROR);
+    }
+    throw err;
+  }
+}
+
+export async function getPrList(): Promise<Pr[]> {
+  if (!config.prList) {
+    config.prList = await fetchPrList();
+  }
+  return config.prList;
+}
+
 export async function createPr({
   sourceBranch,
   targetBranch,
-  prTitle: title,
+  prTitle,
   prBody: rawDescription,
+  draftPR,
   labels,
   platformOptions,
 }: CreatePRConfig): Promise<Pr> {
+  let title = prTitle;
+  if (draftPR) {
+    title = draftPrefix + title;
+  }
   const description = sanitize(rawDescription);
   logger.debug(`Creating Merge Request: ${title}`);
   const res = await gitlabApi.postJson<Pr & { iid: number }>(
@@ -397,7 +469,7 @@ export async function createPr({
     }
   }
 
-  return pr;
+  return massagePr(pr);
 }
 
 export async function getPr(iid: number): Promise<Pr> {
@@ -438,15 +510,19 @@ export async function getPr(iid: number): Promise<Pr> {
       pr.canMerge = true;
     }
   }
-  return pr;
+  return massagePr(pr);
 }
 
 export async function updatePr({
   number: iid,
-  prTitle: title,
+  prTitle,
   prBody: description,
   state,
 }: UpdatePrConfig): Promise<void> {
+  let title = prTitle;
+  if ((await getPrList()).find((pr) => pr.number === iid).isDraft) {
+    title = draftPrefix + title;
+  }
   const newState = {
     [PrState.Closed]: 'close',
     [PrState.Open]: 'reopen',
@@ -893,45 +969,6 @@ export async function ensureCommentRemoval({
   }
 }
 
-async function fetchPrList(): Promise<Pr[]> {
-  const query = new URLSearchParams({
-    per_page: '100',
-    author_id: `${authorId}`,
-  }).toString();
-  const urlString = `projects/${config.repository}/merge_requests?${query}`;
-  try {
-    const res = await gitlabApi.getJson<
-      {
-        iid: number;
-        source_branch: string;
-        title: string;
-        state: string;
-        created_at: string;
-      }[]
-    >(urlString, { paginate: true });
-    return res.body.map((pr) => ({
-      number: pr.iid,
-      sourceBranch: pr.source_branch,
-      title: pr.title,
-      state: pr.state === 'opened' ? PrState.Open : pr.state,
-      createdAt: pr.created_at,
-    }));
-  } catch (err) /* istanbul ignore next */ {
-    logger.debug({ err }, 'Error fetching PR list');
-    if (err.statusCode === 403) {
-      throw new Error(PLATFORM_AUTHENTICATION_ERROR);
-    }
-    throw err;
-  }
-}
-
-export async function getPrList(): Promise<Pr[]> {
-  if (!config.prList) {
-    config.prList = await fetchPrList();
-  }
-  return config.prList;
-}
-
 function matchesState(state: string, desiredState: string): boolean {
   if (desiredState === PrState.All) {
     return true;
-- 
GitLab