Skip to content

Commit

Permalink
feat: support linting from the last tag
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben Quarmby committed Aug 2, 2024
1 parent 6ae3d69 commit f60ef02
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 1 deletion.
1 change: 1 addition & 0 deletions @commitlint/cli/src/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ test('should print help', async () => {
-x, --extends array of shareable configurations to extend [array]
-H, --help-url help url in error message [string]
-f, --from lower end of the commit range to lint; applies if edit=false [string]
--from-last-tag uses the last tag as the lower end of the commit range to lint; applies if edit=false and from is not set [boolean]
--git-log-args additional git log arguments as space separated string, example '--first-parent --cherry-pick' [string]
-l, --last just analyze the last commit; applies if edit=false [boolean]
-o, --format output format of the results [string]
Expand Down
7 changes: 7 additions & 0 deletions @commitlint/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ const cli = yargs(process.argv.slice(2))
'lower end of the commit range to lint; applies if edit=false',
type: 'string',
},
'from-last-tag': {
description:
'uses the last tag as the lower end of the commit range to lint; applies if edit=false and from is not set',
type: 'boolean',
},
'git-log-args': {
description:
"additional git log arguments as space separated string, example '--first-parent --cherry-pick'",
Expand Down Expand Up @@ -237,6 +242,7 @@ async function main(args: MainArgs): Promise<void> {
: read({
to: flags.to,
from: flags.from,
fromLastTag: flags['from-last-tag'],
last: flags.last,
edit: flags.edit,
cwd: flags.cwd,
Expand Down Expand Up @@ -395,6 +401,7 @@ function checkFromEdit(flags: CliFlags): boolean {
function checkFromHistory(flags: CliFlags): boolean {
return (
typeof flags.from === 'string' ||
typeof flags['from-last-tag'] === 'boolean' ||
typeof flags.to === 'string' ||
typeof flags.last === 'boolean'
);
Expand Down
1 change: 1 addition & 0 deletions @commitlint/cli/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface CliFlags {
help?: boolean;
'help-url'?: string;
from?: string;
'from-last-tag'?: boolean;
'git-log-args'?: string;
last?: boolean;
format?: string;
Expand Down
46 changes: 46 additions & 0 deletions @commitlint/read/src/read.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,49 @@ test('should only read the last commit', async () => {

expect(result).toEqual(['commit X']);
});

test('should read commits from the last annotated tag', async () => {
const cwd: string = await git.bootstrap();

await execa(
'git',
['commit', '--allow-empty', '-m', 'chore: release v1.0.0'],
{cwd}
);
await execa('git', ['tag', 'v1.0.0', '--annotate', '-m', 'v1.0.0'], {cwd});
await execa('git', ['commit', '--allow-empty', '-m', 'commit 1'], {cwd});
await execa('git', ['commit', '--allow-empty', '-m', 'commit 2'], {cwd});

const result = await read({cwd, fromLastTag: true});

expect(result).toEqual(['commit 2\n\n', 'commit 1\n\n']);
});

test('should read commits from the last lightweight tag', async () => {
const cwd: string = await git.bootstrap();

await execa(
'git',
['commit', '--allow-empty', '-m', 'chore: release v1.0.0'],
{cwd}
);
await execa('git', ['tag', 'v1.0.0'], {cwd});
await execa('git', ['commit', '--allow-empty', '-m', 'commit A'], {cwd});
await execa('git', ['commit', '--allow-empty', '-m', 'commit B'], {cwd});

const result = await read({cwd, fromLastTag: true});

expect(result).toEqual(['commit B\n\n', 'commit A\n\n']);
});

test('should not read any commits when there are no tags', async () => {
const cwd: string = await git.bootstrap();

await execa('git', ['commit', '--allow-empty', '-m', 'commit 7'], {cwd});
await execa('git', ['commit', '--allow-empty', '-m', 'commit 8'], {cwd});
await execa('git', ['commit', '--allow-empty', '-m', 'commit 9'], {cwd});

const result = await read({cwd, fromLastTag: true});

expect(result).toHaveLength(0);
});
32 changes: 31 additions & 1 deletion @commitlint/read/src/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {execa} from 'execa';
interface GetCommitMessageOptions {
cwd?: string;
from?: string;
fromLastTag?: boolean;
to?: string;
last?: boolean;
edit?: boolean | string;
Expand All @@ -19,7 +20,8 @@ interface GetCommitMessageOptions {
export default async function getCommitMessages(
settings: GetCommitMessageOptions
): Promise<string[]> {
const {cwd, from, to, last, edit, gitLogArgs} = settings;
const {cwd, fromLastTag, to, last, edit, gitLogArgs} = settings;
let from = settings.from;

if (edit) {
return getEditCommit(cwd, edit);
Expand All @@ -38,6 +40,34 @@ export default async function getCommitMessages(
return [output];
}

if (!from && fromLastTag) {
const {stdout} = await execa(
'git',
[
'describe',
'--abbrev=40',
'--always',
'--first-parent',
'--long',
'--tags',
],
{cwd}
);

if (stdout.length === 40) {
// Hash only means no last tag. Use that as the from ref which
// results in a no-op.
from = stdout;
} else {
// Description will be in the format: <tag>-<count>-g<hash>
// Example: v3.2.0-11-g9057371a52adaae5180d93fe4d0bb808d874b9fb
// Minus zero based (1), dash (1), "g" prefix (1), hash (40) = -43
const tagSlice = stdout.lastIndexOf('-', stdout.length - 43);

from = stdout.slice(0, tagSlice);
}
}

let gitOptions: GitOptions = {from, to};
if (gitLogArgs) {
gitOptions = {
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Options:
-H, --help-url help url in error message [string]
-f, --from lower end of the commit range to lint; applies if
edit=false [string]
--from-last-tag uses the last tag as the lower end of the commit range to
lint; applies if edit=false and from is not set [boolean]
-o, --format output format of the results [string]
-p, --parser-preset configuration preset to use for
conventional-commits-parser [string]
Expand Down

0 comments on commit f60ef02

Please sign in to comment.