package jobqueue import ( "context" "fmt" "os" "os/exec" "strings" ) func NewShellRunnableFromMap(data map[string]interface{}) (*ShellRunnable, error) { scriptPath, _ := data["scriptpath"].(string) script, _ := data["script"].(string) if scriptPath != "" && script != "" { return nil, fmt.Errorf("%w: ScriptPath and Script are mutually exclusive", ErrInvalidData) } if scriptPath == "" && script == "" { return nil, fmt.Errorf("%w: ScriptPath or Script is required", ErrInvalidData) } return &ShellRunnable{ ScriptPath: scriptPath, Script: script, }, nil } // ShellResult is a result of a shell script type ShellResult struct { Output string Error string ExitCode int } type ShellRunnable struct { ScriptPath string Script string } func (s *ShellRunnable) Run(ctx context.Context) (RunResult[ShellResult], error) { scriptPath := s.ScriptPath if s.Script != "" { // write to temp tmp, err := os.CreateTemp("", "script-*.sh") if err != nil { return RunResult[ShellResult]{ Status: ResultStatusFailed, Data: ShellResult{ Output: "", ExitCode: DefaultErrorExitCode, Error: fmt.Errorf("%w: %v", ErrFailedToCreateTempFile, err).Error(), }, }, err } scriptPath = tmp.Name() defer os.Remove(scriptPath) _, err = tmp.WriteString(s.Script) defer tmp.Close() if err != nil { return RunResult[ShellResult]{ Status: ResultStatusFailed, Data: ShellResult{ Output: "", ExitCode: DefaultErrorExitCode, Error: fmt.Errorf("%w: %v", ErrFailedToWriteTempFile, err).Error(), }, }, err } } // #nosec cmd := exec.CommandContext(ctx, "sh", scriptPath) output, err := cmd.Output() var stderr []byte if err != nil { stderr = err.(*exec.ExitError).Stderr } exitCode := SuccessExitCode if err != nil { if exitError, ok := err.(*exec.ExitError); ok { exitCode = exitError.ExitCode() } return RunResult[ShellResult]{ Status: ResultStatusFailed, Data: ShellResult{ Output: string(output), ExitCode: exitCode, Error: string(stderr), }, }, err } return RunResult[ShellResult]{ Status: ResultStatusSuccess, Data: ShellResult{ Output: strings.TrimSpace(string(output)), ExitCode: exitCode, Error: string(stderr), }, }, nil } func (s *ShellRunnable) GetType() string { return "shell" } func (c *ShellRunnable) GetPersistence() RunnableImport { data := JSONMap{ "script_path": c.ScriptPath, "script": c.Script, } return RunnableImport{ Type: c.GetType(), Data: data, } }