feat: complete mise env base code

This commit is contained in:
engigu
2026-02-13 17:27:55 +08:00
parent 800ac40200
commit c1c1d69e66
49 changed files with 2724 additions and 470 deletions
+45
View File
@@ -0,0 +1,45 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type BunManager struct {
BaseManager
}
func NewBunManager(language string) *BunManager {
return &BunManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"bun", "add", "-g"},
UninstallCmd: []string{"bun", "remove", "-g"},
ListCmd: []string{"bun", "pm", "ls", "-g"},
Separator: "@",
},
}
}
func (m *BunManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "Tool") {
continue
}
fields := strings.Fields(line)
if len(fields) > 0 {
packages = append(packages, models.Dependency{Name: fields[0], Language: language})
}
}
return packages, nil
}
+45
View File
@@ -0,0 +1,45 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type CrystalManager struct {
BaseManager
}
func NewCrystalManager(language string) *CrystalManager {
return &CrystalManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"shards", "install"}, // 主要是针对当前目录,但 mise exec 下可以安装 tools
UninstallCmd: []string{"rm", "-rf"}, // 这里的逻辑可能不完全正确
ListCmd: []string{"shards", "list"},
Separator: " ",
},
}
}
func (m *CrystalManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "Shards") {
continue
}
fields := strings.Fields(line)
if len(fields) > 1 {
packages = append(packages, models.Dependency{Name: fields[1], Language: language})
}
}
return packages, nil
}
+45
View File
@@ -0,0 +1,45 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type DartManager struct {
BaseManager
}
func NewDartManager(language string) *DartManager {
return &DartManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"dart", "pub", "global", "activate"},
UninstallCmd: []string{"dart", "pub", "global", "deactivate"},
ListCmd: []string{"dart", "pub", "global", "list"},
Separator: " ",
},
}
}
func (m *DartManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
fields := strings.Fields(line)
if len(fields) > 0 {
packages = append(packages, models.Dependency{Name: fields[0], Language: language})
}
}
return packages, nil
}
+26
View File
@@ -0,0 +1,26 @@
package deps
import (
"github.com/engigu/baihu-panel/internal/models"
)
type DenoManager struct {
BaseManager
}
func NewDenoManager(language string) *DenoManager {
return &DenoManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"deno", "install", "--global", "-A"},
UninstallCmd: []string{"deno", "uninstall"},
ListCmd: []string{"ls", "-1", "/root/.deno/bin"}, // 这是一个简单的 fallback 尝试
Separator: "@",
},
}
}
func (m *DenoManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
// Deno 没有官方的 list 命令,暂时留空或通过文件系统猜测
return []models.Dependency{}, nil
}
+45
View File
@@ -0,0 +1,45 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type DotnetManager struct {
BaseManager
}
func NewDotnetManager(language string) *DotnetManager {
return &DotnetManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"dotnet", "tool", "install", "-g"},
UninstallCmd: []string{"dotnet", "tool", "uninstall", "-g"},
ListCmd: []string{"dotnet", "tool", "list", "-g"},
Separator: " ",
},
}
}
func (m *DotnetManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for i, line := range lines {
line = strings.TrimSpace(line)
if i < 2 || line == "" { // 跳过表头
continue
}
fields := strings.Fields(line)
if len(fields) > 0 {
packages = append(packages, models.Dependency{Name: fields[0], Language: language, Version: fields[1]})
}
}
return packages, nil
}
+45
View File
@@ -0,0 +1,45 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type ElixirManager struct {
BaseManager
}
func NewElixirManager(language string) *ElixirManager {
return &ElixirManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"mix", "archive.install", "hex", "--force"},
UninstallCmd: []string{"mix", "archive.uninstall"},
ListCmd: []string{"mix", "archive"},
Separator: " ",
},
}
}
func (m *ElixirManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "*") {
continue
}
fields := strings.Fields(line)
if len(fields) > 0 {
packages = append(packages, models.Dependency{Name: fields[0], Language: language})
}
}
return packages, nil
}
+45
View File
@@ -0,0 +1,45 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type GoManager struct {
BaseManager
}
func NewGoManager(language string) *GoManager {
return &GoManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"go", "install"},
UninstallCmd: []string{"go", "clean", "-i"},
ListCmd: []string{"go", "list", "..."},
Separator: "@",
},
}
}
func (m *GoManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "Tool") {
continue
}
fields := strings.Fields(line)
if len(fields) > 0 {
packages = append(packages, models.Dependency{Name: fields[0], Language: language})
}
}
return packages, nil
}
+45
View File
@@ -0,0 +1,45 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type LuaManager struct {
BaseManager
}
func NewLuaManager(language string) *LuaManager {
return &LuaManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"luarocks", "install"},
UninstallCmd: []string{"luarocks", "remove"},
ListCmd: []string{"luarocks", "list"},
Separator: " ",
},
}
}
func (m *LuaManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "Rock") || strings.HasPrefix(line, "--") {
continue
}
fields := strings.Fields(line)
if len(fields) > 0 {
packages = append(packages, models.Dependency{Name: fields[0], Language: language})
}
}
return packages, nil
}
+174
View File
@@ -0,0 +1,174 @@
package deps
import (
"errors"
"os/exec"
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
// Manager 依赖管理器接口
type Manager interface {
Install(dep *models.Dependency) error
Uninstall(dep *models.Dependency) error
GetInstalledPackages(language, langVersion string) ([]models.Dependency, error)
GetInstallCommand(dep *models.Dependency) (string, error)
GetReinstallAllCommand(deps []models.Dependency) (string, error)
}
// BaseManager 基础管理器,提供通用方法
type BaseManager struct {
Language string
InstallCmd []string
UninstallCmd []string
ListCmd []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...)
cmd := exec.Command(args[0], args[1:]...)
return cmd.CombinedOutput()
}
func (m *BaseManager) Install(dep *models.Dependency) error {
var packageSpec string
if dep.Version != "" {
packageSpec = dep.Name + m.Separator + dep.Version
} else {
packageSpec = dep.Name
}
args := append([]string{}, m.InstallCmd...)
args = append(args, packageSpec)
logger.Infof("Installing %s package: %s", m.Language, packageSpec)
output, err := m.runMiseCommand(dep.LangVersion, args)
dep.Log = string(output)
if err != nil {
logger.Errorf("Install failed: %v, output: %s", err, string(output))
return errors.New("安装失败: " + string(output))
}
logger.Infof("Install success: %s", packageSpec)
return nil
}
func (m *BaseManager) GetInstallCommand(dep *models.Dependency) (string, error) {
var packageSpec string
if dep.Version != "" {
packageSpec = dep.Name + m.Separator + dep.Version
} else {
packageSpec = dep.Name
}
var args []string
if dep.LangVersion != "" {
args = []string{"mise", "exec", m.Language + "@" + dep.LangVersion, "--"}
}
args = append(args, m.InstallCmd...)
args = append(args, packageSpec)
fullCmd := strings.Join(args, " ")
return fullCmd + " && echo \"__INSTALL_SUCCESS__\" || echo \"__INSTALL_FAILED__\"", nil
}
func (m *BaseManager) GetReinstallAllCommand(deps []models.Dependency) (string, error) {
if len(deps) == 0 {
return "echo \"没有需要安装的依赖\"", nil
}
var packageSpecs []string
var langVersion string
for _, dep := range deps {
if dep.LangVersion != "" {
langVersion = dep.LangVersion
}
if dep.Version != "" {
packageSpecs = append(packageSpecs, dep.Name+m.Separator+dep.Version)
} else {
packageSpecs = append(packageSpecs, dep.Name)
}
}
var args []string
if langVersion != "" {
args = []string{"mise", "exec", m.Language + "@" + langVersion, "--"}
}
args = append(args, m.InstallCmd...)
args = append(args, packageSpecs...)
fullCmd := strings.Join(args, " ")
return fullCmd + " && echo \"__INSTALL_SUCCESS__\" || echo \"__INSTALL_FAILED__\"", nil
}
func (m *BaseManager) Uninstall(dep *models.Dependency) error {
args := append([]string{}, m.UninstallCmd...)
args = append(args, dep.Name)
logger.Infof("Uninstalling %s package: %s", m.Language, dep.Name)
output, err := m.runMiseCommand(dep.LangVersion, args)
if err != nil {
logger.Errorf("Uninstall failed: %v, output: %s", err, string(output))
return errors.New("卸载失败: " + string(output))
}
return nil
}
// GetManager 根据语言获取对应的管理器
func GetManager(language string) Manager {
lang := strings.ToLower(language)
if strings.Contains(lang, "python") {
return NewPythonManager(language)
}
if strings.Contains(lang, "node") {
return NewNodeManager(language)
}
if strings.Contains(lang, "ruby") {
return NewRubyManager(language)
}
if strings.Contains(lang, "go") {
return NewGoManager(language)
}
if strings.Contains(lang, "rust") {
return NewRustManager(language)
}
if strings.Contains(lang, "bun") {
return NewBunManager(language)
}
if strings.Contains(lang, "php") {
return NewPhpManager(language)
}
if strings.Contains(lang, "deno") {
return NewDenoManager(language)
}
if strings.Contains(lang, "dotnet") {
return NewDotnetManager(language)
}
if strings.Contains(lang, "elixir") || strings.Contains(lang, "erlang") {
return NewElixirManager(language)
}
if strings.Contains(lang, "lua") {
return NewLuaManager(language)
}
if strings.Contains(lang, "nim") {
return NewNimManager(language)
}
if strings.Contains(lang, "dart") || strings.Contains(lang, "flutter") {
return NewDartManager(language)
}
if strings.Contains(lang, "perl") {
return NewPerlManager(language)
}
if strings.Contains(lang, "crystal") {
return NewCrystalManager(language)
}
return nil
}
+45
View File
@@ -0,0 +1,45 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type NimManager struct {
BaseManager
}
func NewNimManager(language string) *NimManager {
return &NimManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"nimble", "install", "-y"},
UninstallCmd: []string{"nimble", "uninstall", "-y"},
ListCmd: []string{"nimble", "list", "-i"},
Separator: "@",
},
}
}
func (m *NimManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || !strings.Contains(line, "[") {
continue
}
fields := strings.Fields(line)
if len(fields) > 0 {
packages = append(packages, models.Dependency{Name: fields[0], Language: language})
}
}
return packages, nil
}
+47
View File
@@ -0,0 +1,47 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type NodeManager struct {
BaseManager
}
func NewNodeManager(language string) *NodeManager {
return &NodeManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"npm", "install", "-g"},
UninstallCmd: []string{"npm", "uninstall", "-g"},
ListCmd: []string{"npm", "list", "-g", "--depth=0", "--json"},
Separator: "@",
},
}
}
func (m *NodeManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.Contains(line, `"version"`) || strings.HasPrefix(line, "{") || strings.HasPrefix(line, "}") || strings.HasPrefix(line, "]") {
continue
}
if strings.HasPrefix(line, `"`) && strings.Contains(line, ":") {
name := strings.Trim(strings.Split(line, ":")[0], `" `)
if name != "" && name != "dependencies" && name != "name" {
packages = append(packages, models.Dependency{Name: name, Language: language})
}
}
}
return packages, nil
}
+26
View File
@@ -0,0 +1,26 @@
package deps
import (
"github.com/engigu/baihu-panel/internal/models"
)
type PerlManager struct {
BaseManager
}
func NewPerlManager(language string) *PerlManager {
return &PerlManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"cpanm"}, // 需要系统预装 cpanm
UninstallCmd: []string{"cpanm", "--uninstall"}, // 部分 cpanm 版本不支持,这是一个占位
ListCmd: []string{"perldoc", "-l"}, // 很难列出所有,暂时简单处理
Separator: " ",
},
}
}
func (m *PerlManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
// Perl 依赖列出比较复杂,暂时返回空
return []models.Dependency{}, nil
}
+45
View File
@@ -0,0 +1,45 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type PhpManager struct {
BaseManager
}
func NewPhpManager(language string) *PhpManager {
return &PhpManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"composer", "global", "require"},
UninstallCmd: []string{"composer", "global", "remove"},
ListCmd: []string{"composer", "global", "show", "--name-only"},
Separator: ":",
},
}
}
func (m *PhpManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "Tool") {
continue
}
fields := strings.Fields(line)
if len(fields) > 0 {
packages = append(packages, models.Dependency{Name: fields[0], Language: language})
}
}
return packages, nil
}
+47
View File
@@ -0,0 +1,47 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type PythonManager struct {
BaseManager
}
func NewPythonManager(language string) *PythonManager {
return &PythonManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"pip", "install"},
UninstallCmd: []string{"pip", "uninstall", "-y"},
ListCmd: []string{"pip", "list", "--format=freeze"},
Separator: "==",
},
}
}
func (m *PythonManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
parts := strings.SplitN(line, "==", 2)
pkg := models.Dependency{Name: parts[0], Language: language}
if len(parts) > 1 {
pkg.Version = parts[1]
}
packages = append(packages, pkg)
}
return packages, nil
}
+45
View File
@@ -0,0 +1,45 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type RubyManager struct {
BaseManager
}
func NewRubyManager(language string) *RubyManager {
return &RubyManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"gem", "install"},
UninstallCmd: []string{"gem", "uninstall", "-a", "-x"},
ListCmd: []string{"gem", "list", "--local"},
Separator: " ",
},
}
}
func (m *RubyManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "Tool") || strings.HasPrefix(line, "(") {
continue
}
fields := strings.Fields(line)
if len(fields) > 0 {
packages = append(packages, models.Dependency{Name: fields[0], Language: language})
}
}
return packages, nil
}
+45
View File
@@ -0,0 +1,45 @@
package deps
import (
"strings"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
)
type RustManager struct {
BaseManager
}
func NewRustManager(language string) *RustManager {
return &RustManager{
BaseManager: BaseManager{
Language: language,
InstallCmd: []string{"cargo", "install"},
UninstallCmd: []string{"cargo", "uninstall"},
ListCmd: []string{"cargo", "install", "--list"},
Separator: " v",
},
}
}
func (m *RustManager) GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) {
output, err := m.runMiseCommand(langVersion, m.ListCmd)
if err != nil {
logger.Warnf("GetInstalledPackages for %s failed: %v", language, err)
}
var packages []models.Dependency
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || !strings.Contains(line, " v") {
continue
}
fields := strings.Fields(line)
if len(fields) > 0 {
packages = append(packages, models.Dependency{Name: fields[0], Language: language})
}
}
return packages, nil
}