Files
renovate/lib/util/git/commit-trailers.spec.ts
T
Felipe Santos ddbbb687f9 feat: add commitTrailers option (#44651)
* feat(config): add commitTrailers option

Renovate's only way to add git trailers today is `commitBody`, but it's
broken on Gerrit: Renovate appends its own `Renovate-Branch`/`Change-Id`
block after the body, so user trailers end up in a non-final message
block and are not real trailers per git-interpret-trailers rules.

This adds a `commitTrailers: string[]` option (each entry a full
`Key: value` line), standardizes Gerrit's own trailers through the same
mechanism so user trailers and platform trailers merge into one final
block, and verifies the result at runtime with `git interpret-trailers`
(commitlint's trailer-exists rule uses the same approach). The
`:gitSignOff` preset is migrated from `commitBody` to `commitTrailers`
so sign-off works correctly on Gerrit too.

* Simplify a bit

* Simplify docs even more

* Simplify another bit

* Add comment about rawExec

* Drop runtime commit trailer verification

Bypass simple-git only to emit a proactive warn log is not worth the
extra surface area. Config validation already enforces Key: value form.

* test(git): cover string commit message with trailers

Codecov patch failed on the partial ternary branch that normalizes a
string message before appending trailers.

* test(gerrit): fix trailers test after scm rework

Align the commitTrailers merge test with the post-rework Gerrit SCM:
use fakeSha helpers and drop the removed force flag.

* Improve wording on gitSignOff docs

* Validate compiled commitTrailers as well

* Improve trailers example

* Use isString instead

* Update lib/util/git/index.ts

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>

* Update lib/workers/repository/onboarding/branch/create.ts

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>

* Format code

* Fix type-check

* Fix tests

---------

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
2026-07-27 06:49:09 +00:00

67 lines
2.0 KiB
TypeScript

import { logger } from '~test/util.ts';
import {
filterValidCommitTrailers,
isValidCommitTrailer,
} from './commit-trailers.ts';
describe('util/git/commit-trailers', () => {
describe('isValidCommitTrailer', () => {
it.each([
'Signed-off-by: Renovate Bot <bot@renovateapp.com>',
'Signed-off-by: {{{gitAuthor}}}',
'Co-authored-by: First Contributor <first@example.com>',
'Renovate-Update-Type: major',
'X-Custom: value with spaces',
])('accepts %j', (trailer) => {
expect(isValidCommitTrailer(trailer)).toBeTrue();
});
it.each([
'no colon',
'Bad key: value',
'Key:no-space',
'Key: multi\nline',
'Signed-off-by: ',
'Signed-off-by:',
'',
42,
null,
undefined,
])('rejects %j', (trailer) => {
expect(isValidCommitTrailer(trailer)).toBeFalse();
});
});
describe('filterValidCommitTrailers', () => {
it('returns valid trailers unchanged', () => {
const trailers = [
'Signed-off-by: Renovate Bot <bot@renovateapp.com>',
'Co-authored-by: First Contributor <first@example.com>',
];
expect(filterValidCommitTrailers(trailers)).toEqual(trailers);
expect(logger.logger.warn).not.toHaveBeenCalled();
});
it('drops invalid trailers and warns', () => {
expect(
filterValidCommitTrailers([
'Signed-off-by: Renovate Bot <bot@renovateapp.com>',
'Signed-off-by: ',
'Key: multi\nline',
]),
).toEqual(['Signed-off-by: Renovate Bot <bot@renovateapp.com>']);
expect(logger.logger.warn).toHaveBeenCalledExactlyOnceWith(
{ invalid: ['Signed-off-by: ', 'Key: multi\nline'] },
'Ignoring invalid commit trailers (must be a single-line Key: value)',
);
});
it('returns empty array when all trailers are invalid', () => {
expect(
filterValidCommitTrailers(['Signed-off-by: ', 'no colon']),
).toEqual([]);
expect(logger.logger.warn).toHaveBeenCalled();
});
});
});