Skip to content

Commit

Permalink
feat: add shell option
Browse files Browse the repository at this point in the history
  • Loading branch information
pvdlg committed Aug 15, 2018
1 parent de16234 commit 2091593
Show file tree
Hide file tree
Showing 12 changed files with 109 additions and 11 deletions.
7 changes: 4 additions & 3 deletions README.md
Expand Up @@ -93,9 +93,10 @@ Execute a shell command to notify of a failed release.

### Options

| Options | Description |
|---------|------------------------------------------------|
| `cmd` | The shell command to execute. See [cmd](#cmd). |
| Options | Description |
|---------|------------------------------------------------------------------------------------------------------|
| `cmd` | The shell command to execute. See [cmd](#cmd). |
| `shell` | The shell to use to run the command. See [execa#shell](https://github.com/sindresorhus/execa#shell). |

#### `cmd`

Expand Down
4 changes: 2 additions & 2 deletions lib/exec-script.js
@@ -1,12 +1,12 @@
const {template} = require('lodash');
const execa = require('execa');

module.exports = async ({cmd, ...config}, {cwd, env, stdout, stderr, logger, ...context}) => {
module.exports = async ({cmd, shell, ...config}, {cwd, env, stdout, stderr, logger, ...context}) => {
const script = template(cmd)({config, ...context});

logger.log('Call script %s', script);

const result = execa.shell(script, {cwd, env});
const result = execa.shell(script, {shell, cwd, env});

result.stdout.pipe(
stdout,
Expand Down
15 changes: 11 additions & 4 deletions lib/verify-config.js
@@ -1,11 +1,18 @@
const {isString} = require('lodash');
const {isUndefined, isString} = require('lodash');
const SemanticReleaseError = require('@semantic-release/error');

module.exports = config => {
if (!isString(config.cmd) || !config.cmd.trim()) {
module.exports = ({cmd, shell}) => {
if (!isString(cmd) || !cmd.trim()) {
throw new SemanticReleaseError(
'The script plugin must be configured with the shell command to execute in the a "cmd" option.',
'The exec plugin must be configured with the shell command to execute in the a "cmd" option.',
'EINVALIDCMD'
);
}

if (!isUndefined(shell) && (!isString(shell) || !shell.trim())) {
throw new SemanticReleaseError(
'The "shell" option, if specified, must be a non empty String or the value "true".',
'EINVALIDSHELL'
);
}
};
10 changes: 10 additions & 0 deletions test/analyze-commits.test.js
Expand Up @@ -52,6 +52,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
t.is(error.code, 'EINVALIDCMD');
});

test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};

const error = await t.throws(analyzeCommits(pluginConfig, context));

t.is(error.name, 'SemanticReleaseError');
t.is(error.code, 'EINVALIDSHELL');
});

test('Throw Error if if the analyzeCommits script does not returns 0', async t => {
const pluginConfig = {cmd: 'exit 1'};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
Expand Down
14 changes: 12 additions & 2 deletions test/exec-script.test.js
Expand Up @@ -12,7 +12,7 @@ test.beforeEach(t => {
t.context.logger = {log: t.context.log, error: t.context.error};
});

test.serial('Pipe script output to stdout and stderr', async t => {
test('Pipe script output to stdout and stderr', async t => {
const pluginConfig = {cmd: '>&2 echo "write to stderr" && echo "write to stdout"'};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};

Expand All @@ -23,7 +23,7 @@ test.serial('Pipe script output to stdout and stderr', async t => {
t.is(t.context.stderr.getContentsAsString('utf8').trim(), 'write to stderr');
});

test.serial('Generate command with template', async t => {
test('Generate command with template', async t => {
const pluginConfig = {cmd: `./test/fixtures/echo-args.sh \${config.conf} \${lastRelease.version}`, conf: 'confValue'};
const context = {
stdout: t.context.stdout,
Expand All @@ -35,3 +35,13 @@ test.serial('Generate command with template', async t => {
const result = await execScript(pluginConfig, context);
t.is(result, 'confValue 1.0.0');
});

test('Execute the script with the specified "shell"', async t => {
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};

let result = await execScript({cmd: 'echo $0', shell: 'bash'}, context);
t.is(result, 'bash');

result = await execScript({cmd: 'echo $0', shell: 'sh'}, context);
t.is(result, 'sh');
});
10 changes: 10 additions & 0 deletions test/fail.test.js
Expand Up @@ -39,6 +39,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
t.is(error.code, 'EINVALIDCMD');
});

test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};

const error = await t.throws(fail(pluginConfig, context));

t.is(error.name, 'SemanticReleaseError');
t.is(error.code, 'EINVALIDSHELL');
});

test('Throw "Error" if the fail script does not returns 0', async t => {
const pluginConfig = {cmd: 'exit 1'};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
Expand Down
10 changes: 10 additions & 0 deletions test/generate-notes.test.js
Expand Up @@ -42,6 +42,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
t.is(error.code, 'EINVALIDCMD');
});

test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};

const error = await t.throws(generateNotes(pluginConfig, context));

t.is(error.name, 'SemanticReleaseError');
t.is(error.code, 'EINVALIDSHELL');
});

test('Throw "Error" if if the generateNotes script does not returns 0', async t => {
const pluginConfig = {cmd: 'exit 1'};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
Expand Down
10 changes: 10 additions & 0 deletions test/prepare.test.js
Expand Up @@ -39,6 +39,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
t.is(error.code, 'EINVALIDCMD');
});

test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};

const error = await t.throws(prepare(pluginConfig, context));

t.is(error.name, 'SemanticReleaseError');
t.is(error.code, 'EINVALIDSHELL');
});

test('Throw "Error" if the prepare script does not returns 0', async t => {
const pluginConfig = {cmd: 'exit 1'};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
Expand Down
10 changes: 10 additions & 0 deletions test/publish.test.js
Expand Up @@ -63,6 +63,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
t.is(error.code, 'EINVALIDCMD');
});

test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};

const error = await t.throws(publish(pluginConfig, context));

t.is(error.name, 'SemanticReleaseError');
t.is(error.code, 'EINVALIDSHELL');
});

test('Throw "Error" if the publish script does not returns 0', async t => {
const pluginConfig = {cmd: 'exit 1'};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
Expand Down
10 changes: 10 additions & 0 deletions test/success.test.js
Expand Up @@ -39,6 +39,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
t.is(error.code, 'EINVALIDCMD');
});

test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};

const error = await t.throws(success(pluginConfig, context));

t.is(error.name, 'SemanticReleaseError');
t.is(error.code, 'EINVALIDSHELL');
});

test('Throw "Error" if the success script does not returns 0', async t => {
const pluginConfig = {cmd: 'exit 1'};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
Expand Down
10 changes: 10 additions & 0 deletions test/verify-confitions.test.js
Expand Up @@ -39,6 +39,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
t.is(error.code, 'EINVALIDCMD');
});

test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};

const error = await t.throws(verifyConditions(pluginConfig, context));

t.is(error.name, 'SemanticReleaseError');
t.is(error.code, 'EINVALIDSHELL');
});

test('Return if the verifyConditions script returns 0', async t => {
const pluginConfig = {cmd: 'exit 0'};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
Expand Down
10 changes: 10 additions & 0 deletions test/verify-release.test.js
Expand Up @@ -39,6 +39,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
t.is(error.code, 'EINVALIDCMD');
});

test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};

const error = await t.throws(verifyRelease(pluginConfig, context));

t.is(error.name, 'SemanticReleaseError');
t.is(error.code, 'EINVALIDSHELL');
});

test('Throw "SemanticReleaseError" if the verifyRelease script does not returns 0', async t => {
const pluginConfig = {cmd: 'exit 1'};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
Expand Down

0 comments on commit 2091593

Please sign in to comment.