61 lines
1.6 KiB
Go
61 lines
1.6 KiB
Go
// Package process runs child commands for ReleaseKit's internal adapters.
|
|
package process
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"sort"
|
|
)
|
|
|
|
// Invocation describes one child process. Environment values overlay the
|
|
// current process environment for this child only.
|
|
type Invocation struct {
|
|
Args []string
|
|
Dir string
|
|
Env map[string]string
|
|
Stdout io.Writer
|
|
Stderr io.Writer
|
|
}
|
|
|
|
// Run starts the command in the requested directory and waits for it to exit.
|
|
// Errors preserve the underlying exec error, including *exec.ExitError.
|
|
func Run(ctx context.Context, invocation Invocation) error {
|
|
if len(invocation.Args) == 0 || invocation.Args[0] == "" {
|
|
return errors.New("process invocation requires a command")
|
|
}
|
|
|
|
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)...)
|
|
}
|
|
command.Stdout = invocation.Stdout
|
|
command.Stderr = invocation.Stderr
|
|
|
|
if err := command.Run(); err != nil {
|
|
if contextErr := ctx.Err(); contextErr != nil {
|
|
return fmt.Errorf("run %q: %w", invocation.Args[0], contextErr)
|
|
}
|
|
return fmt.Errorf("run %q: %w", invocation.Args[0], err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func environmentEntries(environment map[string]string) []string {
|
|
keys := make([]string, 0, len(environment))
|
|
for key := range environment {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
entries := make([]string, 0, len(keys))
|
|
for _, key := range keys {
|
|
entries = append(entries, key+"="+environment[key])
|
|
}
|
|
return entries
|
|
}
|