Skip to content

Commit 2ba5271

Browse files
committed
feat: log script output to stdout/stderr passed by semantic-release core
BREAKING CHANGE: require `semantic-release` >= `15.9.0`
1 parent cdb248f commit 2ba5271

11 files changed

+67
-66
lines changed

lib/exec-script.js

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

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

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

99
const shell = execa.shell(script, {cwd, env});
10-
shell.stdout.pipe(process.stdout);
11-
shell.stderr.pipe(process.stderr);
10+
shell.stdout.pipe(stdout);
11+
shell.stderr.pipe(stderr);
1212

1313
return (await shell).stdout.trim();
1414
};

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"nyc": "^12.0.1",
3131
"semantic-release": "^15.0.0",
3232
"sinon": "^6.0.0",
33+
"stream-buffers": "^3.0.2",
3334
"xo": "^0.21.0"
3435
},
3536
"engines": {
@@ -64,7 +65,7 @@
6465
"all": true
6566
},
6667
"peerDependencies": {
67-
"semantic-release": ">=13.0.0 <16.0.0"
68+
"semantic-release": ">=15.9.0 <16.0.0"
6869
},
6970
"prettier": {
7071
"printWidth": 120,

test/analyze-commits.test.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import test from 'ava';
22
import {stub} from 'sinon';
3+
import {WritableStreamBuffer} from 'stream-buffers';
34
import {analyzeCommits} from '..';
45

5-
// Disable logs during tests
6-
stub(process.stdout, 'write');
7-
stub(process.stderr, 'write');
8-
96
test.beforeEach(t => {
7+
t.context.stdout = new WritableStreamBuffer();
8+
t.context.stderr = new WritableStreamBuffer();
109
// Mock logger
1110
t.context.log = stub();
1211
t.context.error = stub();
@@ -17,7 +16,7 @@ test('Return the value analyzeCommits script wrote to stdout', async t => {
1716
const pluginConfig = {
1817
cmd: './test/fixtures/echo-args.sh "minor "',
1918
};
20-
const context = {logger: t.context.logger};
19+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
2120

2221
const result = await analyzeCommits(pluginConfig, context);
2322
t.is(result, 'minor');
@@ -27,15 +26,15 @@ test('Return "undefined" if the analyzeCommits script wrtite nothing to stdout',
2726
const pluginConfig = {
2827
cmd: './test/fixtures/echo-args.sh " "',
2928
};
30-
const context = {logger: t.context.logger};
29+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
3130

3231
const result = await analyzeCommits(pluginConfig, context);
3332
t.is(result, undefined);
3433
});
3534

3635
test('Throw Error if if the analyzeCommits script does not returns 0', async t => {
3736
const pluginConfig = {cmd: 'exit 1'};
38-
const context = {logger: t.context.logger};
37+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
3938

4039
await t.throws(analyzeCommits(pluginConfig, context), Error);
4140
});

test/exec-script.test.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,36 @@
11
import test from 'ava';
22
import {stub} from 'sinon';
3+
import {WritableStreamBuffer} from 'stream-buffers';
34
import execScript from '../lib/exec-script';
45

56
test.beforeEach(t => {
7+
t.context.stdout = new WritableStreamBuffer();
8+
t.context.stderr = new WritableStreamBuffer();
69
// Mock logger
710
t.context.log = stub();
811
t.context.error = stub();
912
t.context.logger = {log: t.context.log, error: t.context.error};
10-
t.context.stdout = stub(process.stdout, 'write');
11-
t.context.stderr = stub(process.stderr, 'write');
12-
});
13-
14-
test.afterEach.always(t => {
15-
// Restore the logs
16-
t.context.stdout.restore();
17-
t.context.stderr.restore();
1813
});
1914

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

2419
const result = await execScript(pluginConfig, context);
2520

2621
t.is(result, 'write to stdout');
27-
t.is(t.context.stdout.args[0][0].toString().trim(), 'write to stdout');
28-
t.is(t.context.stderr.args[0][0].toString().trim(), 'write to stderr');
22+
t.is(t.context.stdout.getContentsAsString('utf8').trim(), 'write to stdout');
23+
t.is(t.context.stderr.getContentsAsString('utf8').trim(), 'write to stderr');
2924
});
3025

3126
test.serial('Generate command with template', async t => {
3227
const pluginConfig = {cmd: `./test/fixtures/echo-args.sh \${config.conf} \${lastRelease.version}`, conf: 'confValue'};
33-
const context = {lastRelease: {version: '1.0.0'}, logger: t.context.logger};
28+
const context = {
29+
stdout: t.context.stdout,
30+
stderr: t.context.stderr,
31+
lastRelease: {version: '1.0.0'},
32+
logger: t.context.logger,
33+
};
3434

3535
const result = await execScript(pluginConfig, context);
3636
t.is(result, 'confValue 1.0.0');

test/fail.test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import test from 'ava';
22
import {stub} from 'sinon';
3+
import {WritableStreamBuffer} from 'stream-buffers';
34
import {fail} from '..';
45

5-
stub(process.stdout, 'write');
6-
stub(process.stderr, 'write');
7-
86
test.beforeEach(t => {
7+
t.context.stdout = new WritableStreamBuffer();
8+
t.context.stderr = new WritableStreamBuffer();
99
// Mock logger
1010
t.context.log = stub();
1111
t.context.error = stub();
@@ -14,14 +14,14 @@ test.beforeEach(t => {
1414

1515
test('Execute script in fail step', async t => {
1616
const pluginConfig = {cmd: './test/fixtures/echo-args.sh'};
17-
const context = {logger: t.context.logger};
17+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
1818

1919
await t.notThrows(fail(pluginConfig, context));
2020
});
2121

2222
test('Throw "Error" if the fail script does not returns 0', async t => {
2323
const pluginConfig = {cmd: 'exit 1'};
24-
const context = {logger: t.context.logger};
24+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
2525

2626
await t.throws(fail(pluginConfig, context), Error);
2727
});

test/generate-notes.test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import test from 'ava';
22
import {stub} from 'sinon';
3+
import {WritableStreamBuffer} from 'stream-buffers';
34
import {generateNotes} from '..';
45

5-
stub(process.stdout, 'write');
6-
stub(process.stderr, 'write');
7-
86
test.beforeEach(t => {
7+
t.context.stdout = new WritableStreamBuffer();
8+
t.context.stderr = new WritableStreamBuffer();
99
// Mock logger
1010
t.context.log = stub();
1111
t.context.error = stub();
@@ -16,15 +16,15 @@ test('Return the value generateNotes script wrote to stdout', async t => {
1616
const pluginConfig = {
1717
cmd: './test/fixtures/echo-args.sh "\nRelease note \n\n"',
1818
};
19-
const context = {logger: t.context.logger};
19+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
2020

2121
const result = await generateNotes(pluginConfig, context);
2222
t.is(result, 'Release note');
2323
});
2424

2525
test('Throw "Error" if if the generateNotes script does not returns 0', async t => {
2626
const pluginConfig = {cmd: 'exit 1'};
27-
const context = {logger: t.context.logger};
27+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
2828

2929
await t.throws(generateNotes(pluginConfig, context), Error);
3030
});

test/prepare.test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import test from 'ava';
22
import {stub} from 'sinon';
3+
import {WritableStreamBuffer} from 'stream-buffers';
34
import {prepare} from '..';
45

5-
stub(process.stdout, 'write');
6-
stub(process.stderr, 'write');
7-
86
test.beforeEach(t => {
7+
t.context.stdout = new WritableStreamBuffer();
8+
t.context.stderr = new WritableStreamBuffer();
99
// Mock logger
1010
t.context.log = stub();
1111
t.context.error = stub();
@@ -14,14 +14,14 @@ test.beforeEach(t => {
1414

1515
test('Execute script in prepare step', async t => {
1616
const pluginConfig = {cmd: './test/fixtures/echo-args.sh'};
17-
const context = {logger: t.context.logger};
17+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
1818

1919
await t.notThrows(prepare(pluginConfig, context));
2020
});
2121

2222
test('Throw "Error" if the prepare script does not returns 0', async t => {
2323
const pluginConfig = {cmd: 'exit 1'};
24-
const context = {logger: t.context.logger};
24+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
2525

2626
await t.throws(prepare(pluginConfig, context), Error);
2727
});

test/publish.test.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import test from 'ava';
22
import {stub} from 'sinon';
3+
import {WritableStreamBuffer} from 'stream-buffers';
34
import {publish} from '..';
45

5-
// Disable logs during tests
6-
stub(process.stdout, 'write');
7-
stub(process.stderr, 'write');
8-
96
test.beforeEach(t => {
7+
t.context.stdout = new WritableStreamBuffer();
8+
t.context.stderr = new WritableStreamBuffer();
109
// Mock logger
1110
t.context.log = stub();
1211
t.context.error = stub();
@@ -18,7 +17,7 @@ test('Parse JSON returned by publish script', async t => {
1817
cmd:
1918
'./test/fixtures/echo-args.sh {\\"name\\": \\"Release name\\", \\"url\\": \\"https://host.com/release/1.0.0\\"}',
2019
};
21-
const context = {logger: t.context.logger};
20+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
2221

2322
const result = await publish(pluginConfig, context);
2423
t.deepEqual(result, {name: 'Release name', url: 'https://host.com/release/1.0.0'});
@@ -28,7 +27,7 @@ test('Return "undefined" if the publish script wrtite invalid JSON to stdout', a
2827
const pluginConfig = {
2928
cmd: './test/fixtures/echo-args.sh invalid_json',
3029
};
31-
const context = {logger: t.context.logger};
30+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
3231

3332
const result = await publish(pluginConfig, context);
3433
t.is(result, undefined);
@@ -38,15 +37,15 @@ test('Return "undefined" if the publish script wrtite nothing to stdout', async
3837
const pluginConfig = {
3938
cmd: './test/fixtures/echo-args.sh',
4039
};
41-
const context = {logger: t.context.logger};
40+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
4241

4342
const result = await publish(pluginConfig, context);
4443
t.is(result, undefined);
4544
});
4645

4746
test('Throw "Error" if the publish script does not returns 0', async t => {
4847
const pluginConfig = {cmd: 'exit 1'};
49-
const context = {logger: t.context.logger, options: {}};
48+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
5049

5150
await t.throws(publish(pluginConfig, context), Error);
5251
});

test/success.test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import test from 'ava';
22
import {stub} from 'sinon';
3+
import {WritableStreamBuffer} from 'stream-buffers';
34
import {success} from '..';
45

5-
stub(process.stdout, 'write');
6-
stub(process.stderr, 'write');
7-
86
test.beforeEach(t => {
7+
t.context.stdout = new WritableStreamBuffer();
8+
t.context.stderr = new WritableStreamBuffer();
99
// Mock logger
1010
t.context.log = stub();
1111
t.context.error = stub();
@@ -14,14 +14,14 @@ test.beforeEach(t => {
1414

1515
test('Execute script in success step', async t => {
1616
const pluginConfig = {cmd: './test/fixtures/echo-args.sh'};
17-
const context = {logger: t.context.logger};
17+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
1818

1919
await t.notThrows(success(pluginConfig, context));
2020
});
2121

2222
test('Throw "Error" if the success script does not returns 0', async t => {
2323
const pluginConfig = {cmd: 'exit 1'};
24-
const context = {logger: t.context.logger};
24+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
2525

2626
await t.throws(success(pluginConfig, context), Error);
2727
});

test/verify-confitions.test.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import test from 'ava';
22
import {stub} from 'sinon';
3+
import {WritableStreamBuffer} from 'stream-buffers';
34
import {verifyConditions} from '..';
45

5-
// Disable logs during tests
6-
stub(process.stdout, 'write');
7-
stub(process.stderr, 'write');
8-
96
test.beforeEach(t => {
7+
t.context.stdout = new WritableStreamBuffer();
8+
t.context.stderr = new WritableStreamBuffer();
109
// Mock logger
1110
t.context.log = stub();
1211
t.context.error = stub();
@@ -15,14 +14,14 @@ test.beforeEach(t => {
1514

1615
test('Verify plugin config is an Object with a "cmd" property', async t => {
1716
const pluginConfig = {cmd: './test/fixtures/echo-args.sh'};
18-
const context = {logger: t.context.logger};
17+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
1918

2019
await t.notThrows(verifyConditions(pluginConfig, context));
2120
});
2221

2322
test('Throw "SemanticReleaseError" if "cmd" options is missing', async t => {
2423
const pluginConfig = {};
25-
const context = {logger: t.context.logger};
24+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger};
2625

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

@@ -32,7 +31,7 @@ test('Throw "SemanticReleaseError" if "cmd" options is missing', async t => {
3231

3332
test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
3433
const pluginConfig = {cmd: ' '};
35-
const context = {logger: t.context.logger, options: {}};
34+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
3635

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

@@ -43,6 +42,8 @@ test('Throw "SemanticReleaseError" if "cmd" options is empty', async t => {
4342
test('Throw "SemanticReleaseError" if another exec plugin "cmd" options is missing', async t => {
4443
const pluginConfig = {cmd: './test/fixtures/echo-args.sh'};
4544
const context = {
45+
stdout: t.context.stdout,
46+
stderr: t.context.stderr,
4647
logger: t.context.logger,
4748
options: {publish: ['@semantic-release/npm', {path: '@semantic-release/exec'}]},
4849
};
@@ -56,6 +57,8 @@ test('Throw "SemanticReleaseError" if another exec plugin "cmd" options is missi
5657
test('Throw "SemanticReleaseError" if another exec plugin "cmd" options is empty', async t => {
5758
const pluginConfig = {cmd: './test/fixtures/echo-args.sh'};
5859
const context = {
60+
stdout: t.context.stdout,
61+
stderr: t.context.stderr,
5962
logger: t.context.logger,
6063
options: {
6164
branch: 'master',
@@ -71,14 +74,14 @@ test('Throw "SemanticReleaseError" if another exec plugin "cmd" options is empty
7174

7275
test('Return if the verifyConditions script returns 0', async t => {
7376
const pluginConfig = {cmd: 'exit 0'};
74-
const context = {logger: t.context.logger, options: {}};
77+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
7578

7679
await t.notThrows(verifyConditions(pluginConfig, context));
7780
});
7881

7982
test('Throw "SemanticReleaseError" if the verifyConditions script does not returns 0', async t => {
8083
const pluginConfig = {cmd: 'exit 1'};
81-
const context = {logger: t.context.logger, options: {}};
84+
const context = {stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger, options: {}};
8285

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

0 commit comments

Comments
 (0)