Skip to content

Commit 6961dd8

Browse files
pvdlggr2m
authored andcommitted
feat: Add logs and debug
BREAKING CHANGE: Expect to be passed `logger` in `options`. Require `semantic-remantic` `>=9.0.0`.
1 parent 0495801 commit 6961dd8

File tree

4 files changed

+150
-51
lines changed

4 files changed

+150
-51
lines changed

lib/analyze-commit.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const {isMatchWith, isRegExp, omit} = require('lodash');
2+
const debug = require('debug')('semantic-release:commit-analyzer');
23
const RELEASE_TYPES = require('./default/release-types');
34
const compareReleaseTypes = require('./compare-release-types');
45

@@ -26,9 +27,18 @@ module.exports = (releaseRules, commit) => {
2627
.every(match => {
2728
if (compareReleaseTypes(releaseType, match.release)) {
2829
releaseType = match.release;
30+
debug('The rule %o match commit with release type %o', match, releaseType);
2931
if (releaseType === RELEASE_TYPES[0]) {
32+
debug('Release type %o is the highest possible. Stop analysis.', releaseType);
3033
return false;
3134
}
35+
} else {
36+
debug(
37+
'The rule %o match commit with release type %o but the higher release type %o has already been found for this commit',
38+
match,
39+
match.release,
40+
releaseType
41+
);
3242
}
3343
return true;
3444
});

lib/index.js

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
const {callbackify} = require('util');
2+
const parser = require('conventional-commits-parser').sync;
3+
const debug = require('debug')('semantic-release:commit-analyzer');
14
const loadParserConfig = require('./load/parser-config');
25
const loadReleaseRules = require('./load/release-rules');
36
const analyzeCommit = require('./analyze-commit');
@@ -23,37 +26,48 @@ const DEFAULT_RELEASE_RULES = require('./default/release-rules');
2326
* @param {Array} options.commits array of commits
2427
* @param {commitAnalyzerCallback} callback The callback called with the release type.
2528
*/
26-
module.exports = async (pluginConfig = {}, {commits}, callback) => {
27-
try {
28-
const releaseRules = loadReleaseRules(pluginConfig);
29-
const config = await loadParserConfig(pluginConfig);
30-
let releaseType = null;
29+
async function commitAnalyzer(pluginConfig = {}, {commits, logger}, callback) {
30+
const releaseRules = loadReleaseRules(pluginConfig);
31+
const config = await loadParserConfig(pluginConfig);
32+
let releaseType = null;
3133

32-
commits.every(rawCommit => {
33-
let commitReleaseType;
34+
commits.every(rawCommit => {
3435
const commit = parser(rawCommit.message, config);
36+
logger.log(`Analyzing commit: %s`, rawCommit.message);
37+
let commitReleaseType;
3538

36-
// Determine release type based on custom releaseRules
37-
if (releaseRules) {
38-
commitReleaseType = analyzeCommit(releaseRules, commit);
39+
// Determine release type based on custom releaseRules
40+
if (releaseRules) {
41+
debug('Analyzing with custom rules');
42+
commitReleaseType = analyzeCommit(releaseRules, commit);
43+
if (commitReleaseType) {
44+
logger.log('The release type for the commit is %s', commitReleaseType);
3945
}
40-
// If no custom releaseRules or none matched the commit, try with default releaseRules
41-
if (!commitReleaseType) {
42-
commitReleaseType = analyzeCommit(DEFAULT_RELEASE_RULES, commit);
43-
}
44-
// Set releaseType if commit's release type is higher
45-
if (commitReleaseType && compareReleaseTypes(releaseType, commitReleaseType)) {
46-
releaseType = commitReleaseType;
46+
}
47+
// If no custom releaseRules or none matched the commit, try with default releaseRules
48+
if (!commitReleaseType) {
49+
debug('Analyzing with default rules');
50+
commitReleaseType = analyzeCommit(DEFAULT_RELEASE_RULES, commit);
51+
if (commitReleaseType) {
52+
logger.log('The release type for the commit is %s', commitReleaseType);
53+
} else {
54+
logger.log('The commit should not trigger a release');
4755
}
56+
}
57+
// Set releaseType if commit's release type is higher
58+
if (commitReleaseType && compareReleaseTypes(releaseType, commitReleaseType)) {
59+
releaseType = commitReleaseType;
60+
}
4861

49-
// Break loop if releaseType is the highest
50-
if (releaseType === RELEASE_TYPES[0]) {
51-
return false;
52-
}
53-
return true;
54-
});
55-
callback(null, releaseType);
56-
} catch (err) {
57-
callback(err);
58-
}
59-
};
62+
// Break loop if releaseType is the highest
63+
if (releaseType === RELEASE_TYPES[0]) {
64+
return false;
65+
}
66+
return true;
67+
});
68+
logger.log('Analysis of %s commits complete: %s release', commits.length, releaseType || 'no');
69+
70+
return releaseType;
71+
}
72+
73+
module.exports = callbackify(commitAnalyzer);

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"dependencies": {
1515
"conventional-changelog-angular": "^1.4.0",
1616
"conventional-commits-parser": "^2.0.0",
17+
"debug": "^3.1.0",
1718
"import-from": "^2.1.0",
1819
"lodash": "^4.17.4"
1920
},
@@ -38,7 +39,8 @@
3839
"nyc": "^11.1.0",
3940
"prettier": "^1.7.2",
4041
"rimraf": "^2.6.1",
41-
"semantic-release": "^8.0.0"
42+
"semantic-release": "^8.0.0",
43+
"sinon": "^4.0.2"
4244
},
4345
"engines": {
4446
"node": ">=4"

test/integration.test.js

Lines changed: 95 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,51 @@
11
import {promisify} from 'util';
22
import test from 'ava';
3+
import {stub} from 'sinon';
34
import commitAnalyzer from '../lib/index';
45

6+
test.beforeEach(t => {
7+
const log = stub();
8+
t.context.log = log;
9+
t.context.logger = {log};
10+
});
11+
512
test('Parse with "conventional-changelog-angular" by default', async t => {
613
const commits = [{message: 'fix(scope1): First fix'}, {message: 'feat(scope2): Second feature'}];
7-
const releaseType = await promisify(commitAnalyzer)({}, {commits});
14+
const releaseType = await promisify(commitAnalyzer)({}, {commits, logger: t.context.logger});
815

916
t.is(releaseType, 'minor');
17+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
18+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'patch'));
19+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
20+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'minor'));
21+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'minor'));
1022
});
1123

1224
test('Accept "preset" option', async t => {
1325
const commits = [{message: 'Fix: First fix (fixes #123)'}, {message: 'Update: Second feature (fixes #456)'}];
14-
const releaseType = await promisify(commitAnalyzer)({preset: 'eslint'}, {commits});
26+
const releaseType = await promisify(commitAnalyzer)({preset: 'eslint'}, {commits, logger: t.context.logger});
1527

1628
t.is(releaseType, 'minor');
29+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
30+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'patch'));
31+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
32+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'minor'));
33+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'minor'));
1734
});
1835

1936
test('Accept "config" option', async t => {
2037
const commits = [{message: 'Fix: First fix (fixes #123)'}, {message: 'Update: Second feature (fixes #456)'}];
21-
const releaseType = await promisify(commitAnalyzer)({config: 'conventional-changelog-eslint'}, {commits});
38+
const releaseType = await promisify(commitAnalyzer)(
39+
{config: 'conventional-changelog-eslint'},
40+
{commits, logger: t.context.logger}
41+
);
2242

2343
t.is(releaseType, 'minor');
44+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
45+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'patch'));
46+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
47+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'minor'));
48+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'minor'));
2449
});
2550

2651
test('Accept a "parseOpts" object as option', async t => {
@@ -30,10 +55,15 @@ test('Accept a "parseOpts" object as option', async t => {
3055
];
3156
const releaseType = await promisify(commitAnalyzer)(
3257
{parserOpts: {headerPattern: /^##(.*?)## (.*)$/, headerCorrespondence: ['tag', 'shortDesc']}},
33-
{commits}
58+
{commits, logger: t.context.logger}
3459
);
3560

3661
t.is(releaseType, 'minor');
62+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
63+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'patch'));
64+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
65+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'minor'));
66+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'minor'));
3767
});
3868

3969
test('Accept a partial "parseOpts" object as option', async t => {
@@ -43,34 +73,57 @@ test('Accept a partial "parseOpts" object as option', async t => {
4373
config: 'conventional-changelog-eslint',
4474
parserOpts: {headerPattern: /^##(.*?)## (.*)$/, headerCorrespondence: ['type', 'shortDesc']},
4575
},
46-
{commits}
76+
{commits, logger: t.context.logger}
4777
);
4878

4979
t.is(releaseType, 'patch');
80+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
81+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'patch'));
82+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
83+
t.true(t.context.log.calledWith('The commit should not trigger a release'));
84+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'patch'));
5085
});
5186

5287
test('Accept a "releaseRules" option that reference a requierable module', async t => {
5388
const commits = [{message: 'fix(scope1): First fix'}, {message: 'feat(scope2): Second feature'}];
54-
const releaseType = await promisify(commitAnalyzer)({releaseRules: './test/fixtures/release-rules'}, {commits});
89+
const releaseType = await promisify(commitAnalyzer)(
90+
{releaseRules: './test/fixtures/release-rules'},
91+
{commits, logger: t.context.logger}
92+
);
5593

5694
t.is(releaseType, 'minor');
95+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
96+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'patch'));
97+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
98+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'minor'));
99+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'minor'));
57100
});
58101

59102
test('Return "major" if there is a breaking change, using default releaseRules', async t => {
60103
const commits = [
61104
{message: 'Fix: First fix (fixes #123)'},
62105
{message: 'Update: Second feature (fixes #456) \n\n BREAKING CHANGE: break something'},
63106
];
64-
const releaseType = await promisify(commitAnalyzer)({preset: 'eslint'}, {commits});
107+
const releaseType = await promisify(commitAnalyzer)({preset: 'eslint'}, {commits, logger: t.context.logger});
65108

66109
t.is(releaseType, 'major');
110+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
111+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'patch'));
112+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
113+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'major'));
114+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'major'));
67115
});
68116

69117
test('Return "patch" if there is only types set to "patch", using default releaseRules', async t => {
70118
const commits = [{message: 'fix: First fix (fixes #123)'}, {message: 'perf: perf improvement'}];
71-
const releaseType = await promisify(commitAnalyzer)({}, {commits});
119+
const releaseType = await promisify(commitAnalyzer)({}, {commits, logger: t.context.logger});
72120

73121
t.is(releaseType, 'patch');
122+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
123+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'patch'));
124+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
125+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'patch'));
126+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'patch'));
74127
});
75128

76129
test('Allow to use regex in "releaseRules" configuration', async t => {
@@ -80,27 +133,42 @@ test('Allow to use regex in "releaseRules" configuration', async t => {
80133
preset: 'eslint',
81134
releaseRules: [{tag: 'Chore', release: 'patch'}, {message: '/README/', release: 'minor'}],
82135
},
83-
{commits}
136+
{commits, logger: t.context.logger}
84137
);
85138

86139
t.is(releaseType, 'minor');
140+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
141+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'minor'));
142+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
143+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'minor'));
144+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'minor'));
87145
});
88146

89147
test('Return "null" if no rule match', async t => {
90148
const commits = [{message: 'doc: doc update'}, {message: 'chore: Chore'}];
91-
const releaseType = await promisify(commitAnalyzer)({}, {commits});
149+
const releaseType = await promisify(commitAnalyzer)({}, {commits, logger: t.context.logger});
92150

93151
t.is(releaseType, null);
152+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
153+
t.true(t.context.log.calledWith('The commit should not trigger a release'));
154+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
155+
t.true(t.context.log.calledWith('The commit should not trigger a release'));
156+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'no'));
94157
});
95158

96159
test('Process rules in order and apply highest match', async t => {
97160
const commits = [{message: 'Chore: First chore (fixes #123)'}, {message: 'Docs: update README (fixes #456)'}];
98161
const releaseType = await promisify(commitAnalyzer)(
99162
{preset: 'eslint', releaseRules: [{tag: 'Chore', release: 'minor'}, {tag: 'Chore', release: 'patch'}]},
100-
{commits}
163+
{commits, logger: t.context.logger}
101164
);
102165

103166
t.is(releaseType, 'minor');
167+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
168+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'minor'));
169+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
170+
t.true(t.context.log.calledWith('The commit should not trigger a release'));
171+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'minor'));
104172
});
105173

106174
test('Process rules in order and apply highest match from config even if default has an higher match', async t => {
@@ -110,27 +178,30 @@ test('Process rules in order and apply highest match from config even if default
110178
];
111179
const releaseType = await promisify(commitAnalyzer)(
112180
{preset: 'eslint', releaseRules: [{tag: 'Chore', release: 'patch'}, {breaking: true, release: 'minor'}]},
113-
{commits}
181+
{commits, logger: t.context.logger}
114182
);
115183

116184
t.is(releaseType, 'minor');
185+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
186+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'patch'));
187+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
188+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'minor'));
189+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'minor'));
117190
});
118191

119192
test('Use default "releaseRules" if none of provided match', async t => {
120193
const commits = [{message: 'Chore: First chore'}, {message: 'Update: new feature'}];
121194
const releaseType = await promisify(commitAnalyzer)(
122195
{preset: 'eslint', releaseRules: [{tag: 'Chore', release: 'patch'}]},
123-
{commits}
196+
{commits, logger: t.context.logger}
124197
);
125198

126199
t.is(releaseType, 'minor');
127-
});
128-
129-
test('Ignore malformatted commits and process valid ones', async t => {
130-
const commits = [{message: 'fix(scope1): First fix'}, {message: 'Feature => Invalid message'}];
131-
const releaseType = await promisify(commitAnalyzer)({}, {commits});
132-
133-
t.is(releaseType, 'patch');
200+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[0].message));
201+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'patch'));
202+
t.true(t.context.log.calledWith('Analyzing commit: %s', commits[1].message));
203+
t.true(t.context.log.calledWith('The release type for the commit is %s', 'minor'));
204+
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'minor'));
134205
});
135206

136207
test('Throw error if "preset" doesn`t exist', async t => {
@@ -155,7 +226,9 @@ test('Throw error if "releaseRules" option reference a requierable module that i
155226

156227
test('Throw error if "config" doesn`t exist', async t => {
157228
const commits = [{message: 'Fix: First fix (fixes #123)'}, {message: 'Update: Second feature (fixes #456)'}];
158-
const error = await t.throws(promisify(commitAnalyzer)({config: 'unknown-config'}, {commits}));
229+
const error = await t.throws(
230+
promisify(commitAnalyzer)({config: 'unknown-config'}, {commits, logger: t.context.logger})
231+
);
159232

160233
t.is(error.code, 'MODULE_NOT_FOUND');
161234
});
@@ -174,7 +247,7 @@ test('Re-Throw error from "conventional-changelog-parser"', async t => {
174247

175248
test('Accept an undefined "pluginConfig"', async t => {
176249
const commits = [{message: 'fix(scope1): First fix'}, {message: 'feat(scope2): Second feature'}];
177-
const releaseType = await promisify(commitAnalyzer)(undefined, {commits});
250+
const releaseType = await promisify(commitAnalyzer)(undefined, {commits, logger: t.context.logger});
178251

179252
t.is(releaseType, 'minor');
180253
});

0 commit comments

Comments
 (0)