1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | 'use strict';
|
12 |
|
13 | var fs = require('fs');
|
14 | var util = require('util');
|
15 | var resolve = require('path').resolve;
|
16 | var findup = require('findup');
|
17 | var semverRegex = require('semver-regex')
|
18 |
|
19 | var config = getConfig();
|
20 | var MAX_LENGTH = config.maxSubjectLength || 100;
|
21 | var IGNORED = new RegExp(util.format('(^WIP)|(^%s$)', semverRegex().source));
|
22 |
|
23 |
|
24 | var PATTERN = /^((fixup! |squash! )?(\w+)(?:\(([^\)\s]+)\))?: (.+))(?:\n|$)/;
|
25 | var MERGE_COMMIT_PATTERN = /^Merge /;
|
26 | var error = function() {
|
27 |
|
28 |
|
29 |
|
30 | console[config.warnOnFail
|
31 | ? 'warn'
|
32 | : 'error']('INVALID COMMIT MSG: ' + util.format.apply(null, arguments));
|
33 | };
|
34 |
|
35 | var validateMessage = function(raw) {
|
36 | var types = config.types = config.types || 'conventional-commit-types';
|
37 |
|
38 |
|
39 | if (typeof types === 'string' && types !== '*') {
|
40 | types = Object.keys(require(types).types);
|
41 | }
|
42 |
|
43 | var messageWithBody = (raw || '').split('\n').filter(function(str) {
|
44 | return str.indexOf('#') !== 0;
|
45 | }).join('\n');
|
46 |
|
47 | var message = messageWithBody.split('\n').shift();
|
48 |
|
49 | if (message === '') {
|
50 | console.log('Aborting commit due to empty commit message.');
|
51 | return false;
|
52 | }
|
53 |
|
54 | var isValid = true;
|
55 |
|
56 | if (MERGE_COMMIT_PATTERN.test(message)) {
|
57 | console.log('Merge commit detected.');
|
58 | return true
|
59 | }
|
60 |
|
61 | if (IGNORED.test(message)) {
|
62 | console.log('Commit message validation ignored.');
|
63 | return true;
|
64 | }
|
65 |
|
66 | var match = PATTERN.exec(message);
|
67 |
|
68 | if (!match) {
|
69 | error('提交信息格式不通过,具体格式请按照:"<type>(<scope>): <subject>" !');
|
70 | isValid = false;
|
71 | } else {
|
72 | var firstLine = match[1];
|
73 | var squashing = !!match[2];
|
74 | var type = match[3];
|
75 | var scope = match[4];
|
76 | var subject = match[5];
|
77 |
|
78 | var SUBJECT_PATTERN = new RegExp(config.subjectPattern || '.+');
|
79 | var SUBJECT_PATTERN_ERROR_MSG = config.subjectPatternErrorMsg || 'subject does not match subject pattern!';
|
80 |
|
81 | if (firstLine.length > MAX_LENGTH && !squashing) {
|
82 | error('is longer than %d characters !', MAX_LENGTH);
|
83 | isValid = false;
|
84 | }
|
85 |
|
86 | if (types !== '*' && types.indexOf(type) === -1) {
|
87 | error('"%s" is not allowed type ! Valid types are: %s', type, types.join(', '));
|
88 | isValid = false;
|
89 | }
|
90 |
|
91 | if (!SUBJECT_PATTERN.exec(subject)) {
|
92 | error(SUBJECT_PATTERN_ERROR_MSG);
|
93 | isValid = false;
|
94 | }
|
95 | }
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 | isValid = isValid || config.warnOnFail;
|
108 |
|
109 | if (isValid) {
|
110 | return true;
|
111 | }
|
112 |
|
113 | var argInHelp = config.helpMessage && config.helpMessage.indexOf('%s') !== -1;
|
114 |
|
115 | if (argInHelp) {
|
116 | console.log(config.helpMessage, messageWithBody);
|
117 | } else if (message) {
|
118 | console.log(message);
|
119 | }
|
120 |
|
121 | if (!argInHelp && config.helpMessage) {
|
122 | console.log(config.helpMessage);
|
123 | }
|
124 |
|
125 | return false;
|
126 | };
|
127 |
|
128 |
|
129 | exports.validateMessage = validateMessage;
|
130 | exports.getGitFolder = getGitFolder;
|
131 | exports.config = config;
|
132 |
|
133 |
|
134 |
|
135 | if (process.argv.join('').indexOf('mocha') === -1) {
|
136 |
|
137 | var commitMsgFile = process.argv[2] || getGitFolder() + '/COMMIT_EDITMSG';
|
138 | var incorrectLogFile = commitMsgFile.replace('COMMIT_EDITMSG', 'logs/incorrect-commit-msgs');
|
139 |
|
140 | var hasToString = function hasToString(x) {
|
141 | return x && typeof x.toString === 'function';
|
142 | };
|
143 |
|
144 | fs.readFile(commitMsgFile, function(err, buffer) {
|
145 | var msg = getCommitMessage(buffer);
|
146 |
|
147 | if (!validateMessage(msg)) {
|
148 | fs.appendFile(incorrectLogFile, msg + '\n', function() {
|
149 | process.exit(1);
|
150 | });
|
151 | } else {
|
152 | process.exit(0);
|
153 | }
|
154 |
|
155 | function getCommitMessage(buffer) {
|
156 | return hasToString(buffer) && buffer.toString();
|
157 | }
|
158 | });
|
159 | }
|
160 |
|
161 | function getConfig() {
|
162 | var pkgFile = findup.sync(process.cwd(), 'package.json');
|
163 | var pkg = JSON.parse(fs.readFileSync(resolve(pkgFile, 'package.json')));
|
164 | return pkg && pkg.config && pkg.config['validate-commit-msg'] || {};
|
165 | }
|
166 |
|
167 | function getGitFolder() {
|
168 | var gitDirLocation = './.git';
|
169 | if (!fs.existsSync(gitDirLocation)) {
|
170 | throw new Error('Cannot find file ' + gitDirLocation);
|
171 | }
|
172 |
|
173 | if (!fs.lstatSync(gitDirLocation).isDirectory()) {
|
174 | var unparsedText = '' + fs.readFileSync(gitDirLocation);
|
175 | gitDirLocation = unparsedText.substring('gitdir: '.length).trim();
|
176 | }
|
177 |
|
178 | if (!fs.existsSync(gitDirLocation)) {
|
179 | throw new Error('Cannot find file ' + gitDirLocation);
|
180 | }
|
181 |
|
182 | return gitDirLocation;
|
183 | }
|