Skip to content
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
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ This is an `Array` of rule objects. A rule object has a `release` property and 1
"releaseRules": [
{"type": "docs", "scope": "README", "release": "patch"},
{"type": "refactor", "scope": "/core-.*/", "release": "minor"},
{"type": "refactor", "release": "patch"}
{"type": "refactor", "release": "patch"},
{"scope": "no-release", "release": false}
]
}],
"@semantic-release/release-notes-generator"
Expand All @@ -100,6 +101,7 @@ With the previous example:
- Commits with `type` 'docs' and `scope` 'README' will be associated with a `patch` release.
- Commits with `type` 'refactor' and `scope` starting with 'core-' (i.e. 'core-ui', 'core-rules', ...) will be associated with a `minor` release.
- Other commits with `type` 'refactor' (without `scope` or with a `scope` not matching the regexp `/core-.*/`) will be associated with a `patch` release.
- Commits with scope `no-release` will not be associated with a release type.

##### Default rules matching

Expand All @@ -110,6 +112,7 @@ With the previous example:
- Commits with `type` 'feat' will be associated with a `minor` release.
- Commits with `type` 'fix' will be associated with a `patch` release.
- Commits with `type` 'perf' will be associated with a `patch` release.
- Commits with scope `no-release` will not be associated with a release type even if they have a breaking change or the `type` 'feat', 'fix' or 'perf'.

##### No rules matching

Expand Down
17 changes: 8 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const {isUndefined} = require('lodash');
const parser = require('conventional-commits-parser').sync;
const filter = require('conventional-commits-filter');
const debug = require('debug')('semantic-release:commit-analyzer');
Expand Down Expand Up @@ -38,20 +39,18 @@ async function analyzeCommits(pluginConfig, context) {
if (releaseRules) {
debug('Analyzing with custom rules');
commitReleaseType = analyzeCommit(releaseRules, commit);
if (commitReleaseType) {
logger.log('The release type for the commit is %s', commitReleaseType);
}
}

// If no custom releaseRules or none matched the commit, try with default releaseRules
if (!commitReleaseType) {
if (isUndefined(commitReleaseType)) {
debug('Analyzing with default rules');
commitReleaseType = analyzeCommit(DEFAULT_RELEASE_RULES, commit);
if (commitReleaseType) {
logger.log('The release type for the commit is %s', commitReleaseType);
} else {
logger.log('The commit should not trigger a release');
}
}

if (commitReleaseType) {
logger.log('The release type for the commit is %s', commitReleaseType);
} else {
logger.log('The commit should not trigger a release');
}

// Set releaseType if commit's release type is higher
Expand Down
5 changes: 3 additions & 2 deletions lib/load-release-rules.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const {isUndefined} = require('lodash');
const importFrom = require('import-from');
const RELEASE_TYPES = require('./default-release-types');

Expand Down Expand Up @@ -28,9 +29,9 @@ module.exports = ({releaseRules}, {cwd}) => {
}

loadedReleaseRules.forEach(rule => {
if (!rule || !rule.release) {
if (!rule || isUndefined(rule.release)) {
throw new Error('Error in commit-analyzer configuration: rules must be an object with a "release" property');
} else if (RELEASE_TYPES.indexOf(rule.release) === -1) {
} else if (RELEASE_TYPES.indexOf(rule.release) === -1 && rule.release !== null && rule.release !== false) {
throw new Error(
`Error in commit-analyzer configuration: "${
rule.release
Expand Down
12 changes: 12 additions & 0 deletions test/analyze-commit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,15 @@ test('Return highest release type if multiple rules match', t => {
'major'
);
});

test('Return "false" for release type if the matching rule has "release" set to "false"', t => {
const commit = {type: 'fix'};

t.is(analyzeCommit([{type: 'fix', release: false}], commit), false);
});

test('Return "null" for release type if the matching rule has "release" set to "null"', t => {
const commit = {type: 'fix'};

t.is(analyzeCommit([{type: 'fix', release: null}], commit), null);
});
8 changes: 8 additions & 0 deletions test/compare-release-types.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,12 @@ test('Compares release types', t => {
t.false(compareReleaseTypes('major', 'minor'));
t.false(compareReleaseTypes('major', 'patch'));
t.false(compareReleaseTypes('minor', 'patch'));

t.true(compareReleaseTypes('major', false));
t.true(compareReleaseTypes('minor', false));
t.true(compareReleaseTypes('patch', false));

t.true(compareReleaseTypes('major', null));
t.true(compareReleaseTypes('minor', null));
t.true(compareReleaseTypes('patch', null));
});
45 changes: 45 additions & 0 deletions test/integration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,51 @@ test('Process rules in order and apply highest match from config even if default
t.true(t.context.log.calledWith('Analysis of %s commits complete: %s release', 2, 'minor'));
});

test('Allow to overwrite default "releaseRules" with "false"', async t => {
const commits = [{message: 'chore: First chore'}, {message: 'feat: new feature'}];
const releaseType = await analyzeCommits(
{preset: 'angular', releaseRules: [{type: 'feat', release: false}]},
{cwd, commits, logger: t.context.logger}
);

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

test('Commits with an associated custom release type have higher priority than commits with release "false"', async t => {
const commits = [{message: 'feat: Feature to skip'}, {message: 'docs: update README'}];
const releaseType = await analyzeCommits(
{preset: 'angular', releaseRules: [{type: 'feat', release: false}, {type: 'docs', release: 'patch'}]},
{cwd, commits, logger: t.context.logger}
);

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

test('Commits with an associated default release type have higher priority than commits with release "false"', async t => {
const commits = [{message: 'feat: new feature'}, {message: 'fix: new Fix'}];
const releaseType = await analyzeCommits(
{preset: 'angular', releaseRules: [{type: 'feat', release: false}]},
{cwd, commits, logger: t.context.logger}
);

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

test('Use default "releaseRules" if none of provided match', async t => {
const commits = [{message: 'Chore: First chore'}, {message: 'Update: new feature'}];
const releaseType = await analyzeCommits(
Expand Down
9 changes: 9 additions & 0 deletions test/load-release-rules.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ test('Return undefined if "releaseRules" not set', t => {
t.is(releaseRules, undefined);
});

test('Preserve release rules set to "false" or "null"', t => {
const releaseRules = loadReleaseRules(
{releaseRules: [{type: 'feat', release: false}, {type: 'fix', release: null}]},
{cwd}
);

t.deepEqual(releaseRules, [{type: 'feat', release: false}, {type: 'fix', release: null}]);
});

test('Throw error if "releaseRules" reference invalid commit type', t => {
t.throws(
() => loadReleaseRules({releaseRules: [{tag: 'Update', release: 'invalid'}]}, {cwd}),
Expand Down