diff --git a/README.md b/README.md index 957db2b8..a1c51e11 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,8 @@ The plugin can be configured in the [**semantic-release** configuration file](ht "@semantic-release/release-notes-generator", ["@semantic-release/git", { "assets": ["dist/**/*.{js,css}", "docs", "package.json"], - "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}", + "push_remote": true }] ] } @@ -69,10 +70,11 @@ When configuring branches permission on a Git hosting service (e.g. [GitHub prot ### Options -| Options | Description | Default | -|-----------|------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------| -| `message` | The message for the release commit. See [message](#message). | `chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}` | -| `assets` | Files to include in the release commit. Set to `false` to disable adding files to the release commit. See [assets](#assets). | `['CHANGELOG.md', 'package.json', 'package-lock.json', 'npm-shrinkwrap.json']` | +| Options | Description | Default | +|---------------|------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------| +| `message` | The message for the release commit. See [message](#message). | `chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}` | +| `assets` | Files to include in the release commit. Set to `false` to disable adding files to the release commit. See [assets](#assets). | `['CHANGELOG.md', 'package.json', 'package-lock.json', 'npm-shrinkwrap.json']` | +| `push_remote` | Whether to push the release to the remote repository. Set to `false` to generate the changes locally only. | `true` | #### `message` diff --git a/index.js b/index.js index a088dcff..f955cf36 100644 --- a/index.js +++ b/index.js @@ -13,6 +13,7 @@ function verifyConditions(pluginConfig, context) { pluginConfig.assets = defaultTo(pluginConfig.assets, preparePlugin.assets); pluginConfig.message = defaultTo(pluginConfig.message, preparePlugin.message); + pluginConfig.push_remote = defaultTo(pluginConfig.push, preparePlugin.push_remote); } verifyGit(pluginConfig); diff --git a/lib/prepare.js b/lib/prepare.js index cbac855e..e2c7f2c8 100644 --- a/lib/prepare.js +++ b/lib/prepare.js @@ -12,6 +12,7 @@ const {getModifiedFiles, add, commit, push} = require('./git.js'); * @param {Object} pluginConfig The plugin configuration. * @param {String|Array} [pluginConfig.assets] Files to include in the release commit. Can be files path or globs. * @param {String} [pluginConfig.message] The message for the release commit. + * @param {Boolean} [pluginConfig.push_remote] Whether to push to the remote repository. * @param {Object} context semantic-release context. * @param {Object} context.options `semantic-release` configuration. * @param {Object} context.lastRelease The last release. @@ -28,7 +29,7 @@ module.exports = async (pluginConfig, context) => { nextRelease, logger, } = context; - const {message, assets} = resolveConfig(pluginConfig, logger); + const {message, assets, push_remote} = resolveConfig(pluginConfig, logger); const modifiedFiles = await getModifiedFiles({env, cwd}); @@ -66,7 +67,11 @@ module.exports = async (pluginConfig, context) => { : `chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}`, {env, cwd} ); - await push(repositoryUrl, branch.name, {env, cwd}); logger.log('Prepared Git release: %s', nextRelease.gitTag); + + if (push_remote) { + await push(repositoryUrl, branch.name, {env, cwd}); + logger.log('Pushed Git release %s to remote', nextRelease.gitTag); + } } }; diff --git a/lib/resolve-config.js b/lib/resolve-config.js index dd62abfe..efae6d1c 100644 --- a/lib/resolve-config.js +++ b/lib/resolve-config.js @@ -1,10 +1,13 @@ const {isNil, castArray} = require('lodash'); -module.exports = ({assets, message}) => ({ +module.exports = ({assets, message, push_remote}) => ({ assets: isNil(assets) ? ['CHANGELOG.md', 'package.json', 'package-lock.json', 'npm-shrinkwrap.json'] : assets ? castArray(assets) : assets, message, + push_remote: isNil(push_remote) + ? true + : push_remote }); diff --git a/lib/verify.js b/lib/verify.js index fb1ae95a..d143fb5d 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -1,4 +1,4 @@ -const {isString, isNil, isArray, isPlainObject} = require('lodash'); +const {isString, isNil, isArray, isPlainObject, isBoolean} = require('lodash'); const AggregateError = require('aggregate-error'); const getError = require('./get-error.js'); const resolveConfig = require('./resolve-config.js'); @@ -16,6 +16,7 @@ const VALIDATORS = { isArrayOf((asset) => isStringOrStringArray(asset) || (isPlainObject(asset) && isStringOrStringArray(asset.path))) ), message: isNonEmptyString, + push_remote: isBoolean, }; /** diff --git a/test/integration.test.js b/test/integration.test.js index bc68a267..721e8917 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -42,6 +42,7 @@ test('Prepare from a shallow clone', async (t) => { const pluginConfig = { message: `Release version \${nextRelease.version} from branch \${branch}\n\n\${nextRelease.notes}`, assets: '**/*.{js,json}', + push_remote: true, }; await t.context.m.prepare(pluginConfig, { cwd, @@ -77,6 +78,7 @@ test('Prepare from a detached head repository', async (t) => { const pluginConfig = { message: `Release version \${nextRelease.version} from branch \${branch}\n\n\${nextRelease.notes}`, assets: '**/*.{js,json}', + push_remote: true, }; await t.context.m.prepare(pluginConfig, { cwd, diff --git a/test/prepare.test.js b/test/prepare.test.js index 8e7d97a1..d0aeeee0 100644 --- a/test/prepare.test.js +++ b/test/prepare.test.js @@ -3,7 +3,7 @@ const test = require('ava'); const {outputFile, remove} = require('fs-extra'); const {stub} = require('sinon'); const prepare = require('../lib/prepare.js'); -const {gitRepo, gitGetCommits, gitCommitedFiles, gitAdd, gitCommits, gitPush} = require('./helpers/git-utils.js'); +const {gitRepo, gitGetCommits, gitCommitedFiles, gitAdd, gitCommits, gitPush, gitRemoteHead} = require('./helpers/git-utils.js'); test.beforeEach((t) => { // Stub the logger functions @@ -43,6 +43,8 @@ test('Commit CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap.j t.is(commit.gitTags, `(HEAD -> ${branch.name})`); t.deepEqual(t.context.log.args[0], ['Found %d file(s) to commit', 4]); t.deepEqual(t.context.log.args[1], ['Prepared Git release: %s', nextRelease.gitTag]); + // Verify push has occurred + t.deepEqual(t.context.log.args[2], ['Pushed Git release %s to remote', nextRelease.gitTag]); }); test('Exclude CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap.json if "assets" is defined without it', async (t) => { @@ -270,3 +272,23 @@ test('Skip commit if there is no files to commit', async (t) => { // Verify the files that have been commited t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), []); }); + +test('Skip push if `push_remote` is configured to `false`', async (t) => { + const {cwd, repositoryUrl} = await gitRepo(true); + const pluginConfig = {assets: ['!**/*', 'file.js'], push_remote: false}; + const branch = {name: 'master'}; + const options = {repositoryUrl}; + const env = {}; + const lastRelease = {}; + const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0'}; + await outputFile(path.resolve(cwd, 'file.js'), 'Test content'); + + await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger}); + + t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), ['file.js']); + t.deepEqual(t.context.log.args[0], ['Found %d file(s) to commit', 1]); + + // Verify push has not occurred + const [commit] = await gitGetCommits(undefined, {cwd}); + t.notDeepEqual(await gitRemoteHead(repositoryUrl, {cwd}), commit.commit.long); +});