From 25ae92d631d34507c194a2fd4353069bc03bb528 Mon Sep 17 00:00:00 2001 From: RahulGautamSingh Date: Thu, 21 Mar 2024 11:53:28 +0545 Subject: [PATCH] feat(platform/bitbucket-server): allow blobless clone (#27975) --- docs/usage/self-hosted-experimental.md | 4 +- .../platform/bitbucket-server/index.spec.ts | 51 +++++++++++++++++-- .../platform/bitbucket-server/index.ts | 34 +++++++++++-- 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/docs/usage/self-hosted-experimental.md b/docs/usage/self-hosted-experimental.md index b05d952b2c7180..bd95401a8cfc64 100644 --- a/docs/usage/self-hosted-experimental.md +++ b/docs/usage/self-hosted-experimental.md @@ -143,8 +143,8 @@ The expected value for this environment variable is a JSON array of strings. ## `RENOVATE_X_PLATFORM_VERSION` -If set, Renovate will use this string as GitLab server version instead of checking via the GitLab API. -This can be useful when you use the GitLab `CI_JOB_TOKEN` to authenticate Renovate. +Specify this string for Renovate to skip API checks and provide GitLab/Bitbucket server version directly. +Particularly useful with GitLab's `CI_JOB_TOKEN` to authenticate Renovate or to reduce API calls for Bitbucket. Read [platform details](modules/platform/gitlab/index.md) to learn why we need the server version on GitLab. diff --git a/lib/modules/platform/bitbucket-server/index.spec.ts b/lib/modules/platform/bitbucket-server/index.spec.ts index feb59252d1fa8b..1130a3bb678740 100644 --- a/lib/modules/platform/bitbucket-server/index.spec.ts +++ b/lib/modules/platform/bitbucket-server/index.spec.ts @@ -1,11 +1,13 @@ import is from '@sindresorhus/is'; import { mockDeep } from 'jest-mock-extended'; import * as httpMock from '../../../../test/http-mock'; +import { mocked } from '../../../../test/util'; import { REPOSITORY_CHANGED, REPOSITORY_EMPTY, REPOSITORY_NOT_FOUND, } from '../../../constants/error-messages'; +import type { logger as _logger } from '../../../logger'; import type * as _git from '../../../util/git'; import type { LongCommitSha } from '../../../util/git/types'; import type { Platform } from '../types'; @@ -185,6 +187,7 @@ describe('modules/platform/bitbucket-server/index', () => { let hostRules: jest.Mocked; let git: jest.Mocked; + let logger: jest.Mocked; const username = 'abc'; const password = '123'; @@ -211,6 +214,7 @@ describe('modules/platform/bitbucket-server/index', () => { // reset module jest.resetModules(); bitbucket = await import('.'); + logger = mocked(await import('../../../logger')).logger; hostRules = jest.requireMock('../../../util/host-rules'); git = jest.requireMock('../../../util/git'); git.branchExists.mockReturnValue(true); @@ -226,6 +230,10 @@ describe('modules/platform/bitbucket-server/index', () => { username, password, }); + httpMock + .scope(urlHost) + .get(`${urlPath}/rest/api/1.0/application-properties`) + .reply(200, { version: '8.0.0' }); await bitbucket.initPlatform({ endpoint, username, @@ -234,19 +242,52 @@ describe('modules/platform/bitbucket-server/index', () => { }); describe('initPlatform()', () => { - it('should throw if no endpoint', () => { + it('should throw if no endpoint', async () => { expect.assertions(1); - expect(() => bitbucket.initPlatform({})).toThrow(); + await expect(bitbucket.initPlatform({})).rejects.toThrow(); }); - it('should throw if no username/password', () => { + it('should throw if no username/password', async () => { expect.assertions(1); - expect(() => + await expect( bitbucket.initPlatform({ endpoint: 'endpoint' }), - ).toThrow(); + ).rejects.toThrow(); + }); + + it('should throw if version could not be fetched', async () => { + httpMock + .scope('https://stash.renovatebot.com') + .get('/rest/api/1.0/application-properties') + .reply(403); + + await bitbucket.initPlatform({ + endpoint: 'https://stash.renovatebot.com', + username: 'abc', + password: '123', + }); + expect(logger.debug).toHaveBeenCalledWith( + expect.any(Object), + 'Error authenticating with Bitbucket. Check that your token includes "api" permissions', + ); + }); + + it('should skip api call to fetch version when platform version is set in environment', async () => { + process.env.RENOVATE_X_PLATFORM_VERSION = '8.0.0'; + await expect( + bitbucket.initPlatform({ + endpoint: 'https://stash.renovatebot.com', + username: 'abc', + password: '123', + }), + ).toResolve(); + delete process.env.RENOVATE_X_PLATFORM_VERSION; }); it('should init', async () => { + httpMock + .scope('https://stash.renovatebot.com') + .get('/rest/api/1.0/application-properties') + .reply(200, { version: '8.0.0' }); expect( await bitbucket.initPlatform({ endpoint: 'https://stash.renovatebot.com', diff --git a/lib/modules/platform/bitbucket-server/index.ts b/lib/modules/platform/bitbucket-server/index.ts index 3b87a94a5916fc..32600a1926c59e 100644 --- a/lib/modules/platform/bitbucket-server/index.ts +++ b/lib/modules/platform/bitbucket-server/index.ts @@ -1,4 +1,5 @@ import { setTimeout } from 'timers/promises'; +import semver from 'semver'; import type { PartialDeep } from 'type-fest'; import { REPOSITORY_CHANGED, @@ -68,8 +69,10 @@ const bitbucketServerHttp = new BitbucketServerHttp(); const defaults: { endpoint?: string; hostType: string; + version: string; } = { hostType: 'bitbucket-server', + version: '0.0.0', }; /* istanbul ignore next */ @@ -79,7 +82,7 @@ function updatePrVersion(pr: number, version: number): number { return res; } -export function initPlatform({ +export async function initPlatform({ endpoint, username, password, @@ -98,7 +101,32 @@ export function initPlatform({ const platformConfig: PlatformResult = { endpoint: defaults.endpoint, }; - return Promise.resolve(platformConfig); + try { + let bitbucketServerVersion: string; + // istanbul ignore if: experimental feature + if (process.env.RENOVATE_X_PLATFORM_VERSION) { + bitbucketServerVersion = process.env.RENOVATE_X_PLATFORM_VERSION; + } else { + const { version } = ( + await bitbucketServerHttp.getJson<{ version: string }>( + `./rest/api/1.0/application-properties`, + ) + ).body; + bitbucketServerVersion = version; + logger.debug('Bitbucket Server version is: ' + bitbucketServerVersion); + } + + if (semver.valid(bitbucketServerVersion)) { + defaults.version = bitbucketServerVersion; + } + } catch (err) { + logger.debug( + { err }, + 'Error authenticating with Bitbucket. Check that your token includes "api" permissions', + ); + } + + return platformConfig; } // Get all repositories that the user has access to @@ -204,7 +232,7 @@ export async function initRepo({ ...config, url, cloneSubmodules, - fullClone: true, + fullClone: semver.lte(defaults.version, '8.0.0'), }); config.mergeMethod = 'merge';