github.com/swaros/contxt/module/taskrun@v0.0.0-20240305083542-3dbd4436ac40/cmdhandl.go (about) 1 // Copyright (c) 2020 Thomas Ziegler <thomas.zglr@googlemail.com>. All rights reserved. 2 // 3 // # Licensed under the MIT License 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in all 13 // copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 // SOFTWARE. 22 package taskrun 23 24 import ( 25 "bufio" 26 "fmt" 27 28 "os" 29 "os/exec" 30 "strings" 31 "sync" 32 "syscall" 33 34 "github.com/sirupsen/logrus" 35 "github.com/swaros/contxt/module/configure" 36 "github.com/swaros/contxt/module/dirhandle" 37 "github.com/swaros/contxt/module/systools" 38 ) 39 40 const ( 41 // DefaultExecFile is the filename of the script defaut file 42 DefaultExecFile = string(os.PathSeparator) + ".context.json" 43 44 // DefaultExecYaml is the default yaml configuration file 45 DefaultExecYaml = string(os.PathSeparator) + ".contxt.yml" 46 defaultExecYamlName = ".contxt.yml" 47 48 // TargetScript is script default target 49 TargetScript = "script" 50 51 // InitScript is script default target 52 InitScript = "init" 53 54 // ClearScript is script default target 55 ClearScript = "clear" 56 57 // TestScript is teh test target 58 TestScript = "test" 59 60 // DefaultCommandFallBack is used if no command is defined 61 DefaultCommandFallBack = "bash" 62 63 // On windows we have a different default 64 DefaultCommandFallBackWindows = "powershell" 65 ) 66 67 // ExecuteTemplateWorker runs ExecCurrentPathTemplate in context of a waitgroup 68 func ExecuteTemplateWorker(waitGroup *sync.WaitGroup, useWaitGroup bool, target string, template configure.RunConfig) { 69 if useWaitGroup { 70 defer waitGroup.Done() 71 } 72 //ExecCurrentPathTemplate(path) 73 exitCode := ExecPathFile(waitGroup, useWaitGroup, template, target) 74 GetLogger().WithField("exitcode", exitCode).Info("ExecuteTemplateWorker done with exitcode") 75 76 } 77 78 func GetExecDefaults() (string, []string) { 79 cmd := GetDefaultCmd() 80 var args []string 81 args = GetDefaultCmdOpts(cmd, args) 82 return cmd, args 83 } 84 85 func GetDefaultCmd() string { 86 87 envCmd := os.Getenv("CTX_DEFAULT_CMD") 88 if envCmd != "" { 89 GetLogger().WithField("defaultcmd", envCmd).Info("Got default cmd from environment") 90 return envCmd 91 } 92 93 if configure.GetOs() == "windows" { 94 return DefaultCommandFallBackWindows 95 } 96 return DefaultCommandFallBack 97 } 98 99 func GetDefaultCmdOpts(ShellToUse string, cmdArg []string) []string { 100 if configure.GetOs() == "windows" { 101 if envCmd := os.Getenv("CTX_DEFAULT_CMD_ARGUMENTS"); envCmd != "" { 102 GetLogger().WithField("arguments", envCmd).Info("Got cmd arguments form environment") 103 return strings.Split(envCmd, " ") 104 } 105 if cmdArg == nil && ShellToUse == DefaultCommandFallBackWindows { 106 cmdArg = []string{"-nologo", "-noprofile"} 107 } 108 } else { 109 if cmdArg == nil && ShellToUse == DefaultCommandFallBack { 110 cmdArg = []string{"-c"} 111 } 112 } 113 return cmdArg 114 } 115 116 // ExecuteScriptLine executes a simple shell script 117 // returns internal exitsCode, process existcode, error 118 func ExecuteScriptLine(ShellToUse string, cmdArg []string, command string, callback func(string, error) bool, startInfo func(*os.Process)) (int, int, error) { 119 cmdArg = GetDefaultCmdOpts(ShellToUse, cmdArg) 120 cmdArg = append(cmdArg, command) 121 cmd := exec.Command(ShellToUse, cmdArg...) 122 stdoutPipe, _ := cmd.StdoutPipe() 123 cmd.Stderr = cmd.Stdout 124 125 err := cmd.Start() 126 if err != nil { 127 GetLogger().Warn("execution error: ", err) 128 return systools.ExitCmdError, 0, err 129 } 130 131 GetLogger().WithFields(logrus.Fields{ 132 "env": cmd.Env, 133 "args": cmd.Args, 134 "dir": cmd.Dir, 135 "extrafiles": cmd.ExtraFiles, 136 "pid": cmd.Process.Pid, 137 }).Info(":::EXEC") 138 139 startInfo(cmd.Process) 140 scanner := bufio.NewScanner(stdoutPipe) 141 142 scanner.Split(bufio.ScanLines) 143 for scanner.Scan() { 144 m := scanner.Text() 145 146 keepRunning := callback(m, nil) 147 148 GetLogger().WithFields(logrus.Fields{ 149 "keep-running": keepRunning, 150 "out": m, 151 }).Info("handle-result") 152 if !keepRunning { 153 cmd.Process.Kill() 154 return systools.ExitByStopReason, 0, err 155 } 156 157 } 158 err = cmd.Wait() 159 if err != nil { 160 callback(err.Error(), err) 161 errRealCode := 0 162 if exiterr, ok := err.(*exec.ExitError); ok { 163 if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { 164 GetLogger().Warn("(maybe expected...) Exit Status reported: ", status.ExitStatus()) 165 errRealCode = status.ExitStatus() 166 } 167 168 } else { 169 GetLogger().Warn("execution error: ", err) 170 } 171 return systools.ExitCmdError, errRealCode, err 172 } 173 174 return systools.ExitOk, 0, err 175 } 176 177 // WriteTemplate create path based execution file 178 func WriteTemplate() { 179 dir, error := dirhandle.Current() 180 if error != nil { 181 fmt.Println("error getting current directory") 182 return 183 } 184 185 var path = dir + DefaultExecYaml 186 already, errEx := dirhandle.Exists(path) 187 if errEx != nil { 188 fmt.Println("error ", errEx) 189 return 190 } 191 192 if already { 193 fmt.Println("file already exists.", path, " aborted") 194 return 195 } 196 197 fmt.Println("write execution template to ", path) 198 199 var demoContent = `task: 200 - id: script 201 script: 202 - echo "hello world" 203 ` 204 err := os.WriteFile(path, []byte(demoContent), 0644) 205 if err != nil { 206 fmt.Println(err) 207 } 208 } 209 210 // ExecPathFile executes the default exec file 211 func ExecPathFile(waitGroup *sync.WaitGroup, useWaitGroup bool, template configure.RunConfig, target string) int { 212 var scopeVars map[string]string = make(map[string]string) 213 return executeTemplate(waitGroup, useWaitGroup, template, target, scopeVars) 214 }