diff --git a/lib/workers/repository/process/limits.spec.ts b/lib/workers/repository/process/limits.spec.ts index 8c2fc901ec3738ad17841a7a30ea133f9f84101f..ae31e9a46d1cb96bd301abda1531efa6089a9d40 100644 --- a/lib/workers/repository/process/limits.spec.ts +++ b/lib/workers/repository/process/limits.spec.ts @@ -1,15 +1,9 @@ import moment from 'moment'; -import { - RenovateConfig, - getConfig, - git, - platform, -} from '../../../../test/util'; +import { RenovateConfig, getConfig, platform } from '../../../../test/util'; +import { PrState } from '../../../types'; import { BranchConfig } from '../../common'; import * as limits from './limits'; -jest.mock('../../../util/git'); - let config: RenovateConfig; beforeEach(() => { jest.resetAllMocks(); @@ -39,18 +33,25 @@ describe('workers/repository/process/limits', () => { }); }); describe('getConcurrentPrsRemaining()', () => { - it('calculates concurrent limit remaining', () => { + it('calculates concurrent limit remaining', async () => { config.prConcurrentLimit = 20; - git.branchExists.mockReturnValueOnce(true); + platform.getBranchPr.mockImplementation((branchName) => + branchName + ? Promise.resolve({ + sourceBranch: branchName, + state: PrState.Open, + } as never) + : Promise.reject('some error') + ); const branches: BranchConfig[] = [ - { branchName: 'test', upgrades: [] }, - { branchName: undefined, upgrades: [] }, - ]; - const res = limits.getConcurrentPrsRemaining(config, branches); + { branchName: 'test' }, + { branchName: null }, + ] as never; + const res = await limits.getConcurrentPrsRemaining(config, branches); expect(res).toEqual(19); }); - it('returns 99 if no concurrent limit', () => { - const res = limits.getConcurrentPrsRemaining(config, []); + it('returns 99 if no concurrent limit', async () => { + const res = await limits.getConcurrentPrsRemaining(config, []); expect(res).toEqual(99); }); }); diff --git a/lib/workers/repository/process/limits.ts b/lib/workers/repository/process/limits.ts index d26b5391cf76c27e1bfd80d85a659d1250df776a..088c84752d6be574aac349f9d71eff3cb67f8f3d 100644 --- a/lib/workers/repository/process/limits.ts +++ b/lib/workers/repository/process/limits.ts @@ -1,8 +1,8 @@ import moment from 'moment'; import { RenovateConfig } from '../../../config'; import { logger } from '../../../logger'; -import { platform } from '../../../platform'; -import { branchExists } from '../../../util/git'; +import { Pr, platform } from '../../../platform'; +import { PrState } from '../../../types'; import { BranchConfig } from '../../common'; export async function getPrHourlyRemaining( @@ -40,22 +40,39 @@ export async function getPrHourlyRemaining( return 99; } -export function getConcurrentPrsRemaining( +export async function getConcurrentPrsRemaining( config: RenovateConfig, branches: BranchConfig[] -): number { +): Promise<number> { if (config.prConcurrentLimit) { - logger.debug(`Enforcing prConcurrentLimit (${config.prConcurrentLimit})`); - let currentlyOpen = 0; - for (const branch of branches) { - if (branchExists(branch.branchName)) { - currentlyOpen += 1; + logger.debug(`Calculating prConcurrentLimit (${config.prConcurrentLimit})`); + try { + const openPrs: Pr[] = []; + for (const { branchName } of branches) { + try { + const pr = await platform.getBranchPr(branchName); + if ( + pr && + pr.sourceBranch !== config.onboardingBranch && + pr.state === PrState.Open + ) { + openPrs.push(pr); + } + } catch (err) { + // no-op + } } + logger.debug(`${openPrs.length} PRs are currently open`); + const concurrentRemaining = Math.max( + 0, + config.prConcurrentLimit - openPrs.length + ); + logger.debug(`PR concurrent limit remaining: ${concurrentRemaining}`); + return concurrentRemaining; + } catch (err) /* istanbul ignore next */ { + logger.error({ err }, 'Error checking concurrent PRs'); + return config.prConcurrentLimit; } - logger.debug(`${currentlyOpen} PRs are currently open`); - const concurrentRemaining = config.prConcurrentLimit - currentlyOpen; - logger.debug(`PR concurrent limit remaining: ${concurrentRemaining}`); - return concurrentRemaining; } return 99; } @@ -65,7 +82,7 @@ export async function getPrsRemaining( branches: BranchConfig[] ): Promise<number> { const hourlyRemaining = await getPrHourlyRemaining(config); - const concurrentRemaining = getConcurrentPrsRemaining(config, branches); + const concurrentRemaining = await getConcurrentPrsRemaining(config, branches); return hourlyRemaining < concurrentRemaining ? hourlyRemaining : concurrentRemaining; diff --git a/lib/workers/repository/process/write.ts b/lib/workers/repository/process/write.ts index 62752e0b15536ded7c3ef359b13b02cef0ff674d..7af253ec845c070399bfad3bca85d748eea8b1e8 100644 --- a/lib/workers/repository/process/write.ts +++ b/lib/workers/repository/process/write.ts @@ -1,5 +1,7 @@ import { RenovateConfig } from '../../../config'; import { addMeta, logger, removeMeta } from '../../../logger'; +import { platform } from '../../../platform'; +import { PrState } from '../../../types'; import { branchExists } from '../../../util/git'; import { processBranch } from '../../branch'; import { BranchConfig, ProcessBranchResult } from '../../common'; @@ -8,6 +10,15 @@ import { getPrsRemaining } from './limits'; export type WriteUpdateResult = 'done' | 'automerged'; +async function prExists(branchName: string): Promise<boolean> { + try { + const pr = await platform.getBranchPr(branchName); + return pr?.state === PrState.Open; + } catch (err) /* istanbul ignore next */ { + return false; + } +} + export async function writeUpdates( config: RenovateConfig, allBranches: BranchConfig[] @@ -35,6 +46,7 @@ export async function writeUpdates( const prLimitReached = prsRemaining <= 0; const commitLimitReached = isLimitReached(Limit.Commits); const branchExisted = branchExists(branch.branchName); + const prExisted = await prExists(branch.branchName); const res = await processBranch(branch, prLimitReached, commitLimitReached); branch.res = res; if ( @@ -64,6 +76,14 @@ export async function writeUpdates( ) { deductPrRemainingCount = 1; } + // istanbul ignore if + if ( + deductPrRemainingCount === 0 && + !prExisted && + (await prExists(branch.branchName)) + ) { + deductPrRemainingCount = 1; + } prsRemaining -= deductPrRemainingCount; } removeMeta(['branch']);