Initial QSfera import
This commit is contained in:
+490
@@ -0,0 +1,490 @@
|
||||
package cmdr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gookit/goutil"
|
||||
"github.com/gookit/goutil/arrutil"
|
||||
"github.com/gookit/goutil/cliutil/cmdline"
|
||||
"github.com/gookit/goutil/internal/comfunc"
|
||||
)
|
||||
|
||||
// Cmd struct
|
||||
type Cmd struct {
|
||||
*exec.Cmd
|
||||
// Name of the command
|
||||
Name string
|
||||
// DryRun setting. if True, not really execute command
|
||||
DryRun bool
|
||||
// Vars mapping TODO
|
||||
Vars map[string]string
|
||||
// PrintLine placeholder for print command cline.
|
||||
PrintLine string
|
||||
|
||||
// BeforeRun hook
|
||||
BeforeRun func(c *Cmd)
|
||||
// AfterRun hook
|
||||
AfterRun func(c *Cmd, err error)
|
||||
}
|
||||
|
||||
// NewGitCmd instance
|
||||
func NewGitCmd(subCmd string, args ...string) *Cmd {
|
||||
return NewCmd("git", subCmd).AddArgs(args)
|
||||
}
|
||||
|
||||
// NewCmdline instance
|
||||
//
|
||||
// see exec.Command
|
||||
func NewCmdline(line string) *Cmd {
|
||||
bin, args := cmdline.NewParser(line).WithParseEnv().BinAndArgs()
|
||||
return NewCmd(bin, args...)
|
||||
}
|
||||
|
||||
// NewCmd instance
|
||||
//
|
||||
// see exec.Command
|
||||
func NewCmd(bin string, args ...string) *Cmd {
|
||||
return WrapGoCmd(exec.Command(bin, args...))
|
||||
}
|
||||
|
||||
// CmdWithCtx create new instance with context.
|
||||
//
|
||||
// see exec.CommandContext
|
||||
func CmdWithCtx(ctx context.Context, bin string, args ...string) *Cmd {
|
||||
return WrapGoCmd(exec.CommandContext(ctx, bin, args...))
|
||||
}
|
||||
|
||||
// WrapGoCmd instance
|
||||
func WrapGoCmd(cmd *exec.Cmd) *Cmd {
|
||||
return &Cmd{
|
||||
Cmd: cmd,
|
||||
Vars: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
// config the command
|
||||
// -------------------------------------------------
|
||||
|
||||
// Config the command
|
||||
func (c *Cmd) Config(fn func(c *Cmd)) *Cmd {
|
||||
fn(c)
|
||||
return c
|
||||
}
|
||||
|
||||
// WithDryRun on exec command
|
||||
func (c *Cmd) WithDryRun(dryRun bool) *Cmd {
|
||||
c.DryRun = dryRun
|
||||
return c
|
||||
}
|
||||
|
||||
// PrintCmdline on exec command
|
||||
func (c *Cmd) PrintCmdline() *Cmd {
|
||||
c.BeforeRun = PrintCmdline
|
||||
return c
|
||||
}
|
||||
|
||||
// PrintCmdline2 on exec command
|
||||
func (c *Cmd) PrintCmdline2() *Cmd {
|
||||
c.BeforeRun = PrintCmdline2
|
||||
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("cmdr: look %q path error: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// WorkDirOnNE set workdir on input is not empty
|
||||
func (c *Cmd) WorkDirOnNE(dir string) *Cmd {
|
||||
if dir != "" {
|
||||
c.Dir = dir
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// WithEnvMap override set new ENV for run
|
||||
func (c *Cmd) WithEnvMap(mp map[string]string) *Cmd {
|
||||
if ln := len(mp); ln > 0 {
|
||||
c.Env = make([]string, 0, ln)
|
||||
for key, val := range mp {
|
||||
c.Env = append(c.Env, key+"="+val)
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// AppendEnv to the os ENV for run command
|
||||
func (c *Cmd) AppendEnv(mp map[string]string) *Cmd {
|
||||
if len(mp) > 0 {
|
||||
// init env data
|
||||
if c.Env == nil {
|
||||
c.Env = os.Environ()
|
||||
}
|
||||
|
||||
for name, val := range mp {
|
||||
c.Env = append(c.Env, name+"="+val)
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// OutputToOS output to OS stdout and error
|
||||
func (c *Cmd) OutputToOS() *Cmd { return c.ToOSStdoutStderr() }
|
||||
|
||||
// ToOSStdoutStderr output to OS stdout and error
|
||||
func (c *Cmd) ToOSStdoutStderr() *Cmd {
|
||||
c.Stdout = os.Stdout
|
||||
c.Stderr = os.Stderr
|
||||
return c
|
||||
}
|
||||
|
||||
// ToOSStdout output to OS stdout
|
||||
func (c *Cmd) ToOSStdout() *Cmd {
|
||||
c.Stdout = os.Stdout
|
||||
c.Stderr = os.Stdout
|
||||
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 return the current object
|
||||
func (c *Cmd) AddArg(args ...string) *Cmd { return c.WithArg(args...) }
|
||||
|
||||
// WithArg add args and return 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 return 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 return 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 return 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 return 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 return 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
|
||||
}
|
||||
|
||||
// WithVars add vars and return the current object
|
||||
func (c *Cmd) WithVars(vs map[string]string) *Cmd {
|
||||
if len(vs) > 0 {
|
||||
c.Vars = vs
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// SetVar add var and return the current object
|
||||
func (c *Cmd) SetVar(name, val string) *Cmd {
|
||||
c.Vars[name] = val
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
// Workdir of the command
|
||||
func (c *Cmd) Workdir() string { return c.Dir }
|
||||
|
||||
// Cmdline to command line
|
||||
func (c *Cmd) Cmdline() string { return comfunc.Cmdline(c.Args) }
|
||||
|
||||
// RawLine raw command line for print show.
|
||||
func (c *Cmd) RawLine() string {
|
||||
if c.PrintLine != "" {
|
||||
return c.PrintLine
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// HasStdout output setting.
|
||||
func (c *Cmd) HasStdout() bool {
|
||||
return c.Stdout != 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)
|
||||
}
|
||||
|
||||
if c.DryRun {
|
||||
return "DRY-RUN: ok", nil
|
||||
}
|
||||
|
||||
bs, err := c.Cmd.Output()
|
||||
|
||||
if c.AfterRun != nil {
|
||||
c.AfterRun(c, err)
|
||||
}
|
||||
return string(bs), err
|
||||
}
|
||||
|
||||
// AllOutput run and return output, will combine stderr and stdout output
|
||||
func (c *Cmd) AllOutput() (string, error) {
|
||||
return c.CombinedOutput()
|
||||
}
|
||||
|
||||
// CombinedOutput run and return output, will combine stderr and stdout output
|
||||
func (c *Cmd) CombinedOutput() (string, error) {
|
||||
if c.BeforeRun != nil {
|
||||
c.BeforeRun(c)
|
||||
}
|
||||
|
||||
if c.DryRun {
|
||||
return "DRY-RUN: ok", nil
|
||||
}
|
||||
|
||||
bs, err := c.Cmd.CombinedOutput()
|
||||
if c.AfterRun != nil {
|
||||
c.AfterRun(c, err)
|
||||
}
|
||||
return string(bs), 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 {
|
||||
return c.ToOSStdoutStderr().Run()
|
||||
}
|
||||
|
||||
// Run runs command
|
||||
func (c *Cmd) Run() error {
|
||||
if c.BeforeRun != nil {
|
||||
c.BeforeRun(c)
|
||||
}
|
||||
|
||||
if c.DryRun {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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())
|
||||
// }
|
||||
Reference in New Issue
Block a user