Skip to content

Commit aa194d6

Browse files
committed
feat: add execCwd option
1 parent 1b66a36 commit aa194d6

File tree

6 files changed

+51
-18
lines changed

6 files changed

+51
-18
lines changed

README.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,18 @@ With this example:
5151

5252
### Options
5353

54-
| Options | Description |
55-
|-----------------------|-----------------------------------------------------------------------------------------------------------------|
56-
| `verifyConditionsCmd` | The shell command to execute during the verify condition step. See [verifyConditionsCmd](#verifyconditionscmd). |
57-
| `analyzeCommitsCmd` | The shell command to execute during the analyze commits step. See [analyzeCommitsCmd](#analyzecommitscmd). |
58-
| `verifyReleaseCmd` | The shell command to execute during the verify release step. See [verifyReleaseCmd](#verifyreleasecmd). |
59-
| `generateNotesCmd` | The shell command to execute during the generate notes step. See [generateNotesCmd](#generatenotescmd). |
60-
| `prepareCmd` | The shell command to execute during the prepare step. See [prepareCmd](#preparecmd). |
61-
| `publishCmd` | The shell command to execute during the publish step. See [publishCmd](#publishcmd). |
62-
| `successCmd` | The shell command to execute during the success step. See [successCmd](#successcmd). |
63-
| `failCmd` | The shell command to execute during the fail step. See [failCmd](#failcmd). |
64-
| `shell` | The shell to use to run the command. See [execa#shell](https://github.com/sindresorhus/execa#shell). |
54+
| Options | Description |
55+
|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
56+
| `verifyConditionsCmd` | The shell command to execute during the verify condition step. See [verifyConditionsCmd](#verifyconditionscmd). |
57+
| `analyzeCommitsCmd` | The shell command to execute during the analyze commits step. See [analyzeCommitsCmd](#analyzecommitscmd). |
58+
| `verifyReleaseCmd` | The shell command to execute during the verify release step. See [verifyReleaseCmd](#verifyreleasecmd). |
59+
| `generateNotesCmd` | The shell command to execute during the generate notes step. See [generateNotesCmd](#generatenotescmd). |
60+
| `prepareCmd` | The shell command to execute during the prepare step. See [prepareCmd](#preparecmd). |
61+
| `publishCmd` | The shell command to execute during the publish step. See [publishCmd](#publishcmd). |
62+
| `successCmd` | The shell command to execute during the success step. See [successCmd](#successcmd). |
63+
| `failCmd` | The shell command to execute during the fail step. See [failCmd](#failcmd). |
64+
| `shell` | The shell to use to run the command. See [execa#shell](https://github.com/sindresorhus/execa#shell). |
65+
| `execCwd` | The path to use as current working directory when executing the shell commands. This path is relative to the path from which **semantic-release** is running. For example if **semantic-release** runs from `/my-project` and `execCwd` is set to `buildScripts` then the shell command will be executed from `/my-project/buildScripts` |
6566

6667
Each shell command is generated with [Lodash template](https://lodash.com/docs#template). All the objets passed to the [semantic-release plugins](https://github.com/semantic-release/semantic-release#plugins) are available as template options.
6768

lib/definitions/errors.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,10 @@ Your configuration for the \`${cmdProp}\` option is \`${stringify(cmd)}\`.`,
2424
2525
Your configuration for the \`shell\` option is \`${stringify(shell)}\`.`,
2626
}),
27+
EINVALIDEXECCWD: ({execCwd}) => ({
28+
message: 'Invalid `shell` option.',
29+
details: `The [\`execCwd\` option](${linkify('README.md#options')}) if defined, must be a non empty \`String\`.
30+
31+
Your configuration for the \`execCwd\` option is \`${stringify(execCwd)}\`.`,
32+
}),
2733
};

lib/exec.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1+
const path = require('path');
12
const {template} = require('lodash');
23
const execa = require('execa');
34

4-
module.exports = async (cmdProp, {shell, ...config}, {cwd, env, stdout, stderr, logger, ...context}) => {
5+
module.exports = async (cmdProp, {shell, execCwd, ...config}, {cwd, env, stdout, stderr, logger, ...context}) => {
56
const cmd = config[cmdProp] ? cmdProp : 'cmd';
67
const script = template(config[cmd])({config, ...context});
78

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

10-
const result = execa.shell(script, {shell, cwd, env});
11+
const result = execa.shell(script, {shell, cwd: execCwd ? path.resolve(cwd, execCwd) : cwd, env});
1112

1213
result.stdout.pipe(
1314
stdout,

lib/verify-config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ const isOptional = validator => value => isNil(value) || validator(value);
88
const VALIDATORS = {
99
cmd: isNonEmptyString,
1010
shell: isOptional(shell => shell === true || isNonEmptyString(shell)),
11+
execCwd: isOptional(isNonEmptyString),
1112
};
1213

13-
module.exports = (cmdProp, {shell, ...pluginConfig}) => {
14+
module.exports = (cmdProp, {shell, execCwd, ...pluginConfig}) => {
1415
const cmd = pluginConfig[cmdProp] ? cmdProp : pluginConfig.cmd ? 'cmd' : cmdProp;
1516

16-
const errors = Object.entries({shell, cmd: pluginConfig[cmd]}).reduce(
17+
const errors = Object.entries({shell, execCwd, cmd: pluginConfig[cmd]}).reduce(
1718
(errors, [option, value]) =>
1819
VALIDATORS[option](value)
1920
? errors

test/exec.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import path from 'path';
12
import test from 'ava';
23
import {stub} from 'sinon';
34
import {WritableStreamBuffer} from 'stream-buffers';
@@ -55,3 +56,14 @@ test('Execute the script in "cmd" if no step specific command is passed', async
5556
const result = await exec('publishCmd', {cmd: 'echo run cmd'}, context);
5657
t.is(result, 'run cmd');
5758
});
59+
60+
test('Exececute the script in cmd from the relative in "execCwd"', async t => {
61+
const pluginConfig = {
62+
publishCmd: `./fixtures/echo-args.sh $PWD`,
63+
execCwd: 'test',
64+
};
65+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, cwd: process.cwd()};
66+
67+
const result = await exec('publishCmd', pluginConfig, context);
68+
t.is(result, path.resolve(process.cwd(), 'test'));
69+
});

test/verify-config.test.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import test from 'ava';
22
import verify from '../lib/verify-config';
33

4-
test('Verify "cmd" and "shell" options', t => {
4+
test('Verify "cmd", "shell" and "execCwd" options', t => {
55
t.notThrows(() => verify('verifyConditionsCmd', {verifyConditionsCmd: 'shell cmd'}));
66
t.notThrows(() => verify('analyzeCommitsCmd', {analyzeCommitsCmd: 'shell cmd'}));
77
t.notThrows(() => verify('verifyReleaseCmd', {verifyReleaseCmd: 'shell cmd'}));
@@ -13,8 +13,8 @@ test('Verify "cmd" and "shell" options', t => {
1313

1414
t.notThrows(() => verify('verifyConditionsCmd', {cmd: 'shell cmd'}));
1515

16-
t.notThrows(() => verify('verifyConditionsCmd', {cmd: 'shell cmd', shell: true}));
17-
t.notThrows(() => verify('verifyConditionsCmd', {cmd: 'shell cmd', shell: 'bash'}));
16+
t.notThrows(() => verify('verifyConditionsCmd', {cmd: 'shell cmd', shell: true, execCwd: 'scripts'}));
17+
t.notThrows(() => verify('verifyConditionsCmd', {cmd: 'shell cmd', shell: 'bash', execCwd: 'scripts'}));
1818
});
1919

2020
test('Throw SemanticReleaseError if "cmd" option is missing', t => {
@@ -111,6 +111,18 @@ test('Throw SemanticReleaseError if "shell" option is an empty String', t => {
111111
t.is(error.code, 'EINVALIDSHELL');
112112
});
113113

114+
test('Throw SemanticReleaseError if "execCwd" option is not a String', t => {
115+
const [error] = t.throws(() => verify('verifyConditionsCmd', {execCwd: 1}));
116+
t.is(error.name, 'SemanticReleaseError');
117+
t.is(error.code, 'EINVALIDEXECCWD');
118+
});
119+
120+
test('Throw SemanticReleaseError if "execCwd" option is an empty String', t => {
121+
const [error] = t.throws(() => verify('verifyConditionsCmd', {execCwd: ' '}));
122+
t.is(error.name, 'SemanticReleaseError');
123+
t.is(error.code, 'EINVALIDEXECCWD');
124+
});
125+
114126
test('Return SemanticReleaseError Array if multiple config are invalid', t => {
115127
const [error1, error2] = t.throws(() => verify('verifyConditionsCmd', {verifyConditionsCmd: 1, shell: false}));
116128

0 commit comments

Comments
 (0)