diff --git a/lib/modules/platform/bitbucket/__snapshots__/index.spec.ts.snap b/lib/modules/platform/bitbucket/__snapshots__/index.spec.ts.snap index 1a100b3d1972da49d1c17d5e5f802925956530f9..d85ff37cef732a00095ad5e713e5b121c5f54b58 100644 --- a/lib/modules/platform/bitbucket/__snapshots__/index.spec.ts.snap +++ b/lib/modules/platform/bitbucket/__snapshots__/index.spec.ts.snap @@ -144,13 +144,7 @@ exports[`modules/platform/bitbucket/index getPrList() filters PR list by author ] `; -exports[`modules/platform/bitbucket/index initPlatform() should init 1`] = ` -{ - "endpoint": "https://api.bitbucket.org/", -} -`; - -exports[`modules/platform/bitbucket/index initRepo() works 1`] = ` +exports[`modules/platform/bitbucket/index initRepo() works with username and password 1`] = ` { "defaultBranch": "master", "isFork": false, diff --git a/lib/modules/platform/bitbucket/index.spec.ts b/lib/modules/platform/bitbucket/index.spec.ts index f4cb3b0685b9897933c45a9c13f8e702ce6cbf6d..16dafb7141e89f0abef13cffa8c7ea7568080a1a 100644 --- a/lib/modules/platform/bitbucket/index.spec.ts +++ b/lib/modules/platform/bitbucket/index.spec.ts @@ -3,7 +3,7 @@ import type { logger as _logger } from '../../../logger'; import { BranchStatus, PrState } from '../../../types'; import type * as _git from '../../../util/git'; import { setBaseUrl } from '../../../util/http/bitbucket'; -import type { Platform, RepoParams } from '../types'; +import type { Platform, PlatformResult, RepoParams } from '../types'; const baseUrl = 'https://api.bitbucket.org'; @@ -69,7 +69,7 @@ describe('modules/platform/bitbucket/index', () => { } describe('initPlatform()', () => { - it('should throw if no username/password', async () => { + it('should throw if no token or username/password', async () => { expect.assertions(1); await expect(bitbucket.initPlatform({})).rejects.toThrow(); }); @@ -85,14 +85,31 @@ describe('modules/platform/bitbucket/index', () => { ); }); - it('should init', async () => { + it('should init with username/password', async () => { + const expectedResult: PlatformResult = { + endpoint: baseUrl, + }; httpMock.scope(baseUrl).get('/2.0/user').reply(200); expect( await bitbucket.initPlatform({ + endpoint: baseUrl, username: 'abc', password: '123', }) - ).toMatchSnapshot(); + ).toEqual(expectedResult); + }); + + it('should init with only token', async () => { + const expectedResult: PlatformResult = { + endpoint: baseUrl, + }; + httpMock.scope(baseUrl).get('/2.0/user').reply(200); + expect( + await bitbucket.initPlatform({ + endpoint: baseUrl, + token: 'abc', + }) + ).toEqual(expectedResult); }); it('should warn for missing "profile" scope', async () => { @@ -121,7 +138,7 @@ describe('modules/platform/bitbucket/index', () => { }); describe('initRepo()', () => { - it('works', async () => { + it('works with username and password', async () => { httpMock .scope(baseUrl) .get('/2.0/repositories/some/repo') @@ -132,6 +149,27 @@ describe('modules/platform/bitbucket/index', () => { }) ).toMatchSnapshot(); }); + + it('works with only token', async () => { + hostRules.clear(); + hostRules.find.mockReturnValue({ + token: 'abc', + }); + httpMock + .scope(baseUrl) + .get('/2.0/repositories/some/repo') + .reply(200, { owner: {}, mainbranch: { name: 'master' } }); + expect( + await bitbucket.initRepo({ + repository: 'some/repo', + }) + ).toEqual({ + defaultBranch: 'master', + isFork: false, + repoFingerprint: + '56653db0e9341ef4957c92bb78ee668b0a3f03c75b77db94d520230557385fca344cc1f593191e3594183b5b050909d29996c040045e8852f21774617b240642', + }); + }); }); describe('getRepoForceRebase()', () => { diff --git a/lib/modules/platform/bitbucket/index.ts b/lib/modules/platform/bitbucket/index.ts index d68db6debe1b612894c7413526b33e684eb580c2..299a39e6d1acaf4ba916675ea61a65f27c5e568b 100644 --- a/lib/modules/platform/bitbucket/index.ts +++ b/lib/modules/platform/bitbucket/index.ts @@ -8,6 +8,7 @@ import { BranchStatus, PrState, VulnerabilityAlert } from '../../../types'; import * as git from '../../../util/git'; import * as hostRules from '../../../util/host-rules'; import { BitbucketHttp, setBaseUrl } from '../../../util/http/bitbucket'; +import type { HttpOptions } from '../../../util/http/types'; import { regEx } from '../../../util/regex'; import { sanitize } from '../../../util/sanitize'; import type { @@ -55,10 +56,11 @@ export async function initPlatform({ endpoint, username, password, + token, }: PlatformParams): Promise<PlatformResult> { - if (!(username && password)) { + if (!(username && password) && !token) { throw new Error( - 'Init: You must configure a Bitbucket username and password' + 'Init: You must configure either a Bitbucket token or username and password' ); } if (endpoint && endpoint !== BITBUCKET_PROD_ENDPOINT) { @@ -69,13 +71,18 @@ export async function initPlatform({ } setBaseUrl(defaults.endpoint); renovateUserUuid = null; + const options: HttpOptions = { + useCache: false, + }; + if (token) { + options.token = token; + } else { + options.username = username; + options.password = password; + } try { const { uuid } = ( - await bitbucketHttp.getJson<Account>('/2.0/user', { - username, - password, - useCache: false, - }) + await bitbucketHttp.getJson<Account>('/2.0/user', options) ).body; renovateUserUuid = uuid; } catch (err) { @@ -193,11 +200,12 @@ export async function initRepo({ // TODO #7154 const hostnameWithoutApiPrefix = regEx(/api[.|-](.+)/).exec(hostname!)?.[1]; + const auth = opts.token + ? `x-token-auth:${opts.token}` + : `${opts.username!}:${opts.password!}`; const url = git.getUrl({ protocol: 'https', - // TODO: types (#7154) - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - auth: `${opts.username}:${opts.password}`, + auth, hostname: hostnameWithoutApiPrefix, repository, });