Skip to content

Commit

Permalink
feat: add execCwd option
Browse files Browse the repository at this point in the history
  • Loading branch information
pvdlg committed Oct 8, 2018
1 parent 1b66a36 commit aa194d6
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 18 deletions.
23 changes: 12 additions & 11 deletions README.md
Expand Up @@ -51,17 +51,18 @@ With this example:

### Options

| Options | Description |
|-----------------------|-----------------------------------------------------------------------------------------------------------------|
| `verifyConditionsCmd` | The shell command to execute during the verify condition step. See [verifyConditionsCmd](#verifyconditionscmd). |
| `analyzeCommitsCmd` | The shell command to execute during the analyze commits step. See [analyzeCommitsCmd](#analyzecommitscmd). |
| `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). |
| `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). |
| `shell` | The shell to use to run the command. See [execa#shell](https://github.com/sindresorhus/execa#shell). |
| Options | Description |
|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `verifyConditionsCmd` | The shell command to execute during the verify condition step. See [verifyConditionsCmd](#verifyconditionscmd). |
| `analyzeCommitsCmd` | The shell command to execute during the analyze commits step. See [analyzeCommitsCmd](#analyzecommitscmd). |
| `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). |
| `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). |
| `shell` | The shell to use to run the command. See [execa#shell](https://github.com/sindresorhus/execa#shell). |
| `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` |

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.

Expand Down
6 changes: 6 additions & 0 deletions lib/definitions/errors.js
Expand Up @@ -24,4 +24,10 @@ Your configuration for the \`${cmdProp}\` option is \`${stringify(cmd)}\`.`,
Your configuration for the \`shell\` option is \`${stringify(shell)}\`.`,
}),
EINVALIDEXECCWD: ({execCwd}) => ({
message: 'Invalid `shell` option.',
details: `The [\`execCwd\` option](${linkify('README.md#options')}) if defined, must be a non empty \`String\`.
Your configuration for the \`execCwd\` option is \`${stringify(execCwd)}\`.`,
}),
};
5 changes: 3 additions & 2 deletions lib/exec.js
@@ -1,13 +1,14 @@
const path = require('path');
const {template} = require('lodash');
const execa = require('execa');

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

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

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

result.stdout.pipe(
stdout,
Expand Down
5 changes: 3 additions & 2 deletions lib/verify-config.js
Expand Up @@ -8,12 +8,13 @@ const isOptional = validator => value => isNil(value) || validator(value);
const VALIDATORS = {
cmd: isNonEmptyString,
shell: isOptional(shell => shell === true || isNonEmptyString(shell)),
execCwd: isOptional(isNonEmptyString),
};

module.exports = (cmdProp, {shell, ...pluginConfig}) => {
module.exports = (cmdProp, {shell, execCwd, ...pluginConfig}) => {
const cmd = pluginConfig[cmdProp] ? cmdProp : pluginConfig.cmd ? 'cmd' : cmdProp;

const errors = Object.entries({shell, cmd: pluginConfig[cmd]}).reduce(
const errors = Object.entries({shell, execCwd, cmd: pluginConfig[cmd]}).reduce(
(errors, [option, value]) =>
VALIDATORS[option](value)
? errors
Expand Down
12 changes: 12 additions & 0 deletions test/exec.test.js
@@ -1,3 +1,4 @@
import path from 'path';
import test from 'ava';
import {stub} from 'sinon';
import {WritableStreamBuffer} from 'stream-buffers';
Expand Down Expand Up @@ -55,3 +56,14 @@ test('Execute the script in "cmd" if no step specific command is passed', async
const result = await exec('publishCmd', {cmd: 'echo run cmd'}, context);
t.is(result, 'run cmd');
});

test('Exececute the script in cmd from the relative in "execCwd"', async t => {
const pluginConfig = {
publishCmd: `./fixtures/echo-args.sh $PWD`,
execCwd: 'test',
};
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, cwd: process.cwd()};

const result = await exec('publishCmd', pluginConfig, context);
t.is(result, path.resolve(process.cwd(), 'test'));
});
18 changes: 15 additions & 3 deletions test/verify-config.test.js
@@ -1,7 +1,7 @@
import test from 'ava';
import verify from '../lib/verify-config';

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

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

t.notThrows(() => verify('verifyConditionsCmd', {cmd: 'shell cmd', shell: true}));
t.notThrows(() => verify('verifyConditionsCmd', {cmd: 'shell cmd', shell: 'bash'}));
t.notThrows(() => verify('verifyConditionsCmd', {cmd: 'shell cmd', shell: true, execCwd: 'scripts'}));
t.notThrows(() => verify('verifyConditionsCmd', {cmd: 'shell cmd', shell: 'bash', execCwd: 'scripts'}));
});

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

test('Throw SemanticReleaseError if "execCwd" option is not a String', t => {
const [error] = t.throws(() => verify('verifyConditionsCmd', {execCwd: 1}));
t.is(error.name, 'SemanticReleaseError');
t.is(error.code, 'EINVALIDEXECCWD');
});

test('Throw SemanticReleaseError if "execCwd" option is an empty String', t => {
const [error] = t.throws(() => verify('verifyConditionsCmd', {execCwd: ' '}));
t.is(error.name, 'SemanticReleaseError');
t.is(error.code, 'EINVALIDEXECCWD');
});

test('Return SemanticReleaseError Array if multiple config are invalid', t => {
const [error1, error2] = t.throws(() => verify('verifyConditionsCmd', {verifyConditionsCmd: 1, shell: false}));

Expand Down

0 comments on commit aa194d6

Please sign in to comment.