2
0
mirror of https://github.com/acepanel/helper.git synced 2026-02-04 02:57:17 +08:00

feat: 支持命令输出记录

This commit is contained in:
2026-01-26 16:54:05 +08:00
parent cd6464972e
commit e51693b2cc
4 changed files with 146 additions and 33 deletions

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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))
}
})
}

View File

@@ -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)
}