diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index c3049b6f28fffa..b5db090e3d6256 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -79,6 +79,11 @@ With the above config: - ESLint dependencies will have the label `linting` - All other dependencies will have the label `dependencies` + +!!! note + Keep your labels within the maximum character limit for your Git hosting platform. + Renovate usually truncates labels to 50 characters, except for GitLab, which has a 255 character limit. + ## additionalBranchPrefix By default, the value for this config option is an empty string. @@ -2200,6 +2205,11 @@ Behavior details: The `labels` array is non-mergeable, meaning if multiple `packageRules` match then Renovate uses the last value for `labels`. If you want to add/combine labels, use the `addLabels` config option, which is mergeable. + +!!! note + Keep your labels within the maximum character limit for your Git hosting platform. + Renovate usually truncates labels to 50 characters, except for GitLab, which has a 255 character limit. + ## lockFileMaintenance You can use `lockFileMaintenance` to refresh lock files to keep them up-to-date. diff --git a/lib/modules/platform/gitlab/index.ts b/lib/modules/platform/gitlab/index.ts index de544f48cce3e0..c8ba81c0dfc894 100644 --- a/lib/modules/platform/gitlab/index.ts +++ b/lib/modules/platform/gitlab/index.ts @@ -923,6 +923,11 @@ export function maxBodyLength(): number { } } +// istanbul ignore next: no need to test +export function labelCharLimit(): number { + return 255; +} + // Branch function matchesState(state: string, desiredState: string): boolean { diff --git a/lib/modules/platform/types.ts b/lib/modules/platform/types.ts index 752d98d779b274..91575d0981572c 100644 --- a/lib/modules/platform/types.ts +++ b/lib/modules/platform/types.ts @@ -280,6 +280,7 @@ export interface Platform { expandGroupMembers?(reviewersOrAssignees: string[]): Promise; maxBodyLength(): number; + labelCharLimit?(): number; } export interface PlatformScm { diff --git a/lib/workers/repository/update/pr/labels.spec.ts b/lib/workers/repository/update/pr/labels.spec.ts index 7dca6fbc4517ee..480405997fbe90 100644 --- a/lib/workers/repository/update/pr/labels.spec.ts +++ b/lib/workers/repository/update/pr/labels.spec.ts @@ -1,3 +1,4 @@ +import { platform } from '../../../../../test/util'; import { areLabelsModified, getChangedLabels, @@ -82,6 +83,44 @@ describe('workers/repository/update/pr/labels', () => { expect(result).toBeArrayOfSize(0); expect(result).toEqual([]); }); + + describe('trim labels that go over the max char limit', () => { + const labels = [ + 'All', + 'The quick brown fox jumped over the lazy sleeping dog', // len: 51 + // len: 256 + 'Torem ipsum dolor sit amet, consectetur adipiscing elit. Sed fringilla erat eu lectus gravida varius. Maecenas suscipit risus nec erat mollis tempus. Vestibulum cursus urna et faucibus tempor. Nam eleifend libero in enim sodales, eu placerat enim dice rep!', + ]; + + it('github', () => { + expect(prepareLabels({ labels })).toEqual([ + 'All', + 'The quick brown fox jumped over the lazy sleeping', // len: 50 + 'Torem ipsum dolor sit amet, consectetur adipiscing', // len: 50 + ]); + }); + + it('gitlab', () => { + jest.spyOn(platform, 'labelCharLimit').mockImplementationOnce(() => { + return 255; + }); + // platform.labelCharLimit.mockReturnValueOnce(255); + expect(prepareLabels({ labels })).toEqual([ + 'All', + 'The quick brown fox jumped over the lazy sleeping dog', // len: 51 + // len: 255 + 'Torem ipsum dolor sit amet, consectetur adipiscing elit. Sed fringilla erat eu lectus gravida varius. Maecenas suscipit risus nec erat mollis tempus. Vestibulum cursus urna et faucibus tempor. Nam eleifend libero in enim sodales, eu placerat enim dice rep', + ]); + }); + + it('gitea', () => { + expect(prepareLabels({ labels })).toEqual([ + 'All', + 'The quick brown fox jumped over the lazy sleeping', // len: 50 + 'Torem ipsum dolor sit amet, consectetur adipiscing', // len: 50 + ]); + }); + }); }); describe('getChangedLabels', () => { diff --git a/lib/workers/repository/update/pr/labels.ts b/lib/workers/repository/update/pr/labels.ts index 3c41e47b19740f..36b5d912cebab9 100644 --- a/lib/workers/repository/update/pr/labels.ts +++ b/lib/workers/repository/update/pr/labels.ts @@ -2,15 +2,30 @@ import is from '@sindresorhus/is'; import { dequal } from 'dequal'; import type { RenovateConfig } from '../../../../config/types'; import { logger } from '../../../../logger'; +import { platform } from '../../../../modules/platform'; import * as template from '../../../../util/template'; +/** + * Filter labels that go over the maximum char limit, based on platform limits. + */ +function trimLabel(label: string, limit: number): string { + const trimmed = label.trim(); + if (trimmed.length <= limit) { + return trimmed; + } + + return trimmed.slice(0, limit).trim(); +} + export function prepareLabels(config: RenovateConfig): string[] { + const labelCharLimit = platform.labelCharLimit?.() ?? 50; const labels = config.labels ?? []; const addLabels = config.addLabels ?? []; return [...new Set([...labels, ...addLabels])] .filter(is.nonEmptyStringAndNotWhitespace) .map((label) => template.compile(label, config)) .filter(is.nonEmptyStringAndNotWhitespace) + .map((label) => trimLabel(label, labelCharLimit)) .sort(); }