/
cmd.go
113 lines (92 loc) · 2.46 KB
/
cmd.go
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package tart
import (
"bytes"
"context"
"errors"
"fmt"
"github.com/cirruslabs/echelon"
"io"
"os/exec"
"strings"
"time"
)
const tartCommandName = "tart"
var (
ErrTartNotFound = errors.New("tart command not found")
ErrTartFailed = errors.New("tart command returned non-zero exit code")
)
type loggerAsWriter struct {
level echelon.LogLevel
delegate *echelon.Logger
}
func (l loggerAsWriter) Write(p []byte) (n int, err error) {
if l.delegate != nil {
l.delegate.Logf(l.level, "%s", strings.TrimSpace(string(p)))
}
return len(p), nil
}
func Installed() bool {
_, err := exec.LookPath(tartCommandName)
return err == nil
}
func Cmd(
ctx context.Context,
additionalEnvironment map[string]string,
name string,
args ...string,
) (string, string, error) {
return CmdWithLogger(ctx, additionalEnvironment, nil, name, args...)
}
func CmdWithLogger(
ctx context.Context,
additionalEnvironment map[string]string,
logger *echelon.Logger,
name string,
args ...string,
) (string, string, error) {
ctx, span := tracer.Start(ctx, fmt.Sprintf("exec-command/%s-%s", tartCommandName, name))
defer span.End()
args = append([]string{name}, args...)
cmd := exec.CommandContext(ctx, tartCommandName, args...)
// Work around https://github.com/golang/go/issues/23019,
// most likely happens when running with --net-softnet
cmd.WaitDelay = time.Second
// Default environment
cmd.Env = cmd.Environ()
// Additional environment
for key, value := range additionalEnvironment {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, value))
}
var stdout, stderr bytes.Buffer
cmd.Stdout = io.MultiWriter(&stdout, &loggerAsWriter{
level: echelon.InfoLevel,
delegate: logger,
})
cmd.Stderr = io.MultiWriter(&stderr, &loggerAsWriter{
level: echelon.WarnLevel,
delegate: logger,
})
err := cmd.Run()
if err != nil {
if errors.Is(err, exec.ErrNotFound) {
return "", "", fmt.Errorf("%w: %s command not found in PATH, make sure Tart is installed",
ErrTartNotFound, tartCommandName)
}
if _, ok := err.(*exec.ExitError); ok {
// Tart command failed, redefine the error
// to be the Tart-specific output
err = fmt.Errorf("%w: %q", ErrTartFailed, firstNonEmptyLine(stderr.String(), stdout.String()))
}
}
return stdout.String(), stderr.String(), err
}
func firstNonEmptyLine(outputs ...string) string {
for _, output := range outputs {
for _, line := range strings.Split(output, "\n") {
if line != "" {
return line
}
}
}
return ""
}