github.com/informationsea/shellflow@v0.1.3/main.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "flag" 6 "fmt" 7 "io" 8 "os" 9 "path" 10 "strings" 11 12 "bufio" 13 14 "github.com/chzyer/readline" 15 "github.com/informationsea/shellflow/flowscript" 16 ) 17 18 func main() { 19 if len(os.Args) <= 1 { 20 helpMode([]string{}) 21 return 22 } 23 24 var err error 25 26 switch os.Args[1] { 27 case "flowscript": 28 runFlowscriptIntepreter() 29 case "run": 30 err = runMode() 31 case "dot": 32 err = dotMode() 33 case "filelog": 34 err = fileLogMode() 35 case "viewlog": 36 err = viewLogMode() 37 case "-h", "-?", "help": 38 helpMode(os.Args[2:]) 39 default: 40 fmt.Fprintf(os.Stderr, "Unknown command: %s\n", os.Args[1]) 41 } 42 43 if err != nil { 44 fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error()) 45 os.Exit(1) 46 } 47 } 48 49 func helpMode(args []string) error { 50 _, err := fmt.Print(`shellflow @DEV@: shell-script like workflow management system 51 52 Commands: 53 run Run workflow 54 dot Export workflow as dot language for visualization 55 flowscript Launch flowscript interpreter 56 viewlog Show execution log 57 filelog Create a file log file, which contains SHA256 hash, modification date and so on 58 help Show this help 59 `) 60 return err 61 } 62 63 func viewLogMode() error { 64 f := flag.NewFlagSet("shellflow filelog", flag.ExitOnError) 65 var showAll bool 66 var failedOnly bool 67 f.BoolVar(&showAll, "all", false, "Show All") 68 f.BoolVar(&failedOnly, "failed", false, "Show Failed Job Only") 69 f.Parse(os.Args[2:]) 70 71 var err error 72 if len(f.Args()) > 0 { 73 err = ViewLogDetail(f.Args(), failedOnly) 74 } else { 75 err = ViewLog(showAll, failedOnly) 76 } 77 return err 78 } 79 80 func fileLogMode() error { 81 f := flag.NewFlagSet("shellflow filelog", flag.ExitOnError) 82 var output string 83 f.StringVar(&output, "output", "", "output json file") 84 f.Parse(os.Args[2:]) 85 var writer io.WriteCloser 86 var err error 87 88 files, err := CreateFileLog(f.Args(), false, MaximumContentLogSize) 89 if err != nil { 90 return err 91 } 92 93 if output == "" || output == "-" { 94 writer = os.Stdout 95 } else { 96 writer, err = os.OpenFile(output, os.O_CREATE|os.O_WRONLY, 0644) 97 if err != nil { 98 return err 99 } 100 } 101 defer writer.Close() 102 103 encoder := json.NewEncoder(writer) 104 encoder.SetIndent("", " ") 105 err = encoder.Encode(files) 106 if err != nil { 107 return err 108 } 109 return nil 110 } 111 112 func loadParameter(paramFile string) (map[string]interface{}, error) { 113 parameters := make(map[string]interface{}) 114 115 if paramFile != "" { 116 if strings.HasSuffix(paramFile, ".json") { 117 118 paramReader, err := os.Open(paramFile) 119 if err != nil { 120 return nil, err 121 } 122 decoder := json.NewDecoder(paramReader) 123 err = decoder.Decode(¶meters) 124 if err != nil { 125 return nil, err 126 } 127 //fmt.Println(parameters) 128 129 } else { 130 return nil, fmt.Errorf("Unknown file type %s", paramFile) 131 } 132 } 133 return parameters, nil 134 } 135 136 func dotMode() error { 137 paramFile := "" 138 139 f := flag.NewFlagSet("shellflow dot", flag.ExitOnError) 140 f.StringVar(¶mFile, "param", "", "Parameter File") 141 f.Parse(os.Args[2:]) 142 143 if len(f.Args()) != 1 { 144 helpMode([]string{"dot"}) 145 return fmt.Errorf("No workflow file") 146 } 147 148 env := NewEnvironment() 149 parameters, err := loadParameter(paramFile) 150 if err != nil { 151 return err 152 } 153 154 builder, err := parse(env, f.Args()[0], parameters) 155 if err != nil { 156 return err 157 } 158 dag := builder.CreateDag() 159 fmt.Println(dag) 160 return nil 161 } 162 163 func runMode() error { 164 f := flag.NewFlagSet("shellflow run", flag.ExitOnError) 165 166 useSge := false 167 paramFile := "" 168 169 env := NewEnvironment() 170 f.BoolVar(&env.skipSha, "skip-sha", false, "Skip SHA256 calculation") 171 f.BoolVar(&env.dryRun, "dry-run", false, "Print jobs to run without execute") 172 f.BoolVar(&env.scriptsOnly, "scripts-only", false, "Generate scripts only") 173 f.BoolVar(&env.rerunAll, "rerun", false, "Rerun all commands even if contents are not changed") 174 f.BoolVar(&useSge, "sge", false, "Use SGE/UGE instead of local executer") 175 f.StringVar(¶mFile, "param", "", "Parameter File") 176 f.Parse(os.Args[2:]) 177 178 if len(f.Args()) == 0 { 179 helpMode([]string{"run"}) 180 return fmt.Errorf("No workflow file") 181 } 182 183 parameters, err := loadParameter(paramFile) 184 if err != nil { 185 return err 186 } 187 188 builder, err := parse(env, f.Args()[0], parameters) 189 if err != nil { 190 return err 191 } 192 //fmt.Printf("%s\n", f.Args()) 193 194 if env.dryRun { 195 for _, v := range builder.Tasks { 196 if !v.ShouldSkip || env.rerunAll { 197 fmt.Printf("%s\n", v.ShellScript) 198 } 199 } 200 return nil 201 } 202 203 if env.rerunAll { 204 for _, v := range builder.Tasks { 205 v.ShouldSkip = false 206 } 207 } 208 209 gen, err := GenerateTaskScripts(f.Args()[0], paramFile, env, builder) 210 if err != nil { 211 return err 212 } 213 214 if !env.scriptsOnly { 215 if useSge { 216 err = ExecuteInSge(gen) 217 } else { 218 err = ExecuteLocalSingle(gen) 219 } 220 221 if err != nil { 222 fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error()) 223 exeErr, ok := err.(*executationError) 224 if ok { 225 jobLog, err := CollectLogsForOneJob(exeErr.jobRoot, exeErr.shellTask) 226 var errReader io.ReadCloser 227 if jobLog.ScriptExitCode == 0 { 228 errReader, err = os.Open(path.Join(exeErr.jobRoot, "run.stderr")) 229 } else { 230 errReader, err = os.Open(path.Join(exeErr.jobRoot, "script.stderr")) 231 } 232 if err != nil { 233 return err 234 } 235 defer errReader.Close() 236 io.Copy(os.Stderr, errReader) 237 238 os.Exit(1) 239 } else { 240 return err 241 } 242 } 243 } 244 return nil 245 } 246 247 //func setupRoot() { 248 // rootInfo, err := os.Stat(*workflowRoot) 249 // if err != nil && os.IsNotExist(err) { 250 // os.MkdirAll(*workflowRoot, 0750) 251 // } else if err != nil || !rootInfo.IsDir() { 252 // panic(fmt.Sprintf("workflow root directory path is not directory: %s", *workflowRoot)) 253 // } 254 //} 255 256 func parse(env *Environment, file string, param map[string]interface{}) (*ShellTaskBuilder, error) { 257 reader, err := os.Open(file) 258 if err != nil { 259 return nil, err 260 } 261 builder, err := ParseShellflow(bufio.NewReader(reader), env, param) 262 263 return builder, err 264 } 265 266 func runFlowscriptIntepreter() { 267 rl, err := readline.New("> ") 268 if err != nil { 269 panic(err) 270 } 271 272 defer rl.Close() 273 274 ge := flowscript.NewGlobalEnvironment() 275 276 for { 277 line, err := rl.Readline() 278 if err != nil || line == "exit" { 279 break 280 } 281 282 ev, err := flowscript.EvaluateScript(line, ge) 283 284 if err == nil { 285 fmt.Printf("%s\n", ev.String()) 286 } else { 287 fmt.Printf("Error: %s\n", err.Error()) 288 } 289 290 } 291 }