From bdeda6c5e11d4ada802f41b4cd46b044ffec6a42 Mon Sep 17 00:00:00 2001 From: Pierre Vanduynslager Date: Wed, 31 Oct 2018 10:49:18 -0400 Subject: [PATCH] feat: add `addChannel` plugin step --- README.md | 9 +++++ index.js | 30 +++++++++++++++- package.json | 2 +- test/add-channel.test.js | 78 ++++++++++++++++++++++++++++++++++++++++ test/integration.test.js | 1 + 5 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 test/add-channel.test.js diff --git a/README.md b/README.md index 8027221d..38d8606c 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ With this example: | `verifyReleaseCmd` | The shell command to execute during the verify release step. See [verifyReleaseCmd](#verifyreleasecmd). | | `generateNotesCmd` | The shell command to execute during the generate notes step. See [generateNotesCmd](#generatenotescmd). | | `prepareCmd` | The shell command to execute during the prepare step. See [prepareCmd](#preparecmd). | +| `addChannelCmd` | The shell command to execute during the add channel step. See [addChannelCmd](#addchannelcmd). | | `publishCmd` | The shell command to execute during the publish step. See [publishCmd](#publishcmd). | | `successCmd` | The shell command to execute during the success step. See [successCmd](#successcmd). | | `failCmd` | The shell command to execute during the fail step. See [failCmd](#failcmd). | @@ -108,6 +109,14 @@ Execute a shell command to verify if the release should happen. | `stdout` | Can be used for logging. | | `stderr` | Can be used for logging. | +## addChannelCmd + +| Command property | Description | +|------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `exit code` | Any non `0` code is considered as an unexpected error and will stop the `semantic-release` execution with an error. | +| `stdout` | The `release` information can be written to `stdout` as parseable JSON (for example `{"name": "Release name", "url": "http://url/release/1.0.0"}`). If the command write non parseable JSON to `stdout` no `release` information will be returned. | +| `stderr` | Can be used for logging. | + ## publishCmd | Command property | Description | diff --git a/index.js b/index.js index 8537b789..60796fa4 100644 --- a/index.js +++ b/index.js @@ -74,6 +74,24 @@ async function publish(pluginConfig, context) { } } +async function addChannel(pluginConfig, context) { + if (!isNil(pluginConfig.addChannelCmd) || !isNil(pluginConfig.cmd)) { + verifyConfig('addChannelCmd', pluginConfig); + + const stdout = await exec('addChannelCmd', pluginConfig, context); + + try { + return stdout ? parseJson(stdout) : undefined; + } catch (error) { + debug(stdout); + debug(error); + context.logger.log( + `The command ${pluginConfig.cmd} wrote invalid JSON to stdout. The stdout content will be ignored.` + ); + } + } +} + async function success(pluginConfig, context) { if (!isNil(pluginConfig.successCmd) || !isNil(pluginConfig.cmd)) { verifyConfig('successCmd', pluginConfig); @@ -90,4 +108,14 @@ async function fail(pluginConfig, context) { } } -module.exports = {verifyConditions, analyzeCommits, verifyRelease, generateNotes, prepare, publish, success, fail}; +module.exports = { + verifyConditions, + analyzeCommits, + verifyRelease, + generateNotes, + prepare, + publish, + addChannel, + success, + fail, +}; diff --git a/package.json b/package.json index 4ebbc32c..763a25e1 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "all": true }, "peerDependencies": { - "semantic-release": ">=15.9.0 <16.0.0" + "semantic-release": ">=15.9.0 <17.0.0" }, "prettier": { "printWidth": 120, diff --git a/test/add-channel.test.js b/test/add-channel.test.js new file mode 100644 index 00000000..012f45b3 --- /dev/null +++ b/test/add-channel.test.js @@ -0,0 +1,78 @@ +import test from 'ava'; +import {stub} from 'sinon'; +import {WritableStreamBuffer} from 'stream-buffers'; +import {addChannel} from '..'; + +test.beforeEach(t => { + t.context.stdout = new WritableStreamBuffer(); + t.context.stderr = new WritableStreamBuffer(); + // Mock logger + t.context.log = stub(); + t.context.error = stub(); + t.context.logger = {log: t.context.log, error: t.context.error}; +}); + +test('Parse JSON returned by addChannel script', async t => { + const pluginConfig = { + addChannelCmd: + './test/fixtures/echo-args.sh {\\"name\\": \\"Release name\\", \\"url\\": \\"https://host.com/release/1.0.0\\"}', + }; + const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger}; + + const result = await addChannel(pluginConfig, context); + t.deepEqual(result, {name: 'Release name', url: 'https://host.com/release/1.0.0'}); +}); + +test('Return "undefined" if the addChannel script wrtite invalid JSON to stdout (with "publishCmd")', async t => { + const pluginConfig = {addChannelCmd: './test/fixtures/echo-args.sh invalid_json'}; + const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger}; + + const result = await addChannel(pluginConfig, context); + t.is(result, undefined); +}); + +test('Return "undefined" if the addChannel script wrtite invalid JSON to stdout (with "cmd")', async t => { + const pluginConfig = {cmd: './test/fixtures/echo-args.sh invalid_json'}; + const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger}; + + const result = await addChannel(pluginConfig, context); + t.is(result, undefined); +}); + +test('Return "undefined" if the addChannel script wrtite nothing to stdout', async t => { + const pluginConfig = {addChannelCmd: './test/fixtures/echo-args.sh'}; + const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger}; + + const result = await addChannel(pluginConfig, context); + t.is(result, undefined); +}); + +test('Throw "Error" if the addChannel script does not returns 0', async t => { + const pluginConfig = {addChannelCmd: 'exit 1'}; + const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}}; + + await t.throws(addChannel(pluginConfig, context), Error); +}); + +test('Use "cmd" if defined and "addChannelCmd" is not', async t => { + const pluginConfig = { + cmd: + './test/fixtures/echo-args.sh {\\"name\\": \\"Release name\\", \\"url\\": \\"https://host.com/release/1.0.0\\"}', + }; + const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger}; + + const result = await addChannel(pluginConfig, context); + t.deepEqual(result, {name: 'Release name', url: 'https://host.com/release/1.0.0'}); +}); + +test('Use "addChannelCmd" even if "cmd" is defined', async t => { + const pluginConfig = { + addChannelCmd: + './test/fixtures/echo-args.sh {\\"name\\": \\"Release name\\", \\"url\\": \\"https://host.com/release/1.0.0\\"}', + cmd: 'exit 1', + }; + const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger}; + + const result = await addChannel(pluginConfig, context); + t.deepEqual(result, {name: 'Release name', url: 'https://host.com/release/1.0.0'}); +}); diff --git a/test/integration.test.js b/test/integration.test.js index 4f3362e5..67c95eb3 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -8,6 +8,7 @@ test('Skip step if neither "cmd" nor step cmd is defined', async t => { await t.notThrows(m.generateNotes({}, {})); await t.notThrows(m.prepare({}, {})); await t.notThrows(m.publish({}, {})); + await t.notThrows(m.addChannel({}, {})); await t.notThrows(m.success({}, {})); await t.notThrows(m.fail({}, {})); });