Skip to content

Commit

Permalink
feat: allow to disable default release rules with release: false
Browse files Browse the repository at this point in the history
  • Loading branch information
pvdlg committed Jun 7, 2019
1 parent 9f31fab commit a71866f
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 12 deletions.
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

0 comments on commit a71866f

Please sign in to comment.