diff --git a/lib/modules/platform/github/index.spec.ts b/lib/modules/platform/github/index.spec.ts index c4f0cd59d93578efea19f9f27d1781ff2db5d3e7..443ddf423de46a8c8334e96a831b86da3dc392af 100644 --- a/lib/modules/platform/github/index.spec.ts +++ b/lib/modules/platform/github/index.spec.ts @@ -57,14 +57,55 @@ describe('modules/platform/github/index', () => { ); }); - it('should throw if fine-grained token', async () => { + it('should throw if using fine-grained token with GHE <3.10 ', async () => { + httpMock + .scope('https://ghe.renovatebot.com') + .head('/') + .reply(200, '', { 'x-github-enterprise-version': '3.9.0' }); await expect( - github.initPlatform({ token: 'github_pat_XXXXXX' }) + github.initPlatform({ + endpoint: 'https://ghe.renovatebot.com', + token: 'github_pat_XXXXXX', + }) ).rejects.toThrow( - 'Init: Fine-grained Personal Access Tokens do not support the GitHub GraphQL API and cannot be used with Renovate.' + 'Init: Fine-grained Personal Access Tokens do not support GitHub Enterprise Server API version <3.10 and cannot be used with Renovate.' ); }); + it('should throw if using fine-grained token with GHE unknown version ', async () => { + httpMock.scope('https://ghe.renovatebot.com').head('/').reply(200); + await expect( + github.initPlatform({ + endpoint: 'https://ghe.renovatebot.com', + token: 'github_pat_XXXXXX', + }) + ).rejects.toThrow( + 'Init: Fine-grained Personal Access Tokens do not support GitHub Enterprise Server API version <3.10 and cannot be used with Renovate.' + ); + }); + + it('should support fine-grained token with GHE >=3.10', async () => { + httpMock + .scope('https://ghe.renovatebot.com') + .head('/') + .reply(200, '', { 'x-github-enterprise-version': '3.10.0' }) + .get('/user') + .reply(200, { login: 'renovate-bot' }) + .get('/user/emails') + .reply(200, [{ email: 'user@domain.com' }]); + expect( + await github.initPlatform({ + endpoint: 'https://ghe.renovatebot.com', + token: 'github_pat_XXXXXX', + }) + ).toEqual({ + endpoint: 'https://ghe.renovatebot.com/', + gitAuthor: 'undefined <user@domain.com>', + renovateUsername: 'renovate-bot', + token: 'github_pat_XXXXXX', + }); + }); + it('should throw if user failure', async () => { httpMock.scope(githubApiHost).get('/user').reply(404); await expect(github.initPlatform({ token: '123test' })).rejects.toThrow(); diff --git a/lib/modules/platform/github/index.ts b/lib/modules/platform/github/index.ts index 422318a66169ba4d34afb382a4a967f1e016a17b..44ef1fcb647ca51673dd8070d6388ea92b433ebb 100644 --- a/lib/modules/platform/github/index.ts +++ b/lib/modules/platform/github/index.ts @@ -23,6 +23,7 @@ import { import { logger } from '../../../logger'; import type { BranchStatus, VulnerabilityAlert } from '../../../types'; import { ExternalHostError } from '../../../types/errors/external-host-error'; +import { isGithubFineGrainedPersonalAccessToken } from '../../../util/check-token'; import * as git from '../../../util/git'; import { listCommitTree, pushCommitToRenovateRef } from '../../../util/git'; import type { @@ -133,11 +134,6 @@ export async function initPlatform({ if (!token) { throw new Error('Init: You must configure a GitHub token'); } - if (token.startsWith('github_pat_')) { - throw new Error( - 'Init: Fine-grained Personal Access Tokens do not support the GitHub GraphQL API and cannot be used with Renovate.' - ); - } token = token.replace(/^ghs_/, 'x-access-token:ghs_'); platformConfig.isGHApp = token.startsWith('x-access-token:'); @@ -149,6 +145,20 @@ export async function initPlatform({ } await detectGhe(token); + /** + * GHE requires version >=3.10 to support fine-grained access tokens + * https://docs.github.com/en/enterprise-server@3.10/admin/release-notes#authentication + */ + if ( + isGithubFineGrainedPersonalAccessToken(token) && + platformConfig.isGhe && + (!platformConfig.gheVersion || + semver.lt(platformConfig.gheVersion, '3.10.0')) + ) { + throw new Error( + 'Init: Fine-grained Personal Access Tokens do not support GitHub Enterprise Server API version <3.10 and cannot be used with Renovate.' + ); + } let renovateUsername: string; if (username) { diff --git a/lib/util/check-token.spec.ts b/lib/util/check-token.spec.ts index 71d37d72dfe6a62118673196c68b3c750926cd4b..2251342441891849a4a44e75ac1b1b0a8f16c476 100644 --- a/lib/util/check-token.spec.ts +++ b/lib/util/check-token.spec.ts @@ -246,7 +246,7 @@ describe('util/check-token', () => { ).toBe(githubToken); }); - it('take personal assess token over fine grained token', () => { + it('take personal access token over fine grained token', () => { const githubToken = 'ghp_github'; const gitTagsGithubToken = 'github_pat_gitTags'; expect( diff --git a/lib/workers/global/config/parse/__snapshots__/env.spec.ts.snap b/lib/workers/global/config/parse/__snapshots__/env.spec.ts.snap index 47739dda648ac9b0050f075f10174b1588ff9ca5..0f673583d47ccc37c546459c4479bbbed5b2abba 100644 --- a/lib/workers/global/config/parse/__snapshots__/env.spec.ts.snap +++ b/lib/workers/global/config/parse/__snapshots__/env.spec.ts.snap @@ -1,12 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`workers/global/config/parse/env .getConfig(env) does not support GitHub fine-grained PATs 1`] = ` -{ - "hostRules": [], - "token": "a github.com token", -} -`; - exports[`workers/global/config/parse/env .getConfig(env) supports Azure DevOps 1`] = ` { "endpoint": "an Azure DevOps endpoint", diff --git a/lib/workers/global/config/parse/env.spec.ts b/lib/workers/global/config/parse/env.spec.ts index 1631326e461efc5dc001f1ff56a3bc0952e5cffb..784c219b2c4787b28c2501062fe7f4a9e272c519 100644 --- a/lib/workers/global/config/parse/env.spec.ts +++ b/lib/workers/global/config/parse/env.spec.ts @@ -161,13 +161,20 @@ describe('workers/global/config/parse/env', () => { }); }); - it('does not support GitHub fine-grained PATs', () => { + it('supports GitHub fine-grained PATs', () => { const envParam: NodeJS.ProcessEnv = { GITHUB_COM_TOKEN: 'github_pat_XXXXXX', RENOVATE_TOKEN: 'a github.com token', }; - expect(env.getConfig(envParam)).toMatchSnapshot({ + expect(env.getConfig(envParam)).toEqual({ token: 'a github.com token', + hostRules: [ + { + hostType: 'github', + matchHost: 'github.com', + token: 'github_pat_XXXXXX', + }, + ], }); }); diff --git a/lib/workers/global/config/parse/env.ts b/lib/workers/global/config/parse/env.ts index f176043f0854b34e2cc446817c913272e4608321..81f7d3384dd365bfaee8576c73e041c141629361 100644 --- a/lib/workers/global/config/parse/env.ts +++ b/lib/workers/global/config/parse/env.ts @@ -164,18 +164,12 @@ export function getConfig(inputEnv: NodeJS.ProcessEnv): AllConfig { }); if (env.GITHUB_COM_TOKEN) { - if (env.GITHUB_COM_TOKEN.startsWith('github_pat_')) { - logger.warn( - 'GITHUB_COM_TOKEN: Fine-grained Personal Access Tokens do not support the GitHub GraphQL API. Use a classic PAT instead.' - ); - } else { - logger.debug(`Converting GITHUB_COM_TOKEN into a global host rule`); - config.hostRules.push({ - hostType: 'github', - matchHost: 'github.com', - token: env.GITHUB_COM_TOKEN, - }); - } + logger.debug(`Converting GITHUB_COM_TOKEN into a global host rule`); + config.hostRules.push({ + hostType: 'github', + matchHost: 'github.com', + token: env.GITHUB_COM_TOKEN, + }); } // These env vars are deprecated and deleted to make sure they're not used