pkg/fix_local_shell.go (145 lines of code) (raw):

package pkg import ( "bufio" "fmt" "github.com/Azure/golden" "github.com/lonegunmanb/hclfuncs" "io" "net/http" "os" "runtime" "github.com/ahmetb/go-linq/v3" "github.com/alexellis/go-execute/v2" ) var _ Fix = &LocalShellFix{} type LocalShellFix struct { *golden.BaseBlock *BaseFix ExecuteCommand []string `hcl:"execute_command,optional" default:"[/bin/sh,-c]"` // The command used to execute the script. InlineShebang string `hcl:"inline_shebang,optional" validate:"required_with=Inlines"` Inlines []string `hcl:"inlines,optional" validate:"conflict_with=Script RemoteScript,at_least_one_of=Inlines Script RemoteScript"` Script string `hcl:"script,optional" validate:"conflict_with=Inlines RemoteScript,at_least_one_of=Inlines Script RemoteScript"` RemoteScript string `hcl:"remote_script,optional" validate:"conflict_with=Inlines Script,at_least_one_of=Inlines Script RemoteScript,eq=|http_url"` OnlyOn []string `hcl:"only_on,optional" validate:"all_string_in_slice=windows linux darwin openbsd netbsd freebsd dragonfly android solaris plan9"` Env map[string]string `hcl:"env,optional"` } func (l *LocalShellFix) Type() string { return "local_shell" } var stopByOnlyOnStub = func() {} func (l *LocalShellFix) Apply() (err error) { // user assigned env, must set these env then re-render all attributes if len(l.Env) > 0 { hclfuncs.GoroutineLocalEnv.Set(l.Env) defer hclfuncs.GoroutineLocalEnv.Remove() err := golden.Decode(l) if err != nil { return err } } if len(l.OnlyOn) > 0 && !linq.From(l.OnlyOn).Contains(runtime.GOOS) { stopByOnlyOnStub() return nil } script := l.Script if l.RemoteScript != "" { script, err = l.downloadFile(l.RemoteScript) if script != "" { defer func() { _ = os.RemoveAll(script) }() } defer func() { _ = os.RemoveAll(script) }() } else if len(l.Inlines) > 0 { if l.InlineShebang == "" { l.InlineShebang = "/bin/sh -e" } script, err = l.createTmpFileForInlines(l.InlineShebang, l.Inlines) if script != "" { defer func() { _ = os.RemoveAll(script) }() } if err != nil { return err } } l.ExecuteCommand = append(l.ExecuteCommand, script) env := l.flattenEnv() cmd := execute.ExecTask{ Command: l.ExecuteCommand[0], Args: l.ExecuteCommand[1:], Env: env, StreamStdio: false, } result, err := cmd.Execute(l.Context()) fmt.Printf("%s\n", result.Stdout) if err != nil { return err } if result.ExitCode != 0 { return fmt.Errorf("non-zero exit code: %d fix.%s.%s", result.ExitCode, l.Type(), l.Name()) } return nil } func (l *LocalShellFix) downloadFile(url string) (string, error) { out, err := os.CreateTemp("", "") if err != nil { return "", err } defer out.Close() resp, err := http.Get(url) if err != nil { return out.Name(), err } defer resp.Body.Close() _, err = io.Copy(out, resp.Body) if err != nil { return out.Name(), err } err = os.Chmod(out.Name(), 0700) if err != nil { return out.Name(), err } return out.Name(), nil } func (l *LocalShellFix) createTmpFileForInlines(shebang string, inlines []string) (string, error) { tmp, err := os.CreateTemp("", "grept-local-shell") if err != nil { return "", err } defer func() { _ = tmp.Close() }() writer := bufio.NewWriter(tmp) _, err = writer.WriteString(fmt.Sprintf("#!%s\n", shebang)) if err != nil { return tmp.Name(), err } for _, inline := range inlines { _, err := writer.WriteString(inline) if err != nil { return tmp.Name(), err } _, err = writer.WriteString("\n") if err != nil { return tmp.Name(), err } } if err := writer.Flush(); err != nil { return tmp.Name(), fmt.Errorf("error preparing inlines script %+v", err) } err = os.Chmod(tmp.Name(), 0700) if err != nil { return tmp.Name(), err } return tmp.Name(), nil } func (l *LocalShellFix) flattenEnv() []string { var env []string for k, v := range l.Env { env = append(env, fmt.Sprintf("%s=%s", k, v)) } return env }