switch to go vendoring
This commit is contained in:
+388
@@ -0,0 +1,388 @@
|
||||
package cmdr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/gookit/goutil"
|
||||
"github.com/gookit/goutil/arrutil"
|
||||
"github.com/gookit/goutil/internal/comfunc"
|
||||
)
|
||||
|
||||
// Cmd struct
|
||||
type Cmd struct {
|
||||
*exec.Cmd
|
||||
// Name of the command
|
||||
Name string
|
||||
// inited bool
|
||||
|
||||
// BeforeRun hook
|
||||
BeforeRun func(c *Cmd)
|
||||
// AfterRun hook
|
||||
AfterRun func(c *Cmd, err error)
|
||||
}
|
||||
|
||||
// WrapGoCmd instance
|
||||
func WrapGoCmd(cmd *exec.Cmd) *Cmd {
|
||||
return &Cmd{Cmd: cmd}
|
||||
}
|
||||
|
||||
// NewGitCmd instance
|
||||
func NewGitCmd(subCmd string, args ...string) *Cmd {
|
||||
return NewCmd("git", subCmd).AddArgs(args)
|
||||
}
|
||||
|
||||
// NewCmd instance
|
||||
//
|
||||
// see exec.Command
|
||||
func NewCmd(bin string, args ...string) *Cmd {
|
||||
return &Cmd{
|
||||
Cmd: exec.Command(bin, args...),
|
||||
}
|
||||
}
|
||||
|
||||
// CmdWithCtx create new instance with context.
|
||||
//
|
||||
// see exec.CommandContext
|
||||
func CmdWithCtx(ctx context.Context, bin string, args ...string) *Cmd {
|
||||
return &Cmd{
|
||||
Cmd: exec.CommandContext(ctx, bin, args...),
|
||||
}
|
||||
}
|
||||
|
||||
// PrintCmdline on before exec
|
||||
func PrintCmdline(c *Cmd) {
|
||||
color.Yellowln(">", c.Cmdline())
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
// config the command
|
||||
// -------------------------------------------------
|
||||
|
||||
// Config the command
|
||||
func (c *Cmd) Config(fn func(c *Cmd)) *Cmd {
|
||||
fn(c)
|
||||
return c
|
||||
}
|
||||
|
||||
// OnBefore exec add hook
|
||||
func (c *Cmd) OnBefore(fn func(c *Cmd)) *Cmd {
|
||||
c.BeforeRun = fn
|
||||
return c
|
||||
}
|
||||
|
||||
// OnAfter exec add hook
|
||||
func (c *Cmd) OnAfter(fn func(c *Cmd, err error)) *Cmd {
|
||||
c.AfterRun = fn
|
||||
return c
|
||||
}
|
||||
|
||||
// WithBin name returns the current object
|
||||
func (c *Cmd) WithBin(name string) *Cmd {
|
||||
c.Args[0] = name
|
||||
c.lookPath(name)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Cmd) lookPath(name string) {
|
||||
if filepath.Base(name) == name {
|
||||
lp, err := exec.LookPath(name)
|
||||
if lp != "" {
|
||||
// Update cmd.Path even if err is non-nil.
|
||||
// If err is ErrDot (especially on Windows), lp may include a resolved
|
||||
// extension (like .exe or .bat) that should be preserved.
|
||||
c.Path = lp
|
||||
}
|
||||
if err != nil {
|
||||
goutil.Panicf("look %q path error: %s", name, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithGoCmd and returns the current instance.
|
||||
func (c *Cmd) WithGoCmd(ec *exec.Cmd) *Cmd {
|
||||
c.Cmd = ec
|
||||
return c
|
||||
}
|
||||
|
||||
// WithWorkDir returns the current object
|
||||
func (c *Cmd) WithWorkDir(dir string) *Cmd {
|
||||
c.Dir = dir
|
||||
return c
|
||||
}
|
||||
|
||||
// WorkDirOnNot set, returns the current object
|
||||
func (c *Cmd) WorkDirOnNot(dir string) *Cmd {
|
||||
if c.Dir == "" {
|
||||
c.Dir = dir
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// OutputToStd output to OS stdout and error
|
||||
func (c *Cmd) OutputToStd() *Cmd {
|
||||
c.Stdout = os.Stdout
|
||||
c.Stderr = os.Stderr
|
||||
return c
|
||||
}
|
||||
|
||||
// WithStdin returns the current argument
|
||||
func (c *Cmd) WithStdin(in io.Reader) *Cmd {
|
||||
c.Stdin = in
|
||||
return c
|
||||
}
|
||||
|
||||
// WithOutput returns the current instance
|
||||
func (c *Cmd) WithOutput(out, errOut io.Writer) *Cmd {
|
||||
c.Stdout = out
|
||||
if errOut != nil {
|
||||
c.Stderr = errOut
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// WithAnyArgs add args and returns the current object.
|
||||
func (c *Cmd) WithAnyArgs(args ...any) *Cmd {
|
||||
c.Args = append(c.Args, arrutil.SliceToStrings(args)...)
|
||||
return c
|
||||
}
|
||||
|
||||
// AddArg add args and returns the current object
|
||||
func (c *Cmd) AddArg(args ...string) *Cmd { return c.WithArg(args...) }
|
||||
|
||||
// WithArg add args and returns the current object. alias of the WithArg()
|
||||
func (c *Cmd) WithArg(args ...string) *Cmd {
|
||||
c.Args = append(c.Args, args...)
|
||||
return c
|
||||
}
|
||||
|
||||
// AddArgf add args and returns the current object. alias of the WithArgf()
|
||||
func (c *Cmd) AddArgf(format string, args ...any) *Cmd {
|
||||
return c.WithArgf(format, args...)
|
||||
}
|
||||
|
||||
// WithArgf add arg and returns the current object
|
||||
func (c *Cmd) WithArgf(format string, args ...any) *Cmd {
|
||||
c.Args = append(c.Args, fmt.Sprintf(format, args...))
|
||||
return c
|
||||
}
|
||||
|
||||
// ArgIf add arg and returns the current object
|
||||
func (c *Cmd) ArgIf(arg string, exprOk bool) *Cmd {
|
||||
if exprOk {
|
||||
c.Args = append(c.Args, arg)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// WithArgIf add arg and returns the current object
|
||||
func (c *Cmd) WithArgIf(arg string, exprOk bool) *Cmd {
|
||||
return c.ArgIf(arg, exprOk)
|
||||
}
|
||||
|
||||
// AddArgs for the git. alias of WithArgs()
|
||||
func (c *Cmd) AddArgs(args []string) *Cmd { return c.WithArgs(args) }
|
||||
|
||||
// WithArgs for the git
|
||||
func (c *Cmd) WithArgs(args []string) *Cmd {
|
||||
if len(args) > 0 {
|
||||
c.Args = append(c.Args, args...)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// WithArgsIf add arg and returns the current object
|
||||
func (c *Cmd) WithArgsIf(args []string, exprOk bool) *Cmd {
|
||||
if exprOk && len(args) > 0 {
|
||||
c.Args = append(c.Args, args...)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
// helper command
|
||||
// -------------------------------------------------
|
||||
|
||||
// IDString of the command
|
||||
func (c *Cmd) IDString() string {
|
||||
if c.Name != "" {
|
||||
return c.Name
|
||||
}
|
||||
return c.BinOrPath()
|
||||
}
|
||||
|
||||
// BinName of the command
|
||||
func (c *Cmd) BinName() string {
|
||||
if len(c.Args) > 0 {
|
||||
return c.Args[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// BinOrPath of the command
|
||||
func (c *Cmd) BinOrPath() string {
|
||||
if len(c.Args) > 0 {
|
||||
return c.Args[0]
|
||||
}
|
||||
return c.Path
|
||||
}
|
||||
|
||||
// OnlyArgs of the command, not contains bin name.
|
||||
func (c *Cmd) OnlyArgs() (ss []string) {
|
||||
if len(c.Args) > 1 {
|
||||
return c.Args[1:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ResetArgs for command, but will keep bin name.
|
||||
func (c *Cmd) ResetArgs() {
|
||||
if len(c.Args) > 0 {
|
||||
c.Args = c.Args[0:1]
|
||||
} else {
|
||||
c.Args = c.Args[:0]
|
||||
}
|
||||
}
|
||||
|
||||
// Cmdline to command line
|
||||
func (c *Cmd) Cmdline() string {
|
||||
return comfunc.Cmdline(c.Args)
|
||||
}
|
||||
|
||||
// Copy new instance from current command, with new args.
|
||||
func (c *Cmd) Copy(args ...string) *Cmd {
|
||||
nc := *c
|
||||
|
||||
// copy bin name.
|
||||
if len(c.Args) > 0 {
|
||||
nc.Args = append([]string{c.Args[0]}, args...)
|
||||
} else {
|
||||
nc.Args = args
|
||||
}
|
||||
|
||||
return &nc
|
||||
}
|
||||
|
||||
// GoCmd get exec.Cmd
|
||||
func (c *Cmd) GoCmd() *exec.Cmd { return c.Cmd }
|
||||
|
||||
// -------------------------------------------------
|
||||
// run command
|
||||
// -------------------------------------------------
|
||||
|
||||
// Success run and return whether success
|
||||
func (c *Cmd) Success() bool {
|
||||
return c.Run() == nil
|
||||
}
|
||||
|
||||
// SafeLines run and return output as lines
|
||||
func (c *Cmd) SafeLines() []string {
|
||||
ss, _ := c.OutputLines()
|
||||
return ss
|
||||
}
|
||||
|
||||
// OutputLines run and return output as lines
|
||||
func (c *Cmd) OutputLines() ([]string, error) {
|
||||
out, err := c.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return OutputLines(out), err
|
||||
}
|
||||
|
||||
// SafeOutput run and return output
|
||||
func (c *Cmd) SafeOutput() string {
|
||||
out, err := c.Output()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Output run and return output
|
||||
func (c *Cmd) Output() (string, error) {
|
||||
if c.BeforeRun != nil {
|
||||
c.BeforeRun(c)
|
||||
}
|
||||
|
||||
output, err := c.Cmd.Output()
|
||||
|
||||
if c.AfterRun != nil {
|
||||
c.AfterRun(c, err)
|
||||
}
|
||||
return string(output), err
|
||||
}
|
||||
|
||||
// CombinedOutput run and return output, will combine stderr and stdout output
|
||||
func (c *Cmd) CombinedOutput() (string, error) {
|
||||
if c.BeforeRun != nil {
|
||||
c.BeforeRun(c)
|
||||
}
|
||||
|
||||
output, err := c.Cmd.CombinedOutput()
|
||||
|
||||
if c.AfterRun != nil {
|
||||
c.AfterRun(c, err)
|
||||
}
|
||||
return string(output), err
|
||||
}
|
||||
|
||||
// MustRun a command. will panic on error
|
||||
func (c *Cmd) MustRun() {
|
||||
if err := c.Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// FlushRun runs command and flush output to stdout
|
||||
func (c *Cmd) FlushRun() error {
|
||||
c.OutputToStd()
|
||||
return c.Run()
|
||||
}
|
||||
|
||||
// Run runs command
|
||||
func (c *Cmd) Run() error {
|
||||
if c.BeforeRun != nil {
|
||||
c.BeforeRun(c)
|
||||
}
|
||||
|
||||
// do running
|
||||
err := c.Cmd.Run()
|
||||
|
||||
if c.AfterRun != nil {
|
||||
c.AfterRun(c, err)
|
||||
}
|
||||
return err
|
||||
|
||||
// if IsWindows() {
|
||||
// return c.Spawn()
|
||||
// }
|
||||
// return c.Exec()
|
||||
}
|
||||
|
||||
// Spawn runs command with spawn(3)
|
||||
// func (c *Cmd) Spawn() error {
|
||||
// return c.Cmd.Run()
|
||||
// }
|
||||
//
|
||||
// // Exec runs command with exec(3)
|
||||
// // Note that Windows doesn't support exec(3): http://golang.org/src/pkg/syscall/exec_windows.go#L339
|
||||
// func (c *Cmd) Exec() error {
|
||||
// binary, err := exec.LookPath(c.Path)
|
||||
// if err != nil {
|
||||
// return &exec.Error{
|
||||
// Name: c.Path,
|
||||
// Err: errorx.Newf("%s not found in the system", c.Path),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// args := []string{binary}
|
||||
// args = append(args, c.Args...)
|
||||
//
|
||||
// return syscall.Exec(binary, args, os.Environ())
|
||||
// }
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// Package cmdr Provide for quick build and run a cmd, batch run multi cmd tasks
|
||||
package cmdr
|
||||
|
||||
import "strings"
|
||||
|
||||
// OutputLines split output to lines
|
||||
func OutputLines(output string) []string {
|
||||
output = strings.TrimSuffix(output, "\n")
|
||||
if output == "" {
|
||||
return nil
|
||||
}
|
||||
return strings.Split(output, "\n")
|
||||
}
|
||||
|
||||
// FirstLine from command output
|
||||
func FirstLine(output string) string {
|
||||
if i := strings.Index(output, "\n"); i >= 0 {
|
||||
return output[0:i]
|
||||
}
|
||||
return output
|
||||
}
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
package cmdr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/gookit/goutil/arrutil"
|
||||
"github.com/gookit/goutil/errorx"
|
||||
"github.com/gookit/goutil/maputil"
|
||||
"github.com/gookit/goutil/mathutil"
|
||||
)
|
||||
|
||||
// Task struct
|
||||
type Task struct {
|
||||
err error
|
||||
index int
|
||||
|
||||
// ID for task
|
||||
ID string
|
||||
Cmd *Cmd
|
||||
|
||||
// BeforeRun hook
|
||||
BeforeRun func(t *Task)
|
||||
PrevCond func(prev *Task) bool
|
||||
}
|
||||
|
||||
func NewTask(cmd *Cmd) *Task {
|
||||
return &Task{
|
||||
Cmd: cmd,
|
||||
}
|
||||
}
|
||||
|
||||
// get task id by cmd.Name
|
||||
func (t *Task) ensureID(idx int) {
|
||||
t.index = idx
|
||||
if t.ID != "" {
|
||||
return
|
||||
}
|
||||
|
||||
id := t.Cmd.IDString()
|
||||
if t.Cmd.Name == "" {
|
||||
id += mathutil.String(idx)
|
||||
}
|
||||
t.ID = id
|
||||
}
|
||||
|
||||
// Run command
|
||||
func (t *Task) Run() error {
|
||||
if t.BeforeRun != nil {
|
||||
t.BeforeRun(t)
|
||||
}
|
||||
|
||||
t.err = t.Cmd.Run()
|
||||
return t.err
|
||||
}
|
||||
|
||||
// Err get
|
||||
func (t *Task) Err() error {
|
||||
return t.err
|
||||
}
|
||||
|
||||
// Index get
|
||||
func (t *Task) Index() int {
|
||||
return t.index
|
||||
}
|
||||
|
||||
// Cmdline get
|
||||
func (t *Task) Cmdline() string {
|
||||
return t.Cmd.Cmdline()
|
||||
}
|
||||
|
||||
// IsSuccess of task
|
||||
func (t *Task) IsSuccess() bool {
|
||||
return t.err == nil
|
||||
}
|
||||
|
||||
// Runner use for batch run multi task commands
|
||||
type Runner struct {
|
||||
prev *Task
|
||||
// task name to index
|
||||
idMap map[string]int
|
||||
tasks []*Task
|
||||
// Errs on run tasks, key is Task.ID
|
||||
Errs errorx.ErrMap
|
||||
|
||||
// TODO Concurrent run
|
||||
// common workdir
|
||||
// wordDir string
|
||||
|
||||
// Params for add custom params
|
||||
Params maputil.Map
|
||||
|
||||
// DryRun dry run all commands
|
||||
DryRun bool
|
||||
// IgnoreErr continue on error
|
||||
IgnoreErr bool
|
||||
// BeforeRun hooks on each task. return false to skip current task.
|
||||
BeforeRun func(r *Runner, t *Task) bool
|
||||
// AfterRun hook on each task. return false to stop running.
|
||||
AfterRun func(r *Runner, t *Task) bool
|
||||
}
|
||||
|
||||
// NewRunner instance with config func
|
||||
func NewRunner(fns ...func(rr *Runner)) *Runner {
|
||||
rr := &Runner{
|
||||
idMap: make(map[string]int, 0),
|
||||
tasks: make([]*Task, 0),
|
||||
Errs: make(errorx.ErrMap),
|
||||
Params: make(maputil.Map),
|
||||
}
|
||||
|
||||
for _, fn := range fns {
|
||||
fn(rr)
|
||||
}
|
||||
return rr
|
||||
}
|
||||
|
||||
// Add multitask at once
|
||||
func (r *Runner) Add(tasks ...*Task) *Runner {
|
||||
for _, task := range tasks {
|
||||
r.AddTask(task)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// AddTask add one task
|
||||
func (r *Runner) AddTask(task *Task) *Runner {
|
||||
if task.Cmd == nil {
|
||||
panic("task command cannot be empty")
|
||||
}
|
||||
|
||||
idx := len(r.tasks)
|
||||
task.ensureID(idx)
|
||||
|
||||
// TODO check id repeat
|
||||
r.idMap[task.ID] = idx
|
||||
r.tasks = append(r.tasks, task)
|
||||
return r
|
||||
}
|
||||
|
||||
// AddCmd commands
|
||||
func (r *Runner) AddCmd(cmds ...*Cmd) *Runner {
|
||||
for _, cmd := range cmds {
|
||||
r.AddTask(&Task{Cmd: cmd})
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// GitCmd quick a git command task
|
||||
func (r *Runner) GitCmd(subCmd string, args ...string) *Runner {
|
||||
r.AddTask(&Task{
|
||||
Cmd: NewGitCmd(subCmd, args...),
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
// CmdWithArgs a command task
|
||||
func (r *Runner) CmdWithArgs(cmdName string, args ...string) *Runner {
|
||||
r.AddTask(&Task{
|
||||
Cmd: NewCmd(cmdName, args...),
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
// CmdWithAnys a command task
|
||||
func (r *Runner) CmdWithAnys(cmdName string, args ...any) *Runner {
|
||||
r.AddTask(&Task{
|
||||
Cmd: NewCmd(cmdName, arrutil.SliceToStrings(args)...),
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
// Run all tasks
|
||||
func (r *Runner) Run() error {
|
||||
// do run tasks
|
||||
for i, task := range r.tasks {
|
||||
if r.BeforeRun != nil && !r.BeforeRun(r, task) {
|
||||
continue
|
||||
}
|
||||
|
||||
if r.prev != nil && task.PrevCond != nil && !task.PrevCond(r.prev) {
|
||||
continue
|
||||
}
|
||||
|
||||
if r.DryRun {
|
||||
color.Infof("DRY-RUN: task #%d execute completed\n", i+1)
|
||||
continue
|
||||
}
|
||||
|
||||
if !r.RunTask(task) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.Errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return r.Errs
|
||||
}
|
||||
|
||||
// RunTask command
|
||||
func (r *Runner) RunTask(task *Task) (goon bool) {
|
||||
// do running
|
||||
if err := task.Run(); err != nil {
|
||||
r.Errs[task.ID] = err
|
||||
color.Errorf("Task #%d run error: %s\n", task.Index()+1, err)
|
||||
|
||||
// not ignore error, stop.
|
||||
if !r.IgnoreErr {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if r.AfterRun != nil && !r.AfterRun(r, task) {
|
||||
return false
|
||||
}
|
||||
|
||||
// store prev
|
||||
r.prev = task
|
||||
return true
|
||||
}
|
||||
|
||||
// Len of tasks
|
||||
func (r *Runner) Len() int {
|
||||
return len(r.tasks)
|
||||
}
|
||||
|
||||
// TaskIDs get
|
||||
func (r *Runner) TaskIDs() []string {
|
||||
ss := make([]string, 0, len(r.idMap))
|
||||
for id := range r.idMap {
|
||||
ss = append(ss, id)
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
// Prev task instance after running
|
||||
func (r *Runner) Prev() *Task {
|
||||
return r.prev
|
||||
}
|
||||
|
||||
// Task get by id name
|
||||
func (r *Runner) Task(id string) (*Task, error) {
|
||||
if idx, ok := r.idMap[id]; ok {
|
||||
return r.tasks[idx], nil
|
||||
}
|
||||
return nil, fmt.Errorf("task %q is not exists", id)
|
||||
}
|
||||
Reference in New Issue
Block a user