github.com/jaylevin/jenkins-library@v1.230.4/cmd/shellExecute.go (about) 1 package cmd 2 3 import ( 4 "fmt" 5 "net/http" 6 "os/exec" 7 "path/filepath" 8 "strings" 9 10 "github.com/pkg/errors" 11 12 "github.com/SAP/jenkins-library/pkg/command" 13 piperhttp "github.com/SAP/jenkins-library/pkg/http" 14 "github.com/SAP/jenkins-library/pkg/log" 15 "github.com/SAP/jenkins-library/pkg/piperutils" 16 "github.com/SAP/jenkins-library/pkg/telemetry" 17 ) 18 19 type shellExecuteUtils interface { 20 command.ExecRunner 21 piperutils.FileUtils 22 piperhttp.Downloader 23 } 24 25 type shellExecuteUtilsBundle struct { 26 *command.Command 27 *piperutils.Files 28 *piperhttp.Client 29 } 30 31 func newShellExecuteUtils() shellExecuteUtils { 32 utils := shellExecuteUtilsBundle{ 33 Command: &command.Command{}, 34 Files: &piperutils.Files{}, 35 Client: &piperhttp.Client{}, 36 } 37 utils.Stdout(log.Writer()) 38 utils.Stderr(log.Writer()) 39 return &utils 40 } 41 42 func shellExecute(config shellExecuteOptions, telemetryData *telemetry.CustomData) { 43 utils := newShellExecuteUtils() 44 45 err := runShellExecute(&config, telemetryData, utils) 46 if err != nil { 47 log.Entry().WithError(err).Fatal("step execution failed") 48 } 49 } 50 51 func runShellExecute(config *shellExecuteOptions, telemetryData *telemetry.CustomData, utils shellExecuteUtils) error { 52 // check input data 53 // example for script: sources: ["./script.sh"] 54 for position, source := range config.Sources { 55 56 if strings.Contains(source, "https") { 57 scriptLocation, err := downloadScript(config, utils, source) 58 if err != nil { 59 return errors.Wrapf(err, "script download error") 60 } 61 source = scriptLocation 62 } 63 // check if the script is physically present 64 exists, err := utils.FileExists(source) 65 if err != nil { 66 log.Entry().WithError(err).Error("failed to check for defined script") 67 return fmt.Errorf("failed to check for defined script: %w", err) 68 } 69 if !exists { 70 log.Entry().WithError(err).Errorf("the script '%v' could not be found: %v", source, err) 71 return fmt.Errorf("the script '%v' could not be found", source) 72 } 73 74 args := []string{} 75 if len(config.ScriptArguments) > 0 && isArgumentAtPosition(config.ScriptArguments, position) { 76 args = strings.Split(config.ScriptArguments[position], " ") 77 } 78 79 log.Entry().Info("starting running script:", source) 80 81 err = utils.RunExecutable(source, args...) 82 if err != nil { 83 log.Entry().Errorln("starting running script:", source) 84 } 85 // handle exit code 86 if ee, ok := err.(*exec.ExitError); ok { 87 switch ee.ExitCode() { 88 case 0: 89 // success 90 return nil 91 case 1: 92 return errors.Wrap(err, "an error occurred while executing the script") 93 default: 94 // exit code 2 or >2 - unstable 95 return errors.Wrap(err, "script execution unstable or something went wrong") 96 } 97 } else if err != nil { 98 return errors.Wrap(err, "script execution error occurred") 99 } 100 } 101 102 return nil 103 } 104 105 func isArgumentAtPosition(scriptArguments []string, index int) bool { 106 return ((len(scriptArguments) > index) && scriptArguments[index] != "") 107 } 108 109 func downloadScript(config *shellExecuteOptions, utils shellExecuteUtils, url string) (string, error) { 110 header := http.Header{} 111 if len(config.GithubToken) > 0 { 112 header = http.Header{"Authorization": []string{"Token " + config.GithubToken}} 113 header.Set("Accept", "application/vnd.github.v3.raw") 114 } 115 116 log.Entry().Infof("downloading script : %v", url) 117 fileNameParts := strings.Split(url, "/") 118 fileName := fileNameParts[len(fileNameParts)-1] 119 err := utils.DownloadFile(url, filepath.Join(".pipeline", fileName), header, []*http.Cookie{}) 120 if err != nil { 121 return "", errors.Wrapf(err, "unable to download script from %v", url) 122 } 123 log.Entry().Infof("downloaded script %v successfully", url) 124 err = fileUtils.Chmod(filepath.Join(".pipeline", fileName), 0555) 125 if err != nil { 126 return "", errors.Wrapf(err, "unable to change script permission for %v", filepath.Join(".pipeline", fileName)) 127 } 128 return filepath.Join(".pipeline", fileName), nil 129 }