Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): use special errorCode for missing rules/config #4142 #4143

Merged
merged 6 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions @commitlint/cli/src/cli-error.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
export enum ExitCode {
escapedcat marked this conversation as resolved.
Show resolved Hide resolved
CommitlintDefault = 0,
CommitlintErrorDefault = 1,
CommitLintWarning = 2,
CommitLintError = 3,
CommitlintInvalidArgument = 9,
}

export class CliError extends Error {
__proto__ = Error;

public type: string;
public error_code: number;
public error_code: ExitCode;

constructor(message: string, type: string, error_code = 1) {
constructor(
message: string,
type: string,
error_code = ExitCode.CommitlintErrorDefault
) {
super(message);

this.type = type;
Expand Down
80 changes: 40 additions & 40 deletions @commitlint/cli/src/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import {describe, test, expect} from 'vitest';
import {createRequire} from 'module';
import path from 'path';
import {fileURLToPath} from 'url';

import {fix, git} from '@commitlint/test';
import fs from 'fs-extra';
import merge from 'lodash.merge';
import {x} from 'tinyexec';
import {ExitCode} from './cli-error.js';

const require = createRequire(import.meta.url);

Expand Down Expand Up @@ -42,7 +42,7 @@ test('should throw when called without [input]', async () => {
const cwd = await gitBootstrap('fixtures/default');
const result = cli([], {cwd})();
await result;
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should reprint input from stdin', async () => {
Expand Down Expand Up @@ -107,7 +107,7 @@ test('should produce help for empty config', async () => {
const result = cli([], {cwd})('foo: bar');
const output = await result;
expect(output.stdout.trim()).toContain('Please add rules');
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintInvalidArgument);
});

test('should produce help for problems', async () => {
Expand All @@ -117,7 +117,7 @@ test('should produce help for problems', async () => {
expect(output.stdout.trim()).toContain(
'Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint'
);
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should produce help for problems with correct helpurl', async () => {
Expand All @@ -130,29 +130,29 @@ test('should produce help for problems with correct helpurl', async () => {
expect(output.stdout.trim()).toContain(
'Get help: https://github.com/conventional-changelog/commitlint/#testhelpurl'
);
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should fail for input from stdin without rules', async () => {
const cwd = await gitBootstrap('fixtures/empty');
const result = cli([], {cwd})('foo: bar');
await result;
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintInvalidArgument);
});

test('should succeed for input from stdin with rules', async () => {
const cwd = await gitBootstrap('fixtures/default');
const result = cli([], {cwd})('type: bar');
await result;
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('should fail for input from stdin with rule from rc', async () => {
const cwd = await gitBootstrap('fixtures/simple');
const result = cli([], {cwd})('foo: bar');
const output = await result;
expect(output.stdout.trim()).toContain('type must not be one of [foo]');
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should work with --config option', async () => {
Expand All @@ -161,15 +161,15 @@ test('should work with --config option', async () => {
const result = cli(['--config', file], {cwd})('foo: bar');
const output = await result;
expect(output.stdout.trim()).toContain('type must not be one of [foo]');
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should fail for input from stdin with rule from js', async () => {
const cwd = await gitBootstrap('fixtures/extends-root');
const result = cli(['--extends', './extended'], {cwd})('foo: bar');
const output = await result;
expect(output.stdout.trim()).toContain('type must not be one of [foo]');
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should output help URL defined in config file', async () => {
Expand All @@ -179,7 +179,7 @@ test('should output help URL defined in config file', async () => {
expect(output.stdout.trim()).toContain(
'Get help: https://www.example.com/foo'
);
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should produce no error output with --quiet flag', async () => {
Expand All @@ -188,7 +188,7 @@ test('should produce no error output with --quiet flag', async () => {
const output = await result;
expect(output.stdout.trim()).toEqual('');
expect(output.stderr).toEqual('');
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should produce no error output with -q flag', async () => {
Expand All @@ -197,7 +197,7 @@ test('should produce no error output with -q flag', async () => {
const output = await result;
expect(output.stdout.trim()).toEqual('');
expect(output.stderr).toEqual('');
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should work with husky commitmsg hook and git commit', async () => {
Expand Down Expand Up @@ -294,7 +294,7 @@ test('should allow reading of environment variables for edit file, succeeding if
env: {variable: 'commit-msg-file'},
})();
await result;
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('should allow reading of environment variables for edit file, failing if invalid', async () => {
Expand All @@ -308,7 +308,7 @@ test('should allow reading of environment variables for edit file, failing if in
env: {variable: 'commit-msg-file'},
})();
await result;
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should pick up parser preset and fail accordingly', async () => {
Expand All @@ -318,7 +318,7 @@ test('should pick up parser preset and fail accordingly', async () => {
);
const output = await result;
expect(output.stdout.trim()).toContain('may not be empty');
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should pick up parser preset and succeed accordingly', async () => {
Expand All @@ -327,7 +327,7 @@ test('should pick up parser preset and succeed accordingly', async () => {
'----type(scope): subject'
);
await result;
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('should pick up config from outside git repo and fail accordingly', async () => {
Expand All @@ -336,7 +336,7 @@ test('should pick up config from outside git repo and fail accordingly', async (

const result = cli([], {cwd})('inner: bar');
await result;
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should pick up config from outside git repo and succeed accordingly', async () => {
Expand All @@ -345,7 +345,7 @@ test('should pick up config from outside git repo and succeed accordingly', asyn

const result = cli([], {cwd})('outer: bar');
await result;
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('should pick up config from inside git repo with precedence and succeed accordingly', async () => {
Expand All @@ -354,7 +354,7 @@ test('should pick up config from inside git repo with precedence and succeed acc

const result = cli([], {cwd})('inner: bar');
await result;
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('should pick up config from inside git repo with precedence and fail accordingly', async () => {
Expand All @@ -363,7 +363,7 @@ test('should pick up config from inside git repo with precedence and fail accord

const result = cli([], {cwd})('outer: bar');
await result;
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should handle --amend with signoff', async () => {
Expand All @@ -389,7 +389,7 @@ test('it uses parserOpts.commentChar when not using edit mode', async () => {
const result = cli([], {cwd})(input);
const output = await result;
expect(output.stdout.trim()).toContain('[body-empty]');
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test("it doesn't use parserOpts.commentChar when using edit mode", async () => {
Expand All @@ -402,7 +402,7 @@ test("it doesn't use parserOpts.commentChar when using edit mode", async () => {
const result = cli(['--edit', '.git/COMMIT_EDITMSG'], {cwd})();
const output = await result;
expect(output.stdout.trim()).not.toContain('[body-empty]');
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('it uses core.commentChar git config when using edit mode', async () => {
Expand All @@ -418,7 +418,7 @@ test('it uses core.commentChar git config when using edit mode', async () => {
const result = cli(['--edit', '.git/COMMIT_EDITMSG'], {cwd})();
const output = await result;
expect(output.stdout.trim()).toContain('[body-empty]');
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('it falls back to # for core.commentChar when using edit mode', async () => {
Expand All @@ -432,14 +432,14 @@ test('it falls back to # for core.commentChar when using edit mode', async () =>
const output = await result;
expect(output.stdout.trim()).toContain('[body-empty]');
expect(output.stderr).toEqual('');
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should handle linting with issue prefixes', async () => {
const cwd = await gitBootstrap('fixtures/issue-prefixes');
const result = cli([], {cwd})('foobar REF-1');
await result;
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
}, 10000);

test('should print full commit message when input from stdin fails', async () => {
Expand All @@ -449,7 +449,7 @@ test('should print full commit message when input from stdin fails', async () =>
const result = cli(['--color=false'], {cwd})(input);
const output = await result;
expect(output.stdout.trim()).toContain(input);
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should not print commit message fully or partially when input succeeds', async () => {
Expand All @@ -460,7 +460,7 @@ test('should not print commit message fully or partially when input succeeds', a
const output = await result;
expect(output.stdout.trim()).not.toContain(message);
expect(output.stdout.trim()).not.toContain(message.split('\n')[0]);
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('should fail for invalid formatters from configuration', async () => {
Expand All @@ -471,42 +471,42 @@ test('should fail for invalid formatters from configuration', async () => {
'Using format custom-formatter, but cannot find the module'
);
expect(output.stdout.trim()).toEqual('');
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should skip linting if message matches ignores config', async () => {
const cwd = await gitBootstrap('fixtures/ignores');
const result = cli([], {cwd})('WIP');
await result;
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('should not skip linting if message does not match ignores config', async () => {
const cwd = await gitBootstrap('fixtures/ignores');
const result = cli([], {cwd})('foo');
await result;
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should not skip linting if defaultIgnores is false', async () => {
const cwd = await gitBootstrap('fixtures/default-ignores-false');
const result = cli([], {cwd})('fixup! foo: bar');
await result;
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should skip linting if defaultIgnores is true', async () => {
const cwd = await gitBootstrap('fixtures/default-ignores-true');
const result = cli([], {cwd})('fixup! foo: bar');
await result;
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('should skip linting if defaultIgnores is unset', async () => {
const cwd = await gitBootstrap('fixtures/default-ignores-unset');
const result = cli([], {cwd})('fixup! foo: bar');
await result;
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('should fail for invalid formatters from flags', async () => {
Expand All @@ -517,7 +517,7 @@ test('should fail for invalid formatters from flags', async () => {
'Using format through-flag, but cannot find the module'
);
expect(output.stdout.trim()).toEqual('');
expect(result.exitCode).toBe(1);
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
});

test('should work with absolute formatter path', async () => {
Expand All @@ -531,7 +531,7 @@ test('should work with absolute formatter path', async () => {
);
const output = await result;
expect(output.stdout.trim()).toContain('custom-formatter-ok');
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('should work with relative formatter path', async () => {
Expand All @@ -544,28 +544,28 @@ test('should work with relative formatter path', async () => {
);
const output = await result;
expect(output.stdout.trim()).toContain('custom-formatter-ok');
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('strict: should exit with 3 on error', async () => {
const cwd = await gitBootstrap('fixtures/warning');
const result = cli(['--strict'], {cwd})('foo: abcdef');
await result;
expect(result.exitCode).toBe(3);
expect(result.exitCode).toBe(ExitCode.CommitLintError);
});

test('strict: should exit with 2 on warning', async () => {
const cwd = await gitBootstrap('fixtures/warning');
const result = cli(['--strict'], {cwd})('feat: abcdef');
await result;
expect(result.exitCode).toBe(2);
expect(result.exitCode).toBe(ExitCode.CommitLintWarning);
});

test('strict: should exit with 0 on success', async () => {
const cwd = await gitBootstrap('fixtures/warning');
const result = cli(['--strict'], {cwd})('feat: abc');
await result;
expect(result.exitCode).toBe(0);
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
});

test('should print help', async () => {
Expand Down
Loading