diff --git a/README.md b/README.md index ad0921f3..4a2f9722 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ | Step | Description | | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `verifyConditions` | Verify the `changelogFile` and `changelogTitle` options configuration. | +| `verifyConditions` | Verify the `changelogFile`, `changelogTitle` and `skipOnPrerelease` options configuration. | | `prepare` | Create or update a changelog file in the local project directory with the changelog content created in the [generate notes step](https://github.com/semantic-release/semantic-release#release-steps). | ## Install @@ -47,10 +47,11 @@ With this example, for each release, a `docs/CHANGELOG.md` will be created or up ### Options -| Options | Description | Default | -| ---------------- | ----------------------------------------------------- | -------------- | -| `changelogFile` | File path of the changelog. | `CHANGELOG.md` | -| `changelogTitle` | Title of the changelog file (first line of the file). | - | +| Options | Description | Default | +| ------------------ | -------------------------------------------------------- | -------------- | +| `changelogFile` | File path of the changelog. | `CHANGELOG.md` | +| `changelogTitle` | Title of the changelog file (first line of the file). | - | +| `skipOnPrerelease` | Skip changelog update when branch is a prerelease branch | `false` | ### Examples diff --git a/lib/definitions/errors.js b/lib/definitions/errors.js index fecd1369..1bb16aae 100644 --- a/lib/definitions/errors.js +++ b/lib/definitions/errors.js @@ -20,4 +20,10 @@ Your configuration for the \`changelogFile\` option is \`${changelogFile}\`.`, Your configuration for the \`changelogTitle\` option is \`${changelogTitle}\`.`, }), + EINVALIDSKIPONPRERELEASE: ({skipOnPrerelease}) => ({ + message: 'Invalid `skipOnPrerelease` option.', + details: `The [skipOnPrerelease option](${linkify('README.md#options')}) option, if defined, must be a \`Boolean\`. + +Your configuration for the \`skipOnPrerelease\` option is \`${skipOnPrerelease}\`.`, + }), }; diff --git a/lib/prepare.js b/lib/prepare.js index 7ce101a3..19f1bd8e 100644 --- a/lib/prepare.js +++ b/lib/prepare.js @@ -2,10 +2,17 @@ const path = require('path'); const {readFile, writeFile, ensureFile} = require('fs-extra'); const resolveConfig = require('./resolve-config.js'); -module.exports = async (pluginConfig, {cwd, nextRelease: {notes}, logger}) => { - const {changelogFile, changelogTitle} = resolveConfig(pluginConfig); +const isPrerelease = ({type, main}) => type === 'prerelease' || (type === 'release' && !main); + +module.exports = async (pluginConfig, {cwd, nextRelease: {notes}, logger, branch}) => { + const {changelogFile, changelogTitle, skipOnPrerelease} = resolveConfig(pluginConfig); const changelogPath = path.resolve(cwd, changelogFile); + if (skipOnPrerelease && isPrerelease(branch)) { + logger.log('Skipping because branch is a prerelease branch and option skipOnPrerelease is active'); + return; + } + if (notes) { await ensureFile(changelogPath); const currentFile = (await readFile(changelogPath)).toString().trim(); diff --git a/lib/resolve-config.js b/lib/resolve-config.js index 2ee6c59a..fd6defe4 100644 --- a/lib/resolve-config.js +++ b/lib/resolve-config.js @@ -1,6 +1,7 @@ const {isNil} = require('lodash'); -module.exports = ({changelogFile, changelogTitle}) => ({ +module.exports = ({changelogFile, changelogTitle, skipOnPrerelease}) => ({ changelogFile: isNil(changelogFile) ? 'CHANGELOG.md' : changelogFile, changelogTitle, + skipOnPrerelease: isNil(skipOnPrerelease) ? false : skipOnPrerelease, }); diff --git a/lib/verify.js b/lib/verify.js index f4b93edb..c047b8f7 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -1,4 +1,4 @@ -const {isString, isNil} = require('lodash'); +const {isString, isNil, isBoolean} = require('lodash'); const AggregateError = require('aggregate-error'); const getError = require('./get-error.js'); const resolveConfig = require('./resolve-config.js'); @@ -8,6 +8,7 @@ const isNonEmptyString = (value) => isString(value) && value.trim(); const VALIDATORS = { changelogFile: isNonEmptyString, changelogTitle: isNonEmptyString, + skipOnPrerelease: isBoolean, }; module.exports = (pluginConfig) => { diff --git a/test/integration.test.js b/test/integration.test.js index ac294a8a..e4be3b2a 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -48,6 +48,21 @@ test.serial('Skip changelog update if the release is empty', async (t) => { t.is((await readFile(changelogPath)).toString(), 'Initial CHANGELOG'); }); +test.serial('Skip changelog update if the release is a prerelease and skipOnPrerelease option is set', async (t) => { + const cwd = tempy.directory(); + const changelogFile = 'CHANGELOG.txt'; + const changelogPath = path.resolve(cwd, changelogFile); + await outputFile(changelogPath, 'Initial CHANGELOG'); + + await t.context.m.prepare( + {skipOnPrerelease: true}, + {cwd, options: {}, nextRelease: {}, logger: t.context.logger, branch: {type: 'prerelease'}} + ); + + // Verify the content of the CHANGELOG.md + t.is((await readFile(changelogPath)).toString(), 'Initial CHANGELOG'); +}); + test.serial('Verify only on the fist call', async (t) => { const cwd = tempy.directory(); const notes = 'Test release note'; diff --git a/test/prepare.test.js b/test/prepare.test.js index e3037d35..26a99b75 100644 --- a/test/prepare.test.js +++ b/test/prepare.test.js @@ -1,6 +1,6 @@ const path = require('path'); const test = require('ava'); -const {outputFile, readFile} = require('fs-extra'); +const {outputFile, readFile, pathExists} = require('fs-extra'); const {stub} = require('sinon'); const tempy = require('tempy'); const prepare = require('../lib/prepare.js'); @@ -90,3 +90,76 @@ test.serial('Create new changelog with title if specified', async (t) => { t.is((await readFile(changelogPath)).toString(), `${changelogTitle}\n\n${notes}\n`); }); + +test.serial('Skip creation of changelog if skipOnPrerelease is set on prerelease branches', async (t) => { + const cwd = tempy.directory(); + const notes = 'Test release note'; + const changelogFile = 'CHANGELOG.md'; + const changelogPath = path.resolve(cwd, changelogFile); + + await prepare( + {skipOnPrerelease: true}, + {cwd, nextRelease: {notes}, branch: {type: 'prerelease', main: false}, logger: t.context.logger} + ); + + // Verify the content of the CHANGELOG.md + t.is(await pathExists(changelogPath), false); + t.deepEqual(t.context.log.args[0], [ + 'Skipping because branch is a prerelease branch and option skipOnPrerelease is active', + ]); +}); + +test('Skip update of changelog if skipOnPrerelease is set on prerelease branches', async (t) => { + const cwd = tempy.directory(); + const notes = 'Test release note'; + const changelogFile = 'CHANGELOG.md'; + const changelogPath = path.resolve(cwd, changelogFile); + await outputFile(changelogPath, 'Initial CHANGELOG'); + + await prepare( + {skipOnPrerelease: true}, + {cwd, nextRelease: {notes}, branch: {type: 'prerelease', main: false}, logger: t.context.logger} + ); + + // Verify the content of the CHANGELOG.md + t.is((await readFile(changelogPath)).toString(), `Initial CHANGELOG`); + t.deepEqual(t.context.log.args[0], [ + 'Skipping because branch is a prerelease branch and option skipOnPrerelease is active', + ]); +}); + +test('Skip update of changelog if skipOnPrerelease is set on release branches but it is not main', async (t) => { + const cwd = tempy.directory(); + const notes = 'Test release note'; + const changelogFile = 'CHANGELOG.md'; + const changelogPath = path.resolve(cwd, changelogFile); + await outputFile(changelogPath, 'Initial CHANGELOG'); + + await prepare( + {skipOnPrerelease: true}, + {cwd, nextRelease: {notes}, branch: {type: 'release', main: false}, logger: t.context.logger} + ); + + // Verify the content of the CHANGELOG.md + t.is((await readFile(changelogPath)).toString(), `Initial CHANGELOG`); + t.deepEqual(t.context.log.args[0], [ + 'Skipping because branch is a prerelease branch and option skipOnPrerelease is active', + ]); +}); + +test('Ensure update of changelog if skipOnPrerelease is not set on prerelease branches', async (t) => { + const cwd = tempy.directory(); + const notes = 'Test release note'; + const changelogFile = 'CHANGELOG.md'; + const changelogPath = path.resolve(cwd, changelogFile); + await outputFile(changelogPath, 'Initial CHANGELOG'); + + await prepare( + {skipOnPrerelease: false}, + {cwd, nextRelease: {notes}, branch: {type: 'prerelease', main: false}, logger: t.context.logger} + ); + + // Verify the content of the CHANGELOG.md + t.is((await readFile(changelogPath)).toString(), `${notes}\n\nInitial CHANGELOG\n`); + t.deepEqual(t.context.log.args[0], ['Update %s', changelogPath]); +}); diff --git a/test/verify.test.js b/test/verify.test.js index 57703e64..3e7e1e1e 100644 --- a/test/verify.test.js +++ b/test/verify.test.js @@ -57,3 +57,11 @@ test('Throw SemanticReleaseError if "changelogTitle" option is a whitespace Stri t.is(error.name, 'SemanticReleaseError'); t.is(error.code, 'EINVALIDCHANGELOGTITLE'); }); + +test('Throw SemanticReleaseError if "skipOnPrerelease" option is not a boolean', (t) => { + const skipOnPrerelease = 'wrong'; + const [error] = t.throws(() => verify({skipOnPrerelease})); + + t.is(error.name, 'SemanticReleaseError'); + t.is(error.code, 'EINVALIDSKIPONPRERELEASE'); +});