From c0b9a50ce36a1c770640be97b5cc8f20670e78e1 Mon Sep 17 00:00:00 2001 From: Pierre Vanduynslager Date: Mon, 12 Feb 2018 22:52:51 -0500 Subject: [PATCH] feat: return release info in `publish` and add `success` and `fail` hooks (#11) --- README.md | 20 ++++++++++++++++++++ index.js | 12 +++++++++++- package.json | 3 ++- test/fail.test.js | 29 +++++++++++++++++++++++++++++ test/publish.test.js | 32 ++++++++++++++++++++++++++++---- test/success.test.js | 29 +++++++++++++++++++++++++++++ 6 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 test/fail.test.js create mode 100644 test/success.test.js diff --git a/README.md b/README.md index a1bdb417..96e8fa04 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,26 @@ Execute a shell command to generate the release note. Execute a shell command to publish the release. +| 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` | Only the `release` information must be written to `stdout` as parseable JSON (for example `{"name": "Release name", "url": "http://url/release/1.0.0"}`). | +| `stderr` | Can be used for logging. | + +## success + +Execute a shell command to notify of a successful release. + +| 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` | Can be used for logging. | +| `stderr` | Can be used for logging. | + +## fail + +Execute a shell command to notify of a failed release. + | 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. | diff --git a/index.js b/index.js index 300946e4..f8cb2076 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,5 @@ const {castArray, isPlainObject} = require('lodash'); +const parseJson = require('parse-json'); const SemanticReleaseError = require('@semantic-release/error'); const execScript = require('./lib/exec-script'); const verifyConfig = require('./lib/verify-config'); @@ -46,7 +47,16 @@ async function generateNotes(pluginConfig, params) { } async function publish(pluginConfig, params) { + const stdout = await execScript(pluginConfig, params); + return stdout.trim() ? parseJson(stdout) : undefined; +} + +async function success(pluginConfig, params) { + await execScript(pluginConfig, params); +} + +async function fail(pluginConfig, params) { await execScript(pluginConfig, params); } -module.exports = {verifyConditions, analyzeCommits, verifyRelease, generateNotes, publish}; +module.exports = {verifyConditions, analyzeCommits, verifyRelease, generateNotes, publish, success, fail}; diff --git a/package.json b/package.json index 93f7e934..4d815ec7 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "@semantic-release/error": "^2.1.0", "debug": "^3.1.0", "execa": "^0.9.0", - "lodash": "^4.17.4" + "lodash": "^4.17.4", + "parse-json": "^4.0.0" }, "devDependencies": { "ava": "^0.25.0", diff --git a/test/fail.test.js b/test/fail.test.js new file mode 100644 index 00000000..f948fc9e --- /dev/null +++ b/test/fail.test.js @@ -0,0 +1,29 @@ +import test from 'ava'; +import {stub} from 'sinon'; +import {fail} from '..'; + +stub(process.stdout, 'write'); +stub(process.stderr, 'write'); + +test.beforeEach(t => { + // Mock logger + t.context.log = stub(); + t.context.error = stub(); + t.context.logger = {log: t.context.log, error: t.context.error}; +}); + +test.serial('Return the value fail script wrote to stdout', async t => { + const pluginConfig = { + cmd: './test/fixtures/echo-args.sh', + }; + const params = {logger: t.context.logger}; + + await t.notThrows(fail(pluginConfig, params)); +}); + +test.serial('Throw "Error" if the fail script does not returns 0', async t => { + const pluginConfig = {cmd: 'exit 1'}; + const params = {logger: t.context.logger}; + + await t.throws(fail(pluginConfig, params), Error); +}); diff --git a/test/publish.test.js b/test/publish.test.js index cd666a1f..5a7f519d 100644 --- a/test/publish.test.js +++ b/test/publish.test.js @@ -13,11 +13,35 @@ test.beforeEach(t => { t.context.logger = {log: t.context.log, error: t.context.error}; }); -test.serial('Return if the publish script returns 0', async t => { - const pluginConfig = {cmd: 'exit 0'}; - const params = {logger: t.context.logger, options: {}}; +test.serial('Parse JSON returned by publish script', async t => { + const pluginConfig = { + cmd: + './test/fixtures/echo-args.sh {\\"name\\": \\"Release name\\", \\"url\\": \\"https://host.com/release/1.0.0\\"}', + }; + const params = {logger: t.context.logger}; + + const result = await publish(pluginConfig, params); + t.deepEqual(result, {name: 'Release name', url: 'https://host.com/release/1.0.0'}); +}); + +test.serial('Return "undefined" if the publish script wrtite nothing to stdout', async t => { + const pluginConfig = { + cmd: './test/fixtures/echo-args.sh', + }; + const params = {logger: t.context.logger}; + + const result = await publish(pluginConfig, params); + t.is(result, undefined); +}); + +test.serial('Throw JSONError if publish script write invalid JSON to stdout', async t => { + const pluginConfig = { + cmd: './test/fixtures/echo-args.sh invalid_json', + }; + const params = {logger: t.context.logger}; - await t.notThrows(publish(pluginConfig, params)); + const error = await t.throws(publish(pluginConfig, params)); + t.is(error.name, 'JSONError'); }); test.serial('Throw "Error" if the publish script does not returns 0', async t => { diff --git a/test/success.test.js b/test/success.test.js new file mode 100644 index 00000000..de8dacc4 --- /dev/null +++ b/test/success.test.js @@ -0,0 +1,29 @@ +import test from 'ava'; +import {stub} from 'sinon'; +import {success} from '..'; + +stub(process.stdout, 'write'); +stub(process.stderr, 'write'); + +test.beforeEach(t => { + // Mock logger + t.context.log = stub(); + t.context.error = stub(); + t.context.logger = {log: t.context.log, error: t.context.error}; +}); + +test.serial('Return the value success script wrote to stdout', async t => { + const pluginConfig = { + cmd: './test/fixtures/echo-args.sh', + }; + const params = {logger: t.context.logger}; + + await t.notThrows(success(pluginConfig, params)); +}); + +test.serial('Throw "Error" if the success script does not returns 0', async t => { + const pluginConfig = {cmd: 'exit 1'}; + const params = {logger: t.context.logger}; + + await t.throws(success(pluginConfig, params), Error); +});