Skip to content

Commit fd1296a

Browse files
clydinhansl
authored andcommitted
test: validate commit messages (#4903)
1 parent b15d1f3 commit fd1296a

File tree

5 files changed

+156
-1
lines changed

5 files changed

+156
-1
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
"build": "node ./scripts/publish/build.js",
1313
"build:patch": "node ./scripts/patch.js",
1414
"build:packages": "for PKG in packages/*; do echo Building $PKG...; tsc -p $PKG; done",
15-
"test": "npm-run-all -c test:packages test:cli test:deps test:licenses",
15+
"test": "npm-run-all -c test:packages test:cli test:deps test:licenses test:messages",
1616
"e2e": "npm run test:e2e",
1717
"e2e:nightly": "node tests/run_e2e.js --nightly",
1818
"test:e2e": "node tests/run_e2e.js",
1919
"test:cli": "node tests/runner",
2020
"test:deps": "node scripts/publish/validate_dependencies.js",
2121
"test:inspect": "node --inspect --debug-brk tests/runner",
2222
"test:licenses": "node scripts/test-licenses.js",
23+
"test:messages": "node scripts/test-commit-messages.js",
2324
"test:packages": "node scripts/run-packages-spec.js",
2425
"eslint": "eslint .",
2526
"tslint": "tslint \"**/*.ts\" -c tslint.json -e \"**/config/schema.d.ts\" -e \"**/tests/**\" -e \"**/blueprints/*/files/**/*.ts\" -e \"node_modules/**\" -e \"tmp/**\" -e \"dist/**\"",

scripts/test-commit-messages.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
require('../lib/bootstrap-local');
2+
3+
const validateCommitMessage = require('./validate-commit-message');
4+
const exec = require('child_process').exec;
5+
const chalk = require('chalk');
6+
const Logger = require('@ngtools/logger').Logger;
7+
require('rxjs/add/operator/filter');
8+
9+
// Configure logger
10+
const logger = new Logger('test-commit-messages');
11+
12+
logger.subscribe((entry) => {
13+
let color = chalk.white;
14+
let output = process.stdout;
15+
switch (entry.level) {
16+
case 'info': color = chalk.white; break;
17+
case 'warn': color = chalk.yellow; break;
18+
case 'error': color = chalk.red; output = process.stderr; break;
19+
case 'fatal': color = (x) => chalk.bold(chalk.red(x)); output = process.stderr; break;
20+
}
21+
22+
output.write(color(entry.message) + '\n');
23+
});
24+
25+
logger
26+
.filter((entry) => entry.level == 'fatal')
27+
.subscribe(() => {
28+
process.stderr.write('A fatal error happened. See details above.');
29+
process.exit(1);
30+
});
31+
32+
// Note: This is based on the gulp task found in the angular/angular repository
33+
34+
exec(
35+
'git fetch origin master && git log --reverse --format=%s origin/master.. --no-merges',
36+
(error, stdout, stderr) => {
37+
if (error) {
38+
logger.fatal(stderr);
39+
return;
40+
}
41+
42+
const output = stdout.trim();
43+
if (output.length == 0) {
44+
logger.warn('There are zero new commits between this HEAD and master');
45+
return;
46+
}
47+
48+
const commitsByLine = output.split(/\n/);
49+
50+
logger.info(`Examining ${commitsByLine.length} commit(s) between HEAD and master`);
51+
52+
const someCommitsInvalid = !commitsByLine.every(validateCommitMessage);
53+
54+
if (someCommitsInvalid) {
55+
logger.error('Please fix the failing commit messages before continuing...');
56+
logger.fatal(
57+
'Commit message guidelines: https://github.com/angular/angular-cli/blob/master/CONTRIBUTING.md#commit');
58+
} else {
59+
logger.info('All commit messages are valid.');
60+
}
61+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"maxLength": 100,
3+
"types": [
4+
"build",
5+
"ci",
6+
"docs",
7+
"feat",
8+
"fix",
9+
"perf",
10+
"refactor",
11+
"style",
12+
"test"
13+
],
14+
"scopes": [
15+
"@angular/cli",
16+
"@ngtools/json-schema",
17+
"@ngtools/logger",
18+
"@ngtools/webpack",
19+
20+
"packaging",
21+
"changelog"
22+
]
23+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
module.exports = require('./validate-commit-message');
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* @license
5+
* Copyright Google Inc. All Rights Reserved.
6+
*
7+
* Use of this source code is governed by an MIT-style license that can be
8+
* found in the LICENSE file at https://angular.io/license
9+
*/
10+
11+
/**
12+
* GIT commit message format enforcement
13+
*
14+
* Note: this script was originally written by Vojta for AngularJS :-)
15+
*/
16+
17+
/* eslint-disable no-console */
18+
'use strict';
19+
20+
const fs = require('fs');
21+
const path = require('path');
22+
const configPath = path.resolve(__dirname, './commit-message.json');
23+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
24+
const PATTERN = /^(revert\: )?(\w+)(?:\(([^)]+)\))?\: (.+)$/;
25+
26+
module.exports = function(commitSubject) {
27+
if (commitSubject.length > config['maxLength']) {
28+
error(`The commit message is longer than ${config['maxLength']} characters`, commitSubject);
29+
return false;
30+
}
31+
32+
const match = PATTERN.exec(commitSubject);
33+
if (!match || match[2] === 'revert') {
34+
error(
35+
'The commit message does not match the format of "<type>(<scope>): <subject> OR revert: type(<scope>): <subject>"',
36+
commitSubject);
37+
return false;
38+
}
39+
40+
const type = match[2];
41+
if (config['types'].indexOf(type) === -1) {
42+
error(
43+
`${type} is not an allowed type.\n => TYPES: ${config['types'].join(', ')}`, commitSubject);
44+
return false;
45+
}
46+
47+
const scope = match[3];
48+
49+
if (scope && !config['scopes'].includes(scope)) {
50+
error(
51+
`"${scope}" is not an allowed scope.\n => SCOPES: ${config['scopes'].join(', ')}`,
52+
commitSubject);
53+
return false;
54+
}
55+
56+
return true;
57+
};
58+
59+
function error(errorMessage, commitMessage) {
60+
console.error(`INVALID COMMIT MSG: "${commitMessage}"\n => ERROR: ${errorMessage}`);
61+
}

0 commit comments

Comments
 (0)