From 9f791552d32d8efa5fa22e7b49d5a292ba16d72a Mon Sep 17 00:00:00 2001 From: Jesko Steinberg <61695677+jeskosda@users.noreply.github.com> Date: Wed, 25 Aug 2021 09:07:23 +0200 Subject: [PATCH] feat: enable right filtering when specifing hostRules with github-api using datasources (#11136) Co-authored-by: Michael Kriese <michael.kriese@visualon.de> Co-authored-by: Rhys Arkins <rhys@arkins.net> --- lib/datasource/github-releases/common.ts | 3 +- lib/datasource/github-releases/index.ts | 3 +- lib/datasource/github-tags/index.ts | 3 +- lib/datasource/pod/index.ts | 2 +- .../github/__snapshots__/index.spec.ts.snap | 27 +++++++ lib/platform/github/index.spec.ts | 11 +++ lib/platform/github/index.ts | 9 ++- lib/types/github.spec.ts | 25 +++++++ lib/types/github.ts | 8 +++ lib/types/index.ts | 1 + lib/util/host-rules.spec.ts | 71 ++++++++++++++++++- lib/util/http/auth.spec.ts | 38 ++++++++++ lib/util/http/auth.ts | 4 +- lib/util/http/github.spec.ts | 18 +++++ lib/util/http/github.ts | 7 +- 15 files changed, 220 insertions(+), 10 deletions(-) create mode 100644 lib/types/github.spec.ts create mode 100644 lib/types/github.ts diff --git a/lib/datasource/github-releases/common.ts b/lib/datasource/github-releases/common.ts index 58bb4a56cd..8b395e47b5 100644 --- a/lib/datasource/github-releases/common.ts +++ b/lib/datasource/github-releases/common.ts @@ -3,9 +3,10 @@ import { ensureTrailingSlash } from '../../util/url'; import type { GithubRelease } from './types'; const defaultSourceUrlBase = 'https://github.com/'; +export const id = 'github-releases'; export const cacheNamespace = 'datasource-github-releases'; -export const http = new GithubHttp(); +export const http = new GithubHttp(id); export function getSourceUrlBase(registryUrl: string): string { // default to GitHub.com if no GHE host is specified. diff --git a/lib/datasource/github-releases/index.ts b/lib/datasource/github-releases/index.ts index bcc91241a3..05e09e93d5 100644 --- a/lib/datasource/github-releases/index.ts +++ b/lib/datasource/github-releases/index.ts @@ -7,11 +7,12 @@ import { getGithubRelease, getSourceUrlBase, http, + id, } from './common'; import { findDigestAsset, mapDigestAssetToRelease } from './digest'; import type { GithubRelease } from './types'; -export const id = 'github-releases'; +export { id }; export const customRegistrySupport = true; export const defaultRegistryUrls = ['https://github.com']; export const registryStrategy = 'first'; diff --git a/lib/datasource/github-tags/index.ts b/lib/datasource/github-tags/index.ts index 1801050c2b..2300a58e29 100644 --- a/lib/datasource/github-tags/index.ts +++ b/lib/datasource/github-tags/index.ts @@ -11,9 +11,10 @@ export const customRegistrySupport = true; export const defaultRegistryUrls = ['https://github.com']; export const registryStrategy = 'first'; -const http = new GithubHttp(); +const http = new GithubHttp(id); const cacheNamespace = 'datasource-github-tags'; + function getCacheKey(registryUrl: string, repo: string, type: string): string { return `${registryUrl}:${repo}:${type}`; } diff --git a/lib/datasource/pod/index.ts b/lib/datasource/pod/index.ts index 54be19969d..31b53e167f 100644 --- a/lib/datasource/pod/index.ts +++ b/lib/datasource/pod/index.ts @@ -17,7 +17,7 @@ export const registryStrategy = 'hunt'; const cacheNamespace = `datasource-${id}`; const cacheMinutes = 30; -const githubHttp = new GithubHttp(); +const githubHttp = new GithubHttp(id); const http = new Http(id); function shardParts(lookupName: string): string[] { diff --git a/lib/platform/github/__snapshots__/index.spec.ts.snap b/lib/platform/github/__snapshots__/index.spec.ts.snap index 37ff4dedb2..57149d2ed2 100644 --- a/lib/platform/github/__snapshots__/index.spec.ts.snap +++ b/lib/platform/github/__snapshots__/index.spec.ts.snap @@ -6875,6 +6875,33 @@ Array [ ] `; +exports[`platform/github/index initPlatform() should add initialized platform with predefined generic host rule for github api 1`] = ` +Object { + "endpoint": "https://api.github.com/", + "gitAuthor": "renovate@whitesourcesoftware.com", + "renovateUsername": "renovate-bot", +} +`; + +exports[`platform/github/index initPlatform() should add initialized platform with predefined generic host rule for github api 2`] = ` +[MockFunction] { + "calls": Array [ + Array [ + Object { + "matchHost": "api.github.com", + "token": "abc123", + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + exports[`platform/github/index initPlatform() should support custom endpoint 1`] = ` Object { "endpoint": "https://ghe.renovatebot.com/", diff --git a/lib/platform/github/index.spec.ts b/lib/platform/github/index.spec.ts index 99a5463b1a..3d0c716cff 100644 --- a/lib/platform/github/index.spec.ts +++ b/lib/platform/github/index.spec.ts @@ -130,6 +130,17 @@ describe('platform/github/index', () => { ).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot(); }); + + it('should add initialized platform with predefined generic host rule for github api', async () => { + expect( + await github.initPlatform({ + token: 'abc123', + username: 'renovate-bot', + gitAuthor: 'renovate@whitesourcesoftware.com', + } as any) + ).toMatchSnapshot(); + expect(hostRules.add).toMatchSnapshot(); + }); }); describe('getRepos', () => { diff --git a/lib/platform/github/index.ts b/lib/platform/github/index.ts index b9b3ca365d..7b3af04531 100644 --- a/lib/platform/github/index.ts +++ b/lib/platform/github/index.ts @@ -23,7 +23,7 @@ import * as git from '../../util/git'; import * as hostRules from '../../util/host-rules'; import * as githubHttp from '../../util/http/github'; import { sanitize } from '../../util/sanitize'; -import { ensureTrailingSlash } from '../../util/url'; +import { ensureTrailingSlash, parseUrl } from '../../util/url'; import type { AggregatedVulnerabilities, BranchStatusConfig, @@ -114,6 +114,13 @@ export async function initPlatform({ gitAuthor: gitAuthor || discoveredGitAuthor, renovateUsername, }; + + // Generic github hostRule that per default all datasources using github api are enabled + const genericGithubHostRule = { + matchHost: parseUrl(defaults.endpoint).hostname, + token, + }; + hostRules.add(genericGithubHostRule); return platformConfig; } diff --git a/lib/types/github.spec.ts b/lib/types/github.spec.ts new file mode 100644 index 0000000000..8e79ef350a --- /dev/null +++ b/lib/types/github.spec.ts @@ -0,0 +1,25 @@ +import { + PLATFORM_TYPE_GITHUB, + PLATFORM_TYPE_GITLAB, +} from '../constants/platforms'; +import { id as GH_RELEASES_DS } from '../datasource/github-releases'; +import { id as GH_TAGS_DS } from '../datasource/github-tags'; +import { id as POD_DS } from '../datasource/pod'; +import { GITHUB_API_USING_HOST_TYPES } from './github'; + +describe('types/github', () => { + it('should be part of the GITHUB_API_USING_HOST_TYPES ', () => { + expect(GITHUB_API_USING_HOST_TYPES.includes(GH_TAGS_DS)).toBeTrue(); + expect(GITHUB_API_USING_HOST_TYPES.includes(GH_RELEASES_DS)).toBeTrue(); + expect(GITHUB_API_USING_HOST_TYPES.includes(POD_DS)).toBeTrue(); + expect( + GITHUB_API_USING_HOST_TYPES.includes(PLATFORM_TYPE_GITHUB) + ).toBeTrue(); + }); + + it('should be not part of the GITHUB_API_USING_HOST_TYPES ', () => { + expect( + GITHUB_API_USING_HOST_TYPES.includes(PLATFORM_TYPE_GITLAB) + ).toBeFalse(); + }); +}); diff --git a/lib/types/github.ts b/lib/types/github.ts new file mode 100644 index 0000000000..9281983377 --- /dev/null +++ b/lib/types/github.ts @@ -0,0 +1,8 @@ +import { PLATFORM_TYPE_GITHUB } from '../constants/platforms'; + +export const GITHUB_API_USING_HOST_TYPES = [ + PLATFORM_TYPE_GITHUB, + 'github-releases', + 'github-tags', + 'pod', +]; diff --git a/lib/types/index.ts b/lib/types/index.ts index e1e0fa9d42..a89dd31798 100644 --- a/lib/types/index.ts +++ b/lib/types/index.ts @@ -4,3 +4,4 @@ export * from './versioning'; export * from './branch-status'; export * from './vulnerability-alert'; export * from './pr-state'; +export * from './github'; diff --git a/lib/util/host-rules.spec.ts b/lib/util/host-rules.spec.ts index 145bc91937..1e01f83c89 100644 --- a/lib/util/host-rules.spec.ts +++ b/lib/util/host-rules.spec.ts @@ -1,4 +1,7 @@ -import { PLATFORM_TYPE_AZURE } from '../constants/platforms'; +import { + PLATFORM_TYPE_AZURE, + PLATFORM_TYPE_GITHUB, +} from '../constants/platforms'; import * as datasourceNuget from '../datasource/nuget'; import { add, clear, find, findAll, hosts } from './host-rules'; @@ -106,6 +109,72 @@ describe('util/host-rules', () => { .token ).toBeUndefined(); }); + + it('matches on specific path', () => { + // Initialized platform holst rule + add({ + hostType: PLATFORM_TYPE_GITHUB, + matchHost: 'https://api.github.com', + token: 'abc', + }); + // Initialized generic host rule for github platform + add({ + hostType: PLATFORM_TYPE_GITHUB, + matchHost: 'https://api.github.com', + token: 'abc', + }); + // specific host rule for using other token in different org + add({ + hostType: PLATFORM_TYPE_GITHUB, + matchHost: 'https://api.github.com/repos/org-b/', + token: 'def', + }); + expect( + find({ + hostType: PLATFORM_TYPE_GITHUB, + url: 'https://api.github.com/repos/org-b/someRepo/tags?per_page=100', + }).token + ).toEqual('def'); + }); + + it('matches for several hostTypes when no hostType rule is configured', () => { + add({ + matchHost: 'https://api.github.com', + token: 'abc', + }); + expect( + find({ + hostType: PLATFORM_TYPE_GITHUB, + url: 'https://api.github.com/repos/org-b/someRepo/tags?per_page=100', + }).token + ).toEqual('abc'); + expect( + find({ + hostType: 'github-releases', + url: 'https://api.github.com/repos/org-b/someRepo/tags?per_page=100', + }).token + ).toEqual('abc'); + }); + + it('matches if hostType is configured and host rule is filtered with datasource', () => { + add({ + hostType: PLATFORM_TYPE_GITHUB, + matchHost: 'https://api.github.com', + token: 'abc', + }); + add({ + hostType: 'github-tags', + matchHost: 'https://api.github.com/repos/org-b/', + token: 'def', + }); + expect( + find({ + hostType: 'github-tags', + url: 'https://api.github.com/repos/org-b/someRepo/tags?per_page=100', + }).token + ).toEqual('def'); + }); + it('matches on hostName', () => { add({ hostName: 'nuget.local', diff --git a/lib/util/http/auth.spec.ts b/lib/util/http/auth.spec.ts index 11d5c9d877..a2d4df7175 100644 --- a/lib/util/http/auth.spec.ts +++ b/lib/util/http/auth.spec.ts @@ -2,6 +2,7 @@ import { NormalizedOptions } from 'got'; import { partial } from '../../../test/util'; import { PLATFORM_TYPE_GITEA, + PLATFORM_TYPE_GITHUB, PLATFORM_TYPE_GITLAB, } from '../../constants/platforms'; import { applyAuthorization, removeAuthorization } from './auth'; @@ -69,6 +70,43 @@ describe('util/http/auth', () => { `); }); + it('github token', () => { + const opts: GotOptions = { + headers: {}, + token: 'XXX', + hostType: PLATFORM_TYPE_GITHUB, + }; + + applyAuthorization(opts); + + expect(opts).toEqual({ + headers: { + authorization: 'token XXX', + }, + hostType: 'github', + token: 'XXX', + }); + }); + + it('github token for datasource using github api', () => { + const opts: GotOptions = { + headers: {}, + token: 'ZZZZ', + hostType: 'github-releases', + }; + applyAuthorization(opts); + + expect(opts).toMatchInlineSnapshot(` + Object { + "headers": Object { + "authorization": "token ZZZZ", + }, + "hostType": "github-releases", + "token": "ZZZZ", + } + `); + }); + it(`gitlab personal access token`, () => { const opts: GotOptions = { headers: {}, diff --git a/lib/util/http/auth.ts b/lib/util/http/auth.ts index 74e90b0d46..4a7671eeb8 100644 --- a/lib/util/http/auth.ts +++ b/lib/util/http/auth.ts @@ -2,9 +2,9 @@ import is from '@sindresorhus/is'; import { NormalizedOptions } from 'got'; import { PLATFORM_TYPE_GITEA, - PLATFORM_TYPE_GITHUB, PLATFORM_TYPE_GITLAB, } from '../../constants/platforms'; +import { GITHUB_API_USING_HOST_TYPES } from '../../types'; import { GotOptions } from './types'; export function applyAuthorization(inOptions: GotOptions): GotOptions { @@ -15,7 +15,7 @@ export function applyAuthorization(inOptions: GotOptions): GotOptions { if (options.token) { if (options.hostType === PLATFORM_TYPE_GITEA) { options.headers.authorization = `token ${options.token}`; - } else if (options.hostType === PLATFORM_TYPE_GITHUB) { + } else if (GITHUB_API_USING_HOST_TYPES.includes(options.hostType)) { options.headers.authorization = `token ${options.token}`; if (options.token.startsWith('x-access-token:')) { const appToken = options.token.replace('x-access-token:', ''); diff --git a/lib/util/http/github.spec.ts b/lib/util/http/github.spec.ts index 53111f6cc2..36e19ebcd8 100644 --- a/lib/util/http/github.spec.ts +++ b/lib/util/http/github.spec.ts @@ -6,6 +6,7 @@ import { PLATFORM_RATE_LIMIT_EXCEEDED, REPOSITORY_CHANGED, } from '../../constants/error-messages'; +import { id as GITHUB_RELEASES_ID } from '../../datasource/github-releases'; import * as hostRules from '../host-rules'; import { GithubHttp, setBaseUrl } from './github'; @@ -61,7 +62,23 @@ describe('util/http/github', () => { expect(req.headers.accept).toBe( 'some-accept, application/vnd.github.machine-man-preview+json' ); + expect(req.headers.authorization).toBe('token abc123'); }); + + it('supports different datasources', async () => { + const githubApiDatasource = new GithubHttp(GITHUB_RELEASES_ID); + hostRules.add({ hostType: 'github', token: 'abc' }); + hostRules.add({ + hostType: GITHUB_RELEASES_ID, + token: 'def', + }); + httpMock.scope(githubApiHost).get('/some-url').reply(200); + await githubApiDatasource.get('/some-url'); + const [req] = httpMock.getTrace(); + expect(req).toBeDefined(); + expect(req.headers.authorization).toBe('token def'); + }); + it('paginates', async () => { const url = '/some-url'; httpMock @@ -148,6 +165,7 @@ describe('util/http/github', () => { ); await githubApi.getJson(url); } + async function failWithError(error: string | Record<string, unknown>) { const url = '/some-url'; httpMock.scope(githubApiHost).get(url).replyWithError(error); diff --git a/lib/util/http/github.ts b/lib/util/http/github.ts index 722ea322a3..478c72f54f 100644 --- a/lib/util/http/github.ts +++ b/lib/util/http/github.ts @@ -158,8 +158,11 @@ function constructAcceptString(input?: any): string { } export class GithubHttp extends Http<GithubHttpOptions, GithubHttpOptions> { - constructor(options?: GithubHttpOptions) { - super(PLATFORM_TYPE_GITHUB, options); + constructor( + hostType: string = PLATFORM_TYPE_GITHUB, + options?: GithubHttpOptions + ) { + super(hostType, options); } protected override async request<T>( -- GitLab