From 02b1f6d15f61139260d6ae008fa8261e05542aeb Mon Sep 17 00:00:00 2001 From: Chris Miller Date: Mon, 10 Dec 2018 23:33:48 -0800 Subject: [PATCH] feat: support `branch` parameter from semantic-release@16.0.0 --- README.md | 15 +++++++---- lib/prepare.js | 7 +++--- package.json | 2 +- test/integration.test.js | 26 ++++++++++--------- test/prepare.test.js | 54 ++++++++++++++++++++++++---------------- 5 files changed, 61 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 5341e897..2c787fb9 100644 --- a/README.md +++ b/README.md @@ -69,11 +69,16 @@ When configuring branches permission on a Git hosting service (e.g. [GitHub prot The message for the release commit is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available: -| Parameter | Description | -|---------------|-------------------------------------------------------------------------------------| -| `branch` | The branch from which the release is done. | -| `lastRelease` | `Object` with `version`, `gitTag` and `gitHead` of the last release. | -| `nextRelease` | `Object` with `version`, `gitTag`, `gitHead` and `notes` of the release being done. | +| Parameter | Description | +|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------| +| `branch` | The branch from which the release is done. | +| `branch.name` | The branch name. | +| `branch.type` | The [type of branch](https://github.com/semantic-release/semantic-release/blob/beta/docs/usage/workflow-configuration.md#branch-types). | +| `branch.channel` | The distribution channel on which to publish releases from this branch. | +| `branch.range` | The range of [semantic versions](https://semver.org) to support on this branch. | +| `branch.prerelease` | The pre-release detonation to append to [semantic versions](https://semver.org) released from this branch. | +| `lastRelease` | `Object` with `version`, `gitTag` and `gitHead` of the last release. | +| `nextRelease` | `Object` with `version`, `gitTag`, `gitHead` and `notes` of the release being done. | **Note**: It is recommended to include `[skip ci]` in the commit message to not trigger a new build. Some CI service support the `[skip ci]` keyword only in the subject of the message. diff --git a/lib/prepare.js b/lib/prepare.js index f0ab1905..0e7a7939 100644 --- a/lib/prepare.js +++ b/lib/prepare.js @@ -20,7 +20,8 @@ module.exports = async (pluginConfig, context) => { const { env, cwd, - options: {branch, repositoryUrl}, + branch, + options: {repositoryUrl}, lastRelease, nextRelease, logger, @@ -39,13 +40,13 @@ module.exports = async (pluginConfig, context) => { debug('commited files: %o', filesToCommit); await commit( message - ? template(message)({branch, lastRelease, nextRelease}) + ? template(message)({branch: branch.name, lastRelease, nextRelease}) : `chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}`, {env, cwd} ); } - await push(repositoryUrl, branch, {env, cwd}); + await push(repositoryUrl, branch.name, {env, cwd}); logger.log('Prepared Git release: %s', nextRelease.gitTag); } }; diff --git a/package.json b/package.json index 6d76a3e4..f71d9bd9 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "all": true }, "peerDependencies": { - "semantic-release": ">=15.4.0 <16.0.0" + "semantic-release": ">=16.0.0-beta <17.0.0" }, "prettier": { "printWidth": 120, diff --git a/test/integration.test.js b/test/integration.test.js index f50d9794..64caae6c 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -24,7 +24,7 @@ test.beforeEach(t => { }); test('Prepare from a shallow clone', async t => { - const branch = 'master'; + const branch = {name: 'master'}; let {cwd, repositoryUrl} = await gitRepo(true); await outputFile(path.resolve(cwd, 'package.json'), "{name: 'test-package', version: '1.0.0'}"); await outputFile(path.resolve(cwd, 'dist/file.js'), 'Initial content'); @@ -32,7 +32,7 @@ test('Prepare from a shallow clone', async t => { await add('.', {cwd}); await gitCommits(['First'], {cwd}); await gitTagVersion('v1.0.0', undefined, {cwd}); - await push(repositoryUrl, branch, {cwd}); + await push(repositoryUrl, branch.name, {cwd}); cwd = await gitShallowClone(repositoryUrl); await outputFile(path.resolve(cwd, 'package.json'), "{name: 'test-package', version: '2.0.0'}"); await outputFile(path.resolve(cwd, 'dist/file.js'), 'Updated content'); @@ -45,20 +45,21 @@ test('Prepare from a shallow clone', async t => { }; await t.context.m.prepare(pluginConfig, { cwd, - options: {repositoryUrl, branch}, + branch, + options: {repositoryUrl}, nextRelease, logger: t.context.logger, }); t.deepEqual((await gitCommitedFiles('HEAD', {cwd})).sort(), ['dist/file.js', 'package.json'].sort()); const [commit] = await gitGetCommits(undefined, {cwd}); - t.is(commit.subject, `Release version ${nextRelease.version} from branch ${branch}`); + t.is(commit.subject, `Release version ${nextRelease.version} from branch ${branch.name}`); t.is(commit.body, `${nextRelease.notes}\n`); - t.is(commit.gitTags, `(HEAD -> ${branch})`); + t.is(commit.gitTags, `(HEAD -> ${branch.name})`); }); test('Prepare from a detached head repository', async t => { - const branch = 'master'; + const branch = {name: 'master'}; let {cwd, repositoryUrl} = await gitRepo(true); await outputFile(path.resolve(cwd, 'package.json'), "{name: 'test-package', version: '1.0.0'}"); await outputFile(path.resolve(cwd, 'dist/file.js'), 'Initial content'); @@ -66,7 +67,7 @@ test('Prepare from a detached head repository', async t => { await add('.', {cwd}); const [{hash}] = await gitCommits(['First'], {cwd}); await gitTagVersion('v1.0.0', undefined, {cwd}); - await push(repositoryUrl, branch, {cwd}); + await push(repositoryUrl, branch.name, {cwd}); cwd = await gitDetachedHead(repositoryUrl, hash); await outputFile(path.resolve(cwd, 'package.json'), "{name: 'test-package', version: '2.0.0'}"); await outputFile(path.resolve(cwd, 'dist/file.js'), 'Updated content'); @@ -79,26 +80,27 @@ test('Prepare from a detached head repository', async t => { }; await t.context.m.prepare(pluginConfig, { cwd, - options: {repositoryUrl, branch}, + branch, + options: {repositoryUrl}, nextRelease, logger: t.context.logger, }); t.deepEqual((await gitCommitedFiles('HEAD', {cwd})).sort(), ['dist/file.js', 'package.json'].sort()); const [commit] = await gitGetCommits(undefined, {cwd}); - t.is(commit.subject, `Release version ${nextRelease.version} from branch ${branch}`); + t.is(commit.subject, `Release version ${nextRelease.version} from branch ${branch.name}`); t.is(commit.body, `${nextRelease.notes}\n`); t.is(commit.gitTags, `(HEAD)`); }); test('Verify authentication only on the fist call', async t => { - const branch = 'master'; + const branch = {name: 'master'}; const {cwd, repositoryUrl} = await gitRepo(true); const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0'}; - const options = {repositoryUrl, branch, prepare: ['@semantic-release/npm']}; + const options = {repositoryUrl, prepare: ['@semantic-release/npm']}; t.notThrows(() => t.context.m.verifyConditions({}, {cwd, options, logger: t.context.logger})); - await t.context.m.prepare({}, {cwd, options: {repositoryUrl, branch}, nextRelease, logger: t.context.logger}); + await t.context.m.prepare({}, {cwd, options: {repositoryUrl}, branch, nextRelease, logger: t.context.logger}); }); test('Throw SemanticReleaseError if prepare config is invalid', t => { diff --git a/test/prepare.test.js b/test/prepare.test.js index 0f40f3c2..f725e6aa 100644 --- a/test/prepare.test.js +++ b/test/prepare.test.js @@ -14,7 +14,8 @@ test.beforeEach(t => { test('Commit CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap.json if they exists and have been changed', async t => { const {cwd, repositoryUrl} = await gitRepo(true); const pluginConfig = {}; - const options = {repositoryUrl, branch: 'master'}; + const branch = {name: 'master'}; + const options = {repositoryUrl}; const env = {}; const lastRelease = {}; const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0', notes: 'Test release note'}; @@ -28,7 +29,7 @@ test('Commit CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap.j await outputFile(pkgLockPath, "{name: 'test-package'}"); await outputFile(shrinkwrapPath, "{name: 'test-package'}"); - await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger}); + await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger}); // Verify the remote repo has a the version referencing the same commit sha at the local head const [commit] = await gitGetCommits(undefined, {cwd, env}); @@ -39,7 +40,7 @@ test('Commit CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap.j ); t.is(commit.subject, `chore(release): ${nextRelease.version} [skip ci]`); t.is(commit.body, `${nextRelease.notes}\n`); - t.is(commit.gitTags, `(HEAD -> ${options.branch})`); + 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]); }); @@ -47,7 +48,8 @@ test('Commit CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap.j test('Exclude CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap.json if "assets" is defined without it', async t => { const {cwd, repositoryUrl} = await gitRepo(true); const pluginConfig = {assets: []}; - const options = {repositoryUrl, branch: 'master'}; + const branch = {name: 'master'}; + const options = {repositoryUrl}; const env = {}; const lastRelease = {}; const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0'}; @@ -56,7 +58,7 @@ test('Exclude CHANGELOG.md, package.json, package-lock.json, and npm-shrinkwrap. await outputFile(path.resolve(cwd, 'package-lock.json'), "{name: 'test-package'}"); await outputFile(path.resolve(cwd, 'npm-shrinkwrap.json'), "{name: 'test-package'}"); - await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger}); + await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger}); // Verify no files have been commited t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), []); @@ -70,19 +72,20 @@ test.serial('Allow to customize the commit message', async t => { Last release: \${lastRelease.version} \${nextRelease.notes}`, }; - const options = {repositoryUrl, branch: 'master'}; + const branch = {name: 'master'}; + const options = {repositoryUrl}; const env = {}; const lastRelease = {version: 'v1.0.0'}; const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0', notes: 'Test release note'}; await outputFile(path.resolve(cwd, 'CHANGELOG.md'), 'Initial CHANGELOG'); - await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger}); + await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger}); // Verify the files that have been commited t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), ['CHANGELOG.md']); // Verify the commit message contains on the new release notes const [commit] = await gitGetCommits(undefined, {cwd, env}); - t.is(commit.subject, `Release version ${nextRelease.version} from branch ${options.branch}`); + t.is(commit.subject, `Release version ${nextRelease.version} from branch ${branch.name}`); t.is(commit.body, `Last release: ${lastRelease.version}\n${nextRelease.notes}\n`); }); @@ -91,7 +94,8 @@ test('Commit files matching the patterns in "assets"', async t => { const pluginConfig = { assets: ['file1.js', '*1.js', ['dir/*.js', '!dir/*.css'], 'file5.js', 'dir2', ['**/*.js', '!**/*.js']], }; - const options = {repositoryUrl, branch: 'master'}; + const branch = {name: 'master'}; + const options = {repositoryUrl}; const env = {}; const lastRelease = {}; const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0'}; @@ -105,7 +109,7 @@ test('Commit files matching the patterns in "assets"', async t => { await outputFile(path.resolve(cwd, 'dir2/file6.js'), 'Test content'); await outputFile(path.resolve(cwd, 'dir2/file7.css'), 'Test content'); - await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger}); + await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger}); // Verify file2 and file1 have been commited // file4.js is excluded as no glob matching @@ -124,7 +128,8 @@ test('Commit files matching the patterns in "assets" as Objects', async t => { const pluginConfig = { assets: ['file1.js', {path: ['dir/*.js', '!dir/*.css']}, {path: 'file5.js'}, 'dir2'], }; - const options = {repositoryUrl, branch: 'master'}; + const branch = {name: 'master'}; + const options = {repositoryUrl}; const env = {}; const lastRelease = {}; const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0'}; @@ -138,7 +143,7 @@ test('Commit files matching the patterns in "assets" as Objects', async t => { await outputFile(path.resolve(cwd, 'dir2/file6.js'), 'Test content'); await outputFile(path.resolve(cwd, 'dir2/file7.css'), 'Test content'); - await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger}); + await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger}); // Verify file2 and file1 have been commited // file4.js is excluded as no glob matching @@ -155,14 +160,15 @@ test('Commit files matching the patterns in "assets" as Objects', async t => { test('Commit files matching the patterns in "assets" as single glob', async t => { const {cwd, repositoryUrl} = await gitRepo(true); const pluginConfig = {assets: 'dist/**/*.js'}; - const options = {repositoryUrl, branch: 'master'}; + 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, 'dist/file1.js'), 'Test content'); await outputFile(path.resolve(cwd, 'dist/file2.css'), 'Test content'); - await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger}); + await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger}); t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), ['dist/file1.js']); t.deepEqual(t.context.log.args[0], ['Found %d file(s) to commit', 1]); @@ -171,13 +177,14 @@ test('Commit files matching the patterns in "assets" as single glob', async t => test('Commit files matching the patterns in "assets", including dot files', async t => { const {cwd, repositoryUrl} = await gitRepo(true); const pluginConfig = {assets: 'dist'}; - const options = {repositoryUrl, branch: 'master'}; + 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, 'dist/.dotfile'), 'Test content'); - await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger}); + await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger}); t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), ['dist/.dotfile']); t.deepEqual(t.context.log.args[0], ['Found %d file(s) to commit', 1]); @@ -185,7 +192,8 @@ test('Commit files matching the patterns in "assets", including dot files', asyn test('Set the commit author and committer name/email based on environment variables', async t => { const {cwd, repositoryUrl} = await gitRepo(true); - const options = {repositoryUrl, branch: 'master'}; + const branch = {name: 'master'}; + const options = {repositoryUrl}; const env = { GIT_AUTHOR_NAME: 'author name', GIT_AUTHOR_EMAIL: 'author email', @@ -196,7 +204,7 @@ test('Set the commit author and committer name/email based on environment variab const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0', notes: 'Test release note'}; await outputFile(path.resolve(cwd, 'CHANGELOG.md'), 'Initial CHANGELOG'); - await prepare({}, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger}); + await prepare({}, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger}); // Verify the files that have been commited t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), ['CHANGELOG.md']); @@ -211,13 +219,14 @@ test('Set the commit author and committer name/email based on environment variab test('Skip negated pattern if its alone in its group', async t => { const {cwd, repositoryUrl} = await gitRepo(true); const pluginConfig = {assets: ['!**/*', 'file.js']}; - const options = {repositoryUrl, branch: 'master'}; + 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, lastRelease, nextRelease, logger: t.context.logger}); + 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]); @@ -226,12 +235,13 @@ test('Skip negated pattern if its alone in its group', async t => { test('Skip commit if there is no files to commit', async t => { const {cwd, repositoryUrl} = await gitRepo(true); const pluginConfig = {}; - const options = {repositoryUrl, branch: 'master'}; + const branch = {name: 'master'}; + const options = {repositoryUrl}; const env = {}; const lastRelease = {}; const nextRelease = {version: '2.0.0', gitTag: 'v2.0.0', notes: 'Test release note'}; - await prepare(pluginConfig, {cwd, env, options, lastRelease, nextRelease, logger: t.context.logger}); + await prepare(pluginConfig, {cwd, env, options, branch, lastRelease, nextRelease, logger: t.context.logger}); // Verify the files that have been commited t.deepEqual(await gitCommitedFiles('HEAD', {cwd, env}), []);