Skip to content

Commit 2091593

Browse files
committed
feat: add shell option
1 parent de16234 commit 2091593

12 files changed

+109
-11
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,10 @@ Execute a shell command to notify of a failed release.
9393

9494
### Options
9595

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

100101
#### `cmd`
101102

lib/exec-script.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
const {template} = require('lodash');
22
const execa = require('execa');
33

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

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

9-
const result = execa.shell(script, {cwd, env});
9+
const result = execa.shell(script, {shell, cwd, env});
1010

1111
result.stdout.pipe(
1212
stdout,

lib/verify-config.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
const {isString} = require('lodash');
1+
const {isUndefined, isString} = require('lodash');
22
const SemanticReleaseError = require('@semantic-release/error');
33

4-
module.exports = config => {
5-
if (!isString(config.cmd) || !config.cmd.trim()) {
4+
module.exports = ({cmd, shell}) => {
5+
if (!isString(cmd) || !cmd.trim()) {
66
throw new SemanticReleaseError(
7-
'The script plugin must be configured with the shell command to execute in the a "cmd" option.',
7+
'The exec plugin must be configured with the shell command to execute in the a "cmd" option.',
88
'EINVALIDCMD'
99
);
1010
}
11+
12+
if (!isUndefined(shell) && (!isString(shell) || !shell.trim())) {
13+
throw new SemanticReleaseError(
14+
'The "shell" option, if specified, must be a non empty String or the value "true".',
15+
'EINVALIDSHELL'
16+
);
17+
}
1118
};

test/analyze-commits.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
5252
t.is(error.code, 'EINVALIDCMD');
5353
});
5454

55+
test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
56+
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
57+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
58+
59+
const error = await t.throws(analyzeCommits(pluginConfig, context));
60+
61+
t.is(error.name, 'SemanticReleaseError');
62+
t.is(error.code, 'EINVALIDSHELL');
63+
});
64+
5565
test('Throw Error if if the analyzeCommits script does not returns 0', async t => {
5666
const pluginConfig = {cmd: 'exit 1'};
5767
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};

test/exec-script.test.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ test.beforeEach(t => {
1212
t.context.logger = {log: t.context.log, error: t.context.error};
1313
});
1414

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

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

26-
test.serial('Generate command with template', async t => {
26+
test('Generate command with template', async t => {
2727
const pluginConfig = {cmd: `./test/fixtures/echo-args.sh \${config.conf} \${lastRelease.version}`, conf: 'confValue'};
2828
const context = {
2929
stdout: t.context.stdout,
@@ -35,3 +35,13 @@ test.serial('Generate command with template', async t => {
3535
const result = await execScript(pluginConfig, context);
3636
t.is(result, 'confValue 1.0.0');
3737
});
38+
39+
test('Execute the script with the specified "shell"', async t => {
40+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
41+
42+
let result = await execScript({cmd: 'echo $0', shell: 'bash'}, context);
43+
t.is(result, 'bash');
44+
45+
result = await execScript({cmd: 'echo $0', shell: 'sh'}, context);
46+
t.is(result, 'sh');
47+
});

test/fail.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
3939
t.is(error.code, 'EINVALIDCMD');
4040
});
4141

42+
test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
43+
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
44+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
45+
46+
const error = await t.throws(fail(pluginConfig, context));
47+
48+
t.is(error.name, 'SemanticReleaseError');
49+
t.is(error.code, 'EINVALIDSHELL');
50+
});
51+
4252
test('Throw "Error" if the fail script does not returns 0', async t => {
4353
const pluginConfig = {cmd: 'exit 1'};
4454
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};

test/generate-notes.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
4242
t.is(error.code, 'EINVALIDCMD');
4343
});
4444

45+
test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
46+
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
47+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
48+
49+
const error = await t.throws(generateNotes(pluginConfig, context));
50+
51+
t.is(error.name, 'SemanticReleaseError');
52+
t.is(error.code, 'EINVALIDSHELL');
53+
});
54+
4555
test('Throw "Error" if if the generateNotes script does not returns 0', async t => {
4656
const pluginConfig = {cmd: 'exit 1'};
4757
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};

test/prepare.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
3939
t.is(error.code, 'EINVALIDCMD');
4040
});
4141

42+
test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
43+
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
44+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
45+
46+
const error = await t.throws(prepare(pluginConfig, context));
47+
48+
t.is(error.name, 'SemanticReleaseError');
49+
t.is(error.code, 'EINVALIDSHELL');
50+
});
51+
4252
test('Throw "Error" if the prepare script does not returns 0', async t => {
4353
const pluginConfig = {cmd: 'exit 1'};
4454
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};

test/publish.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
6363
t.is(error.code, 'EINVALIDCMD');
6464
});
6565

66+
test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
67+
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
68+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
69+
70+
const error = await t.throws(publish(pluginConfig, context));
71+
72+
t.is(error.name, 'SemanticReleaseError');
73+
t.is(error.code, 'EINVALIDSHELL');
74+
});
75+
6676
test('Throw "Error" if the publish script does not returns 0', async t => {
6777
const pluginConfig = {cmd: 'exit 1'};
6878
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};

test/success.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
3939
t.is(error.code, 'EINVALIDCMD');
4040
});
4141

42+
test('Throw "SemanticReleaseError" if "shell" options is invalid', async t => {
43+
const pluginConfig = {cmd: './test/fixtures/echo-args.sh', shell: ' '};
44+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
45+
46+
const error = await t.throws(success(pluginConfig, context));
47+
48+
t.is(error.name, 'SemanticReleaseError');
49+
t.is(error.code, 'EINVALIDSHELL');
50+
});
51+
4252
test('Throw "Error" if the success script does not returns 0', async t => {
4353
const pluginConfig = {cmd: 'exit 1'};
4454
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};

0 commit comments

Comments
 (0)