diff --git a/README.md b/README.md index 11b957f..54d4952 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,20 @@ Automatically confirm the generated commit message without prompting the user. aicommit config set auto-confirm=true ``` +#### prepend-reference + +Default: `false` + +Prepends issue reference from branch name to commit message. + +branch name: `feature/abc-123-branch-name` + +commit message: `ABC-123: ` + +```sh +aicommit config set prepend-reference=true +``` + ## How it works This CLI tool runs `git diff` to grab all your latest code changes, sends them to OpenAI's GPT-3, then returns the AI diff --git a/src/commands/aicommit.ts b/src/commands/aicommit.ts index 0f35f07..267a877 100644 --- a/src/commands/aicommit.ts +++ b/src/commands/aicommit.ts @@ -104,6 +104,19 @@ export default async ( message = selected as string; } + if (config['prepend-reference']) { + // Get the current branch name + const { stdout} = await execa('git', ['branch', '--show-current']); + + // Get reference from branch name + const taskNumber = stdout.match(/([a-zA-Z])+-([0-9]+)/)?.[0]; + + if (taskNumber?.length) { + // Prepend reference to commit message + message = `${taskNumber?.toUpperCase()}: ${message}`; + } + } + await execa('git', ['commit', '-m', message, ...rawArgv]); outro(`${green('✔')} Successfully committed!`); diff --git a/src/utils/config.ts b/src/utils/config.ts index 5820489..b906824 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -116,6 +116,18 @@ const configParsers = { parseAssert('auto-confirm', /^(?:true|false)$/.test(autoConfirm), 'Must be a boolean'); return autoConfirm === 'true'; }, + 'prepend-reference'(prependReference?: string | boolean) { + if (!prependReference) { + return false; + } + + if (typeof prependReference === 'boolean') { + return prependReference; + } + + parseAssert('prepend-reference', /^(?:true|false)$/.test(prependReference), 'Must be a boolean'); + return prependReference === 'true'; + }, } as const; type ConfigKeys = keyof typeof configParsers; diff --git a/tests/specs/cli/commits.ts b/tests/specs/cli/commits.ts index 07d2e39..498eb2c 100644 --- a/tests/specs/cli/commits.ts +++ b/tests/specs/cli/commits.ts @@ -60,6 +60,68 @@ export default testSuite(({ describe }) => { await fixture.rm(); }); + test('Generates commit message with prepended reference from branch name', async () => { + + const { fixture, aicommit } = await createFixture(files); + const git = await createGit(fixture.path); + + await git('checkout', ['-b', 'feature/abc-123-some-feature']); + await git('add', ['data.json']); + + const autoConfirm = 'prepend-reference=true' + await aicommit(['config', 'set', autoConfirm]) + + const committing = aicommit(); + committing.stdout!.on('data', (buffer: Buffer) => { + const stdout = buffer.toString(); + if (stdout.match('└')) { + committing.stdin!.write('y'); + committing.stdin!.end(); + } + }); + + await committing; + + const { stdout: commitMessage } = await git('log', ['--pretty=format:%s']); + console.log({ + commitMessage, + length: commitMessage.length, + }); + expect(commitMessage.startsWith('ABC-123: ')).toBe(true); + await fixture.rm(); + }); + + test('Generates commit message will no prepend if no valid reference', async () => { + + const { fixture, aicommit } = await createFixture(files); + const git = await createGit(fixture.path); + + await git('checkout', ['-b', 'feature/some-feature']); + await git('add', ['data.json']); + + const autoConfirm = 'prepend-reference=true' + await aicommit(['config', 'set', autoConfirm]) + + const committing = aicommit(); + committing.stdout!.on('data', (buffer: Buffer) => { + const stdout = buffer.toString(); + if (stdout.match('└')) { + committing.stdin!.write('y'); + committing.stdin!.end(); + } + }); + + await committing; + + const { stdout: commitMessage } = await git('log', ['--pretty=format:%s']); + console.log({ + commitMessage, + length: commitMessage.length, + }); + expect(commitMessage.length).toBeLessThanOrEqual(50); + await fixture.rm(); + }); + test('Generated commit message must be under 50 characters', async () => { const { fixture, aicommit } = await createFixture({ ...files, diff --git a/tests/specs/config.ts b/tests/specs/config.ts index 170fc2e..822afe4 100644 --- a/tests/specs/config.ts +++ b/tests/specs/config.ts @@ -125,6 +125,30 @@ export default testSuite(({ describe }) => { }) }) + await describe('prepend-reference', ({ test }) => { + test('must be a boolean', async () => { + const { stderr } = await aicommit(['config', 'set', 'prepend-reference=abc'], { + reject: false + }) + + expect(stderr).toMatch('Must be a boolean') + }) + + test('updates config', async () => { + const defaultConfig = await aicommit(['config', 'get', 'prepend-reference']) + expect(defaultConfig.stdout).toBe('prepend-reference=false') + + const autoConfirm = 'prepend-reference=true' + await aicommit(['config', 'set', autoConfirm]) + + const configFile = await fs.readFile(configPath, 'utf8') + expect(configFile).toMatch(autoConfirm) + + const get = await aicommit(['config', 'get', 'prepend-reference']) + expect(get.stdout).toBe(autoConfirm) + }) + }) + await test('set config file', async () => { await aicommit(['config', 'set', openAiToken])