From 56a58d86fae05d85bd09d7b3c9ea1374bd8ef1e9 Mon Sep 17 00:00:00 2001 From: Jason Lunz Date: Tue, 16 Jun 2026 00:44:46 +0200 Subject: [PATCH] Close configured stdout before Output Release any previously configured stdout stream before replacing it with the buffer used by Output, preserving ownership of WithStdoutCloser. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pipe/pipeline_test.go | 17 +++++++++++++++++ pipe/runner.go | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/pipe/pipeline_test.go b/pipe/pipeline_test.go index 7fe7ab5..e003e49 100644 --- a/pipe/pipeline_test.go +++ b/pipe/pipeline_test.go @@ -54,6 +54,23 @@ func TestPipelineEmptyOutput(t *testing.T) { } } +func TestPipelineOutputClosesConfiguredStdoutCloser(t *testing.T) { + t.Parallel() + ctx := context.Background() + stdout := &closeTrackingWriter{} + p := pipe.New( + pipe.WithStdin(strings.NewReader("hello world\n")), + pipe.WithStdoutCloser(stdout), + ) + + out, err := p.Output(ctx) + if assert.NoError(t, err) { + assert.Equal(t, "hello world\n", string(out)) + assert.Equal(t, "", stdout.buf.String()) + assert.True(t, stdout.closed, "WithStdoutCloser destination should be closed") + } +} + func TestPipelineEmptyWithStdoutCloser(t *testing.T) { t.Parallel() ctx := context.Background() diff --git a/pipe/runner.go b/pipe/runner.go index 12e8984..2c44983 100644 --- a/pipe/runner.go +++ b/pipe/runner.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "errors" + "fmt" ) // Env represents the environment that a pipeline stage should run in. @@ -152,6 +153,10 @@ func (r *runner) run(ctx context.Context) error { func (r *runner) output(ctx context.Context) ([]byte, error) { r.oneUse.assertNotStarted("get output") + if err := r.stdout.Close(); err != nil { + return nil, fmt.Errorf("closing previous stdout: %w", err) + } + var buf bytes.Buffer r.applyOptions(WithStdout(&buf)) err := r.run(ctx)