From 6d05dbcf0cff99c108ae8b4e9fce053a1249fe47 Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Wed, 14 Aug 2024 20:26:22 -0400 Subject: [PATCH 01/18] Add Support For Setting Pipe's Environment --- script.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/script.go b/script.go index c471f74..4605b2c 100644 --- a/script.go +++ b/script.go @@ -34,6 +34,12 @@ type Pipe struct { // because pipe stages are concurrent, protect 'err' mu *sync.Mutex err error + + // env contains the environment to run the exec command with. + // Each entry in the array should be of the form key=value. + // If env is not nil, it will replace the default environment variables + // when executing commands. + env []string } // Args creates a pipe containing the program's command-line arguments from @@ -166,6 +172,7 @@ func NewPipe() *Pipe { mu: new(sync.Mutex), stdout: os.Stdout, httpClient: http.DefaultClient, + env: nil, } } @@ -388,6 +395,9 @@ func (p *Pipe) Exec(cmdLine string) *Pipe { if p.stderr != nil { cmd.Stderr = p.stderr } + if p.env != nil { + cmd.Env = p.env + } err = cmd.Start() if err != nil { fmt.Fprintln(cmd.Stderr, err) @@ -428,6 +438,9 @@ func (p *Pipe) ExecForEach(cmdLine string) *Pipe { if p.stderr != nil { cmd.Stderr = p.stderr } + if p.env != nil { + cmd.Env = p.env + } err = cmd.Start() if err != nil { fmt.Fprintln(cmd.Stderr, err) @@ -898,6 +911,14 @@ func (p *Pipe) WithStdout(w io.Writer) *Pipe { return p } +// WithEnv sets the pipe's environment to the string array env. This will override +// the default process environment variables when executing commands run via [Pipe.Exec] +// or [Pipe.ExecForEach]. +func (p *Pipe) WithEnv(env []string) *Pipe { + p.env = env + return p +} + // WriteFile writes the pipe's contents to the file path, truncating it if it // exists, and returns the number of bytes successfully written, or an error. func (p *Pipe) WriteFile(path string) (int64, error) { From 3661071103b41390d3f9a01dd691c9a1078bee45 Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Wed, 14 Aug 2024 21:22:25 -0400 Subject: [PATCH 02/18] Add Test Cases For New Method --- script_test.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/script_test.go b/script_test.go index b19b915..4d53d36 100644 --- a/script_test.go +++ b/script_test.go @@ -12,6 +12,7 @@ import ( "os" "path/filepath" "regexp" + "slices" "strings" "testing" "testing/iotest" @@ -1768,6 +1769,41 @@ func TestWithStdout_SetsSpecifiedWriterAsStdout(t *testing.T) { } } +func TestWithEnvironment_SetsSuppliedEnvironmentOnPipe(t *testing.T) { + t.Parallel() + buf := new(bytes.Buffer) + env := []string{"ENV1=test1", "ENV2=test2"} + + _, err := script.NewPipe().WithStdout(buf).WithEnv(env).Exec("printenv").Stdout() + if err != nil { + t.Fatal(err) + } + + got := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") + + if !slices.Equal(got, env) { + t.Errorf("expected %v, got %v", env, got) + } +} + +func TestWithoutEnvironment_FallsBackToDefaultEnvironment(t *testing.T) { + t.Parallel() + buf := new(bytes.Buffer) + + _, err := script.NewPipe().WithStdout(buf).Exec("printenv").Stdout() + if err != nil { + t.Fatal(err) + } + + // not setting the environment should simply use the task's default environment + expected := os.Environ() + got := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") + + if !slices.Equal(got, expected) { + t.Errorf("expected %v, got %v", expected, got) + } +} + func TestErrorReturnsErrorSetByPreviousPipeStage(t *testing.T) { t.Parallel() p := script.File("testdata/nonexistent.txt") From 2ea0daf7e3e4f73b766b7bc1279c506c4c3faa4e Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Wed, 14 Aug 2024 21:45:29 -0400 Subject: [PATCH 03/18] Add Test For Empty Environment --- script_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/script_test.go b/script_test.go index 4d53d36..d7433d4 100644 --- a/script_test.go +++ b/script_test.go @@ -1786,6 +1786,22 @@ func TestWithEnvironment_SetsSuppliedEnvironmentOnPipe(t *testing.T) { } } +func TestWithEnvironment_SetEmptyEnvironment(t *testing.T) { + t.Parallel() + buf := new(bytes.Buffer) + env := []string{} + + _, err := script.NewPipe().WithStdout(buf).WithEnv(env).Exec("printenv").Stdout() + if err != nil { + t.Fatal(err) + } + + if len(buf.String()) != 0 { + got := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") + t.Errorf("expected 0 environment variables, got %d %v", len(got), got) + } +} + func TestWithoutEnvironment_FallsBackToDefaultEnvironment(t *testing.T) { t.Parallel() buf := new(bytes.Buffer) From 56ce4f3c83b7fa702a21fbc89a38bcf8669ee2a3 Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Wed, 14 Aug 2024 21:48:09 -0400 Subject: [PATCH 04/18] Adjust Godoc Comment --- script.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script.go b/script.go index 4605b2c..cd1c6bc 100644 --- a/script.go +++ b/script.go @@ -35,7 +35,7 @@ type Pipe struct { mu *sync.Mutex err error - // env contains the environment to run the exec command with. + // env contains the environment to run any exec commands with. // Each entry in the array should be of the form key=value. // If env is not nil, it will replace the default environment variables // when executing commands. From 3c0b782cf17a57df4c623832790ea2df3f25ba87 Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Wed, 14 Aug 2024 21:57:43 -0400 Subject: [PATCH 05/18] Fix Test Names --- script_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script_test.go b/script_test.go index d7433d4..faed128 100644 --- a/script_test.go +++ b/script_test.go @@ -1769,7 +1769,7 @@ func TestWithStdout_SetsSpecifiedWriterAsStdout(t *testing.T) { } } -func TestWithEnvironment_SetsSuppliedEnvironmentOnPipe(t *testing.T) { +func TestWithEnv_SetsSuppliedEnvironmentOnPipe(t *testing.T) { t.Parallel() buf := new(bytes.Buffer) env := []string{"ENV1=test1", "ENV2=test2"} @@ -1786,7 +1786,7 @@ func TestWithEnvironment_SetsSuppliedEnvironmentOnPipe(t *testing.T) { } } -func TestWithEnvironment_SetEmptyEnvironment(t *testing.T) { +func TestWithEnv_SetEmptyEnvironment(t *testing.T) { t.Parallel() buf := new(bytes.Buffer) env := []string{} @@ -1802,7 +1802,7 @@ func TestWithEnvironment_SetEmptyEnvironment(t *testing.T) { } } -func TestWithoutEnvironment_FallsBackToDefaultEnvironment(t *testing.T) { +func TestWithoutEnv_FallsBackToDefaultEnvironment(t *testing.T) { t.Parallel() buf := new(bytes.Buffer) From f206b47506f2db45daa938f5bf012e02575327ed Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Wed, 14 Aug 2024 22:29:16 -0400 Subject: [PATCH 06/18] Fix Test Name --- script_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script_test.go b/script_test.go index faed128..d012ec4 100644 --- a/script_test.go +++ b/script_test.go @@ -1802,7 +1802,7 @@ func TestWithEnv_SetEmptyEnvironment(t *testing.T) { } } -func TestWithoutEnv_FallsBackToDefaultEnvironment(t *testing.T) { +func TestNotSettingEnvFallsBackToDefaultEnvironment(t *testing.T) { t.Parallel() buf := new(bytes.Buffer) From 2208a2623dcbe381eed4d0d0fde81d261ad14c4f Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Wed, 14 Aug 2024 22:31:01 -0400 Subject: [PATCH 07/18] Reorder Test Cases --- script_test.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/script_test.go b/script_test.go index d012ec4..7433527 100644 --- a/script_test.go +++ b/script_test.go @@ -1769,36 +1769,36 @@ func TestWithStdout_SetsSpecifiedWriterAsStdout(t *testing.T) { } } -func TestWithEnv_SetsSuppliedEnvironmentOnPipe(t *testing.T) { +func TestWithEnv_SetEmptyEnvironment(t *testing.T) { t.Parallel() buf := new(bytes.Buffer) - env := []string{"ENV1=test1", "ENV2=test2"} + env := []string{} _, err := script.NewPipe().WithStdout(buf).WithEnv(env).Exec("printenv").Stdout() if err != nil { t.Fatal(err) } - got := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") - - if !slices.Equal(got, env) { - t.Errorf("expected %v, got %v", env, got) + if len(buf.String()) != 0 { + got := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") + t.Errorf("expected 0 environment variables, got %d %v", len(got), got) } } -func TestWithEnv_SetEmptyEnvironment(t *testing.T) { +func TestWithEnv_SetMultipleEnvVars(t *testing.T) { t.Parallel() buf := new(bytes.Buffer) - env := []string{} + env := []string{"ENV1=test1", "ENV2=test2"} _, err := script.NewPipe().WithStdout(buf).WithEnv(env).Exec("printenv").Stdout() if err != nil { t.Fatal(err) } - if len(buf.String()) != 0 { - got := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") - t.Errorf("expected 0 environment variables, got %d %v", len(got), got) + got := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") + + if !slices.Equal(got, env) { + t.Errorf("expected %v, got %v", env, got) } } From b556398ada7a6da236c679f3ec0a1bc02d6c7cf8 Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Thu, 15 Aug 2024 09:26:18 -0400 Subject: [PATCH 08/18] Addressed Feedback From PR Review --- script_test.go | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/script_test.go b/script_test.go index 7433527..692e907 100644 --- a/script_test.go +++ b/script_test.go @@ -12,7 +12,6 @@ import ( "os" "path/filepath" "regexp" - "slices" "strings" "testing" "testing/iotest" @@ -1769,54 +1768,49 @@ func TestWithStdout_SetsSpecifiedWriterAsStdout(t *testing.T) { } } -func TestWithEnv_SetEmptyEnvironment(t *testing.T) { +func TestWithEnv_UnsetsAllEnvVarsGivenEmptySlice(t *testing.T) { t.Parallel() - buf := new(bytes.Buffer) - env := []string{} + want := []string{} - _, err := script.NewPipe().WithStdout(buf).WithEnv(env).Exec("printenv").Stdout() + output, err := script.NewPipe().WithEnv(want).Exec("printenv").String() if err != nil { t.Fatal(err) } - if len(buf.String()) != 0 { - got := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") - t.Errorf("expected 0 environment variables, got %d %v", len(got), got) + if output != "" { + t.Errorf("want empty environment, got %q", output) } } func TestWithEnv_SetMultipleEnvVars(t *testing.T) { t.Parallel() - buf := new(bytes.Buffer) env := []string{"ENV1=test1", "ENV2=test2"} - _, err := script.NewPipe().WithStdout(buf).WithEnv(env).Exec("printenv").Stdout() + got, err := script.NewPipe().WithEnv(env).Exec("printenv").String() if err != nil { t.Fatal(err) } - got := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") + want := "ENV1=test1\nENV2=test2\n" - if !slices.Equal(got, env) { - t.Errorf("expected %v, got %v", env, got) + if got != want { + t.Errorf("want %v, got %v", want, got) } } func TestNotSettingEnvFallsBackToDefaultEnvironment(t *testing.T) { t.Parallel() - buf := new(bytes.Buffer) - _, err := script.NewPipe().WithStdout(buf).Exec("printenv").Stdout() + got, err := script.NewPipe().Exec("printenv").String() if err != nil { t.Fatal(err) } // not setting the environment should simply use the task's default environment - expected := os.Environ() - got := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") + want := strings.Join(os.Environ(), "\n") + "\n" - if !slices.Equal(got, expected) { - t.Errorf("expected %v, got %v", expected, got) + if got != want { + t.Errorf("want %v, got %v", want, got) } } From ed2103ba4aa2ec8f2f8c0bafca3bae2fe468e03b Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Fri, 16 Aug 2024 22:09:49 -0400 Subject: [PATCH 09/18] Update Documentation To Clarify Usage --- script.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/script.go b/script.go index cd1c6bc..6afcd10 100644 --- a/script.go +++ b/script.go @@ -911,9 +911,10 @@ func (p *Pipe) WithStdout(w io.Writer) *Pipe { return p } -// WithEnv sets the pipe's environment to the string array env. This will override -// the default process environment variables when executing commands run via [Pipe.Exec] -// or [Pipe.ExecForEach]. +// WithEnv sets the environment for the exec commands to the string array env. +// This will override the default process environment variables when executing +// commands run via [Pipe.Exec] or [Pipe.ExecForEach]. This will not affect the +// environment outside of [Pipe.Exec] or [Pipe.ExecForEach]. func (p *Pipe) WithEnv(env []string) *Pipe { p.env = env return p From 56b09cf00c0738f0e2f785c206e0e8364ecae656 Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Sat, 17 Aug 2024 18:25:26 -0400 Subject: [PATCH 10/18] Address Feedback From PR Review --- script.go | 8 ++++---- script_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/script.go b/script.go index 6afcd10..0c4b3fc 100644 --- a/script.go +++ b/script.go @@ -911,10 +911,10 @@ func (p *Pipe) WithStdout(w io.Writer) *Pipe { return p } -// WithEnv sets the environment for the exec commands to the string array env. -// This will override the default process environment variables when executing -// commands run via [Pipe.Exec] or [Pipe.ExecForEach]. This will not affect the -// environment outside of [Pipe.Exec] or [Pipe.ExecForEach]. +// WithEnv sets the environment for subsequent [Pipe.Exec] and [Pipe.ExecForEach] commands +// to the string slice env. This will override the default process environment variables +// when executing commands run via [Pipe.Exec] or [Pipe.ExecForEach]. This will not affect +// the environment outside of [Pipe.Exec] or [Pipe.ExecForEach]. func (p *Pipe) WithEnv(env []string) *Pipe { p.env = env return p diff --git a/script_test.go b/script_test.go index 692e907..138a6f3 100644 --- a/script_test.go +++ b/script_test.go @@ -1782,7 +1782,7 @@ func TestWithEnv_UnsetsAllEnvVarsGivenEmptySlice(t *testing.T) { } } -func TestWithEnv_SetMultipleEnvVars(t *testing.T) { +func TestWithEnvChangesExecutionEnvironment(t *testing.T) { t.Parallel() env := []string{"ENV1=test1", "ENV2=test2"} From 93de3a0462666153f9a20d9dc4a88f793564bd80 Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Sat, 17 Aug 2024 18:26:17 -0400 Subject: [PATCH 11/18] Alphabetic Ordering --- script.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/script.go b/script.go index 0c4b3fc..047a8a0 100644 --- a/script.go +++ b/script.go @@ -871,6 +871,15 @@ func (p *Pipe) Wait() { } } +// WithEnv sets the environment for subsequent [Pipe.Exec] and [Pipe.ExecForEach] commands +// to the string slice env. This will override the default process environment variables +// when executing commands run via [Pipe.Exec] or [Pipe.ExecForEach]. This will not affect +// the environment outside of [Pipe.Exec] or [Pipe.ExecForEach]. +func (p *Pipe) WithEnv(env []string) *Pipe { + p.env = env + return p +} + // WithError sets the error err on the pipe. func (p *Pipe) WithError(err error) *Pipe { p.SetError(err) @@ -911,15 +920,6 @@ func (p *Pipe) WithStdout(w io.Writer) *Pipe { return p } -// WithEnv sets the environment for subsequent [Pipe.Exec] and [Pipe.ExecForEach] commands -// to the string slice env. This will override the default process environment variables -// when executing commands run via [Pipe.Exec] or [Pipe.ExecForEach]. This will not affect -// the environment outside of [Pipe.Exec] or [Pipe.ExecForEach]. -func (p *Pipe) WithEnv(env []string) *Pipe { - p.env = env - return p -} - // WriteFile writes the pipe's contents to the file path, truncating it if it // exists, and returns the number of bytes successfully written, or an error. func (p *Pipe) WriteFile(path string) (int64, error) { From be908be8a94ad74199bf696e787e0da851c5d3cd Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Mon, 2 Sep 2024 12:42:41 -0400 Subject: [PATCH 12/18] Fix Merge Conflict Signed-off-by: Mahad Zaryab --- script.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/script.go b/script.go index e500b6a..2ee5d13 100644 --- a/script.go +++ b/script.go @@ -32,10 +32,6 @@ type Pipe struct { stdout io.Writer httpClient *http.Client - // because pipe stages are concurrent, protect 'err' - mu *sync.Mutex - err error - // env contains the environment to run any exec commands with. // Each entry in the array should be of the form key=value. // If env is not nil, it will replace the default environment variables From d62893075aa532d3c28110f527896921c149dfa6 Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Mon, 2 Sep 2024 13:02:03 -0400 Subject: [PATCH 13/18] Address Feedback From PR Review Signed-off-by: Mahad Zaryab --- script.go | 29 +++++++++++++++++++++-------- script_test.go | 22 +++------------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/script.go b/script.go index 2ee5d13..78ddc82 100644 --- a/script.go +++ b/script.go @@ -32,16 +32,20 @@ type Pipe struct { stdout io.Writer httpClient *http.Client + // mu protects the following fields because pipe stages are concurrent + // - err + // - stderr + // - env + mu *sync.Mutex + err error + stderr io.Writer // env contains the environment to run any exec commands with. // Each entry in the array should be of the form key=value. // If env is not nil, it will replace the default environment variables // when executing commands. + // If env is an empty array, any exec commands will be run with an + // empty environment. env []string - - // because pipe stages are concurrent, protect 'err' and 'stderr' - mu *sync.Mutex - err error - stderr io.Writer } // Args creates a pipe containing the program's command-line arguments from @@ -381,6 +385,12 @@ func (p *Pipe) EncodeBase64() *Pipe { }) } +func (p *Pipe) environment() []string { + p.mu.Lock() + defer p.mu.Unlock() + return p.env +} + // Error returns any error present on the pipe, or nil otherwise. // Error is not a sink and does not wait until the pipe reaches // completion. To wait for completion before returning the error, @@ -426,8 +436,9 @@ func (p *Pipe) Exec(cmdLine string) *Pipe { if pipeStderr != nil { cmd.Stderr = pipeStderr } - if p.env != nil { - cmd.Env = p.env + pipeEnv := p.environment() + if pipeEnv != nil { + cmd.Env = pipeEnv } err = cmd.Start() if err != nil { @@ -919,8 +930,10 @@ func (p *Pipe) Wait() error { // WithEnv sets the environment for subsequent [Pipe.Exec] and [Pipe.ExecForEach] commands // to the string slice env. This will override the default process environment variables // when executing commands run via [Pipe.Exec] or [Pipe.ExecForEach]. This will not affect -// the environment outside of [Pipe.Exec] or [Pipe.ExecForEach]. +// the current process's environment. func (p *Pipe) WithEnv(env []string) *Pipe { + p.mu.Lock() + defer p.mu.Unlock() p.env = env return p } diff --git a/script_test.go b/script_test.go index a13f4df..dd29e85 100644 --- a/script_test.go +++ b/script_test.go @@ -1782,32 +1782,16 @@ func TestWithEnv_UnsetsAllEnvVarsGivenEmptySlice(t *testing.T) { } } -func TestWithEnvChangesExecutionEnvironment(t *testing.T) { +func TestWithEnv_SetsGivenVariablesForSubsequentExec(t *testing.T) { t.Parallel() env := []string{"ENV1=test1", "ENV2=test2"} - got, err := script.NewPipe().WithEnv(env).Exec("printenv").String() + got, err := script.NewPipe().WithEnv(env).Exec("sh -c 'echo ENV1=$ENV1 ENV2=$ENV2'").String() if err != nil { t.Fatal(err) } - want := "ENV1=test1\nENV2=test2\n" - - if got != want { - t.Errorf("want %v, got %v", want, got) - } -} - -func TestNotSettingEnvFallsBackToDefaultEnvironment(t *testing.T) { - t.Parallel() - - got, err := script.NewPipe().Exec("printenv").String() - if err != nil { - t.Fatal(err) - } - - // not setting the environment should simply use the task's default environment - want := strings.Join(os.Environ(), "\n") + "\n" + want := "ENV1=test1 ENV2=test2\n" if got != want { t.Errorf("want %v, got %v", want, got) From 929de1883a3924be881b19168a1117d293a33e1d Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Tue, 3 Sep 2024 19:34:54 -0400 Subject: [PATCH 14/18] Adjust Test For Empty Slive Env Signed-off-by: Mahad Zaryab --- script_test.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/script_test.go b/script_test.go index dd29e85..9ea7808 100644 --- a/script_test.go +++ b/script_test.go @@ -1770,15 +1770,26 @@ func TestWithStdout_SetsSpecifiedWriterAsStdout(t *testing.T) { func TestWithEnv_UnsetsAllEnvVarsGivenEmptySlice(t *testing.T) { t.Parallel() - want := []string{} - - output, err := script.NewPipe().WithEnv(want).Exec("printenv").String() + os.Setenv("ENV1", "test1") + t.Cleanup(func() { os.Unsetenv("ENV1") }) + // test that ENV1 exists without setting the environment + got, err := script.NewPipe().Exec("sh -c 'echo ENV1=$ENV1'").String() if err != nil { t.Fatal(err) } - - if output != "" { - t.Errorf("want empty environment, got %q", output) + want := "ENV1=test1\n" + if got != want { + t.Errorf("want %v, got %v", want, got) + } + // test that ENV1 does not exist when WithEnv takes in an empty slice + env := []string{} + got, err = script.NewPipe().WithEnv(env).Exec("sh -c 'echo ENV1=$ENV1'").String() + if err != nil { + t.Fatal(err) + } + want = "ENV1=\n" + if got != want { + t.Errorf("want %v, got %v", want, got) } } From dc021619876e4d89280ae8adec6cd62933a826b4 Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Tue, 3 Sep 2024 19:37:32 -0400 Subject: [PATCH 15/18] Update Documentation Signed-off-by: Mahad Zaryab --- script.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/script.go b/script.go index 78ddc82..c723cb1 100644 --- a/script.go +++ b/script.go @@ -39,13 +39,7 @@ type Pipe struct { mu *sync.Mutex err error stderr io.Writer - // env contains the environment to run any exec commands with. - // Each entry in the array should be of the form key=value. - // If env is not nil, it will replace the default environment variables - // when executing commands. - // If env is an empty array, any exec commands will be run with an - // empty environment. - env []string + env []string } // Args creates a pipe containing the program's command-line arguments from @@ -929,8 +923,9 @@ func (p *Pipe) Wait() error { // WithEnv sets the environment for subsequent [Pipe.Exec] and [Pipe.ExecForEach] commands // to the string slice env. This will override the default process environment variables -// when executing commands run via [Pipe.Exec] or [Pipe.ExecForEach]. This will not affect -// the current process's environment. +// when executing commands run via [Pipe.Exec] or [Pipe.ExecForEach]. +// Each entry in the array should be of the form key=value. +// If env is an empty array, any exec commands will be run with an empty environment. func (p *Pipe) WithEnv(env []string) *Pipe { p.mu.Lock() defer p.mu.Unlock() From b174908ce33781f66ade3f578906742c3022d787 Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Tue, 3 Sep 2024 19:39:30 -0400 Subject: [PATCH 16/18] Remove Whitespace Signed-off-by: Mahad Zaryab --- script_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/script_test.go b/script_test.go index 9ea7808..30e7931 100644 --- a/script_test.go +++ b/script_test.go @@ -1796,14 +1796,11 @@ func TestWithEnv_UnsetsAllEnvVarsGivenEmptySlice(t *testing.T) { func TestWithEnv_SetsGivenVariablesForSubsequentExec(t *testing.T) { t.Parallel() env := []string{"ENV1=test1", "ENV2=test2"} - got, err := script.NewPipe().WithEnv(env).Exec("sh -c 'echo ENV1=$ENV1 ENV2=$ENV2'").String() if err != nil { t.Fatal(err) } - want := "ENV1=test1 ENV2=test2\n" - if got != want { t.Errorf("want %v, got %v", want, got) } From 54e80aa98bc0647960e0cd7203870f6715827523 Mon Sep 17 00:00:00 2001 From: Mahad Zaryab Date: Wed, 4 Sep 2024 20:31:30 -0400 Subject: [PATCH 17/18] Adjust Test To Only Check Pipe Environment --- script_test.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/script_test.go b/script_test.go index 30e7931..9795b35 100644 --- a/script_test.go +++ b/script_test.go @@ -1770,26 +1770,22 @@ func TestWithStdout_SetsSpecifiedWriterAsStdout(t *testing.T) { func TestWithEnv_UnsetsAllEnvVarsGivenEmptySlice(t *testing.T) { t.Parallel() - os.Setenv("ENV1", "test1") - t.Cleanup(func() { os.Unsetenv("ENV1") }) - // test that ENV1 exists without setting the environment - got, err := script.NewPipe().Exec("sh -c 'echo ENV1=$ENV1'").String() + p := script.NewPipe().WithEnv([]string{"ENV1=test1"}).Exec("sh -c 'echo ENV1=$ENV1'") + want := "ENV1=test1\n" + got, err := p.String() if err != nil { t.Fatal(err) } - want := "ENV1=test1\n" if got != want { - t.Errorf("want %v, got %v", want, got) + t.Fatalf("want %q, got %q", want, got) } - // test that ENV1 does not exist when WithEnv takes in an empty slice - env := []string{} - got, err = script.NewPipe().WithEnv(env).Exec("sh -c 'echo ENV1=$ENV1'").String() + got, err = p.Echo("").WithEnv([]string{}).Exec("sh -c 'echo ENV1=$ENV1'").String() if err != nil { t.Fatal(err) } want = "ENV1=\n" if got != want { - t.Errorf("want %v, got %v", want, got) + t.Errorf("want %q, got %q", want, got) } } From 638156d5556d21d6de7b8da3fe1f1f6b68446174 Mon Sep 17 00:00:00 2001 From: John Arundel Date: Thu, 5 Sep 2024 10:05:59 +0100 Subject: [PATCH 18/18] polish docs --- README.md | 40 +++++++++++++++++++++++++++------------- script.go | 20 ++++++++++---------- script_test.go | 2 +- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 9a8c7d4..e40d774 100644 --- a/README.md +++ b/README.md @@ -269,18 +269,31 @@ These are functions that create a pipe with a given contents: | Source | Contents | | -------- | ------------- | -| [`Args`](https://pkg.go.dev/github.com/bitfield/script#Args) | command-line arguments -| [`Do`](https://pkg.go.dev/github.com/bitfield/script#Do) | HTTP response -| [`Echo`](https://pkg.go.dev/github.com/bitfield/script#Echo) | a string -| [`Exec`](https://pkg.go.dev/github.com/bitfield/script#Exec) | command output -| [`File`](https://pkg.go.dev/github.com/bitfield/script#File) | file contents -| [`FindFiles`](https://pkg.go.dev/github.com/bitfield/script#FindFiles) | recursive file listing -| [`Get`](https://pkg.go.dev/github.com/bitfield/script#Get) | HTTP response -| [`IfExists`](https://pkg.go.dev/github.com/bitfield/script#IfExists) | do something only if some file exists -| [`ListFiles`](https://pkg.go.dev/github.com/bitfield/script#ListFiles) | file listing (including wildcards) -| [`Post`](https://pkg.go.dev/github.com/bitfield/script#Post) | HTTP response -| [`Slice`](https://pkg.go.dev/github.com/bitfield/script#Slice) | slice elements, one per line -| [`Stdin`](https://pkg.go.dev/github.com/bitfield/script#Stdin) | standard input +| [`Args`](https://pkg.go.dev/github.com/bitfield/script#Args) | command-line arguments | +| [`Do`](https://pkg.go.dev/github.com/bitfield/script#Do) | HTTP response | +| [`Echo`](https://pkg.go.dev/github.com/bitfield/script#Echo) | a string | +| [`Exec`](https://pkg.go.dev/github.com/bitfield/script#Exec) | command output | +| [`File`](https://pkg.go.dev/github.com/bitfield/script#File) | file contents | +| [`FindFiles`](https://pkg.go.dev/github.com/bitfield/script#FindFiles) | recursive file listing | +| [`Get`](https://pkg.go.dev/github.com/bitfield/script#Get) | HTTP response | +| [`IfExists`](https://pkg.go.dev/github.com/bitfield/script#IfExists) | do something only if some file exists | +| [`ListFiles`](https://pkg.go.dev/github.com/bitfield/script#ListFiles) | file listing (including wildcards) | +| [`Post`](https://pkg.go.dev/github.com/bitfield/script#Post) | HTTP response | +| [`Slice`](https://pkg.go.dev/github.com/bitfield/script#Slice) | slice elements, one per line | +| [`Stdin`](https://pkg.go.dev/github.com/bitfield/script#Stdin) | standard input | + +## Modifiers + +These are methods on a pipe that change its configuration: + +| Source | Modifies | +| -------- | ------------- | +| [`WithEnv`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithEnv) | environment for commands | +| [`WithError`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithError) | pipe error status | +| [`WithHTTPClient`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithHTTPClient) | client for HTTP requests | +| [`WithReader`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithReader) | pipe source | +| [`WithStderr`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithStderr) | standard error output stream for command | +| [`WithStdout`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithStdout) | standard output stream for pipe | ## Filters @@ -340,7 +353,8 @@ Sinks are methods that return some data from a pipe, ending the pipeline and ext | Version | New | | ----------- | ------- | -| _next_ | [`DecodeBase64`](https://pkg.go.dev/github.com/bitfield/script#Pipe.DecodeBase64) / [`EncodeBase64`](https://pkg.go.dev/github.com/bitfield/script#Pipe.EncodeBase64) | +| _next_ | [`WithEnv`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithEnv) | +| | [`DecodeBase64`](https://pkg.go.dev/github.com/bitfield/script#Pipe.DecodeBase64) / [`EncodeBase64`](https://pkg.go.dev/github.com/bitfield/script#Pipe.EncodeBase64) | | v0.22.0 | [`Tee`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Tee), [`WithStderr`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithStderr) | | v0.21.0 | HTTP support: [`Do`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Do), [`Get`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Get), [`Post`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Post) | | v0.20.0 | [`JQ`](https://pkg.go.dev/github.com/bitfield/script#Pipe.JQ) | diff --git a/script.go b/script.go index c723cb1..e25b1c4 100644 --- a/script.go +++ b/script.go @@ -32,10 +32,6 @@ type Pipe struct { stdout io.Writer httpClient *http.Client - // mu protects the following fields because pipe stages are concurrent - // - err - // - stderr - // - env mu *sync.Mutex err error stderr io.Writer @@ -403,6 +399,11 @@ func (p *Pipe) Error() error { // error output). The effect of this is to filter the contents of the pipe // through the external command. // +// # Environment +// +// The command inherits the current process's environment, optionally modified +// by [Pipe.WithEnv]. +// // # Error handling // // If the command had a non-zero exit status, the pipe's error status will also @@ -445,7 +446,8 @@ func (p *Pipe) Exec(cmdLine string) *Pipe { // ExecForEach renders cmdLine as a Go template for each line of input, running // the resulting command, and produces the combined output of all these -// commands in sequence. See [Pipe.Exec] for error handling details. +// commands in sequence. See [Pipe.Exec] for details on error handling and +// environment variables. // // This is mostly useful for substituting data into commands using Go template // syntax. For example: @@ -921,11 +923,9 @@ func (p *Pipe) Wait() error { return p.Error() } -// WithEnv sets the environment for subsequent [Pipe.Exec] and [Pipe.ExecForEach] commands -// to the string slice env. This will override the default process environment variables -// when executing commands run via [Pipe.Exec] or [Pipe.ExecForEach]. -// Each entry in the array should be of the form key=value. -// If env is an empty array, any exec commands will be run with an empty environment. +// WithEnv sets the environment for subsequent [Pipe.Exec] and [Pipe.ExecForEach] +// commands to the string slice env, using the same format as [os/exec.Cmd.Env]. +// An empty slice unsets all existing environment variables. func (p *Pipe) WithEnv(env []string) *Pipe { p.mu.Lock() defer p.mu.Unlock() diff --git a/script_test.go b/script_test.go index 9795b35..e674219 100644 --- a/script_test.go +++ b/script_test.go @@ -1798,7 +1798,7 @@ func TestWithEnv_SetsGivenVariablesForSubsequentExec(t *testing.T) { } want := "ENV1=test1 ENV2=test2\n" if got != want { - t.Errorf("want %v, got %v", want, got) + t.Errorf("want %q, got %q", want, got) } }