Centralize Go validation and build environments
This commit is contained in:
@@ -9,16 +9,18 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Invocation describes one child process. Environment values overlay the
|
||||
// current process environment for this child only.
|
||||
// Invocation describes one child process. UnsetEnv removes inherited values
|
||||
// before Env overlays values for this child only.
|
||||
type Invocation struct {
|
||||
Args []string
|
||||
Dir string
|
||||
Env map[string]string
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
Args []string
|
||||
Dir string
|
||||
Env map[string]string
|
||||
UnsetEnv []string
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
}
|
||||
|
||||
// Run starts the command in the requested directory and waits for it to exit.
|
||||
@@ -30,8 +32,8 @@ func Run(ctx context.Context, invocation Invocation) error {
|
||||
|
||||
command := exec.CommandContext(ctx, invocation.Args[0], invocation.Args[1:]...)
|
||||
command.Dir = invocation.Dir
|
||||
if len(invocation.Env) != 0 {
|
||||
command.Env = append(os.Environ(), environmentEntries(invocation.Env)...)
|
||||
if len(invocation.Env) != 0 || len(invocation.UnsetEnv) != 0 {
|
||||
command.Env = environmentEntries(invocation.Env, invocation.UnsetEnv)
|
||||
}
|
||||
command.Stdout = invocation.Stdout
|
||||
command.Stderr = invocation.Stderr
|
||||
@@ -45,7 +47,21 @@ func Run(ctx context.Context, invocation Invocation) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func environmentEntries(environment map[string]string) []string {
|
||||
func environmentEntries(overlay map[string]string, unset []string) []string {
|
||||
environment := make(map[string]string, len(os.Environ())+len(overlay))
|
||||
for _, entry := range os.Environ() {
|
||||
key, value, ok := strings.Cut(entry, "=")
|
||||
if ok {
|
||||
environment[key] = value
|
||||
}
|
||||
}
|
||||
for _, key := range unset {
|
||||
delete(environment, key)
|
||||
}
|
||||
for key, value := range overlay {
|
||||
environment[key] = value
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(environment))
|
||||
for key := range environment {
|
||||
keys = append(keys, key)
|
||||
|
||||
@@ -58,6 +58,24 @@ func TestRunPreservesExitError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCanRemoveInheritedEnvironment(t *testing.T) {
|
||||
t.Setenv("RELEASEKIT_PROCESS_INHERITED", "inherited")
|
||||
var stdout bytes.Buffer
|
||||
err := Run(context.Background(), Invocation{
|
||||
Args: []string{helperExecutable(t), "-test.run=TestProcessHelper", "--", "argument", "success"},
|
||||
Env: map[string]string{"RELEASEKIT_PROCESS_HELPER": "1"},
|
||||
UnsetEnv: []string{"RELEASEKIT_PROCESS_INHERITED"},
|
||||
Stdout: &stdout,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := fmt.Sprintf("%s|argument||", mustGetwd(t))
|
||||
if stdout.String() != want {
|
||||
t.Fatalf("stdout = %q, want %q", stdout.String(), want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunReportsMissingExecutable(t *testing.T) {
|
||||
err := Run(context.Background(), Invocation{Args: []string{"releasekit-command-that-does-not-exist"}})
|
||||
if err == nil || !strings.Contains(err.Error(), "releasekit-command-that-does-not-exist") {
|
||||
@@ -143,3 +161,12 @@ func helperExecutable(t *testing.T) string {
|
||||
}
|
||||
return executable
|
||||
}
|
||||
|
||||
func mustGetwd(t *testing.T) string {
|
||||
t.Helper()
|
||||
workdir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return workdir
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user