From 5568420c78f78810f51f13b38e2debb422f9a9cb Mon Sep 17 00:00:00 2001
From: Olivier Michaelis <38879457+oliviermichaelis@users.noreply.github.com>
Date: Thu, 14 Jul 2022 06:42:05 +0200
Subject: [PATCH] feat(github): support unprefixed App Installation Token
 (#16174)

---
 .../github/__snapshots__/index.spec.ts.snap   |  6 ++++
 lib/modules/platform/github/index.md          | 10 +++++--
 lib/modules/platform/github/index.spec.ts     | 30 ++++++++++++++++++-
 lib/modules/platform/github/index.ts          |  8 +++--
 lib/modules/platform/index.ts                 |  4 +++
 lib/modules/platform/types.ts                 |  1 +
 6 files changed, 53 insertions(+), 6 deletions(-)

diff --git a/lib/modules/platform/github/__snapshots__/index.spec.ts.snap b/lib/modules/platform/github/__snapshots__/index.spec.ts.snap
index ed91cf8007..457144317a 100644
--- a/lib/modules/platform/github/__snapshots__/index.spec.ts.snap
+++ b/lib/modules/platform/github/__snapshots__/index.spec.ts.snap
@@ -118,6 +118,7 @@ Object {
   "endpoint": "https://ghe.renovatebot.com/",
   "gitAuthor": "undefined <user@domain.com>",
   "renovateUsername": "renovate-bot",
+  "token": "123test",
 }
 `;
 
@@ -126,6 +127,7 @@ Object {
   "endpoint": "https://ghe.renovatebot.com/",
   "gitAuthor": "undefined <user@domain.com>",
   "renovateUsername": "renovate-bot",
+  "token": "123test",
 }
 `;
 
@@ -134,6 +136,7 @@ Object {
   "endpoint": "https://api.github.com/",
   "gitAuthor": undefined,
   "renovateUsername": "renovate-bot",
+  "token": "123test",
 }
 `;
 
@@ -142,6 +145,7 @@ Object {
   "endpoint": "https://api.github.com/",
   "gitAuthor": undefined,
   "renovateUsername": "renovate-bot",
+  "token": "123test",
 }
 `;
 
@@ -150,6 +154,7 @@ Object {
   "endpoint": "https://api.github.com/",
   "gitAuthor": "undefined <user@domain.com>",
   "renovateUsername": "renovate-bot",
+  "token": "123test",
 }
 `;
 
@@ -158,6 +163,7 @@ Object {
   "endpoint": "https://api.github.com/",
   "gitAuthor": "renovate@whitesourcesoftware.com",
   "renovateUsername": "renovate-bot",
+  "token": "123test",
 }
 `;
 
diff --git a/lib/modules/platform/github/index.md b/lib/modules/platform/github/index.md
index 7258171ecc..8f4bac23a8 100644
--- a/lib/modules/platform/github/index.md
+++ b/lib/modules/platform/github/index.md
@@ -51,9 +51,15 @@ The [GitHub App associated email](https://github.community/t/logging-into-git-as
 It needs to have the user id _and_ the username followed by the `users.noreply.`-domain of either github.com or the GitHub Enterprise Server.
 A way to get the user id of a GitHub app is to [query the user API](https://docs.github.com/en/rest/reference/users#get-a-user) at `api.github.com/user/self-hosted-renovate[bot]` (github.com) or `github.enterprise.com/api/v3/uer/self-hosted-renovate[bot]` (GitHub Enterprise Server).
 
-**`token:"x-access-token:${github-app-installation}"`**
+**`token:"ghs_123exampletoken"`**
 
-The token needs to be prefixed with `x-access-token` and be a [GitHub App Installation token](https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps#authenticating-as-an-installation).
+You must use a [GitHub App Installation token](https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps#authenticating-as-an-installation).
+
+Previously, the token had to be prefixed with `x-access-token:`.
+We recommend you replace any prefixed tokens with normal tokens.
+We will drop support for prefixed tokens in the future.
+
+Any tokens that do not start with `ghs_` (for example tokens from GitHub Enterprise Server versions before version `3.2`) must be prefixed with `x-access-token:`.
 
 <!-- prettier-ignore -->
 !!! note
diff --git a/lib/modules/platform/github/index.spec.ts b/lib/modules/platform/github/index.spec.ts
index f6fb3ad3f3..8ecbbebf86 100644
--- a/lib/modules/platform/github/index.spec.ts
+++ b/lib/modules/platform/github/index.spec.ts
@@ -50,7 +50,7 @@ describe('modules/platform/github/index', () => {
   describe('initPlatform()', () => {
     it('should throw if no token', async () => {
       await expect(github.initPlatform({} as any)).rejects.toThrow(
-        'Init: You must configure a GitHub personal access token'
+        'Init: You must configure a GitHub token'
       );
     });
 
@@ -210,6 +210,34 @@ describe('modules/platform/github/index', () => {
       const repos = await github.getRepos();
       expect(repos).toStrictEqual(['a/b', 'c/d']);
     });
+
+    it('should return an array of repos when using GitHub App Installation Token', async () => {
+      //Use Github App token
+      await github.initPlatform({
+        endpoint: githubApiHost,
+        username: 'self-hosted-renovate[bot]',
+        gitAuthor:
+          'Self-hosted Renovate Bot <123456+self-hosted-renovate[bot]@users.noreply.github.com>',
+        token: 'ghs_123test',
+      });
+      httpMock
+        .scope(githubApiHost)
+        .get('/installation/repositories?per_page=100')
+        .reply(200, {
+          repositories: [
+            {
+              full_name: 'a/b',
+            },
+            {
+              full_name: 'c/d',
+            },
+            null,
+          ],
+        });
+
+      const repos = await github.getRepos();
+      expect(repos).toStrictEqual(['a/b', 'c/d']);
+    });
   });
 
   function initRepoMock(
diff --git a/lib/modules/platform/github/index.ts b/lib/modules/platform/github/index.ts
index b3503aad7a..1f5c20269d 100644
--- a/lib/modules/platform/github/index.ts
+++ b/lib/modules/platform/github/index.ts
@@ -116,14 +116,15 @@ export async function detectGhe(token: string): Promise<void> {
 
 export async function initPlatform({
   endpoint,
-  token,
+  token: originalToken,
   username,
   gitAuthor,
 }: PlatformParams): Promise<PlatformResult> {
+  let token = originalToken;
   if (!token) {
-    throw new Error('Init: You must configure a GitHub personal access token');
+    throw new Error('Init: You must configure a GitHub token');
   }
-
+  token = token.replace(/^ghs_/, 'x-access-token:ghs_');
   platformConfig.isGHApp = token.startsWith('x-access-token:');
 
   if (endpoint) {
@@ -164,6 +165,7 @@ export async function initPlatform({
     endpoint: platformConfig.endpoint,
     gitAuthor: gitAuthor ?? discoveredGitAuthor,
     renovateUsername,
+    token,
   };
 
   return platformResult;
diff --git a/lib/modules/platform/index.ts b/lib/modules/platform/index.ts
index 889d5cc9ea..be1315a76b 100644
--- a/lib/modules/platform/index.ts
+++ b/lib/modules/platform/index.ts
@@ -59,6 +59,10 @@ export async function initPlatform(config: AllConfig): Promise<AllConfig> {
     // TODO: null check (#7154)
     matchHost: URL.parse(returnConfig.endpoint).hostname!,
   };
+  // There might have been platform-specific modifications to the token
+  if (returnConfig.token) {
+    config.token = returnConfig.token;
+  }
   (
     ['token', 'username', 'password'] as ('token' | 'username' | 'password')[]
   ).forEach((field) => {
diff --git a/lib/modules/platform/types.ts b/lib/modules/platform/types.ts
index b38b9b2084..9fffc1927d 100644
--- a/lib/modules/platform/types.ts
+++ b/lib/modules/platform/types.ts
@@ -21,6 +21,7 @@ export interface PlatformParams {
 export interface PlatformResult {
   endpoint: string;
   renovateUsername?: string;
+  token?: string;
   gitAuthor?: string;
 }
 
-- 
GitLab