diff --git a/lib/platform/bitbucket/bb-got-wrapper.spec.ts b/lib/platform/bitbucket/bb-got-wrapper.spec.ts index ecfdb7eac77a1ac8c2491125b1c959d5889fd138..1685fdf5ff22560e0385dde4ea16be1b5e0a292e 100644 --- a/lib/platform/bitbucket/bb-got-wrapper.spec.ts +++ b/lib/platform/bitbucket/bb-got-wrapper.spec.ts @@ -32,6 +32,17 @@ describe('platform/gl-got-wrapper', () => { const res = await api.post('some-url'); expect(res.body).toEqual(body); }); + it('accepts custom baseUrl', async () => { + got.mockImplementation(() => ({} as any)); + + await api.post('some-url'); + expect(got.mock.calls[0][1].baseUrl).toBe('https://api.bitbucket.org/'); + + const customBaseUrl = 'https://api-test.bitbucket.org'; + api.setBaseUrl(customBaseUrl); + await api.post('some-url'); + expect(got.mock.calls[1][1].baseUrl).toBe(customBaseUrl); + }); it('returns cached', async () => { got.mockReturnValueOnce({ body: {}, diff --git a/lib/platform/bitbucket/bb-got-wrapper.ts b/lib/platform/bitbucket/bb-got-wrapper.ts index e1072c3c68ac91e294634cf185759b5c225a1665..2f1e7bb591a5ef29a1d58068a108755a77b6048b 100644 --- a/lib/platform/bitbucket/bb-got-wrapper.ts +++ b/lib/platform/bitbucket/bb-got-wrapper.ts @@ -3,6 +3,7 @@ import got from '../../util/got'; import { GotApi, GotApiOptions, GotResponse } from '../common'; import { PLATFORM_TYPE_BITBUCKET } from '../../constants/platforms'; +let baseUrl = 'https://api.bitbucket.org/'; async function get( path: string, options: GotApiOptions & GotJSONOptions @@ -10,7 +11,7 @@ async function get( const opts: GotApiOptions & GotJSONOptions = { json: true, hostType: PLATFORM_TYPE_BITBUCKET, - baseUrl: 'https://api.bitbucket.org/', + baseUrl, ...options, }; const res = await got(path, opts); @@ -26,4 +27,9 @@ for (const x of helpers) { get(url, { ...opts, method: x.toUpperCase() }); } +// eslint-disable-next-line @typescript-eslint/unbound-method +api.setBaseUrl = (newBaseUrl: string): void => { + baseUrl = newBaseUrl; +}; + export default api; diff --git a/lib/platform/bitbucket/index.spec.ts b/lib/platform/bitbucket/index.spec.ts index 31f9437d1450f4bf802b54f03a349c61db1c6f1a..7bb812dde0240e19d616eacfd6690fbd7b30f349 100644 --- a/lib/platform/bitbucket/index.spec.ts +++ b/lib/platform/bitbucket/index.spec.ts @@ -3,21 +3,25 @@ import responses from './__fixtures__/responses'; import { GotApi, RepoParams, Platform } from '../common'; import { REPOSITORY_DISABLED } from '../../constants/error-messages'; import { BranchStatus } from '../../types'; +import { logger as _logger } from '../../logger'; describe('platform/bitbucket', () => { let bitbucket: Platform; let api: jest.Mocked<GotApi>; let hostRules: jest.Mocked<typeof import('../../util/host-rules')>; let GitStorage: jest.Mocked<import('../git/storage').Storage> & jest.Mock; + let logger: jest.Mocked<typeof _logger>; beforeEach(async () => { // reset module jest.resetModules(); jest.mock('./bb-got-wrapper'); jest.mock('../git/storage'); jest.mock('../../util/host-rules'); + jest.mock('../../logger'); hostRules = require('../../util/host-rules'); api = require('./bb-got-wrapper').api; bitbucket = await import('.'); + logger = (await import('../../logger')).logger as any; GitStorage = require('../git/storage').Storage; GitStorage.mockImplementation(() => ({ initRepo: jest.fn(), @@ -86,15 +90,15 @@ describe('platform/bitbucket', () => { expect.assertions(1); expect(() => bitbucket.initPlatform({})).toThrow(); }); - it('should throw if wrong endpoint', () => { - expect.assertions(1); - expect(() => - bitbucket.initPlatform({ - endpoint: 'endpoint', - username: 'abc', - password: '123', - }) - ).toThrow(); + it('should show warning message if custom endpoint', async () => { + await bitbucket.initPlatform({ + endpoint: 'endpoint', + username: 'abc', + password: '123', + }); + expect(logger.warn).toHaveBeenCalledWith( + 'Init: Bitbucket Cloud endpoint should generally be https://api.bitbucket.org/ but is being configured to a different value. Did you mean to use Bitbucket Server?' + ); }); it('should init', async () => { expect( diff --git a/lib/platform/bitbucket/index.ts b/lib/platform/bitbucket/index.ts index 5fbd4b3d4beae74c3d38881334d150f55e2973bd..a2091cb6b54d02e379fedb0fe223fac6ef5c366d 100644 --- a/lib/platform/bitbucket/index.ts +++ b/lib/platform/bitbucket/index.ts @@ -1,3 +1,4 @@ +import URL from 'url'; import parseDiff from 'parse-diff'; import addrs from 'email-addresses'; import { api } from './bb-got-wrapper'; @@ -32,6 +33,8 @@ import { PLATFORM_TYPE_BITBUCKET } from '../../constants/platforms'; import { BranchStatus } from '../../types'; import { RenovateConfig } from '../../config'; +const BITBUCKET_PROD_ENDPOINT = 'https://api.bitbucket.org/'; + let config: utils.Config = {} as any; export function initPlatform({ @@ -44,14 +47,14 @@ export function initPlatform({ 'Init: You must configure a Bitbucket username and password' ); } - if (endpoint && endpoint !== 'https://api.bitbucket.org/') { - throw new Error( - 'Init: Bitbucket Cloud endpoint can only be https://api.bitbucket.org/' + if (endpoint && endpoint !== BITBUCKET_PROD_ENDPOINT) { + logger.warn( + `Init: Bitbucket Cloud endpoint should generally be ${BITBUCKET_PROD_ENDPOINT} but is being configured to a different value. Did you mean to use Bitbucket Server?` ); } // TODO: Add a connection check that endpoint/username/password combination are valid const platformConfig: PlatformConfig = { - endpoint: 'https://api.bitbucket.org/', + endpoint: endpoint || BITBUCKET_PROD_ENDPOINT, }; return Promise.resolve(platformConfig); } @@ -76,12 +79,14 @@ export async function initRepo({ localDir, optimizeForDisabled, bbUseDefaultReviewers, + endpoint = BITBUCKET_PROD_ENDPOINT, }: RepoParams): Promise<RepoConfig> { logger.debug(`initRepo("${repository}")`); const opts = hostRules.find({ hostType: PLATFORM_TYPE_BITBUCKET, - url: 'https://api.bitbucket.org/', + url: endpoint, }); + api.setBaseUrl(endpoint); config = { repository, username: opts.username, @@ -130,10 +135,17 @@ export async function initRepo({ throw err; } + const { hostname } = URL.parse(endpoint); + + // Converts API hostnames to their respective HTTP git hosts: + // `api.bitbucket.org` to `bitbucket.org` + // `api-staging.<host>` to `staging.<host>` + const hostnameWithoutApiPrefix = /api[.|-](.+)/.exec(hostname)[1]; + const url = GitStorage.getUrl({ protocol: 'https', auth: `${opts.username}:${opts.password}`, - hostname: 'bitbucket.org', + hostname: hostnameWithoutApiPrefix, repository, });