1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
package main
import (
"bytes"
"context"
"fmt"
"io"
"os"
"os/exec"
"runtime"
)
// Work provides a pending job to be done.
type Work struct {
Path string
Cmd string
}
// WorkLog provides the result of a job.
type WorkLog struct {
Path, Cmd string
Err error
Output io.Reader
}
// CommandWorker provides a consumer of work jobs and posts results to the
// worklog.
func CommandWorker(ctx context.Context, jobs <-chan Work, results chan<- WorkLog, streamOut io.Writer) {
for {
var result WorkLog
select {
case <-ctx.Done():
return
case w, ok := <-jobs:
if !ok {
return
}
outBuffer := bytes.NewBuffer(nil)
outWriter := io.Writer(outBuffer)
if streamOut != nil {
outWriter = io.MultiWriter(outWriter, streamOut)
}
result.Path = w.Path
result.Cmd = w.Cmd
cmd, err := NewCommand(ctx, outWriter, outWriter, w.Path, w.Cmd)
if err != nil {
result.Err = fmt.Errorf("failed to build command, %w", err)
break
}
if err := cmd.Run(); err != nil {
result.Err = fmt.Errorf("failed to run command, %v", err)
}
if streamOut == nil {
outReader := bytes.NewReader(outBuffer.Bytes())
result.Output = outReader
}
}
select {
case <-ctx.Done():
return
case results <- result:
}
}
}
// NewCommand initializes and returns a exec.Cmd for the command provided.
func NewCommand(ctx context.Context, stdout, stderr io.Writer, workingDir string, args ...string) (*exec.Cmd, error) {
var cmdArgs []string
if runtime.GOOS == "windows" {
cmdArgs = []string{"cmd.exe", "/C"}
} else {
cmdArgs = []string{"sh", "-c"}
}
if len(args) == 0 {
return nil, fmt.Errorf("failed to create command, no arguments provided")
}
cmdArgs = append(cmdArgs, args...)
cmd := exec.CommandContext(ctx, cmdArgs[0], cmdArgs[1:]...)
cmd.Env = os.Environ()
cmd.Dir = workingDir
cmd.Stdout = stdout
cmd.Stderr = stderr
return cmd, nil
}
|