package execx import ( "context" "fmt" "log" "os" "os/exec" "strings" ) type Runner struct { DryRun bool Kubeconfig string Logger *log.Logger } func (r *Runner) Run(ctx context.Context, name string, args ...string) (string, error) { if r.DryRun { r.logf("DRY-RUN: %s %s", name, strings.Join(args, " ")) return "", nil } cmd := exec.CommandContext(ctx, name, args...) cmd.Env = os.Environ() if r.Kubeconfig != "" { cmd.Env = append(cmd.Env, "KUBECONFIG="+r.Kubeconfig) } out, err := cmd.CombinedOutput() trimmed := strings.TrimSpace(string(out)) if err != nil { if trimmed == "" { return "", fmt.Errorf("%s %s: %w", name, strings.Join(args, " "), err) } return trimmed, fmt.Errorf("%s %s: %w", name, strings.Join(args, " "), err) } return trimmed, nil } func (r *Runner) CommandExists(name string) bool { _, err := exec.LookPath(name) return err == nil } func (r *Runner) logf(format string, args ...any) { if r.Logger != nil { r.Logger.Printf(format, args...) } }