Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): allow multiple branch/pr limits #32556

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions lib/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ export interface RenovateConfig
packageFile?: string;
packageRules?: PackageRule[];
postUpdateOptions?: string[];
branchConcurrentLimit?: number | null;
prConcurrentLimit?: number;
prHourlyLimit?: number;
forkModeDisallowMaintainerEdits?: boolean;
Expand Down
244 changes: 244 additions & 0 deletions lib/workers/global/limits.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { partial } from '../../../test/util';
import type { BranchConfig, BranchUpgradeConfig } from '../types';
import {
calcLimit,
hasMultipleLimits,
incCountValue,
incLimitedValue,
isLimitReached,
resetAllLimits,
setCount,
setMaxLimit,
} from './limits';

Expand Down Expand Up @@ -60,4 +66,242 @@ describe('workers/global/limits', () => {
setMaxLimit('Commits', -1000);
expect(isLimitReached('Commits')).toBeTrue();
});

describe('calcLimit', () => {
it('handles single upgrade', () => {
const upgrades = partial<BranchUpgradeConfig>([
{
prHourlyLimit: 10,
branchConcurrentLimit: 11,
prConcurrentLimit: 12,
},
]);

expect(calcLimit(upgrades, 'prHourlyLimit')).toBe(10);
expect(calcLimit(upgrades, 'branchConcurrentLimit')).toBe(11);
expect(calcLimit(upgrades, 'prConcurrentLimit')).toBe(12);
});

it('inherits prConcurrentLimit if branchConcurrentLimit is null', () => {
const upgrades = partial<BranchUpgradeConfig>([
{
prHourlyLimit: 10,
branchConcurrentLimit: null,
prConcurrentLimit: 12,
},
]);

expect(calcLimit(upgrades, 'prHourlyLimit')).toBe(10);
expect(calcLimit(upgrades, 'branchConcurrentLimit')).toBe(12);
expect(calcLimit(upgrades, 'prConcurrentLimit')).toBe(12);
});

it('returns 0 if atleast one upgrade has no limit in the branch', () => {
const upgrades = partial<BranchUpgradeConfig>([
{
prHourlyLimit: 10,
branchConcurrentLimit: 11,
prConcurrentLimit: 12,
},
{
prHourlyLimit: 0,
branchConcurrentLimit: 0,
prConcurrentLimit: 0,
},
{
prHourlyLimit: 1,
branchConcurrentLimit: 1,
prConcurrentLimit: 1,
},
]);

expect(calcLimit(upgrades, 'prHourlyLimit')).toBe(0);
expect(calcLimit(upgrades, 'branchConcurrentLimit')).toBe(0);
expect(calcLimit(upgrades, 'prConcurrentLimit')).toBe(0);
});

it('computes the lowest limit if multiple limits are present', () => {
const upgrades = partial<BranchUpgradeConfig>([
{
prHourlyLimit: 10,
branchConcurrentLimit: 11,
prConcurrentLimit: 12,
},
{
prHourlyLimit: 10,
branchConcurrentLimit: 11,
prConcurrentLimit: 12,
},
{
prHourlyLimit: 1,
branchConcurrentLimit: 1,
prConcurrentLimit: 1,
},
{
prHourlyLimit: 5,
branchConcurrentLimit: 6,
prConcurrentLimit: 3,
},
{
prHourlyLimit: 5,
branchConcurrentLimit: null,
prConcurrentLimit: undefined,
},
{
prHourlyLimit: 5,
branchConcurrentLimit: 6,
prConcurrentLimit: 2,
},
]);

expect(calcLimit(upgrades, 'prHourlyLimit')).toBe(1);
expect(calcLimit(upgrades, 'branchConcurrentLimit')).toBe(1);
expect(calcLimit(upgrades, 'prConcurrentLimit')).toBe(1);
});
});

describe('hasMultipleLimits', () => {
it('handles single limit', () => {
const upgrades = partial<BranchUpgradeConfig>([
{
prHourlyLimit: 10,
branchConcurrentLimit: 11,
prConcurrentLimit: 12,
},
]);
expect(hasMultipleLimits(upgrades, 'prHourlyLimit')).toBe(false);
expect(hasMultipleLimits(upgrades, 'branchConcurrentLimit')).toBe(false);
expect(hasMultipleLimits(upgrades, 'prConcurrentLimit')).toBe(false);
});

it('returns false if there are multiple limits with value', () => {
const upgrades = partial<BranchUpgradeConfig>([
{
prHourlyLimit: 10,
branchConcurrentLimit: 11,
prConcurrentLimit: 12,
},
{
prHourlyLimit: 10,
branchConcurrentLimit: 11,
prConcurrentLimit: 12,
},
]);
expect(hasMultipleLimits(upgrades, 'prHourlyLimit')).toBe(false);
expect(hasMultipleLimits(upgrades, 'branchConcurrentLimit')).toBe(false);
expect(hasMultipleLimits(upgrades, 'prConcurrentLimit')).toBe(false);
});

it('handles multiple limits', () => {
const upgrades = partial<BranchUpgradeConfig>([
{
prHourlyLimit: 10,
branchConcurrentLimit: 11,
prConcurrentLimit: 12,
},
{
prHourlyLimit: 11,
branchConcurrentLimit: 12,
prConcurrentLimit: 13,
},
{
prHourlyLimit: 0,
branchConcurrentLimit: null,
prConcurrentLimit: 3,
},
]);
expect(hasMultipleLimits(upgrades, 'prHourlyLimit')).toBe(true);
expect(hasMultipleLimits(upgrades, 'branchConcurrentLimit')).toBe(true);
expect(hasMultipleLimits(upgrades, 'prConcurrentLimit')).toBe(true);
});
});

describe('isLimitReached', () => {
it('returns false based on concurrent limits', () => {
setCount('ConcurrentPRs', 1);
setCount('HourlyPRs', 1);
incCountValue('Branches'); // using incCountValue so it gets test coverage
const upgrades = partial<BranchUpgradeConfig>([
{
prHourlyLimit: 10,
branchConcurrentLimit: 11,
prConcurrentLimit: 12,
},
{
prHourlyLimit: 11,
branchConcurrentLimit: 12,
prConcurrentLimit: 13,
},
{
prHourlyLimit: 0,
branchConcurrentLimit: null,
prConcurrentLimit: 3,
},
]);
expect(
isLimitReached('Branches', partial<BranchConfig>({ upgrades })),
).toBe(false);
expect(
isLimitReached('ConcurrentPRs', partial<BranchConfig>({ upgrades })),
).toBe(false);
});

it('returns true when hourly limit is reached', () => {
setCount('Branches', 2);
setCount('ConcurrentPRs', 2);
setCount('HourlyPRs', 2);
const upgrades = partial<BranchUpgradeConfig>([
{
prHourlyLimit: 10,
branchConcurrentLimit: 11,
prConcurrentLimit: 12,
},
{
prHourlyLimit: 11,
branchConcurrentLimit: 12,
prConcurrentLimit: 13,
},
{
prHourlyLimit: 2,
branchConcurrentLimit: null,
prConcurrentLimit: 3,
},
]);
expect(
isLimitReached('Branches', partial<BranchConfig>({ upgrades })),
).toBe(true);
expect(
isLimitReached('ConcurrentPRs', partial<BranchConfig>({ upgrades })),
).toBe(true);
});

it('returns true when concurrent limit is reached', () => {
setCount('Branches', 3);
setCount('ConcurrentPRs', 3);
setCount('HourlyPRs', 4);
const upgrades = partial<BranchUpgradeConfig>([
{
prHourlyLimit: 10,
branchConcurrentLimit: 11,
prConcurrentLimit: 12,
},
{
prHourlyLimit: 11,
branchConcurrentLimit: 12,
prConcurrentLimit: 13,
},
{
prHourlyLimit: 5,
branchConcurrentLimit: null,
prConcurrentLimit: 3,
},
]);
expect(
isLimitReached('Branches', partial<BranchConfig>({ upgrades })),
).toBe(true);
expect(
isLimitReached('ConcurrentPRs', partial<BranchConfig>({ upgrades })),
).toBe(true);
});
});
});
Loading