Skip to content

Commit d7070d8

Browse files
authoredSep 11, 2024··
feat(cli): use special errorCode for missing rules/config #4142 (#4143)
* feat(cli): use special errorCode for missing rules/config #4142 * test(cli): update related tests #4142 * feat(cli): use errorCode 9 #4142 * refactor(cli): shorten statement #4142 * refactor(cli): use ExitCode Enum #4142 * refactor(cli): rename ExitCode-enum to match commitlints usage #4142
1 parent e12ef33 commit d7070d8

File tree

3 files changed

+63
-45
lines changed

3 files changed

+63
-45
lines changed
 

‎@commitlint/cli/src/cli-error.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1+
export enum ExitCode {
2+
CommitlintDefault = 0,
3+
CommitlintErrorDefault = 1,
4+
CommitLintWarning = 2,
5+
CommitLintError = 3,
6+
CommitlintInvalidArgument = 9,
7+
}
8+
19
export class CliError extends Error {
210
__proto__ = Error;
311

412
public type: string;
5-
public error_code: number;
13+
public error_code: ExitCode;
614

7-
constructor(message: string, type: string, error_code = 1) {
15+
constructor(
16+
message: string,
17+
type: string,
18+
error_code = ExitCode.CommitlintErrorDefault
19+
) {
820
super(message);
921

1022
this.type = type;

‎@commitlint/cli/src/cli.test.ts

+40-40
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import {describe, test, expect} from 'vitest';
22
import {createRequire} from 'module';
33
import path from 'path';
44
import {fileURLToPath} from 'url';
5-
65
import {fix, git} from '@commitlint/test';
76
import fs from 'fs-extra';
87
import merge from 'lodash.merge';
98
import {x} from 'tinyexec';
9+
import {ExitCode} from './cli-error.js';
1010

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

337337
const result = cli([], {cwd})('inner: bar');
338338
await result;
339-
expect(result.exitCode).toBe(1);
339+
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
340340
});
341341

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

346346
const result = cli([], {cwd})('outer: bar');
347347
await result;
348-
expect(result.exitCode).toBe(0);
348+
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
349349
});
350350

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

355355
const result = cli([], {cwd})('inner: bar');
356356
await result;
357-
expect(result.exitCode).toBe(0);
357+
expect(result.exitCode).toBe(ExitCode.CommitlintDefault);
358358
});
359359

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

364364
const result = cli([], {cwd})('outer: bar');
365365
await result;
366-
expect(result.exitCode).toBe(1);
366+
expect(result.exitCode).toBe(ExitCode.CommitlintErrorDefault);
367367
});
368368

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

571571
test('should print help', async () => {

‎@commitlint/cli/src/cli.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import yargs, {type Arguments} from 'yargs';
2020

2121
import {CliFlags} from './types.js';
2222

23-
import {CliError} from './cli-error.js';
23+
import {CliError, ExitCode} from './cli-error.js';
2424

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

@@ -316,6 +316,7 @@ async function main(args: MainArgs): Promise<void> {
316316
messages.map((message) => lint(message, loaded.rules, opts))
317317
);
318318

319+
let isRulesEmpty = false;
319320
if (Object.keys(loaded.rules).length === 0) {
320321
let input = '';
321322

@@ -340,6 +341,8 @@ async function main(args: MainArgs): Promise<void> {
340341
warnings: [],
341342
input,
342343
});
344+
345+
isRulesEmpty = true;
343346
}
344347

345348
const report = results.reduce<{
@@ -378,12 +381,15 @@ async function main(args: MainArgs): Promise<void> {
378381

379382
if (flags.strict) {
380383
if (report.errorCount > 0) {
381-
throw new CliError(output, pkg.name, 3);
384+
throw new CliError(output, pkg.name, ExitCode.CommitLintError);
382385
}
383386
if (report.warningCount > 0) {
384-
throw new CliError(output, pkg.name, 2);
387+
throw new CliError(output, pkg.name, ExitCode.CommitLintWarning);
385388
}
386389
}
390+
if (isRulesEmpty) {
391+
throw new CliError(output, pkg.name, ExitCode.CommitlintInvalidArgument);
392+
}
387393
if (!report.valid) {
388394
throw new CliError(output, pkg.name);
389395
}

0 commit comments

Comments
 (0)
Please sign in to comment.