feat: udpate mise exec logs
This commit is contained in:
@@ -60,3 +60,19 @@ func (c *MiseController) Versions(ctx *gin.Context) {
|
||||
}
|
||||
utils.Success(ctx, versions)
|
||||
}
|
||||
|
||||
// VerifyCommand 获取验证命令
|
||||
func (c *MiseController) VerifyCommand(ctx *gin.Context) {
|
||||
plugin := ctx.Query("plugin")
|
||||
version := ctx.Query("version")
|
||||
if plugin == "" {
|
||||
utils.BadRequest(ctx, "参数 plugin 不能为空")
|
||||
return
|
||||
}
|
||||
cmd, err := c.service.GetVerifyCommand(plugin, version)
|
||||
if err != nil {
|
||||
utils.ServerError(ctx, "获取验证命令失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
utils.Success(ctx, gin.H{"command": cmd})
|
||||
}
|
||||
|
||||
@@ -76,6 +76,20 @@ func Execute(ctx context.Context, req Request, stdout, stderr io.Writer) (*Resul
|
||||
func ExecuteWithHooks(ctx context.Context, req Request, stdout, stderr io.Writer, hooks Hooks) (*Result, error) {
|
||||
start := time.Now()
|
||||
|
||||
// 2. 执行命令
|
||||
timeout := req.Timeout
|
||||
if timeout <= 0 {
|
||||
timeout = 30
|
||||
}
|
||||
execCtx, cancel := context.WithTimeout(ctx, time.Duration(timeout)*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// 如果指定使用 mise,则预先构建好带 mise 的命令,这样 PreExecute 记录的就是完整命令
|
||||
if req.UseMise {
|
||||
req.Command = utils.BuildMiseCommand(req.Command, req.Languages)
|
||||
req.UseMise = false
|
||||
}
|
||||
|
||||
// 1. 执行前钩子
|
||||
var logID uint
|
||||
if hooks != nil {
|
||||
@@ -92,19 +106,7 @@ func ExecuteWithHooks(ctx context.Context, req Request, stdout, stderr io.Writer
|
||||
logID = id
|
||||
}
|
||||
|
||||
// 2. 执行命令
|
||||
timeout := req.Timeout
|
||||
if timeout <= 0 {
|
||||
timeout = 30
|
||||
}
|
||||
execCtx, cancel := context.WithTimeout(ctx, time.Duration(timeout)*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
finalCommand := req.Command
|
||||
if req.UseMise {
|
||||
finalCommand = BuildLanguageCommand(req.Command, req.Languages)
|
||||
}
|
||||
shell, args := utils.GetShellCommand(finalCommand)
|
||||
shell, args := utils.GetShellCommand(req.Command)
|
||||
cmd := exec.CommandContext(execCtx, shell, args...)
|
||||
|
||||
// 设置工作目录
|
||||
@@ -280,31 +282,6 @@ func ExecuteWithHooks(ctx context.Context, req Request, stdout, stderr io.Writer
|
||||
return result, err
|
||||
}
|
||||
|
||||
// BuildLanguageCommand 构建语言环境执行命令 (使用 mise)
|
||||
func BuildLanguageCommand(command string, languages []map[string]string) string {
|
||||
if len(languages) == 0 {
|
||||
return command
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
builder.WriteString("mise exec")
|
||||
|
||||
for _, lang := range languages {
|
||||
name := lang["name"]
|
||||
version := lang["version"]
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if version == "" {
|
||||
version = "latest"
|
||||
}
|
||||
builder.WriteString(" " + name + "@" + version)
|
||||
}
|
||||
|
||||
builder.WriteString(" -- " + command)
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// ParseEnvVars 解析环境变量字符串 "KEY1=VALUE1,KEY2=VALUE2"
|
||||
func ParseEnvVars(envStr string) []string {
|
||||
if envStr == "" {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/engigu/baihu-panel/internal/constant"
|
||||
"github.com/engigu/baihu-panel/internal/utils"
|
||||
)
|
||||
|
||||
// safeBuffer 一个线程安全的字节缓冲区,用于合并 stdout 和 stderr
|
||||
@@ -313,11 +314,12 @@ func (s *Scheduler) executeTask(req *ExecutionRequest) (*ExecutionResult, error)
|
||||
|
||||
s.logger.Infof("[Scheduler] 执行任务 %s (名称: %s, 类型: %s)", req.TaskID, req.Name, req.Type)
|
||||
|
||||
finalCommand := req.Command
|
||||
// 如果指定使用 mise,则预先构建好带 mise 的命令,这样 OnTaskExecuting 记录的就是完整命令
|
||||
if req.UseMise {
|
||||
finalCommand = BuildLanguageCommand(req.Command, req.Languages)
|
||||
req.Command = utils.BuildMiseCommand(req.Command, req.Languages)
|
||||
req.UseMise = false
|
||||
}
|
||||
s.logger.Infof("[Scheduler] 实际执行命令: %s", finalCommand)
|
||||
s.logger.Infof("[Scheduler] 实际执行命令: %s", req.Command)
|
||||
|
||||
if s.config.Verbose {
|
||||
workDir := req.WorkDir
|
||||
|
||||
@@ -233,6 +233,7 @@ func Setup(c *Controllers) *gin.Engine {
|
||||
mise.POST("/sync", c.Mise.Sync)
|
||||
mise.GET("/plugins", c.Mise.Plugins)
|
||||
mise.GET("/versions", c.Mise.Versions)
|
||||
mise.GET("/verify-cmd", c.Mise.VerifyCommand)
|
||||
}
|
||||
|
||||
// Agent API(供前端调用,保持在 v1 下)
|
||||
|
||||
@@ -94,3 +94,12 @@ func (s *DependencyService) GetReinstallAllCommand(language, langVersion string)
|
||||
|
||||
return m.GetReinstallAllCommand(deps_list)
|
||||
}
|
||||
|
||||
// GetVerifyCommand 获取环境验证命令
|
||||
func (s *DependencyService) GetVerifyCommand(language, langVersion string) (string, error) {
|
||||
m := deps.GetManager(language)
|
||||
if m == nil {
|
||||
return "", errors.New("不支持的依赖类型: " + language)
|
||||
}
|
||||
return m.GetVerifyCommand(langVersion)
|
||||
}
|
||||
|
||||
@@ -12,12 +12,17 @@ type ElixirManager struct {
|
||||
}
|
||||
|
||||
func NewElixirManager(language string) *ElixirManager {
|
||||
verifyCmd := []string{"elixir", "-v"}
|
||||
if strings.Contains(strings.ToLower(language), "erlang") {
|
||||
verifyCmd = []string{"erl", "+V"}
|
||||
}
|
||||
return &ElixirManager{
|
||||
BaseManager: BaseManager{
|
||||
Language: language,
|
||||
InstallCmd: []string{"mix", "archive.install", "hex", "--force"},
|
||||
UninstallCmd: []string{"mix", "archive.uninstall"},
|
||||
ListCmd: []string{"mix", "archive"},
|
||||
VerifyCmd: verifyCmd,
|
||||
Separator: " ",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ func NewGoManager(language string) *GoManager {
|
||||
InstallCmd: []string{"go", "install"},
|
||||
UninstallCmd: []string{"go", "clean", "-i"},
|
||||
ListCmd: []string{"go", "list", "..."},
|
||||
VerifyCmd: []string{"go", "version"},
|
||||
Separator: "@",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ func NewLuaManager(language string) *LuaManager {
|
||||
InstallCmd: []string{"luarocks", "install"},
|
||||
UninstallCmd: []string{"luarocks", "remove"},
|
||||
ListCmd: []string{"luarocks", "list"},
|
||||
VerifyCmd: []string{"lua", "-v"},
|
||||
Separator: " ",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/engigu/baihu-panel/internal/logger"
|
||||
"github.com/engigu/baihu-panel/internal/models"
|
||||
"github.com/engigu/baihu-panel/internal/utils"
|
||||
)
|
||||
|
||||
// Manager 依赖管理器接口
|
||||
@@ -16,6 +17,7 @@ type Manager interface {
|
||||
GetInstalledPackages(language, langVersion string) ([]models.Dependency, error)
|
||||
GetInstallCommand(dep *models.Dependency) (string, error)
|
||||
GetReinstallAllCommand(deps []models.Dependency) (string, error)
|
||||
GetVerifyCommand(langVersion string) (string, error)
|
||||
}
|
||||
|
||||
// BaseManager 基础管理器,提供通用方法
|
||||
@@ -24,16 +26,12 @@ type BaseManager struct {
|
||||
InstallCmd []string
|
||||
UninstallCmd []string
|
||||
ListCmd []string
|
||||
VerifyCmd []string
|
||||
Separator string
|
||||
}
|
||||
|
||||
func (m *BaseManager) runMiseCommand(langVersion string, cmdArgs []string) ([]byte, error) {
|
||||
var args []string
|
||||
if langVersion != "" {
|
||||
args = []string{"mise", "exec", m.Language + "@" + langVersion, "--"}
|
||||
}
|
||||
args = append(args, cmdArgs...)
|
||||
|
||||
args := utils.BuildMiseCommandArgsSimple(cmdArgs, m.Language, langVersion)
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
return cmd.CombinedOutput()
|
||||
}
|
||||
@@ -69,14 +67,10 @@ func (m *BaseManager) GetInstallCommand(dep *models.Dependency) (string, error)
|
||||
packageSpec = dep.Name
|
||||
}
|
||||
|
||||
var args []string
|
||||
if dep.LangVersion != "" {
|
||||
args = []string{"mise", "exec", m.Language + "@" + dep.LangVersion, "--"}
|
||||
}
|
||||
args = append(args, m.InstallCmd...)
|
||||
args := append([]string{}, m.InstallCmd...)
|
||||
args = append(args, packageSpec)
|
||||
|
||||
fullCmd := strings.Join(args, " ")
|
||||
fullCmd := utils.BuildMiseCommandSimple(strings.Join(args, " "), m.Language, dep.LangVersion)
|
||||
return fullCmd + " && echo \"__INSTALL_SUCCESS__\" || echo \"__INSTALL_FAILED__\"", nil
|
||||
}
|
||||
|
||||
@@ -98,17 +92,23 @@ func (m *BaseManager) GetReinstallAllCommand(deps []models.Dependency) (string,
|
||||
}
|
||||
}
|
||||
|
||||
var args []string
|
||||
if langVersion != "" {
|
||||
args = []string{"mise", "exec", m.Language + "@" + langVersion, "--"}
|
||||
}
|
||||
args = append(args, m.InstallCmd...)
|
||||
args := append([]string{}, m.InstallCmd...)
|
||||
args = append(args, packageSpecs...)
|
||||
|
||||
fullCmd := strings.Join(args, " ")
|
||||
fullCmd := utils.BuildMiseCommandSimple(strings.Join(args, " "), m.Language, langVersion)
|
||||
return fullCmd + " && echo \"__INSTALL_SUCCESS__\" || echo \"__INSTALL_FAILED__\"", nil
|
||||
}
|
||||
|
||||
func (m *BaseManager) GetVerifyCommand(langVersion string) (string, error) {
|
||||
var cmd string
|
||||
if len(m.VerifyCmd) > 0 {
|
||||
cmd = strings.Join(m.VerifyCmd, " ")
|
||||
} else {
|
||||
cmd = m.Language + " --version"
|
||||
}
|
||||
return utils.BuildMiseCommandSimple(cmd, m.Language, langVersion), nil
|
||||
}
|
||||
|
||||
func (m *BaseManager) Uninstall(dep *models.Dependency) error {
|
||||
args := append([]string{}, m.UninstallCmd...)
|
||||
args = append(args, dep.Name)
|
||||
|
||||
@@ -18,6 +18,7 @@ func NewNodeManager(language string) *NodeManager {
|
||||
InstallCmd: []string{"npm", "install", "-g"},
|
||||
UninstallCmd: []string{"npm", "uninstall", "-g"},
|
||||
ListCmd: []string{"npm", "list", "-g", "--depth=0", "--json"},
|
||||
VerifyCmd: []string{"node", "-v"},
|
||||
Separator: "@",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ func NewPerlManager(language string) *PerlManager {
|
||||
InstallCmd: []string{"cpanm"}, // 需要系统预装 cpanm
|
||||
UninstallCmd: []string{"cpanm", "--uninstall"}, // 部分 cpanm 版本不支持,这是一个占位
|
||||
ListCmd: []string{"perldoc", "-l"}, // 很难列出所有,暂时简单处理
|
||||
VerifyCmd: []string{"perl", "-v"},
|
||||
Separator: " ",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ func NewPhpManager(language string) *PhpManager {
|
||||
InstallCmd: []string{"composer", "global", "require"},
|
||||
UninstallCmd: []string{"composer", "global", "remove"},
|
||||
ListCmd: []string{"composer", "global", "show", "--name-only"},
|
||||
VerifyCmd: []string{"php", "-v"},
|
||||
Separator: ":",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ func NewRubyManager(language string) *RubyManager {
|
||||
InstallCmd: []string{"gem", "install"},
|
||||
UninstallCmd: []string{"gem", "uninstall", "-a", "-x"},
|
||||
ListCmd: []string{"gem", "list", "--local"},
|
||||
VerifyCmd: []string{"ruby", "-v"},
|
||||
Separator: " ",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ func NewRustManager(language string) *RustManager {
|
||||
InstallCmd: []string{"cargo", "install"},
|
||||
UninstallCmd: []string{"cargo", "uninstall"},
|
||||
ListCmd: []string{"cargo", "install", "--list"},
|
||||
VerifyCmd: []string{"rustc", "--version"},
|
||||
Separator: " v",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"github.com/engigu/baihu-panel/internal/database"
|
||||
"github.com/engigu/baihu-panel/internal/logger"
|
||||
"github.com/engigu/baihu-panel/internal/models"
|
||||
"github.com/engigu/baihu-panel/internal/services/deps"
|
||||
"github.com/engigu/baihu-panel/internal/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -322,3 +324,14 @@ func (s *MiseService) syncToDB(languages []MiseLanguage) {
|
||||
db.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&models.Language{})
|
||||
}
|
||||
}
|
||||
|
||||
// GetVerifyCommand 获取环境验证命令
|
||||
func (s *MiseService) GetVerifyCommand(plugin, version string) (string, error) {
|
||||
m := deps.GetManager(plugin)
|
||||
if m == nil {
|
||||
// 如果没找到对应的包管理器(比如 java 等不支持依赖管理的),
|
||||
// 则提供一个基础的通用验证命令
|
||||
return utils.BuildMiseCommandSimple(plugin+" --version", plugin, version), nil
|
||||
}
|
||||
return m.GetVerifyCommand(version)
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func (h *ServerSchedulerHandler) OnTaskExecuting(req *executor.ExecutionRequest)
|
||||
}
|
||||
|
||||
// 1. 创建初始日志记录
|
||||
taskLog, err := h.es.taskLogService.CreateEmptyLog(task.ID, task.Command)
|
||||
taskLog, err := h.es.taskLogService.CreateEmptyLog(task.ID, req.Command)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("创建初始日志失败: %v", err)
|
||||
}
|
||||
@@ -357,7 +357,7 @@ func (es *ExecutorService) ExecuteDispatcher(ctx context.Context, req *executor.
|
||||
Envs: req.Envs,
|
||||
Timeout: req.Timeout,
|
||||
Languages: task.Languages,
|
||||
UseMise: true, // 本地业务任务启用 mise
|
||||
UseMise: req.UseMise, // 使用请求中的 UseMise 标志 (由调度器统一处理过)
|
||||
}, stdout, stderr, hooks)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BuildMiseCommand 构建多语言 mise 执行命令 (字符串形式)
|
||||
func BuildMiseCommand(command string, languages []map[string]string) string {
|
||||
if len(languages) == 0 {
|
||||
return command
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
builder.WriteString("mise exec")
|
||||
|
||||
for _, lang := range languages {
|
||||
name := lang["name"]
|
||||
version := lang["version"]
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if version == "" {
|
||||
version = "latest"
|
||||
}
|
||||
builder.WriteString(" " + name + "@" + version)
|
||||
}
|
||||
|
||||
builder.WriteString(" -- " + command)
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// BuildMiseCommandArgs 构建多语言 mise 执行命令 (参数列表形式)
|
||||
func BuildMiseCommandArgs(cmdArgs []string, languages []map[string]string) []string {
|
||||
if len(languages) == 0 {
|
||||
return cmdArgs
|
||||
}
|
||||
|
||||
args := []string{"mise", "exec"}
|
||||
for _, lang := range languages {
|
||||
name := lang["name"]
|
||||
version := lang["version"]
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if version == "" {
|
||||
version = "latest"
|
||||
}
|
||||
args = append(args, name+"@"+version)
|
||||
}
|
||||
args = append(args, "--")
|
||||
args = append(args, cmdArgs...)
|
||||
return args
|
||||
}
|
||||
|
||||
// BuildMiseCommandSimple 构建单个语言的 mise 执行命令
|
||||
func BuildMiseCommandSimple(command string, language, version string) string {
|
||||
if language == "" {
|
||||
return command
|
||||
}
|
||||
spec := language
|
||||
if version != "" {
|
||||
spec += "@" + version
|
||||
}
|
||||
return "mise exec " + spec + " -- " + command
|
||||
}
|
||||
|
||||
// BuildMiseCommandArgsSimple 构建单个语言的 mise 执行命令 (参数列表形式)
|
||||
func BuildMiseCommandArgsSimple(cmdArgs []string, language, version string) []string {
|
||||
if language == "" {
|
||||
return cmdArgs
|
||||
}
|
||||
spec := language
|
||||
if version != "" {
|
||||
spec += "@" + version
|
||||
}
|
||||
return append([]string{"mise", "exec", spec, "--"}, cmdArgs...)
|
||||
}
|
||||
Reference in New Issue
Block a user