Skip to content

Commit 99441ec

Browse files
authored
wrapper: Write stdout/stderr data to stream when received (#410)
* wrapper: write stdout/stderr data to stream when received * add a delay test * temp comment * uncomment actions * add changelog
1 parent 5f32e8a commit 99441ec

File tree

7 files changed

+85
-18
lines changed

7 files changed

+85
-18
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: BUG FIXES
2+
body: 'wrapper: Fix wrapper to output to stdout and stderr immediately when data is
3+
received'
4+
time: 2024-04-25T10:47:31.19951-04:00
5+
custom:
6+
Issue: "395"

.github/workflows/data/delay/main.tf

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
resource "null_resource" "previous" {}
2+
3+
resource "time_sleep" "wait_30_seconds" {
4+
depends_on = [null_resource.previous]
5+
6+
create_duration = "30s"
7+
}
8+
9+
resource "null_resource" "next" {
10+
depends_on = [time_sleep.wait_30_seconds]
11+
}

.github/workflows/setup-terraform.yml

+30-1
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,6 @@ jobs:
304304
id: plan
305305
run: terraform plan
306306

307-
308307
terraform-stdout-wrapper:
309308
name: 'Terraform STDOUT'
310309
runs-on: ${{ matrix.os }}
@@ -370,3 +369,33 @@ jobs:
370369
- name: Terraform Output to JQ
371370
id: output
372371
run: terraform output -json | jq '.pet.value'
372+
373+
# This test has an artificial delay for testing the streaming of STDOUT
374+
terraform-wrapper-delayed-apply:
375+
name: 'Terraform Delayed Apply'
376+
runs-on: ${{ matrix.os }}
377+
strategy:
378+
matrix:
379+
os: [ubuntu-latest, windows-latest, macos-latest]
380+
defaults:
381+
run:
382+
shell: bash
383+
working-directory: ./.github/workflows/data/delay
384+
steps:
385+
- name: Checkout
386+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
387+
388+
- name: Setup Terraform
389+
uses: ./
390+
with:
391+
terraform_wrapper: true
392+
393+
- name: Terraform Init
394+
run: terraform init
395+
396+
- name: Terraform Format
397+
run: terraform fmt -check
398+
399+
- name: Terraform Apply
400+
id: apply
401+
run: terraform apply -auto-approve

dist/index1.js

+9-8
Original file line numberDiff line numberDiff line change
@@ -27599,13 +27599,18 @@ module.exports = {
2759927599
* console.log(listener.contents);
2760027600
*/
2760127601
class OutputListener {
27602-
constructor () {
27602+
constructor (streamWriter) {
2760327603
this._buff = [];
27604+
this._streamWriter = streamWriter;
2760427605
}
2760527606

2760627607
get listener () {
2760727608
const listen = function listen (data) {
2760827609
this._buff.push(data);
27610+
27611+
if (this._streamWriter) {
27612+
this._streamWriter.write(data);
27613+
}
2760927614
};
2761027615
return listen.bind(this);
2761127616
}
@@ -27946,9 +27951,9 @@ async function checkTerraform () {
2794627951
// This will fail if Terraform isn't found, which is what we want
2794727952
await checkTerraform();
2794827953

27949-
// Create listeners to receive output (in memory) as well
27950-
const stdout = new OutputListener();
27951-
const stderr = new OutputListener();
27954+
// Create listeners to receive output (in memory)
27955+
const stdout = new OutputListener(process.stdout);
27956+
const stderr = new OutputListener(process.stderr);
2795227957
const listeners = {
2795327958
stdout: stdout.listener,
2795427959
stderr: stderr.listener
@@ -27963,10 +27968,6 @@ async function checkTerraform () {
2796327968
};
2796427969
const exitCode = await exec(pathToCLI, args, options);
2796527970

27966-
// Pass-through stdout/err as `exec` won't due to `silent: true` option
27967-
process.stdout.write(stdout.contents);
27968-
process.stderr.write(stderr.contents);
27969-
2797027971
// Set outputs, result, exitcode, and stderr
2797127972
core.setOutput('stdout', stdout.contents);
2797227973
core.setOutput('stderr', stderr.contents);

wrapper/lib/output-listener.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,18 @@
2020
* console.log(listener.contents);
2121
*/
2222
class OutputListener {
23-
constructor () {
23+
constructor (streamWriter) {
2424
this._buff = [];
25+
this._streamWriter = streamWriter;
2526
}
2627

2728
get listener () {
2829
const listen = function listen (data) {
2930
this._buff.push(data);
31+
32+
if (this._streamWriter) {
33+
this._streamWriter.write(data);
34+
}
3035
};
3136
return listen.bind(this);
3237
}

wrapper/terraform.js

+3-7
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ async function checkTerraform () {
2121
// This will fail if Terraform isn't found, which is what we want
2222
await checkTerraform();
2323

24-
// Create listeners to receive output (in memory) as well
25-
const stdout = new OutputListener();
26-
const stderr = new OutputListener();
24+
// Create listeners to receive output (in memory)
25+
const stdout = new OutputListener(process.stdout);
26+
const stderr = new OutputListener(process.stderr);
2727
const listeners = {
2828
stdout: stdout.listener,
2929
stderr: stderr.listener
@@ -38,10 +38,6 @@ async function checkTerraform () {
3838
};
3939
const exitCode = await exec(pathToCLI, args, options);
4040

41-
// Pass-through stdout/err as `exec` won't due to `silent: true` option
42-
process.stdout.write(stdout.contents);
43-
process.stderr.write(stderr.contents);
44-
4541
// Set outputs, result, exitcode, and stderr
4642
core.setOutput('stdout', stdout.contents);
4743
core.setOutput('stderr', stderr.contents);

wrapper/test/output-listener.test.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,31 @@
66
const OutputListener = require('../lib/output-listener');
77

88
describe('output-listener', () => {
9-
it('receives and exposes data', () => {
9+
it('receives and buffers data to .contents', () => {
1010
const listener = new OutputListener();
1111
const listen = listener.listener;
12+
1213
listen(Buffer.from('foo'));
1314
listen(Buffer.from('bar'));
1415
listen(Buffer.from('baz'));
16+
1517
expect(listener.contents).toEqual('foobarbaz');
1618
});
19+
20+
it('receives and writes data to stream immediately', () => {
21+
const mockWrite = jest.fn();
22+
const listener = new OutputListener({ write: mockWrite });
23+
const listen = listener.listener;
24+
25+
listen(Buffer.from('first write'));
26+
expect(mockWrite.mock.lastCall[0]).toStrictEqual(Buffer.from('first write'));
27+
28+
listen(Buffer.from('second write'));
29+
expect(mockWrite.mock.lastCall[0]).toStrictEqual(Buffer.from('second write'));
30+
31+
listen(Buffer.from('third write'));
32+
expect(mockWrite.mock.lastCall[0]).toStrictEqual(Buffer.from('third write'));
33+
34+
expect(mockWrite).toBeCalledTimes(3);
35+
});
1736
});

0 commit comments

Comments
 (0)