diff --git a/lib/workers/repository/model/commit-message.ts b/lib/workers/repository/model/commit-message.ts index 0344e5b6471fc0ee8eb2f321ec58d84ae11edfb6..b909f5bc1d837da52d340f71af9147d1b35549ca 100644 --- a/lib/workers/repository/model/commit-message.ts +++ b/lib/workers/repository/model/commit-message.ts @@ -60,6 +60,10 @@ export abstract class CommitMessage { this._footer = this.normalizeInput(value); } + get subject(): string { + return this._subject; + } + set subject(value: string) { this._subject = this.normalizeInput(value); this._subject = this._subject?.replace( diff --git a/lib/workers/repository/model/semantic-commit-message.spec.ts b/lib/workers/repository/model/semantic-commit-message.spec.ts index ffff30f3258f54c839708d3e335c3e3e6dbbaf59..e2629fcc02edd3575ccca58212b165ba07bd5c46 100644 --- a/lib/workers/repository/model/semantic-commit-message.spec.ts +++ b/lib/workers/repository/model/semantic-commit-message.spec.ts @@ -25,6 +25,15 @@ describe('workers/repository/model/semantic-commit-message', () => { expect(message.toString()).toBe('fix(scope): test'); }); + it('should transform to lowercase only first letter', () => { + const message = new SemanticCommitMessage(); + message.subject = 'Update My Org dependencies'; + message.type = 'fix'; + message.scope = 'deps '; + + expect(message.toString()).toBe('fix(deps): update My Org dependencies'); + }); + it('should create instance from string without scope', () => { const instance = SemanticCommitMessage.fromString('feat: ticket 123'); diff --git a/lib/workers/repository/updates/generate.spec.ts b/lib/workers/repository/updates/generate.spec.ts index a6fa8195bf381bfd1754a62ddc07778764a84b14..87d7c55786962a9d652b5694db1e430c8b07dd76 100644 --- a/lib/workers/repository/updates/generate.spec.ts +++ b/lib/workers/repository/updates/generate.spec.ts @@ -506,7 +506,7 @@ describe('workers/repository/updates/generate', () => { } as BranchUpgradeConfig, ]; const res = generateBranchConfig(branch); - expect(res.prTitle).toBe('chore(): update dependency some-dep to v1.2.0'); + expect(res.prTitle).toBe('chore: update dependency some-dep to v1.2.0'); }); it('scopes monorepo commits with nested package files using parent directory', () => { @@ -915,5 +915,37 @@ describe('workers/repository/updates/generate', () => { '`1.1.1` (+1)', ]); }); + + it('fixes commit message with body', () => { + const branch: BranchUpgradeConfig[] = [ + { + manager: 'some-manager', + branchName: 'some-branch', + commitMessage: 'update to vv1.2.0', + commitBody: 'some body', + }, + ]; + const res = generateBranchConfig(branch); + expect(res.commitMessage).toBe('Update to v1.2.0\n\nsome body'); + }); + + it('generates semantic commit message properly', () => { + const branch: BranchUpgradeConfig[] = [ + { + ...defaultConfig, + manager: 'some-manager', + branchName: 'some-branch', + semanticCommits: 'enabled', + semanticCommitType: 'chore', + semanticCommitScope: 'deps', + depName: 'some-dep', + newValue: '1.2.0', + } as BranchUpgradeConfig, + ]; + const res = generateBranchConfig(branch); + expect(res.commitMessage).toBe( + 'chore(deps): update dependency some-dep to 1.2.0' + ); + }); }); }); diff --git a/lib/workers/repository/updates/generate.ts b/lib/workers/repository/updates/generate.ts index 04af7f203a1bee9f7264b39ba6ead3da4130fd24..2f6c7c2826e60906c386cbf2cbd0f78cd728cdbf 100644 --- a/lib/workers/repository/updates/generate.ts +++ b/lib/workers/repository/updates/generate.ts @@ -6,11 +6,12 @@ import semver from 'semver'; import { mergeChildConfig } from '../../../config'; import { CONFIG_SECRETS_EXPOSED } from '../../../constants/error-messages'; import { logger } from '../../../logger'; -import { newlineRegex, regEx } from '../../../util/regex'; +import { regEx } from '../../../util/regex'; import { sanitize } from '../../../util/sanitize'; import * as template from '../../../util/template'; import type { BranchConfig, BranchUpgradeConfig } from '../../types'; -import { CommitMessage } from '../model/commit-message'; +import { CommitMessageFactory } from '../model/commit-message-factory'; +import { SemanticCommitMessage } from '../model/semantic-commit-message'; function isTypesGroup(branchUpgrades: BranchUpgradeConfig[]): boolean { return ( @@ -167,28 +168,27 @@ export function generateBranchConfig( } else if (semver.valid(toVersions[0])) { upgrade.isRange = false; } + const commitMessageFactory = new CommitMessageFactory(upgrade); + const commitMessage = commitMessageFactory.create(); // Use templates to generate strings - if (upgrade.semanticCommits === 'enabled' && !upgrade.commitMessagePrefix) { + if ( + SemanticCommitMessage.is(commitMessage) && + upgrade.semanticCommitScope + ) { logger.trace('Upgrade has semantic commits enabled'); - let semanticPrefix = upgrade.semanticCommitType; - if (upgrade.semanticCommitScope) { - semanticPrefix += `(${template.compile( - upgrade.semanticCommitScope, - upgrade - )})`; - } - upgrade.commitMessagePrefix = CommitMessage.formatPrefix(semanticPrefix!); - upgrade.toLowerCase = - regEx(/[A-Z]/).exec(upgrade.semanticCommitType!) === null && - !upgrade.semanticCommitType!.startsWith(':'); + commitMessage.scope = template.compile( + upgrade.semanticCommitScope, + upgrade + ); } // Compile a few times in case there are nested templates - upgrade.commitMessage = template.compile( + commitMessage.subject = template.compile( upgrade.commitMessage ?? '', upgrade ); - upgrade.commitMessage = template.compile(upgrade.commitMessage, upgrade); - upgrade.commitMessage = template.compile(upgrade.commitMessage, upgrade); + commitMessage.subject = template.compile(commitMessage.subject, upgrade); + commitMessage.subject = template.compile(commitMessage.subject, upgrade); + upgrade.commitMessage = commitMessage.toString(); // istanbul ignore if if (upgrade.commitMessage !== sanitize(upgrade.commitMessage)) { logger.debug( @@ -197,23 +197,14 @@ export function generateBranchConfig( ); throw new Error(CONFIG_SECRETS_EXPOSED); } - upgrade.commitMessage = upgrade.commitMessage.trim(); // Trim exterior whitespace - upgrade.commitMessage = upgrade.commitMessage.replace(regEx(/\s+/g), ' '); // Trim extra whitespace inside string - upgrade.commitMessage = upgrade.commitMessage.replace( + commitMessage.subject = commitMessage.subject.replace( regEx(/to vv(\d)/), 'to v$1' ); - if (upgrade.toLowerCase) { - // We only need to lowercase the first line - const splitMessage = upgrade.commitMessage.split(newlineRegex); - splitMessage[0] = splitMessage[0].toLowerCase(); - upgrade.commitMessage = splitMessage.join('\n'); - } + upgrade.commitMessage = commitMessage.toString(); if (upgrade.commitBody) { - upgrade.commitMessage = `${upgrade.commitMessage}\n\n${template.compile( - upgrade.commitBody, - upgrade - )}`; + commitMessage.body = template.compile(upgrade.commitBody, upgrade); + upgrade.commitMessage = commitMessage.toString(); } logger.trace(`commitMessage: ` + JSON.stringify(upgrade.commitMessage)); if (upgrade.prTitle) { @@ -235,7 +226,7 @@ export function generateBranchConfig( upgrade.prTitle = upgrade.prTitle.toLowerCase(); } } else { - [upgrade.prTitle] = upgrade.commitMessage.split(newlineRegex); + upgrade.prTitle = commitMessage.title; } upgrade.prTitle += upgrade.hasBaseBranches ? ' ({{baseBranch}})' : ''; if (upgrade.isGroup) {