github.com/aergoio/aergo@v1.3.1/cmd/brick/exec/batch.go (about) 1 package exec 2 3 import ( 4 "bufio" 5 "fmt" 6 "net/http" 7 "os" 8 "path/filepath" 9 "strings" 10 11 "github.com/aergoio/aergo/cmd/brick/context" 12 "github.com/fsnotify/fsnotify" 13 "github.com/mattn/go-colorable" 14 "github.com/rs/zerolog" 15 ) 16 17 var ( 18 verboseBatch = false 19 letBatchKnowErr error 20 batchErrorCount = 0 21 enableWatch = false 22 watchFileList []string 23 watcher *fsnotify.Watcher 24 ) 25 26 func EnableVerbose() { 27 verboseBatch = true 28 } 29 30 func GetBatchErrorCount() int { 31 return batchErrorCount 32 } 33 34 func EnableWatch() { 35 enableWatch = true 36 } 37 38 func init() { 39 registerExec(&batch{}) 40 } 41 42 type batch struct { 43 level int 44 } 45 46 func (c *batch) Command() string { 47 return "batch" 48 } 49 50 func (c *batch) Syntax() string { 51 return fmt.Sprintf("%s", context.PathSymbol) 52 } 53 54 func (c *batch) Usage() string { 55 return fmt.Sprintf("batch `<batch_file_path>`") 56 } 57 58 func (c *batch) Describe() string { 59 return "batch run" 60 } 61 62 func (c *batch) Validate(args string) error { 63 64 _, err := c.parse(args) 65 66 return err 67 } 68 69 func (c *batch) readBatchFile(batchFilePath string) ([]string, error) { 70 if strings.HasPrefix(batchFilePath, "http") { 71 // search in the web 72 req, err := http.NewRequest("GET", batchFilePath, nil) 73 if err != nil { 74 return nil, err 75 } 76 client := &http.Client{} 77 resp, err := client.Do(req) 78 if err != nil { 79 return nil, err 80 } 81 defer resp.Body.Close() 82 83 var cmdLines []string 84 scanner := bufio.NewScanner(resp.Body) 85 for scanner.Scan() { 86 cmdLines = append(cmdLines, scanner.Text()) 87 } 88 89 return cmdLines, nil 90 } 91 92 batchFile, err := os.Open(batchFilePath) 93 if err != nil { 94 return nil, err 95 } 96 defer batchFile.Close() 97 98 var cmdLines []string 99 scanner := bufio.NewScanner(batchFile) 100 for scanner.Scan() { 101 cmdLines = append(cmdLines, scanner.Text()) 102 } 103 104 return cmdLines, nil 105 } 106 107 func (c *batch) parse(args string) (string, error) { 108 splitArgs := context.SplitSpaceAndAccent(args, false) 109 if len(splitArgs) != 1 { 110 return "", fmt.Errorf("invalid format. usage: %s", c.Usage()) 111 } 112 113 batchFilePath := splitArgs[0].Text 114 115 if _, err := c.readBatchFile(batchFilePath); err != nil { 116 return "", fmt.Errorf("fail to read a brick batch file %s: %s", batchFilePath, err.Error()) 117 } 118 119 return batchFilePath, nil 120 } 121 122 func (c *batch) Run(args string) (string, error) { 123 stdOut := colorable.NewColorableStdout() 124 125 var err error 126 127 for { 128 if c.level == 0 && enableWatch { 129 watcher, err = fsnotify.NewWatcher() 130 if err != nil { 131 return "", err 132 } 133 // clear screen 134 fmt.Fprintf(stdOut, "\033[H\033[2J") 135 } 136 137 prefix := "" 138 139 batchFilePath, _ := c.parse(args) 140 cmdLines, err := c.readBatchFile(batchFilePath) 141 if err != nil { 142 return "", err 143 } 144 145 c.level++ 146 147 // set highest log level to turn off verbose 148 if false == verboseBatch { 149 zerolog.SetGlobalLevel(zerolog.ErrorLevel) 150 fmt.Fprintf(stdOut, "> %s\n", batchFilePath) 151 } else if verboseBatch && c.level != 1 { 152 prefix = fmt.Sprintf("%d-", c.level-1) 153 fmt.Fprintf(stdOut, "\n<<<<<<< %s\n", batchFilePath) 154 } 155 156 for i, line := range cmdLines { 157 lineNum := i + 1 158 159 cmd, args := context.ParseFirstWord(line) 160 if len(cmd) == 0 { 161 if verboseBatch { 162 fmt.Fprintf(stdOut, "\x1B[0;37m%s%d\x1B[0m\n", prefix, lineNum) 163 } 164 continue 165 } else if context.Comment == cmd { 166 if verboseBatch { 167 fmt.Fprintf(stdOut, "\x1B[0;37m%s%d \x1B[32m%s\x1B[0m\n", prefix, lineNum, line) 168 } 169 continue 170 } 171 if verboseBatch { 172 fmt.Fprintf(stdOut, "\x1B[0;37m%s%d \x1B[34;1m%s \x1B[0m%s\n", prefix, lineNum, cmd, args) 173 } 174 175 Broker(line) 176 177 if letBatchKnowErr != nil { 178 // if there is error during execution, then print line for error trace 179 fmt.Fprintf(stdOut, "\x1B[0;37m%s:%d \x1B[34;1m%s \x1B[0m%s\n\n", batchFilePath, lineNum, cmd, args) 180 letBatchKnowErr = nil 181 } 182 } 183 184 if c.level != 1 && verboseBatch { 185 fmt.Fprintf(stdOut, ">>>>>>> %s\n", batchFilePath) 186 } 187 188 c.level-- 189 190 // print final result and reset params 191 if c.level == 0 { 192 if batchErrorCount == 0 { 193 fmt.Fprintf(stdOut, "\x1B[32;1mBatch is successfully finished\x1B[0m\n") 194 } else { 195 fmt.Fprintf(stdOut, "\x1B[31;1mBatch is failed: Error %d\x1B[0m\n", batchErrorCount) 196 } 197 // reset params 198 batchErrorCount = 0 199 zerolog.SetGlobalLevel(zerolog.DebugLevel) 200 } 201 202 // add file to watch list 203 if enableWatch && !strings.HasPrefix(batchFilePath, "http") { 204 absPath, _ := filepath.Abs(batchFilePath) 205 watcher.Add(absPath) 206 } 207 208 if c.level == 0 && enableWatch { 209 // wait and check file changes 210 fileWatching: 211 for { 212 select { 213 case <-watcher.Events: 214 break fileWatching 215 case err, _ := <-watcher.Errors: 216 if err != nil { 217 fmt.Fprintf(stdOut, "\x1B[0;37mWatching File %s Error: %s\x1B[0m\n", batchFilePath, err.Error()) 218 } 219 break fileWatching 220 } 221 } 222 watcher.Close() 223 continue 224 } 225 break 226 } 227 228 return "batch exec is finished", nil 229 }