mirror of
https://github.com/acepanel/helper.git
synced 2026-02-04 02:57:17 +08:00
feat: 支持命令输出记录
This commit is contained in:
@@ -8,10 +8,20 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
verbose := flag.Bool("v", false, "verbose mode")
|
||||
verbose := flag.String("v", "", "verbose mode, optionally specify log file path")
|
||||
flag.Parse()
|
||||
|
||||
config.Global.Verbose = *verbose
|
||||
if *verbose != "" {
|
||||
config.Global.Verbose = true
|
||||
// 如果不是 "true" 或 "1",则作为文件路径
|
||||
if *verbose != "true" && *verbose != "1" {
|
||||
config.Global.LogFile = *verbose
|
||||
if err := config.Global.InitLogFile(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer config.Global.CloseLogFile()
|
||||
}
|
||||
}
|
||||
|
||||
helper, err := initHelper()
|
||||
if err != nil {
|
||||
|
||||
@@ -19,8 +19,8 @@ type CommandResult struct {
|
||||
Stderr string
|
||||
}
|
||||
|
||||
// VerboseCallback verbose 模式回调
|
||||
type VerboseCallback func(cmd string)
|
||||
// VerboseCallback verbose 模式回调 (cmd, stdout, stderr, err)
|
||||
type VerboseCallback func(cmd, stdout, stderr string, err error)
|
||||
|
||||
// Executor 命令执行器接口
|
||||
type Executor interface {
|
||||
@@ -47,19 +47,40 @@ func (e *executor) SetVerboseCallback(cb VerboseCallback) {
|
||||
e.verboseCallback = cb
|
||||
}
|
||||
|
||||
func (e *executor) logVerbose(name string, args ...string) {
|
||||
if config.Global.Verbose && e.verboseCallback != nil {
|
||||
cmdStr := name
|
||||
if len(args) > 0 {
|
||||
cmdStr += " " + strings.Join(args, " ")
|
||||
func (e *executor) logVerbose(name string, args []string, result *CommandResult, err error) {
|
||||
cmdStr := name
|
||||
if len(args) > 0 {
|
||||
cmdStr += " " + strings.Join(args, " ")
|
||||
}
|
||||
|
||||
// 写入日志文件
|
||||
if config.Global.LogFile != "" {
|
||||
config.Global.WriteLog("$ %s", cmdStr)
|
||||
if result != nil {
|
||||
if result.Stdout != "" {
|
||||
config.Global.WriteLog("stdout: %s", strings.TrimSpace(result.Stdout))
|
||||
}
|
||||
if result.Stderr != "" {
|
||||
config.Global.WriteLog("stderr: %s", strings.TrimSpace(result.Stderr))
|
||||
}
|
||||
}
|
||||
e.verboseCallback(cmdStr)
|
||||
if err != nil {
|
||||
config.Global.WriteLog("error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 回调 UI
|
||||
if config.Global.Verbose && e.verboseCallback != nil {
|
||||
stdout, stderr := "", ""
|
||||
if result != nil {
|
||||
stdout = result.Stdout
|
||||
stderr = result.Stderr
|
||||
}
|
||||
e.verboseCallback(cmdStr, stdout, stderr, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *executor) Run(ctx context.Context, name string, args ...string) (*CommandResult, error) {
|
||||
e.logVerbose(name, args...)
|
||||
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
@@ -79,17 +100,15 @@ func (e *executor) Run(ctx context.Context, name string, args ...string) (*Comma
|
||||
// 包含 stderr 信息到错误中
|
||||
stderrStr := strings.TrimSpace(stderr.String())
|
||||
if stderrStr != "" {
|
||||
return result, fmt.Errorf("%w: %s", err, stderrStr)
|
||||
err = fmt.Errorf("%w: %s", err, stderrStr)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
e.logVerbose(name, args, result, err)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (e *executor) RunWithInput(ctx context.Context, input string, name string, args ...string) (*CommandResult, error) {
|
||||
e.logVerbose(name, args...)
|
||||
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
cmd.Stdin = bytes.NewBufferString(input)
|
||||
var stdout, stderr bytes.Buffer
|
||||
@@ -110,19 +129,31 @@ func (e *executor) RunWithInput(ctx context.Context, input string, name string,
|
||||
// 包含 stderr 信息到错误中
|
||||
stderrStr := strings.TrimSpace(stderr.String())
|
||||
if stderrStr != "" {
|
||||
return result, fmt.Errorf("%w: %s", err, stderrStr)
|
||||
err = fmt.Errorf("%w: %s", err, stderrStr)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
e.logVerbose(name, args, result, err)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (e *executor) RunStream(ctx context.Context, stdout, stderr io.Writer, name string, args ...string) error {
|
||||
e.logVerbose(name, args...)
|
||||
|
||||
func (e *executor) RunStream(ctx context.Context, stdoutW, stderrW io.Writer, name string, args ...string) error {
|
||||
var stdoutBuf, stderrBuf bytes.Buffer
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
return cmd.Run()
|
||||
cmd.Stdout = io.MultiWriter(stdoutW, &stdoutBuf)
|
||||
cmd.Stderr = io.MultiWriter(stderrW, &stderrBuf)
|
||||
|
||||
err := cmd.Run()
|
||||
result := &CommandResult{
|
||||
Stdout: stdoutBuf.String(),
|
||||
Stderr: stderrBuf.String(),
|
||||
}
|
||||
if err != nil {
|
||||
var exitErr *exec.ExitError
|
||||
if errors.As(err, &exitErr) {
|
||||
result.ExitCode = exitErr.ExitCode()
|
||||
}
|
||||
}
|
||||
e.logVerbose(name, args, result, err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -417,9 +417,19 @@ func (a *App) startInstall() tea.Cmd {
|
||||
|
||||
// 设置 verbose 回调
|
||||
if config.Global.Verbose {
|
||||
a.installer.SetVerboseCallback(func(cmd string) {
|
||||
a.installer.SetVerboseCallback(func(cmd, stdout, stderr string, err error) {
|
||||
if a.program != nil {
|
||||
a.program.Send(verboseMsg("$ " + cmd))
|
||||
msg := "$ " + cmd
|
||||
if stdout != "" {
|
||||
msg += "\n" + strings.TrimSpace(stdout)
|
||||
}
|
||||
if stderr != "" {
|
||||
msg += "\n[stderr] " + strings.TrimSpace(stderr)
|
||||
}
|
||||
if err != nil {
|
||||
msg += "\n[error] " + err.Error()
|
||||
}
|
||||
a.program.Send(verboseMsg(msg))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -611,9 +621,19 @@ func (a *App) startUninstall() tea.Cmd {
|
||||
|
||||
// 设置 verbose 回调
|
||||
if config.Global.Verbose {
|
||||
a.uninstaller.SetVerboseCallback(func(cmd string) {
|
||||
a.uninstaller.SetVerboseCallback(func(cmd, stdout, stderr string, err error) {
|
||||
if a.program != nil {
|
||||
a.program.Send(verboseMsg("$ " + cmd))
|
||||
msg := "$ " + cmd
|
||||
if stdout != "" {
|
||||
msg += "\n" + strings.TrimSpace(stdout)
|
||||
}
|
||||
if stderr != "" {
|
||||
msg += "\n[stderr] " + strings.TrimSpace(stderr)
|
||||
}
|
||||
if err != nil {
|
||||
msg += "\n[error] " + err.Error()
|
||||
}
|
||||
a.program.Send(verboseMsg(msg))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -900,9 +920,19 @@ func (a *App) startMount() tea.Cmd {
|
||||
|
||||
// 设置 verbose 回调
|
||||
if config.Global.Verbose {
|
||||
a.mounter.SetVerboseCallback(func(cmd string) {
|
||||
a.mounter.SetVerboseCallback(func(cmd, stdout, stderr string, err error) {
|
||||
if a.program != nil {
|
||||
a.program.Send(verboseMsg("$ " + cmd))
|
||||
msg := "$ " + cmd
|
||||
if stdout != "" {
|
||||
msg += "\n" + strings.TrimSpace(stdout)
|
||||
}
|
||||
if stderr != "" {
|
||||
msg += "\n[stderr] " + strings.TrimSpace(stderr)
|
||||
}
|
||||
if err != nil {
|
||||
msg += "\n[error] " + err.Error()
|
||||
}
|
||||
a.program.Send(verboseMsg(msg))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,51 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config 全局配置
|
||||
type Config struct {
|
||||
Verbose bool
|
||||
Verbose bool
|
||||
LogFile string
|
||||
logWriter *os.File
|
||||
logMu sync.Mutex
|
||||
}
|
||||
|
||||
// Global 全局配置实例
|
||||
var Global = &Config{}
|
||||
|
||||
// InitLogFile 初始化日志文件
|
||||
func (c *Config) InitLogFile() error {
|
||||
if c.LogFile == "" {
|
||||
return nil
|
||||
}
|
||||
f, err := os.OpenFile(c.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.logWriter = f
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseLogFile 关闭日志文件
|
||||
func (c *Config) CloseLogFile() {
|
||||
if c.logWriter != nil {
|
||||
c.logWriter.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// WriteLog 写日志
|
||||
func (c *Config) WriteLog(format string, args ...interface{}) {
|
||||
if c.logWriter == nil {
|
||||
return
|
||||
}
|
||||
c.logMu.Lock()
|
||||
defer c.logMu.Unlock()
|
||||
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
fmt.Fprintf(c.logWriter, "[%s] %s\n", timestamp, msg)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user