85 lines
1.8 KiB
Go
85 lines
1.8 KiB
Go
package bootstrap
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os/exec"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type CommandResult struct {
|
|
Command string
|
|
Stdout string
|
|
Stderr string
|
|
Err error
|
|
}
|
|
|
|
func (r CommandResult) CombinedOutput() string {
|
|
return strings.TrimSpace(strings.TrimSpace(r.Stdout) + "\n" + strings.TrimSpace(r.Stderr))
|
|
}
|
|
|
|
type Runner interface {
|
|
Run(ctx context.Context, name string, args ...string) CommandResult
|
|
}
|
|
|
|
type ExecRunner struct {
|
|
Timeout time.Duration
|
|
Stream io.Writer
|
|
}
|
|
|
|
func (r ExecRunner) Run(ctx context.Context, name string, args ...string) CommandResult {
|
|
timeout := r.Timeout
|
|
if timeout == 0 {
|
|
timeout = 30 * time.Minute
|
|
}
|
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
|
defer cancel()
|
|
var stdout, stderr bytes.Buffer
|
|
cmd := exec.CommandContext(ctx, name, args...)
|
|
cmd.Cancel = func() error {
|
|
if cmd.Process == nil {
|
|
return nil
|
|
}
|
|
_ = exec.Command("taskkill", "/F", "/T", "/PID", fmt.Sprintf("%d", cmd.Process.Pid)).Run()
|
|
return cmd.Process.Kill()
|
|
}
|
|
if r.Stream != nil {
|
|
cmd.Stdout = io.MultiWriter(r.Stream, &stdout)
|
|
cmd.Stderr = io.MultiWriter(r.Stream, &stderr)
|
|
} else {
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
}
|
|
err := cmd.Run()
|
|
if ctx.Err() == context.DeadlineExceeded {
|
|
err = fmt.Errorf("%s timed out", name)
|
|
}
|
|
return CommandResult{
|
|
Command: name + " " + strings.Join(args, " "),
|
|
Stdout: stdout.String(),
|
|
Stderr: stderr.String(),
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
type Issue struct {
|
|
Step string
|
|
Err error
|
|
}
|
|
|
|
type Issues []Issue
|
|
|
|
func (issues Issues) Err() error {
|
|
if len(issues) == 0 {
|
|
return nil
|
|
}
|
|
var parts []string
|
|
for _, issue := range issues {
|
|
parts = append(parts, fmt.Sprintf("%s: %v", issue.Step, issue.Err))
|
|
}
|
|
return fmt.Errorf("%d step(s) failed: %s", len(issues), strings.Join(parts, "; "))
|
|
}
|