func (r *CommandResponse) FormattedString() string {
	var rx = regexp.MustCompile("(?m)^")
	return rx.ReplaceAllString(r.Raw, "\t\t")
}
FormattedString returns an intented, formatted string
package pantry
import (
	"crypto/sha256"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"syscall"
	"github.com/fatih/color"
	"github.com/hashicorp/hcl2/hcl"
	"github.com/hashicorp/hcl2/hcldec"
	"github.com/mikemackintosh/bakery/cli"
	"github.com/mikemackintosh/bakery/config"
	"github.com/zclconf/go-cty/cty"
)
type Shell struct {
	PantryItem
	Script string `json:"script"`
}
var shellSpec = NewPantrySpec(&hcldec.ObjectSpec{
	"script": &hcldec.AttrSpec{
		Name:     "script",
		Required: true,
		Type:     cty.String,
	},
})
func (p *Shell) Parse(evalContext *hcl.EvalContext) error {
	cli.Debug(cli.INFO, "Preparing Shell", p.Name)
	cfg, diags := hcldec.Decode(p.Config, shellSpec, evalContext)
	if len(diags) != 0 {
		for _, diag := range diags {
			cli.Debug(cli.INFO, "\t#", diag)
		}
		return fmt.Errorf("%s", diags.Errs()[0])
	}
	err := p.Populate(cfg, p)
	if err != nil {
		return err
	}
	return nil
}
func (p *Shell) Bake() {
	var tmpFile = config.Registry.TempDir + fmt.Sprintf("/%x.sh", sha256.Sum256([]byte(p.Script)))[:14]
	err := ioutil.WriteFile(tmpFile, []byte(p.Script), 0744)
	if err != nil {
		cli.Debug(cli.ERROR, fmt.Sprintf("Error writing script to %s", tmpFile), err)
	}
	cli.Debug(cli.INFO, fmt.Sprintf("\t-> Running script %s", tmpFile), err)
	var cmd = []string{
		"/bin/bash",
		"-c",
		tmpFile}
	var o *CommandResponse
	if p.User != nil {
		uid, gid, err := GetUIDAndGID(*p.User)
		if err != nil {
			cli.Debug(cli.ERROR, fmt.Sprintf("Error getting user data, %s", err), err)
		}
		o, err = RunCommandAsUser(cmd, uid, gid)
		if err != nil {
			cli.Debug(cli.ERROR, fmt.Sprintf("Error running %s", tmpFile), err)
		}
	} else {
		o, err = RunCommand(cmd)
		if err != nil {
			cli.Debug(cli.ERROR, fmt.Sprintf("Error running %s", tmpFile), err)
		}
	}
	if len(o.String()) == 0 {
		cli.Debug(cli.DEBUG, "\t-> ", o.ExitCode)
		return
	}
	cli.Debug(cli.DEBUG, "\t-> ", o.String())
}
type CommandResponse struct {
	Command  *exec.Cmd
	Raw      string
	Error    string
	ExitCode int
}
func StreamCommand(prefix string, cmdArgs []string) error {
	cmd := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
	var errStdout, errStderr error
	stdoutIn, _ := cmd.StdoutPipe()
	stderrIn, _ := cmd.StderrPipe()
	cmd.Start()
	prefix = "\t"
	go func() {
		_, errStdout = copyOutputAndCapture(color.Output, stdoutIn, prefix, color.FgGreen)
	}()
	go func() {
		_, errStderr = copyOutputAndCapture(color.Output, stderrIn, prefix, color.FgRed)
	}()
	err := cmd.Wait()
	if err != nil {
		return fmt.Errorf("Failed to run command with error %s\n", err)
	}
	if errStdout != nil || errStderr != nil {
		return fmt.Errorf("No output received\n")
	}
	fmt.Println()
	return nil
}
func copyOutputAndCapture(w io.Writer, r io.Reader, prefix string, c color.Attribute) ([]byte, error) {
	var out []byte
	buf := make([]byte, 1024, 1024)
	for {
		n, err := r.Read(buf[:])
		if n > 0 {
			d := buf[:n]
			comp := color.New(color.FgBlue)
			comp.Fprintf(w, "%s", prefix)
			output := color.New(c)
			out = append(out, d...)
			_, err := output.Fprint(w, strings.TrimSpace(string(d)))
			if err != nil {
				return out, err
			}
		}
		if err != nil {
			if err == io.EOF {
				err = nil
			}
			return out, err
		}
	}
}
var TestRunCommandOutput = `Result
FOO:BAR`
func RunCommandAsUser(cmdArgs []string, uid, gid uint32) (*CommandResponse, error) {
	var exitCode int
	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
		return &CommandResponse{
			Command: nil,
			Raw:     TestRunCommandOutput,
		}, nil
	}
	cmd := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
	cmd.SysProcAttr = &syscall.SysProcAttr{}
	cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
	res, err := cmd.CombinedOutput()
	if err != nil {
		if exitError, ok := err.(*exec.ExitError); ok {
			ws := exitError.Sys().(syscall.WaitStatus)
			exitCode = ws.ExitStatus()
		} else {
			exitCode = 1
		}
	} else {
		ws := cmd.ProcessState.Sys().(syscall.WaitStatus)
		exitCode = ws.ExitStatus()
	}
	return &CommandResponse{
		Command:  cmd,
		Raw:      strings.TrimSpace(string(res)),
		ExitCode: exitCode,
	}, err
}
func RunCommand(cmdArgs []string) (*CommandResponse, error) {
	var exitCode int
	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
		return &CommandResponse{
			Command: nil,
			Raw:     TestRunCommandOutput,
		}, nil
	}
	cmd := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
	res, err := cmd.CombinedOutput()
	if err != nil {
		if exitError, ok := err.(*exec.ExitError); ok {
			ws := exitError.Sys().(syscall.WaitStatus)
			exitCode = ws.ExitStatus()
		} else {
			exitCode = 1
		}
	} else {
		ws := cmd.ProcessState.Sys().(syscall.WaitStatus)
		exitCode = ws.ExitStatus()
	}
	return &CommandResponse{
		Command:  cmd,
		Raw:      strings.TrimSpace(string(res)),
		ExitCode: exitCode,
	}, err
}
func (r *CommandResponse) String() string {
	return r.Raw
}