-
Notifications
You must be signed in to change notification settings - Fork 0
/
batch_run.go
153 lines (137 loc) · 4.14 KB
/
batch_run.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package syscmd
import (
"errors"
"io/fs"
"strings"
"github.com/gookit/gcli/v3"
"github.com/gookit/gcli/v3/gflag"
"github.com/gookit/goutil/arrutil"
"github.com/gookit/goutil/cflag"
"github.com/gookit/goutil/errorx"
"github.com/gookit/goutil/fsutil"
"github.com/gookit/goutil/strutil/textutil"
"github.com/gookit/goutil/sysutil/cmdr"
"github.com/inhere/kite-go/internal/biz/cmdbiz"
)
var btrOpts = struct {
cmdbiz.CommonOpts
cmdTpl string
inDirs gflag.String
allSub bool
exclude gflag.Strings
// custom vars for command template
cmdVars gflag.KVString
// for range vars list, multi by comma
forVars gflag.String
interval int
}{
cmdVars: cflag.NewKVString(),
}
// NewBatchRunCmd instance
func NewBatchRunCmd() *gcli.Command {
return &gcli.Command{
Name: "brun",
Aliases: []string{"batch-run"},
Desc: "batch run more commands at once, and allow with vars",
Help: `
Build-in vars:
{dir} - current dir name
{path} - current dir full path
{pwd} - current workdir path
{parent} - parent dir name
{parentPath} - parent dir full path
{item} - current item in the for range list
`,
Examples: `
# run command multi times in all subdir
{$fullCmd} --cmd "echo {dir}" --subdir
`,
Config: func(c *gcli.Command) {
btrOpts.BindCommonFlags(c)
c.BoolOpt2(&btrOpts.allSub, "all-subdir, all-sub, subdir", "run command on the each subdir in the <cyan>--dirs</>")
c.StrOpt2(&btrOpts.cmdTpl, "cmd, c", "want execute `command` line or template, allow use vars. eg: {dir}")
c.VarOpt(&btrOpts.exclude, "exclude", "e", "exclude some subdir on with <cyan>--all-subdir</>")
c.VarOpt(&btrOpts.inDirs, "dirs", "d", "run command on the each dir path, multi by comma. default is workdir")
c.VarOpt2(&btrOpts.cmdVars, "vars,var,v", "sets template variables for build command. format: `KEY=VALUE`")
c.VarOpt2(&btrOpts.forVars, "foreach,range,for", "for range vars list, multi by comma. use: {item}")
c.IntOpt2(&btrOpts.interval, "interval,i", "interval seconds between each command run")
c.AddArg("cmd", "same of option <cyan>--cmd</>, set execute command line template, allow vars").WithAfterFn(func(a *gflag.CliArg) error {
if btrOpts.cmdTpl == "" {
btrOpts.cmdTpl = a.String()
return nil
}
return errorx.Raw("cmd template has been bounded by option --cmd")
})
},
Func: func(c *gcli.Command, _ []string) error {
wkDirs := btrOpts.inDirs.Strings()
if len(wkDirs) == 0 {
wkDirs = []string{c.WorkDir()}
}
btrOpts.cmdTpl = strings.TrimSpace(btrOpts.cmdTpl)
if btrOpts.cmdTpl == "" {
return errors.New("please input command template")
}
for _, dir := range wkDirs {
if btrOpts.allSub {
err := fsutil.FindInDir(dir, func(path string, ent fs.DirEntry) error {
// check exclude
if arrutil.StringsHas(btrOpts.exclude, ent.Name()) {
return nil
}
return runCmdInDir(path, c)
}, fsutil.OnlyFindDir)
if err != nil {
return err
}
} else {
if err := runCmdInDir(dir, c); err != nil {
return err
}
}
}
return nil
},
}
}
func runCmdInDir(dirPath string, c *gcli.Command) error {
pDir := fsutil.Dir(dirPath)
vars := btrOpts.cmdVars.Data()
vars.Load(map[string]string{
"pwd": c.WorkDir(),
"path": dirPath,
"dir": fsutil.Name(dirPath),
"parent": fsutil.Name(pDir),
"parentPath": pDir,
})
// render command template
rpl := textutil.NewVarReplacer("{,}")
ers := errorx.Errors{}
// for range vars list
items := btrOpts.forVars.Strings()
if len(items) > 0 {
for _, item := range items {
vars.Set("item", item)
cmdStr := rpl.ReplaceSMap(btrOpts.cmdTpl, vars)
execCmd := cmdr.NewCmdline(cmdStr).
WithWorkDir(btrOpts.Workdir).
WithDryRun(btrOpts.DryRun).
OutputToOS().
PrintCmdline()
if err := execCmd.Run(); err != nil {
ers = append(ers, err)
}
}
} else {
cmdStr := rpl.ReplaceSMap(btrOpts.cmdTpl, vars)
execCmd := cmdr.NewCmdline(cmdStr).
WithWorkDir(btrOpts.Workdir).
WithDryRun(btrOpts.DryRun).
OutputToOS().
PrintCmdline()
if err := execCmd.Run(); err != nil {
ers = append(ers, err)
}
}
return ers.ErrorOrNil()
}