From 2e4164d9ed9d49e95021a3095b3e1a5f776b30ed Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Thu, 5 Mar 2026 14:34:57 +0800 Subject: [PATCH 01/10] fix: rename variable for clarity in OsArch function --- mageutil/sys.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mageutil/sys.go b/mageutil/sys.go index 3a958a7..0d9da49 100644 --- a/mageutil/sys.go +++ b/mageutil/sys.go @@ -12,12 +12,12 @@ import ( ) func OsArch() string { - os := runtime.GOOS + goos := runtime.GOOS arch := runtime.GOARCH - if os == "windows" { - return fmt.Sprintf("%s\\%s", os, arch) + if goos == "windows" { + return fmt.Sprintf("%s\\%s", goos, arch) } - return fmt.Sprintf("%s/%s", os, arch) + return fmt.Sprintf("%s/%s", goos, arch) } // CheckProcessNames checks if the number of processes running that match the specified path equals the expected count. From a6785a532205444e7c9156e1c5bd5b686eba3ef9 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Thu, 5 Mar 2026 17:10:01 +0800 Subject: [PATCH 02/10] refactor: replace fmt.Printf with custom Print functions for better output formatting --- cmd/microservice-test/main.go | 15 ++++++++++----- mageutil/build.go | 8 ++++---- mageutil/define.go | 5 ++--- mageutil/gen_protocol.go | 26 +++++++++++++------------- mageutil/process_controller.go | 6 +++--- mageutil/sys.go | 34 +++++++++++++++++----------------- tools/helloworld/main.go | 4 +++- 7 files changed, 52 insertions(+), 46 deletions(-) diff --git a/cmd/microservice-test/main.go b/cmd/microservice-test/main.go index e05471d..e29686a 100644 --- a/cmd/microservice-test/main.go +++ b/cmd/microservice-test/main.go @@ -3,12 +3,13 @@ package main import ( "flag" "fmt" - "log" "math/rand" "net" "net/http" "os" "time" + + "github.com/openimsdk/gomake/mageutil" ) func main() { @@ -17,7 +18,7 @@ func main() { // Parse the flags flag.Parse() - fmt.Printf("This is a microservice-test. Program: %s, args: -i %d -c %s\n", os.Args[0], *index, *config) + mageutil.PrintBlue(fmt.Sprintf("This is a microservice-test. Program: %s, args: -i %d -c %s", os.Args[0], *index, *config)) // Generate a random port rand.Seed(time.Now().UnixNano()) @@ -25,16 +26,20 @@ func main() { listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) if err != nil { - log.Fatalf("Failed to listen on port %d: %v", port, err) + mageutil.PrintRed(fmt.Sprintf("Failed to listen on port %d: %v", port, err)) + os.Exit(1) } defer listener.Close() - fmt.Printf("Listening on port %d\n", port) + mageutil.PrintGreen(fmt.Sprintf("Listening on port %d", port)) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, you've hit %s\n", r.URL.Path) }) // Start serving, using the listener we created - log.Fatal(http.Serve(listener, nil)) + if err := http.Serve(listener, nil); err != nil { + mageutil.PrintRed(fmt.Sprintf("HTTP server exited: %v", err)) + os.Exit(1) + } } diff --git a/mageutil/build.go b/mageutil/build.go index d3831f1..53a4545 100644 --- a/mageutil/build.go +++ b/mageutil/build.go @@ -98,10 +98,10 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, if os.IsNotExist(err) { return nil } - fmt.Printf("Failed read directory %s: %v\n", sourceDir, err) + PrintRed(fmt.Sprintf("Failed read directory %s: %v", sourceDir, err)) os.Exit(1) } else if !info.IsDir() { - fmt.Printf("Failed %s is not dir\n", sourceDir) + PrintRed(fmt.Sprintf("Failed %s is not dir", sourceDir)) os.Exit(1) } @@ -109,7 +109,7 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, outputDir := filepath.Join(outputBase, targetOS, targetArch) if err := os.MkdirAll(outputDir, 0755); err != nil { - fmt.Printf("Failed to create directory %s: %v\n", outputDir, err) + PrintRed(fmt.Sprintf("Failed to create directory %s: %v", outputDir, err)) os.Exit(1) } @@ -387,7 +387,7 @@ func resolveRequestedBinaries(binaries []string) []string { } PrintYellow(fmt.Sprintf("Binary %s not found in cmd (%s) or tools (%s) directories. Skipping...", binary, Paths.SrcDir, Paths.ToolsDir)) } - fmt.Println("Resolved binaries:", resolved) + PrintBlue(fmt.Sprintf("Resolved binaries: %v", resolved)) return resolved } diff --git a/mageutil/define.go b/mageutil/define.go index a839241..b8594c3 100644 --- a/mageutil/define.go +++ b/mageutil/define.go @@ -1,7 +1,6 @@ package mageutil import ( - "fmt" "os" "runtime" @@ -27,14 +26,14 @@ type Config struct { func InitForSSC() { yamlFile, err := os.ReadFile(StartConfigFile) if err != nil { - fmt.Printf("error reading YAML file: %v", err) + PrintRed("error reading YAML file: " + err.Error()) os.Exit(1) } var config Config err = yaml.Unmarshal(yamlFile, &config) if err != nil { - fmt.Printf("error unmarshalling YAML: %v", err) + PrintRed("error unmarshalling YAML: " + err.Error()) os.Exit(1) } diff --git a/mageutil/gen_protocol.go b/mageutil/gen_protocol.go index 51482aa..f450b76 100644 --- a/mageutil/gen_protocol.go +++ b/mageutil/gen_protocol.go @@ -31,21 +31,21 @@ func ensureToolsInstalled() error { for tool, path := range tools { if _, err := exec.LookPath(filepath.Join(targetDir, tool)); err != nil { - fmt.Printf("Installing %s to %s...\n", tool, targetDir) + PrintBlue(fmt.Sprintf("Installing %s to %s...", tool, targetDir)) if err := sh.Run("go", "install", path); err != nil { return fmt.Errorf("failed to install %s: %s", tool, err) } } else { - fmt.Printf("%s is already installed in %s.\n", tool, targetDir) + PrintGreen(fmt.Sprintf("%s is already installed in %s.", tool, targetDir)) } } if _, err := exec.LookPath(filepath.Join(targetDir, "protoc")); err == nil { - fmt.Println("protoc is already installed.") + PrintGreen("protoc is already installed.") return nil } - fmt.Println("Installing protoc...") + PrintBlue("Installing protoc...") return installProtoc(targetDir) } @@ -66,7 +66,7 @@ func installProtoc(installDir string) error { fileName := fmt.Sprintf(protocFile, version, osArch) url := baseURL + "/" + fileName - fmt.Println("URL:", url) + PrintBlue(fmt.Sprintf("URL: %s", url)) resp, err := http.Get(url) if err != nil { @@ -85,7 +85,7 @@ func installProtoc(installDir string) error { if err != nil { return err } - fmt.Println("tmp ", tmpFile.Name(), "install ", installDir) + PrintBlue(fmt.Sprintf("tmp %s install %s", tmpFile.Name(), installDir)) return unzip(tmpFile.Name(), installDir) } @@ -133,27 +133,27 @@ func getProtocArch(archMap map[string]string, goArch string) string { func Protocol() error { if err := ensureToolsInstalled(); err != nil { - fmt.Println("error ", err.Error()) + PrintRed("error " + err.Error()) os.Exit(1) } moduleName, err := getModuleNameFromGoMod() if err != nil { - fmt.Println("error fetching module name from go.mod: ", err.Error()) + PrintRed("error fetching module name from go.mod: " + err.Error()) os.Exit(1) } protoPath := "./pkg/protocol" dirs, err := os.ReadDir(protoPath) if err != nil { - fmt.Println("error ", err.Error()) + PrintRed("error " + err.Error()) os.Exit(1) } for _, dir := range dirs { if dir.IsDir() { if err := compileProtoFiles(protoPath, dir.Name(), moduleName); err != nil { - fmt.Println("error ", err.Error()) + PrintRed("error " + err.Error()) os.Exit(1) } } @@ -180,7 +180,7 @@ func compileProtoFiles(basePath, dirName, moduleName string) error { } // Print which file is being compiled for clarity - fmt.Printf("Compiling %s...\n", protoFile) + PrintBlue(fmt.Sprintf("Compiling %s...", protoFile)) // Execute the protoc command if err := sh.Run("protoc", args...); err != nil { @@ -196,9 +196,9 @@ func fixOmitemptyInDirectory(dir string) error { if err != nil { return fmt.Errorf("failed to list .pb.go files in %s: %s", dir, err) } - fmt.Printf("Fixing omitempty in dir %s...\n", dir) + PrintBlue(fmt.Sprintf("Fixing omitempty in dir %s...", dir)) for _, file := range files { - fmt.Printf("Fixing omitempty in %s...\n", file) + PrintBlue(fmt.Sprintf("Fixing omitempty in %s...", file)) if err := RemoveOmitemptyFromFile(file); err != nil { return fmt.Errorf("failed to replace omitempty in %s: %s", file, err) } diff --git a/mageutil/process_controller.go b/mageutil/process_controller.go index 1b8ea10..1102d96 100644 --- a/mageutil/process_controller.go +++ b/mageutil/process_controller.go @@ -50,7 +50,7 @@ func StartBinaries(specificBinaries ...string) error { } args := []string{"-i", strconv.Itoa(i), "-c", configPath} cmd := exec.Command(binFullPath, args...) - fmt.Printf("Starting %s\n", cmd.String()) + PrintBlue(fmt.Sprintf("Starting %s", cmd.String())) cmd.Dir = Paths.OutputHostBin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -91,7 +91,7 @@ func StartTools(specificTools ...string) error { } cmd := exec.Command(toolFullPath, "-c", configPath) - fmt.Printf("Starting %s\n", cmd.String()) + PrintBlue(fmt.Sprintf("Starting %s", cmd.String())) cmd.Dir = Paths.OutputHostBinTools cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -103,7 +103,7 @@ func StartTools(specificTools ...string) error { if err := cmd.Wait(); err != nil { return fmt.Errorf("failed to execute %s with exit code: %v", toolFullPath, err) } - fmt.Printf("Starting %s successfully \n", cmd.String()) + PrintGreen(fmt.Sprintf("Starting %s successfully", cmd.String())) } return nil } diff --git a/mageutil/sys.go b/mageutil/sys.go index 0d9da49..8cce321 100644 --- a/mageutil/sys.go +++ b/mageutil/sys.go @@ -86,26 +86,26 @@ func FindPIDsByBinaryPath() (map[string][]int, error) { func PrintBinaryPorts(binaryPath string, pidMap map[string][]int) { pids, exists := pidMap[binaryPath] if !exists || len(pids) == 0 { - fmt.Printf("No running processes found for binary: %s\n", binaryPath) + PrintYellow(fmt.Sprintf("No running processes found for binary: %s", binaryPath)) return } for _, pid := range pids { proc, err := process.NewProcess(int32(pid)) if err != nil { - fmt.Printf("Failed to create process object for PID %d: %v\n", pid, err) + PrintYellow(fmt.Sprintf("Failed to create process object for PID %d: %v", pid, err)) continue } cmdline, err := proc.Cmdline() if err != nil { - fmt.Printf("Failed to get command line for PID %d: %v\n", pid, err) + PrintYellow(fmt.Sprintf("Failed to get command line for PID %d: %v", pid, err)) continue } connections, err := net.ConnectionsPid("all", int32(pid)) if err != nil { - fmt.Printf("Error getting connections for PID %d: %v\n", pid, err) + PrintYellow(fmt.Sprintf("Error getting connections for PID %d: %v", pid, err)) continue } @@ -132,7 +132,7 @@ func PrintBinaryPorts(binaryPath string, pidMap map[string][]int) { func BatchKillExistBinaries(binaryPaths []string) { processes, err := process.Processes() if err != nil { - fmt.Printf("Failed to get processes: %v\n", err) + PrintRed(fmt.Sprintf("Failed to get processes: %v", err)) return } @@ -148,7 +148,7 @@ func BatchKillExistBinaries(binaryPaths []string) { for _, binaryPath := range binaryPaths { if procs, found := exePathMap[binaryPath]; found { - fmt.Println("binaryPath found ", binaryPath) + PrintBlue(fmt.Sprintf("binaryPath found %s", binaryPath)) for _, p := range procs { terminateAndKillProcess(p) } @@ -159,7 +159,7 @@ func BatchKillExistBinaries(binaryPaths []string) { func terminateAndKillProcess(p *process.Process) { cmdline, err := p.Cmdline() if err != nil { - fmt.Printf("Failed to get command line for process %d: %v\n", p.Pid, err) + PrintYellow(fmt.Sprintf("Failed to get command line for process %d: %v", p.Pid, err)) return } @@ -167,12 +167,12 @@ func terminateAndKillProcess(p *process.Process) { if err != nil { err = p.Kill() // Fallback to kill if terminate fails if err != nil { - fmt.Printf("Failed to kill process cmdline: %s, pid: %d, err: %v\n", cmdline, p.Pid, err) + PrintRed(fmt.Sprintf("Failed to kill process cmdline: %s, pid: %d, err: %v", cmdline, p.Pid, err)) } else { - fmt.Printf("Killed process cmdline: %s, pid: %d\n", cmdline, p.Pid) + PrintYellow(fmt.Sprintf("Killed process cmdline: %s, pid: %d", cmdline, p.Pid)) } } else { - fmt.Printf("Terminated process cmdline: %s, pid: %d\n", cmdline, p.Pid) + PrintGreen(fmt.Sprintf("Terminated process cmdline: %s, pid: %d", cmdline, p.Pid)) } } @@ -180,7 +180,7 @@ func terminateAndKillProcess(p *process.Process) { func KillExistBinary(binaryPath string) { processes, err := process.Processes() if err != nil { - fmt.Printf("Failed to get processes: %v\n", err) + PrintRed(fmt.Sprintf("Failed to get processes: %v", err)) return } @@ -196,7 +196,7 @@ func KillExistBinary(binaryPath string) { //if strings.EqualFold(exePath, binaryPath) { cmdline, err := p.Cmdline() if err != nil { - fmt.Printf("Failed to get command line for process %d: %v\n", p.Pid, err) + PrintYellow(fmt.Sprintf("Failed to get command line for process %d: %v", p.Pid, err)) continue } @@ -205,12 +205,12 @@ func KillExistBinary(binaryPath string) { err = p.Kill() if err != nil { - fmt.Printf("Failed to kill process cmdline: %s, pid: %d, err: %v\n", cmdline, p.Pid, err) + PrintRed(fmt.Sprintf("Failed to kill process cmdline: %s, pid: %d, err: %v", cmdline, p.Pid, err)) } else { - fmt.Printf("Killed process cmdline: %s, pid: %d\n", cmdline, p.Pid) + PrintYellow(fmt.Sprintf("Killed process cmdline: %s, pid: %d", cmdline, p.Pid)) } } else { - fmt.Printf("Terminated process cmdline: %s, pid: %d\n", cmdline, p.Pid) + PrintGreen(fmt.Sprintf("Terminated process cmdline: %s, pid: %d", cmdline, p.Pid)) } } } @@ -222,7 +222,7 @@ func DetectPlatform() string { switch targetArch { case "amd64", "arm64": default: - fmt.Printf("Unsupported architecture: %s\n", targetArch) + PrintRed(fmt.Sprintf("Unsupported architecture: %s", targetArch)) os.Exit(1) } return fmt.Sprintf("%s_%s", targetOS, targetArch) @@ -232,7 +232,7 @@ func DetectPlatform() string { func rootDir() string { dir, err := os.Getwd() if err != nil { - fmt.Println("Failed to get current directory:", err) + PrintRed(fmt.Sprintf("Failed to get current directory: %v", err)) os.Exit(1) } return dir diff --git a/tools/helloworld/main.go b/tools/helloworld/main.go index 9018f8e..ab8c8c2 100644 --- a/tools/helloworld/main.go +++ b/tools/helloworld/main.go @@ -4,6 +4,8 @@ import ( "flag" "fmt" "os" + + "github.com/openimsdk/gomake/mageutil" ) func main() { @@ -12,5 +14,5 @@ func main() { config := flag.String("c", "", "Configuration directory") // Parse the flags flag.Parse() - fmt.Printf("This is a helloworld tool. Program: %s, args: -i %d -c %s\n", os.Args[0], *index, *config) + mageutil.PrintBlue(fmt.Sprintf("This is a helloworld tool. Program: %s, args: -i %d -c %s", os.Args[0], *index, *config)) } From aecd68debb16d6f4e077c7065909738762573f80 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Fri, 6 Mar 2026 14:33:36 +0800 Subject: [PATCH 03/10] refactor(priority): introduce priority package and update command execution to use new priority levels --- go.sum | 4 +- internal/priority/priority.go | 10 ++ .../priority}/priority_unix.go | 10 +- .../priority}/priority_win.go | 10 +- mageutil/build.go | 9 +- mageutil/cmd.go | 141 ++++++++++++++++++ mageutil/priority.go | 37 ----- 7 files changed, 170 insertions(+), 51 deletions(-) create mode 100644 internal/priority/priority.go rename {mageutil => internal/priority}/priority_unix.go (59%) rename {mageutil => internal/priority}/priority_win.go (77%) create mode 100644 mageutil/cmd.go delete mode 100644 mageutil/priority.go diff --git a/go.sum b/go.sum index 23997b8..dc5469b 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLA github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6+8hHTyWBeQ/P4Nb7dd4/0ohEcWQuM= @@ -21,8 +23,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= -github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI= github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= diff --git a/internal/priority/priority.go b/internal/priority/priority.go new file mode 100644 index 0000000..78a6f21 --- /dev/null +++ b/internal/priority/priority.go @@ -0,0 +1,10 @@ +package priority + +type Level int + +const ( + Low Level = iota + BelowNormal + Normal + High +) diff --git a/mageutil/priority_unix.go b/internal/priority/priority_unix.go similarity index 59% rename from mageutil/priority_unix.go rename to internal/priority/priority_unix.go index 5287aef..1571d17 100644 --- a/mageutil/priority_unix.go +++ b/internal/priority/priority_unix.go @@ -1,19 +1,19 @@ //go:build !windows -package mageutil +package priority import ( "syscall" ) -func SetPriority(pid int, level PriorityLevel) error { +func Set(pid int, level Level) error { var nice int switch level { - case PriorityLow: + case Low: nice = 19 - case PriorityBelowNormal: + case BelowNormal: nice = 10 - case PriorityHigh: + case High: nice = -10 default: nice = 0 diff --git a/mageutil/priority_win.go b/internal/priority/priority_win.go similarity index 77% rename from mageutil/priority_win.go rename to internal/priority/priority_win.go index 5adb407..f4e40bc 100644 --- a/mageutil/priority_win.go +++ b/internal/priority/priority_win.go @@ -1,19 +1,19 @@ //go:build windows -package mageutil +package priority import ( "golang.org/x/sys/windows" ) -func SetPriority(pid int, level PriorityLevel) error { +func Set(pid int, level Level) error { var class uint32 switch level { - case PriorityLow: + case Low: class = windows.IDLE_PRIORITY_CLASS - case PriorityBelowNormal: + case BelowNormal: class = windows.BELOW_NORMAL_PRIORITY_CLASS - case PriorityHigh: + case High: class = windows.HIGH_PRIORITY_CLASS default: class = windows.NORMAL_PRIORITY_CLASS diff --git a/mageutil/build.go b/mageutil/build.go index 53a4545..1e4d6d3 100644 --- a/mageutil/build.go +++ b/mageutil/build.go @@ -8,6 +8,7 @@ import ( "strings" "sync/atomic" + "github.com/openimsdk/gomake/internal/priority" "github.com/openimsdk/gomake/internal/util" ) @@ -217,7 +218,11 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, } buildArgs = append(buildArgs, buildTarget) - err = RunWithPriority(PriorityLow, env, "go", buildArgs...) + err = NewCmd("go"). + WithArgs(buildArgs...). + WithEnv(env). + WithPriority(priority.Low). + Run() os.Chdir(originalDir) @@ -230,7 +235,7 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, if compressEnabled { PrintBlue(fmt.Sprintf("Compressing %s with UPX...", outputFileName)) - if err := RunWithPriority(PriorityLow, nil, "upx", "--lzma", outputPath); err != nil { + if err := NewCmd("upx").WithArgs("--lzma", outputPath).WithPriority(priority.Low).Run(); err != nil { PrintYellow(fmt.Sprintf("UPX compression failed for %s (non-fatal): %v", outputFileName, err)) } else { PrintGreen(fmt.Sprintf("Successfully compressed with UPX: %s", outputFileName)) diff --git a/mageutil/cmd.go b/mageutil/cmd.go new file mode 100644 index 0000000..395cdff --- /dev/null +++ b/mageutil/cmd.go @@ -0,0 +1,141 @@ +package mageutil + +import ( + "errors" + "fmt" + "io" + "os" + "os/exec" + "strings" + + "github.com/openimsdk/gomake/internal/priority" +) + +type Cmd struct { + name string + args []string + + env map[string]string + dir string + + priority *priority.Level + + stdin io.Reader + stdout io.Writer + stderr io.Writer +} + +func NewCmd(name string) *Cmd { + return &Cmd{name: name} +} + +func (c *Cmd) WithArgs(args ...string) *Cmd { + c.args = append([]string(nil), args...) + return c +} + +func (c *Cmd) WithEnv(env map[string]string) *Cmd { + if len(env) == 0 { + return c + } + if c.env == nil { + c.env = make(map[string]string, len(env)) + } + for k, v := range env { + c.env[k] = v + } + return c +} + +func (c *Cmd) WithDir(dir string) *Cmd { + c.dir = strings.TrimSpace(dir) + return c +} + +func (c *Cmd) WithPriority(priority priority.Level) *Cmd { + c.priority = &priority + return c +} + +func (c *Cmd) WithStdin(stdin io.Reader) *Cmd { + c.stdin = stdin + return c +} + +func (c *Cmd) WithStdout(stdout io.Writer) *Cmd { + c.stdout = stdout + return c +} + +func (c *Cmd) WithStderr(stderr io.Writer) *Cmd { + c.stderr = stderr + return c +} + +func (c *Cmd) WithStdio(stdin io.Reader, stdout, stderr io.Writer) *Cmd { + c.stdin = stdin + c.stdout = stdout + c.stderr = stderr + return c +} + +func (c *Cmd) Run() error { + if strings.TrimSpace(c.name) == "" { + return errors.New("command is empty") + } + + execCmd := exec.Command(c.name, c.args...) + execCmd.Env = append(os.Environ(), flattenEnv(c.env)...) + if c.dir != "" { + execCmd.Dir = c.dir + } + + stdin, stdout, stderr := c.resolveIO() + execCmd.Stdin = stdin + execCmd.Stdout = stdout + execCmd.Stderr = stderr + + if err := execCmd.Start(); err != nil { + return err + } + + c.applyPriority(execCmd.Process.Pid) + return execCmd.Wait() +} + +func (c *Cmd) resolveIO() (io.Reader, io.Writer, io.Writer) { + stdin := c.stdin + + stdout := c.stdout + if stdout == nil { + stdout = os.Stdout + } + + stderr := c.stderr + if stderr == nil { + stderr = os.Stderr + } + + return stdin, stdout, stderr +} + +func (c *Cmd) applyPriority(pid int) { + if c.priority == nil { + return + } + if err := priority.Set(pid, *c.priority); err != nil { + PrintYellow(fmt.Sprintf("Failed to set priority for PID %d: %v", pid, err)) + } +} + +func flattenEnv(env map[string]string) []string { + if len(env) == 0 { + return nil + } + + flattened := make([]string, 0, len(env)) + for k, v := range env { + flattened = append(flattened, k+"="+v) + } + return flattened +} diff --git a/mageutil/priority.go b/mageutil/priority.go deleted file mode 100644 index 45150fc..0000000 --- a/mageutil/priority.go +++ /dev/null @@ -1,37 +0,0 @@ -package mageutil - -import ( - "fmt" - "os" - "os/exec" -) - -type PriorityLevel int - -const ( - PriorityLow PriorityLevel = iota - PriorityBelowNormal - PriorityNormal - PriorityHigh -) - -func RunWithPriority(priority PriorityLevel, env map[string]string, cmd string, args ...string) error { - execCmd := exec.Command(cmd, args...) - execCmd.Env = os.Environ() - for k, v := range env { - execCmd.Env = append(execCmd.Env, k+"="+v) - } - execCmd.Stdout = os.Stdout - execCmd.Stderr = os.Stderr - - if err := execCmd.Start(); err != nil { - return err - } - - pid := execCmd.Process.Pid - if err := SetPriority(pid, priority); err != nil { - PrintYellow(fmt.Sprintf("Failed to set priority for PID %d: %v", pid, err)) - } - - return execCmd.Wait() -} From 8ef949bfa5ee5ccf94093d5cc05b535ae343850b Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Thu, 2 Apr 2026 17:39:18 +0800 Subject: [PATCH 04/10] refactor(core): refactor environment handling and logging system - Replace env option resolution with direct env access - Add comprehensive logging with file output support - Refactor command execution with improved Cmd struct - Update binary management with proper process handling --- .gitignore | 2 + internal/util/env.go | 20 ++++ internal/util/option.go | 16 --- mageutil/basic.go | 8 +- mageutil/cmd.go | 115 ++++++++++------------ mageutil/export.go | 16 +-- mageutil/logging.go | 173 ++++++++++++++++++++++----------- mageutil/paths.go | 10 +- mageutil/process_controller.go | 37 ++++--- 9 files changed, 229 insertions(+), 168 deletions(-) delete mode 100644 internal/util/option.go diff --git a/.gitignore b/.gitignore index b096c4c..cd10dd8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ cmd/openmeeting-rtmp/ */openmeeting-rtmp/ work_cmd + +.idea \ No newline at end of file diff --git a/internal/util/env.go b/internal/util/env.go index 5411f24..b3fde8b 100644 --- a/internal/util/env.go +++ b/internal/util/env.go @@ -59,6 +59,14 @@ func GetEnv[T any](key string) (*T, error) { } } +func GetEnvWithNoErr[T any](key string) *T { + value, err := GetEnv[T](key) + if err != nil { + return nil + } + return value +} + func SetEnvs(envMap map[string]string) (func(), error) { oldEnv := make(map[string]string) restore := func() { @@ -83,3 +91,15 @@ func SetEnvs(envMap map[string]string) (func(), error) { } return restore, nil } + +func FlattenEnvs(envs map[string]string) []string { + if len(envs) == 0 { + return nil + } + + flattened := make([]string, 0, len(envs)) + for k, v := range envs { + flattened = append(flattened, k+"="+v) + } + return flattened +} diff --git a/internal/util/option.go b/internal/util/option.go deleted file mode 100644 index ae7f240..0000000 --- a/internal/util/option.go +++ /dev/null @@ -1,16 +0,0 @@ -package util - -import ( - "errors" -) - -func ResolveEnvOption[T any](key string) *T { - value, err := GetEnv[T](key) - if err == nil { - return value - } - if errors.Is(err, ErrEnvNotSet) { - return nil - } - return nil -} diff --git a/mageutil/basic.go b/mageutil/basic.go index 2ce734b..9f3d5ba 100644 --- a/mageutil/basic.go +++ b/mageutil/basic.go @@ -167,10 +167,10 @@ func isExecutableFile(filePath string) bool { func Build(binaries []string, pathOpts *PathOptions, buildOpt *BuildOptions) { resolvedBuildOpt := ResolveBuildOptions(buildOpt, &BuildOptions{ - CgoEnabled: util.ResolveEnvOption[string]("CGO_ENABLED"), - Release: util.ResolveEnvOption[bool]("RELEASE"), - Compress: util.ResolveEnvOption[bool]("COMPRESS"), - Platforms: util.ResolveEnvOption[[]string]("PLATFORMS"), + CgoEnabled: util.GetEnvWithNoErr[string]("CGO_ENABLED"), + Release: util.GetEnvWithNoErr[bool]("RELEASE"), + Compress: util.GetEnvWithNoErr[bool]("COMPRESS"), + Platforms: util.GetEnvWithNoErr[[]string]("PLATFORMS"), }) if _, err := os.Stat(StartConfigFile); err == nil { diff --git a/mageutil/cmd.go b/mageutil/cmd.go index 395cdff..4f68d7f 100644 --- a/mageutil/cmd.go +++ b/mageutil/cmd.go @@ -1,7 +1,7 @@ package mageutil import ( - "errors" + "bytes" "fmt" "io" "os" @@ -9,28 +9,26 @@ import ( "strings" "github.com/openimsdk/gomake/internal/priority" + "github.com/openimsdk/gomake/internal/util" ) type Cmd struct { - name string - args []string + execCmd *exec.Cmd env map[string]string - dir string priority *priority.Level - stdin io.Reader - stdout io.Writer - stderr io.Writer + stdoutBuf bytes.Buffer + stderrBuf bytes.Buffer } -func NewCmd(name string) *Cmd { - return &Cmd{name: name} +func NewCmd(command string) *Cmd { + return &Cmd{execCmd: exec.Command(command)} } func (c *Cmd) WithArgs(args ...string) *Cmd { - c.args = append([]string(nil), args...) + c.execCmd.Args = append(c.execCmd.Args, args...) return c } @@ -48,7 +46,7 @@ func (c *Cmd) WithEnv(env map[string]string) *Cmd { } func (c *Cmd) WithDir(dir string) *Cmd { - c.dir = strings.TrimSpace(dir) + c.execCmd.Dir = strings.TrimSpace(dir) return c } @@ -58,84 +56,73 @@ func (c *Cmd) WithPriority(priority priority.Level) *Cmd { } func (c *Cmd) WithStdin(stdin io.Reader) *Cmd { - c.stdin = stdin + c.execCmd.Stdin = stdin return c } func (c *Cmd) WithStdout(stdout io.Writer) *Cmd { - c.stdout = stdout + if stdout == nil { + stdout = commandOutputWriter(os.Stdout, &c.stdoutBuf) + } else { + c.execCmd.Stdout = stdout + } return c } func (c *Cmd) WithStderr(stderr io.Writer) *Cmd { - c.stderr = stderr + if stderr == nil { + stderr = commandOutputWriter(os.Stderr, &c.stderrBuf) + } else { + c.execCmd.Stderr = stderr + } return c } -func (c *Cmd) WithStdio(stdin io.Reader, stdout, stderr io.Writer) *Cmd { - c.stdin = stdin - c.stdout = stdout - c.stderr = stderr - return c -} +func (c *Cmd) Start() error { + c.execCmd.Env = append(os.Environ(), util.FlattenEnvs(c.env)...) -func (c *Cmd) Run() error { - if strings.TrimSpace(c.name) == "" { - return errors.New("command is empty") - } - - execCmd := exec.Command(c.name, c.args...) - execCmd.Env = append(os.Environ(), flattenEnv(c.env)...) - if c.dir != "" { - execCmd.Dir = c.dir + if err := c.execCmd.Start(); err != nil { + return err } - stdin, stdout, stderr := c.resolveIO() - execCmd.Stdin = stdin - execCmd.Stdout = stdout - execCmd.Stderr = stderr - - if err := execCmd.Start(); err != nil { - return err + if c.priority != nil && c.execCmd.Process != nil { + if err := priority.Set(c.execCmd.Process.Pid, *c.priority); err != nil { + PrintYellow(fmt.Sprintf("Failed to set priority for PID %d: %v", c.execCmd.Process.Pid, err)) + } } - c.applyPriority(execCmd.Process.Pid) - return execCmd.Wait() + return nil } -func (c *Cmd) resolveIO() (io.Reader, io.Writer, io.Writer) { - stdin := c.stdin - - stdout := c.stdout - if stdout == nil { - stdout = os.Stdout - } +func (c *Cmd) Wait() error { + return c.execCmd.Wait() +} - stderr := c.stderr - if stderr == nil { - stderr = os.Stderr +func (c *Cmd) Run() error { + err := c.Start() + if err != nil { + return err } - return stdin, stdout, stderr + return c.Wait() } -func (c *Cmd) applyPriority(pid int) { - if c.priority == nil { - return - } - if err := priority.Set(pid, *c.priority); err != nil { - PrintYellow(fmt.Sprintf("Failed to set priority for PID %d: %v", pid, err)) - } +func (c *Cmd) String() string { + return c.execCmd.String() } -func flattenEnv(env map[string]string) []string { - if len(env) == 0 { - return nil - } +func (c *Cmd) Output() ([]byte, error) { + err := c.Run() + dst := make([]byte, len(c.stdoutBuf.Bytes())) + copy(dst, c.stdoutBuf.Bytes()) + return dst, err +} - flattened := make([]string, 0, len(env)) - for k, v := range env { - flattened = append(flattened, k+"="+v) +func commandOutputWriter(writers ...io.Writer) io.Writer { + logFile, err := getSharedLogFile() + if err != nil { + PrintYellow(fmt.Sprintf("Warning: failed to open log file for command output: %v", err)) + return io.MultiWriter(writers...) } - return flattened + return io.MultiWriter(append(writers, logFile)...) } diff --git a/mageutil/export.go b/mageutil/export.go index ce233b4..c7137ce 100644 --- a/mageutil/export.go +++ b/mageutil/export.go @@ -70,10 +70,9 @@ func ExportMageLauncherArchived(overrideMappingPaths map[string]string, exportOp mageBinaryPath += ".exe" } PrintBlue(fmt.Sprintf("Compiling mage binary for %s: mage -compile %s", platform, mageBinaryPath)) - cmd := exec.Command("mage", "-compile", mageBinaryPath, "-goos", targetOS, "-goarch", targetArch, "-ldflags", "-s -w") - cmd.Dir = Paths.Root - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + cmd := NewCmd("mage"). + WithArgs("-compile", mageBinaryPath, "-goos", targetOS, "-goarch", targetArch, "-ldflags", "-s -w"). + WithDir(Paths.Root) if err := cmd.Run(); err != nil { return fmt.Errorf("failed to compile mage for %s: %v", platform, err) } @@ -177,10 +176,11 @@ func GetAllRootFilesExcludeIgnore() ([]string, error) { return nil, fmt.Errorf("root path is empty") } - cmd := exec.Command("git", "ls-files", "-c", "--exclude-standard", "-z") - cmd.Dir = root + cmdOutput, err := NewCmd("git"). + WithArgs("ls-files", "-c", "--exclude-standard", "-z"). + WithDir(root). + Output() - output, err := cmd.Output() if err != nil { var exitErr *exec.ExitError if errors.As(err, &exitErr) { @@ -190,7 +190,7 @@ func GetAllRootFilesExcludeIgnore() ([]string, error) { } relPaths := make([]string, 0) - for _, relPath := range strings.Split(string(output), "\x00") { + for _, relPath := range strings.Split(string(cmdOutput), "\x00") { if relPath == "" { continue } diff --git a/mageutil/logging.go b/mageutil/logging.go index c7b1683..303ac76 100644 --- a/mageutil/logging.go +++ b/mageutil/logging.go @@ -4,7 +4,9 @@ import ( "fmt" "io" "os" + "path/filepath" "strings" + "sync" "time" ) @@ -19,6 +21,15 @@ const ( const defaultTimeFmt = "[2006-01-02 15:04:05 MST]" +const defaultLogFileName = "gomake.log" + +var ( + logFileStateMu sync.Mutex + logWriteMu sync.Mutex + sharedLogFile *os.File + sharedLogPath string +) + type PrintOptions struct { Writer io.Writer Color string @@ -30,10 +41,6 @@ type PrintOptions struct { } func Print(opt PrintOptions) (int, error) { - w := opt.Writer - if w == nil { - w = os.Stdout - } tf := opt.TimeFmt if tf == "" { tf = defaultTimeFmt @@ -45,36 +52,120 @@ func Print(opt PrintOptions) (int, error) { ) WithActiveSpinnerPaused(func() { - var b strings.Builder - - if opt.WithTime { - ts := time.Now().Format(tf) - if opt.TwoLine { - b.WriteString(ts) - b.WriteByte('\n') - } else { - b.WriteString(ts) - b.WriteByte(' ') - } + consoleWriter := opt.Writer + if consoleWriter == nil { + consoleWriter = os.Stdout } - if opt.Color != "" { - b.WriteString(opt.Color) + consoleMessage := formatPrintMessage(opt, tf, true) + fileMessage := formatPrintMessage(opt, tf, false) + + logWriteMu.Lock() + defer logWriteMu.Unlock() + + nLocal, errLocal := io.WriteString(consoleWriter, consoleMessage) + n, err = nLocal, errLocal + + logFile, logErr := getSharedLogFile() + if logErr != nil { + return } - b.WriteString(opt.Message) - if opt.Color != "" { - b.WriteString(ColorReset) + + if _, logErr = io.WriteString(logFile, fileMessage); err == nil && logErr != nil { + err = logErr } + }) - if !opt.NoNewLine { + return n, err +} + +func formatPrintMessage(opt PrintOptions, timeFmt string, withColor bool) string { + var b strings.Builder + + if opt.WithTime { + ts := time.Now().Format(timeFmt) + if opt.TwoLine { + b.WriteString(ts) b.WriteByte('\n') + } else { + b.WriteString(ts) + b.WriteByte(' ') } + } - nLocal, errLocal := io.WriteString(w, b.String()) - n, err = nLocal, errLocal - }) + if withColor && opt.Color != "" { + b.WriteString(opt.Color) + } + b.WriteString(opt.Message) + if withColor && opt.Color != "" { + b.WriteString(ColorReset) + } - return n, err + if !opt.NoNewLine { + b.WriteByte('\n') + } + + return b.String() +} + +func openDetachedCommandLogFile() (*os.File, error) { + path, err := logFilePath() + if err != nil { + return nil, err + } + + logFile, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) + if err != nil { + return nil, fmt.Errorf("failed to open log file %s: %w", path, err) + } + return logFile, nil +} + +func getSharedLogFile() (*os.File, error) { + path, err := logFilePath() + if err != nil { + return nil, err + } + + logFileStateMu.Lock() + defer logFileStateMu.Unlock() + + if sharedLogFile != nil && sharedLogPath == path { + return sharedLogFile, nil + } + + if sharedLogFile != nil { + _ = sharedLogFile.Close() + sharedLogFile = nil + sharedLogPath = "" + } + + logFile, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) + if err != nil { + return nil, fmt.Errorf("failed to open log file %s: %w", path, err) + } + + sharedLogFile = logFile + sharedLogPath = path + return sharedLogFile, nil +} + +func logFilePath() (string, error) { + if Paths == nil { + return "", fmt.Errorf("paths are not initialized") + } + + logDir := strings.TrimSpace(Paths.OutputLogs) + if logDir == "" { + return "", fmt.Errorf("log directory is empty") + } + + logDir = filepath.Clean(logDir) + if err := os.MkdirAll(logDir, 0755); err != nil { + return "", fmt.Errorf("failed to create log directory %s: %w", logDir, err) + } + + return filepath.Join(logDir, defaultLogFileName), nil } func PrintBlue(message string) { @@ -93,35 +184,3 @@ func PrintYellow(message string) { func PrintRedNoTimeStamp(message string) { _, _ = Print(PrintOptions{Color: ColorRed, Message: message, WithTime: false}) } - -func PrintBlueTwoLine(message string) { - _, _ = Print(PrintOptions{Color: ColorBlue, Message: message, WithTime: true, TwoLine: true}) -} - -func PrintGreenTwoLine(message string) { - _, _ = Print(PrintOptions{Color: ColorGreen, Message: message, WithTime: true, TwoLine: true}) -} - -func PrintGreenNoTimeStamp(message string) { - _, _ = Print(PrintOptions{Color: ColorGreen, Message: message, WithTime: false}) -} - -func PrintRedToStdErr(a ...any) { - _, _ = Print(PrintOptions{ - Writer: os.Stderr, - Color: ColorRed, - Message: fmt.Sprint(a...), - WithTime: false, - NoNewLine: true, - }) -} - -func PrintGreenToStdOut(a ...any) { - _, _ = Print(PrintOptions{ - Writer: os.Stdout, - Color: ColorGreen, - Message: fmt.Sprint(a...), - WithTime: false, - NoNewLine: true, - }) -} diff --git a/mageutil/paths.go b/mageutil/paths.go index bec6a92..7aee292 100644 --- a/mageutil/paths.go +++ b/mageutil/paths.go @@ -12,7 +12,7 @@ const ( DeploymentType = "DEPLOYMENT_TYPE" KUBERNETES = "kubernetes" - // Directory name constants + // Directory command constants ConfigDir = "config" OutputDir = "_output" SrcDir = "cmd" @@ -46,11 +46,11 @@ type PathConfig struct { type PathOptions struct { RootDir *string // Custom root directory, default is current working directory(./) - OutputDir *string // Custom output directory name, default is "_output" - ConfigDir *string // Custom config directory name, default is "config" + OutputDir *string // Custom output directory command, default is "_output" + ConfigDir *string // Custom config directory command, default is "config" - SrcDir *string // Custom cmd source directory name, default is "cmd" - ToolsDir *string // Custom tools source directory name, default is "tools" + SrcDir *string // Custom cmd source directory command, default is "cmd" + ToolsDir *string // Custom tools source directory command, default is "tools" } var Paths *PathConfig diff --git a/mageutil/process_controller.go b/mageutil/process_controller.go index 1102d96..78bf4b3 100644 --- a/mageutil/process_controller.go +++ b/mageutil/process_controller.go @@ -3,7 +3,6 @@ package mageutil import ( "fmt" "os" - "os/exec" "path/filepath" "slices" "strconv" @@ -49,14 +48,20 @@ func StartBinaries(specificBinaries ...string) error { configPath = Paths.K8sConfig } args := []string{"-i", strconv.Itoa(i), "-c", configPath} - cmd := exec.Command(binFullPath, args...) + cmd := NewCmd(binFullPath).WithArgs(args...) PrintBlue(fmt.Sprintf("Starting %s", cmd.String())) - cmd.Dir = Paths.OutputHostBin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + logFile, err := openDetachedCommandLogFile() + if err != nil { + return err + } + cmd.WithDir(Paths.OutputHostBin) + cmd.WithStdout(logFile) + cmd.WithStderr(logFile) if err := cmd.Start(); err != nil { + _ = logFile.Close() return fmt.Errorf("failed to start %s with args %v: %v", binFullPath, args, err) } + _ = logFile.Close() } } return nil @@ -90,19 +95,23 @@ func StartTools(specificTools ...string) error { configPath = Paths.K8sConfig } - cmd := exec.Command(toolFullPath, "-c", configPath) + logFile, err := openDetachedCommandLogFile() + if err != nil { + return err + } + cmd := NewCmd(toolFullPath). + WithArgs("-c", configPath). + WithDir(Paths.OutputHostBinTools). + WithStdout(logFile). + WithStderr(logFile) PrintBlue(fmt.Sprintf("Starting %s", cmd.String())) - cmd.Dir = Paths.OutputHostBinTools - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Start(); err != nil { - return fmt.Errorf("failed to start %s with error: %v", toolFullPath, err) + if err := cmd.Run(); err != nil { + _ = logFile.Close() + return fmt.Errorf("failed to run %s with error: %v", toolFullPath, err) } - if err := cmd.Wait(); err != nil { - return fmt.Errorf("failed to execute %s with exit code: %v", toolFullPath, err) - } + _ = logFile.Close() PrintGreen(fmt.Sprintf("Starting %s successfully", cmd.String())) } return nil From 1bd97480dba11db5d56e3dac55766a0039368089 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Mon, 1 Jun 2026 14:44:17 +0800 Subject: [PATCH 05/10] refactor(core): simplify mageutil error handling Extract shared IO helpers, return errors from core commands, and reduce spinner/process controller complexity. --- go.mod | 12 +++ go.sum | 117 ++++++++++++++++++++++++++- internal/util/io.go | 11 +++ internal/util/terminal.go | 10 ++- magefile.go | 57 ++++++------- mageutil/basic.go | 63 +++++++++------ mageutil/build.go | 114 ++++++++++++++------------ mageutil/cmd.go | 41 +++++----- mageutil/define.go | 8 +- mageutil/export.go | 10 ++- mageutil/gen_protocol.go | 8 +- mageutil/logging.go | 27 +++---- mageutil/paths.go | 4 +- mageutil/process_controller.go | 25 ++---- mageutil/spinner.go | 143 +++++++++++---------------------- mageutil/sys.go | 28 ++----- 16 files changed, 391 insertions(+), 287 deletions(-) create mode 100644 internal/util/io.go diff --git a/go.mod b/go.mod index b7a2a5e..7bd1f0e 100644 --- a/go.mod +++ b/go.mod @@ -6,19 +6,31 @@ require ( github.com/bmatcuk/doublestar/v4 v4.10.0 github.com/magefile/mage v1.15.0 github.com/openimsdk/tools v0.0.49 + github.com/pterm/pterm v0.12.83 github.com/shirou/gopsutil/v4 v4.26.2 golang.org/x/sys v0.41.0 gopkg.in/yaml.v3 v3.0.1 ) require ( + atomicgo.dev/cursor v0.2.0 // indirect + atomicgo.dev/keyboard v0.2.9 // indirect + atomicgo.dev/schedule v0.1.0 // indirect + github.com/clipperhouse/uax29/v2 v2.7.0 // indirect + github.com/containerd/console v1.0.5 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/gookit/color v1.6.0 // indirect github.com/jinzhu/copier v0.4.0 // indirect + github.com/lithammer/fuzzysearch v1.1.8 // indirect github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect + github.com/mattn/go-runewidth v0.0.20 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + golang.org/x/term v0.40.0 // indirect + golang.org/x/text v0.34.0 // indirect ) diff --git a/go.sum b/go.sum index dc5469b..879d5d2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,29 @@ +atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= +atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ= +atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw= +atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= +atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= +atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= +atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= +atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= +github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= +github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= +github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= +github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= +github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI= +github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= +github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= +github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= +github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= +github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= +github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= +github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= @@ -9,12 +33,34 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0= +github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= +github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA= +github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6+8hHTyWBeQ/P4Nb7dd4/0ohEcWQuM= github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ= +github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/openimsdk/tools v0.0.49 h1:yILTgOCqxlqJMc889fE99E5ZGa70v/E3hkCSeTnWl3s= github.com/openimsdk/tools v0.0.49/go.mod h1:oiSQU5Z6fzjxKFjbqDHImD8EmCIwClU1Rkur1sK12Po= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -23,22 +69,91 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= +github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= +github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= +github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= +github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= +github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= +github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= +github.com/pterm/pterm v0.12.83 h1:ie+YmGmA727VuhxBlyGr74Ks+7McV6kT99IB8EU80aA= +github.com/pterm/pterm v0.12.83/go.mod h1:xlgc6bFWyJIMtmLJvGim+L7jhSReilOlOnodeIYe4Tk= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI= github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/util/io.go b/internal/util/io.go new file mode 100644 index 0000000..419ca9e --- /dev/null +++ b/internal/util/io.go @@ -0,0 +1,11 @@ +package util + +import ( + "io" + + "github.com/openimsdk/tools/utils/datautil" +) + +func MultiWriter(writers ...io.Writer) io.Writer { + return io.MultiWriter(datautil.Filter(writers, func(w io.Writer) (io.Writer, bool) { return w, w != nil })...) +} diff --git a/internal/util/terminal.go b/internal/util/terminal.go index 80210c8..26bfd9b 100644 --- a/internal/util/terminal.go +++ b/internal/util/terminal.go @@ -3,7 +3,15 @@ package util import "os" func StdoutIsTerminal() bool { - stat, err := os.Stdout.Stat() + return fileIsTerminal(os.Stdout) +} + +func StderrIsTerminal() bool { + return fileIsTerminal(os.Stderr) +} + +func fileIsTerminal(file *os.File) bool { + stat, err := file.Stat() if err != nil { return false } diff --git a/magefile.go b/magefile.go index 7171a82..6d02243 100644 --- a/magefile.go +++ b/magefile.go @@ -1,11 +1,9 @@ //go:build mage -// +build mage package main import ( "flag" - "os" "github.com/openimsdk/gomake/mageutil" ) @@ -32,19 +30,19 @@ var ( // Build support specifical binary build. // // Example: `mage build openim-api openim-rpc-user seq` -func Build() { +func Build() error { flag.Parse() bin := flag.Args() if len(bin) != 0 { bin = bin[1:] } - mageutil.WithSpinner("Building binaries...", func() { - mageutil.Build(bin, nil, nil) + return mageutil.WithSpinnerR("Building binaries...", func() error { + return mageutil.Build(bin, nil, nil) }) } -func BuildWithCustomConfig() { +func BuildWithCustomConfig() error { flag.Parse() bin := flag.Args() if len(bin) != 0 { @@ -58,17 +56,19 @@ func BuildWithCustomConfig() { ToolsDir: &customToolsDir, // default is "tools" } - mageutil.WithSpinner("Building binaries with custom config...", func() { - mageutil.Build(bin, config, nil) + return mageutil.WithSpinnerR("Building binaries with custom config...", func() error { + return mageutil.Build(bin, config, nil) }) } -func Start() { - mageutil.InitForSSC() +func Start() error { + if err := mageutil.InitForSSC(); err != nil { + return err + } err := setMaxOpenFiles() if err != nil { mageutil.PrintRed("setMaxOpenFiles failed " + err.Error()) - os.Exit(1) + return err } flag.Parse() @@ -77,17 +77,19 @@ func Start() { bin = bin[1:] } - mageutil.WithSpinner("Starting tools and services...", func() { - mageutil.StartToolsAndServices(bin, nil) + return mageutil.WithSpinnerR("Starting tools and services...", func() error { + return mageutil.StartToolsAndServices(bin, nil) }) } -func StartWithCustomConfig() { - mageutil.InitForSSC() +func StartWithCustomConfig() error { + if err := mageutil.InitForSSC(); err != nil { + return err + } err := setMaxOpenFiles() if err != nil { mageutil.PrintRed("setMaxOpenFiles failed " + err.Error()) - os.Exit(1) + return err } flag.Parse() @@ -102,33 +104,34 @@ func StartWithCustomConfig() { ConfigDir: &customConfigDir, // default is "config" } - mageutil.WithSpinner("Starting tools and services with custom config...", func() { - mageutil.StartToolsAndServices(bin, config) + return mageutil.WithSpinnerR("Starting tools and services with custom config...", func() error { + return mageutil.StartToolsAndServices(bin, config) }) } -func Stop() { - mageutil.WithSpinner("Checking service status...", mageutil.StopAndCheckBinaries) +func Stop() error { + return mageutil.WithSpinnerR("Checking service status...", mageutil.StopAndCheckBinaries) } -func Check() { - mageutil.WithSpinner("Checking service status...", mageutil.CheckAndReportBinariesStatus) +func Check() error { + return mageutil.WithSpinnerR("Checking service status...", mageutil.CheckAndReportBinariesStatus) } -func Protocol() { - mageutil.WithSpinnerE("Generating protocol artifacts...", mageutil.Protocol) +func Protocol() error { + return mageutil.WithSpinnerR("Generating protocol artifacts...", mageutil.Protocol) } -func Export() { +func Export() error { exportOpt := &mageutil.ExportOptions{ ProjectName: &customExportProjectName, BuildOpt: customExportBuildOpt, } - err := mageutil.WithSpinnerE("Exporting launcher archive...", func() error { + err := mageutil.WithSpinnerR("Exporting launcher archive...", func() error { return mageutil.ExportMageLauncherArchived(nil, exportOpt) }) if err != nil { mageutil.PrintRed("export failed " + err.Error()) - os.Exit(1) + return err } + return nil } diff --git a/mageutil/basic.go b/mageutil/basic.go index 9f3d5ba..ac89305 100644 --- a/mageutil/basic.go +++ b/mageutil/basic.go @@ -10,13 +10,15 @@ import ( "github.com/openimsdk/gomake/internal/util" ) -func CheckAndReportBinariesStatus() { - InitForSSC() +func CheckAndReportBinariesStatus() error { + if err := InitForSSC(); err != nil { + return err + } err := CheckBinariesRunning() if err != nil { PrintRed("Some programs are not running properly:") PrintRedNoTimeStamp(err.Error()) - os.Exit(1) + return err } PrintGreen("All services are running normally.") PrintBlue("Display details of the ports listened to by the service:") @@ -25,19 +27,23 @@ func CheckAndReportBinariesStatus() { if err != nil { PrintRed("PrintListenedPortsByBinaries error") PrintRedNoTimeStamp(err.Error()) - os.Exit(1) + return err } + return nil } -func StopAndCheckBinaries() { - InitForSSC() +func StopAndCheckBinaries() error { + if err := InitForSSC(); err != nil { + return err + } KillExistBinaries() err := attemptCheckBinaries() if err != nil { PrintRed(err.Error()) - return + return err } PrintGreen("All services have been stopped") + return nil } func attemptCheckBinaries() error { @@ -57,11 +63,11 @@ func attemptCheckBinaries() error { return fmt.Errorf("already waited for %d seconds, some services have still not stopped", maxAttempts) } -func StartToolsAndServices(binaries []string, pathOpts *PathOptions) { +func StartToolsAndServices(binaries []string, pathOpts *PathOptions) error { if pathOpts != nil { if err := UpdateGlobalPaths(pathOpts); err != nil { PrintRed("Failed to update paths: " + err.Error()) - os.Exit(1) + return err } } @@ -87,7 +93,7 @@ func StartToolsAndServices(binaries []string, pathOpts *PathOptions) { if len(cmdBinaries) == 0 && len(toolsBinaries) == 0 { PrintYellow("No valid executable binaries found to start. Please build first.") - return + return nil } PrintBlue(fmt.Sprintf("Cmd binaries to start: %v", cmdBinaries)) @@ -98,7 +104,7 @@ func StartToolsAndServices(binaries []string, pathOpts *PathOptions) { if err := StartTools(toolsBinaries...); err != nil { PrintRed("Some specified tools failed to start:") PrintRedNoTimeStamp(err.Error()) - return + return err } PrintGreen("Specified tools executed successfully") } @@ -108,24 +114,24 @@ func StartToolsAndServices(binaries []string, pathOpts *PathOptions) { err := attemptCheckBinaries() if err != nil { PrintRed("Some services running, details are as follows, abort start " + err.Error()) - return + return err } err = StartBinaries(cmdBinaries...) if err != nil { PrintRed("Failed to start specified binaries:") PrintRedNoTimeStamp(err.Error()) - return + return err } - CheckAndReportBinariesStatus() + return CheckAndReportBinariesStatus() } - return + return nil } PrintBlue("Starting tools primarily involves component verification and other preparatory tasks.") if err := StartTools(); err != nil { PrintRed("Some tools failed to start, details are as follows, abort start") PrintRedNoTimeStamp(err.Error()) - return + return err } PrintGreen("All tools executed successfully") @@ -133,15 +139,15 @@ func StartToolsAndServices(binaries []string, pathOpts *PathOptions) { err := attemptCheckBinaries() if err != nil { PrintRed("Some services running, details are as follows, abort start " + err.Error()) - return + return err } err = StartBinaries() if err != nil { PrintRed("Failed to start all binaries") PrintRedNoTimeStamp(err.Error()) - return + return err } - CheckAndReportBinariesStatus() + return CheckAndReportBinariesStatus() } func isExecutableFile(filePath string) bool { @@ -165,7 +171,7 @@ func isExecutableFile(filePath string) bool { return info.Mode()&0111 != 0 } -func Build(binaries []string, pathOpts *PathOptions, buildOpt *BuildOptions) { +func Build(binaries []string, pathOpts *PathOptions, buildOpt *BuildOptions) error { resolvedBuildOpt := ResolveBuildOptions(buildOpt, &BuildOptions{ CgoEnabled: util.GetEnvWithNoErr[string]("CGO_ENABLED"), Release: util.GetEnvWithNoErr[bool]("RELEASE"), @@ -174,13 +180,15 @@ func Build(binaries []string, pathOpts *PathOptions, buildOpt *BuildOptions) { }) if _, err := os.Stat(StartConfigFile); err == nil { - InitForSSC() + if err := InitForSSC(); err != nil { + return err + } } if pathOpts != nil { if err := UpdateGlobalPaths(pathOpts); err != nil { PrintRed("Failed to update paths: " + err.Error()) - os.Exit(1) + return err } } @@ -190,10 +198,17 @@ func Build(binaries []string, pathOpts *PathOptions, buildOpt *BuildOptions) { } platforms := resolvedBuildOpt.GetPlatforms() if len(platforms) == 0 { - platforms = []string{DetectPlatform()} + platform, err := DetectPlatform() + if err != nil { + return err + } + platforms = []string{platform} } for _, platform := range platforms { - CompileForPlatform(resolvedBuildOpt, platform, compileBinaries) + if err := CompileForPlatform(resolvedBuildOpt, platform, compileBinaries); err != nil { + return err + } } PrintGreen("All specified binaries under cmd and tools were successfully compiled.") + return nil } diff --git a/mageutil/build.go b/mageutil/build.go index 1e4d6d3..536be19 100644 --- a/mageutil/build.go +++ b/mageutil/build.go @@ -6,7 +6,7 @@ import ( "path/filepath" "runtime" "strings" - "sync/atomic" + "sync" "github.com/openimsdk/gomake/internal/priority" "github.com/openimsdk/gomake/internal/util" @@ -35,7 +35,7 @@ func (opt *BuildOptions) GetPlatforms() []string { return util.NilAsZero(util.NilAsZero(opt).Platforms) } -func CompileForPlatform(buildOpt *BuildOptions, platform string, compileBinaries []string) { +func CompileForPlatform(buildOpt *BuildOptions, platform string, compileBinaries []string) error { var cmdBinaries, toolsBinaries []string toolsPrefix := Paths.ToolsDir @@ -77,18 +77,26 @@ func CompileForPlatform(buildOpt *BuildOptions, platform string, compileBinaries if len(cmdBinaries) > 0 { PrintBlue(fmt.Sprintf("Compiling cmd binaries for %s...", platform)) - cmdCompiledDirs = compileDir(buildOpt, filepath.Join(Paths.Root, Paths.SrcDir), Paths.OutputBinPath, platform, cmdBinaries) + compiledDirs, err := compileDir(buildOpt, filepath.Join(Paths.Root, Paths.SrcDir), Paths.OutputBinPath, platform, cmdBinaries) + if err != nil { + return err + } + cmdCompiledDirs = compiledDirs } if len(toolsBinaries) > 0 { PrintBlue(fmt.Sprintf("Compiling tools binaries for %s...", platform)) - toolsCompiledDirs = compileDir(buildOpt, filepath.Join(Paths.Root, Paths.ToolsDir), Paths.OutputBinToolPath, platform, toolsBinaries) + compiledDirs, err := compileDir(buildOpt, filepath.Join(Paths.Root, Paths.ToolsDir), Paths.OutputBinToolPath, platform, toolsBinaries) + if err != nil { + return err + } + toolsCompiledDirs = compiledDirs } - createStartConfigYML(cmdCompiledDirs, toolsCompiledDirs) + return createStartConfigYML(cmdCompiledDirs, toolsCompiledDirs) } -func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, compileBinaries []string) []string { +func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, compileBinaries []string) ([]string, error) { releaseEnabled := buildOpt.GetRelease() compressEnabled := buildOpt.GetCompress() cgoEnabled := buildOpt.GetCgoEnabled() @@ -97,21 +105,26 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, if info, err := os.Stat(sourceDir); err != nil { if os.IsNotExist(err) { - return nil + return nil, nil } PrintRed(fmt.Sprintf("Failed read directory %s: %v", sourceDir, err)) - os.Exit(1) + return nil, err } else if !info.IsDir() { - PrintRed(fmt.Sprintf("Failed %s is not dir", sourceDir)) - os.Exit(1) + err := fmt.Errorf("%s is not dir", sourceDir) + PrintRed("Failed " + err.Error()) + return nil, err } - targetOS, targetArch := strings.Split(platform, "_")[0], strings.Split(platform, "_")[1] + platformParts := strings.SplitN(platform, "_", 2) + if len(platformParts) != 2 { + return nil, fmt.Errorf("invalid platform format: %s", platform) + } + targetOS, targetArch := platformParts[0], platformParts[1] outputDir := filepath.Join(outputBase, targetOS, targetArch) if err := os.MkdirAll(outputDir, 0755); err != nil { PrintRed(fmt.Sprintf("Failed to create directory %s: %v", outputDir, err)) - os.Exit(1) + return nil, err } cpuNum := runtime.GOMAXPROCS(0) @@ -133,16 +146,15 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, } PrintGreen(fmt.Sprintf("The number of concurrent compilations is %d", cpuNum)) - task := make(chan int, cpuNum) - go func() { - for i := range compileBinaries { - task <- i - } - close(task) - }() + task := make(chan int, len(compileBinaries)) + for i := range compileBinaries { + task <- i + } + close(task) - res := make(chan string, 1) - running := int64(cpuNum) + res := make(chan string, len(compileBinaries)) + errCh := make(chan error, cpuNum) + var wg sync.WaitGroup env := map[string]string{ "GOOS": targetOS, @@ -152,28 +164,18 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, env["CGO_ENABLED"] = cgoEnabled } - baseDirAbs, err := filepath.Abs(Paths.Root) - if err != nil { - PrintRed(fmt.Sprintf("Failed to get absolute path for root: %v", err)) - os.Exit(1) - } - for i := 0; i < cpuNum; i++ { + wg.Add(1) go func() { - defer func() { - if atomic.AddInt64(&running, -1) == 0 { - close(res) - } - }() + defer wg.Done() for index := range task { - originalDir := baseDirAbs - binaryPath := filepath.Join(sourceDir, compileBinaries[index]) path, err := util.FindMainGoFile(binaryPath) if err != nil { PrintYellow(fmt.Sprintf("Failed to walk through binary path %s: %v", binaryPath, err)) - os.Exit(1) + errCh <- err + return } if path == "" { continue @@ -193,18 +195,13 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, PrintBlue(fmt.Sprintf("Found go.mod at: %s", goModDir)) } - if err := os.Chdir(goModDir); err != nil { - PrintRed(fmt.Sprintf("Failed to change directory to %s: %v", goModDir, err)) - os.Chdir(originalDir) - continue - } - outputPath := filepath.Join(outputDir, outputFileName) relPath, err := filepath.Rel(goModDir, path) if err != nil { PrintRed(fmt.Sprintf("Failed to get relative path: %v", err)) - os.Exit(1) + errCh <- err + return } buildTarget := relPath @@ -220,22 +217,26 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, err = NewCmd("go"). WithArgs(buildArgs...). + WithDir(goModDir). WithEnv(env). WithPriority(priority.Low). Run() - os.Chdir(originalDir) - if err != nil { - PrintRed("Compilation aborted. " + fmt.Sprintf("failed to compile %s for %s: %v", dirName, platform, err)) - os.Exit(1) + err = fmt.Errorf("failed to compile %s for %s: %w", dirName, platform, err) + PrintRed("Compilation aborted. " + err.Error()) + errCh <- err + return } PrintGreen(fmt.Sprintf("Successfully compiled. dir: %s for platform: %s binary: %s", dirName, platform, outputFileName)) if compressEnabled { PrintBlue(fmt.Sprintf("Compressing %s with UPX...", outputFileName)) - if err := NewCmd("upx").WithArgs("--lzma", outputPath).WithPriority(priority.Low).Run(); err != nil { + cmd := NewCmd("upx"). + WithArgs("--lzma", outputPath). + WithPriority(priority.Low) + if err := cmd.Run(); err != nil { PrintYellow(fmt.Sprintf("UPX compression failed for %s (non-fatal): %v", outputFileName, err)) } else { PrintGreen(fmt.Sprintf("Successfully compressed with UPX: %s", outputFileName)) @@ -246,20 +247,30 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, } }() } + go func() { + wg.Wait() + close(res) + close(errCh) + }() compiledDirs := make([]string, 0, len(compileBinaries)) for str := range res { compiledDirs = append(compiledDirs, str) } - return compiledDirs + for err := range errCh { + if err != nil { + return compiledDirs, err + } + } + return compiledDirs, nil } -func createStartConfigYML(cmdDirs, toolsDirs []string) { +func createStartConfigYML(cmdDirs, toolsDirs []string) error { configPath := filepath.Join(Paths.Root, StartConfigFile) if _, err := os.Stat(configPath); !os.IsNotExist(err) { PrintBlue("start-config.yml already exists, skipping creation.") - return + return nil } var content strings.Builder @@ -276,9 +287,10 @@ func createStartConfigYML(cmdDirs, toolsDirs []string) { err := os.WriteFile(configPath, []byte(content.String()), 0644) if err != nil { PrintRed("Failed to create start-config.yml: " + err.Error()) - return + return err } PrintGreen("start-config.yml created successfully.") + return nil } func ResolveBuildOptions(codeOpt *BuildOptions, envOpt *BuildOptions) *BuildOptions { diff --git a/mageutil/cmd.go b/mageutil/cmd.go index 4f68d7f..f179a3a 100644 --- a/mageutil/cmd.go +++ b/mageutil/cmd.go @@ -18,13 +18,11 @@ type Cmd struct { env map[string]string priority *priority.Level - - stdoutBuf bytes.Buffer - stderrBuf bytes.Buffer } func NewCmd(command string) *Cmd { - return &Cmd{execCmd: exec.Command(command)} + cmd := &Cmd{execCmd: exec.Command(command)} + return cmd } func (c *Cmd) WithArgs(args ...string) *Cmd { @@ -61,20 +59,12 @@ func (c *Cmd) WithStdin(stdin io.Reader) *Cmd { } func (c *Cmd) WithStdout(stdout io.Writer) *Cmd { - if stdout == nil { - stdout = commandOutputWriter(os.Stdout, &c.stdoutBuf) - } else { - c.execCmd.Stdout = stdout - } + c.execCmd.Stdout = stdout return c } func (c *Cmd) WithStderr(stderr io.Writer) *Cmd { - if stderr == nil { - stderr = commandOutputWriter(os.Stderr, &c.stderrBuf) - } else { - c.execCmd.Stderr = stderr - } + c.execCmd.Stderr = stderr return c } @@ -112,17 +102,22 @@ func (c *Cmd) String() string { } func (c *Cmd) Output() ([]byte, error) { + if c.execCmd.Stdout != nil { + return nil, fmt.Errorf("stdout already set") + } + buf := bytes.NewBuffer(nil) + c.execCmd.Stdout = buf err := c.Run() - dst := make([]byte, len(c.stdoutBuf.Bytes())) - copy(dst, c.stdoutBuf.Bytes()) - return dst, err + return buf.Bytes(), err } -func commandOutputWriter(writers ...io.Writer) io.Writer { - logFile, err := getSharedLogFile() - if err != nil { - PrintYellow(fmt.Sprintf("Warning: failed to open log file for command output: %v", err)) - return io.MultiWriter(writers...) +func (c *Cmd) CombinedOutput() ([]byte, error) { + if c.execCmd.Stdout != nil || c.execCmd.Stderr != nil { + return nil, fmt.Errorf("stdout or stderr already set") } - return io.MultiWriter(append(writers, logFile)...) + buf := bytes.NewBuffer(nil) + c.execCmd.Stdout = buf + c.execCmd.Stderr = buf + err := c.Run() + return buf.Bytes(), err } diff --git a/mageutil/define.go b/mageutil/define.go index b8594c3..d146df3 100644 --- a/mageutil/define.go +++ b/mageutil/define.go @@ -1,6 +1,7 @@ package mageutil import ( + "fmt" "os" "runtime" @@ -23,18 +24,18 @@ type Config struct { MaxFileDescriptors int `yaml:"maxFileDescriptors"` } -func InitForSSC() { +func InitForSSC() error { yamlFile, err := os.ReadFile(StartConfigFile) if err != nil { PrintRed("error reading YAML file: " + err.Error()) - os.Exit(1) + return fmt.Errorf("error reading YAML file: %w", err) } var config Config err = yaml.Unmarshal(yamlFile, &config) if err != nil { PrintRed("error unmarshalling YAML: " + err.Error()) - os.Exit(1) + return fmt.Errorf("error unmarshalling YAML: %w", err) } adjustedBinaries := make(map[string]int) @@ -55,4 +56,5 @@ func InitForSSC() { serviceBinaries = adjustedBinaries toolBinaries = adjustedToolsBinaries MaxFileDescriptors = config.MaxFileDescriptors + return nil } diff --git a/mageutil/export.go b/mageutil/export.go index c7137ce..e104f61 100644 --- a/mageutil/export.go +++ b/mageutil/export.go @@ -34,7 +34,9 @@ func (opt *ExportOptions) GetBuildOpt() *BuildOptions { func ExportMageLauncherArchived(overrideMappingPaths map[string]string, exportOpt *ExportOptions) error { PrintBlue("Preparing launcher archive export...") PrintBlue("Building binaries before export...") - Build(nil, nil, exportOpt.GetBuildOpt()) + if err := Build(nil, nil, exportOpt.GetBuildOpt()); err != nil { + return err + } tmpDir := Paths.OutputTmp exportDir := Paths.OutputExport @@ -49,7 +51,11 @@ func ExportMageLauncherArchived(overrideMappingPaths map[string]string, exportOp platforms := os.Getenv("PLATFORMS") if platforms == "" { - platforms = DetectPlatform() + platform, err := DetectPlatform() + if err != nil { + return err + } + platforms = platform } platformList := strings.Fields(platforms) diff --git a/mageutil/gen_protocol.go b/mageutil/gen_protocol.go index f450b76..172296e 100644 --- a/mageutil/gen_protocol.go +++ b/mageutil/gen_protocol.go @@ -134,27 +134,27 @@ func getProtocArch(archMap map[string]string, goArch string) string { func Protocol() error { if err := ensureToolsInstalled(); err != nil { PrintRed("error " + err.Error()) - os.Exit(1) + return err } moduleName, err := getModuleNameFromGoMod() if err != nil { PrintRed("error fetching module name from go.mod: " + err.Error()) - os.Exit(1) + return err } protoPath := "./pkg/protocol" dirs, err := os.ReadDir(protoPath) if err != nil { PrintRed("error " + err.Error()) - os.Exit(1) + return err } for _, dir := range dirs { if dir.IsDir() { if err := compileProtoFiles(protoPath, dir.Name(), moduleName); err != nil { PrintRed("error " + err.Error()) - os.Exit(1) + return err } } } diff --git a/mageutil/logging.go b/mageutil/logging.go index 303ac76..bb203e2 100644 --- a/mageutil/logging.go +++ b/mageutil/logging.go @@ -8,6 +8,8 @@ import ( "strings" "sync" "time" + + "github.com/pterm/pterm" ) const ( @@ -63,8 +65,7 @@ func Print(opt PrintOptions) (int, error) { logWriteMu.Lock() defer logWriteMu.Unlock() - nLocal, errLocal := io.WriteString(consoleWriter, consoleMessage) - n, err = nLocal, errLocal + n, err = writeConsoleMessage(consoleWriter, consoleMessage) logFile, logErr := getSharedLogFile() if logErr != nil { @@ -79,6 +80,15 @@ func Print(opt PrintOptions) (int, error) { return n, err } +func writeConsoleMessage(writer io.Writer, message string) (int, error) { + if spinner := activeSpinner.Load(); spinner != nil && spinner.enabled && (writer == os.Stdout || writer == os.Stderr) { + pterm.Fprint(writer, message) + spinner.Refresh() + return len(message), nil + } + return io.WriteString(writer, message) +} + func formatPrintMessage(opt PrintOptions, timeFmt string, withColor bool) string { var b strings.Builder @@ -108,19 +118,6 @@ func formatPrintMessage(opt PrintOptions, timeFmt string, withColor bool) string return b.String() } -func openDetachedCommandLogFile() (*os.File, error) { - path, err := logFilePath() - if err != nil { - return nil, err - } - - logFile, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) - if err != nil { - return nil, fmt.Errorf("failed to open log file %s: %w", path, err) - } - return logFile, nil -} - func getSharedLogFile() (*os.File, error) { path, err := logFilePath() if err != nil { diff --git a/mageutil/paths.go b/mageutil/paths.go index 7aee292..a72b41e 100644 --- a/mageutil/paths.go +++ b/mageutil/paths.go @@ -206,12 +206,12 @@ func (p *PathConfig) GetBinFullPath(binName string) string { return filepath.Join(p.OutputHostBin, binName) } -// GetToolFullPath returns the full path for a tool +// GetBinToolsFullPath GetToolFullPath returns the full path for a tool func (p *PathConfig) GetBinToolsFullPath(toolName string) string { return filepath.Join(p.OutputHostBinTools, toolName) } -// Compatibility: maintain original global functions +// GetBinFullPath Compatibility: maintain original global functions func GetBinFullPath(binName string) string { return Paths.GetBinFullPath(binName) } diff --git a/mageutil/process_controller.go b/mageutil/process_controller.go index 78bf4b3..e0259d9 100644 --- a/mageutil/process_controller.go +++ b/mageutil/process_controller.go @@ -48,20 +48,19 @@ func StartBinaries(specificBinaries ...string) error { configPath = Paths.K8sConfig } args := []string{"-i", strconv.Itoa(i), "-c", configPath} - cmd := NewCmd(binFullPath).WithArgs(args...) - PrintBlue(fmt.Sprintf("Starting %s", cmd.String())) - logFile, err := openDetachedCommandLogFile() + logFile, err := getSharedLogFile() if err != nil { return err } - cmd.WithDir(Paths.OutputHostBin) - cmd.WithStdout(logFile) - cmd.WithStderr(logFile) + cmd := NewCmd(binFullPath). + WithArgs(args...). + WithDir(Paths.OutputHostBin). + WithStdout(logFile). + WithStderr(logFile) + PrintBlue(fmt.Sprintf("Starting %s", cmd.String())) if err := cmd.Start(); err != nil { - _ = logFile.Close() return fmt.Errorf("failed to start %s with args %v: %v", binFullPath, args, err) } - _ = logFile.Close() } } return nil @@ -95,23 +94,15 @@ func StartTools(specificTools ...string) error { configPath = Paths.K8sConfig } - logFile, err := openDetachedCommandLogFile() - if err != nil { - return err - } cmd := NewCmd(toolFullPath). WithArgs("-c", configPath). - WithDir(Paths.OutputHostBinTools). - WithStdout(logFile). - WithStderr(logFile) + WithDir(Paths.OutputHostBinTools) PrintBlue(fmt.Sprintf("Starting %s", cmd.String())) if err := cmd.Run(); err != nil { - _ = logFile.Close() return fmt.Errorf("failed to run %s with error: %v", toolFullPath, err) } - _ = logFile.Close() PrintGreen(fmt.Sprintf("Starting %s successfully", cmd.String())) } return nil diff --git a/mageutil/spinner.go b/mageutil/spinner.go index aec3d23..fcc0a38 100644 --- a/mageutil/spinner.go +++ b/mageutil/spinner.go @@ -1,58 +1,56 @@ package mageutil import ( - "fmt" + "os" "strings" "sync" "sync/atomic" "time" "github.com/openimsdk/gomake/internal/util" + "github.com/pterm/pterm" ) -const ( - ESCEraseLine = "\033[2K" -) +const spinnerDelay = 120 * time.Millisecond -var ( - activeSpinner atomic.Pointer[Spinner] - spinnerFrames = []string{"|", "/", "-", "\\"} - spinnerRenderInterval = 120 * time.Millisecond - globalPauseDepth atomic.Int32 -) +var activeSpinner atomic.Pointer[Spinner] type Spinner struct { + printer *pterm.SpinnerPrinter enabled bool stopOnce sync.Once - stopCh chan struct{} - doneCh chan struct{} - - message atomic.Value - - start time.Time } func NewSpinner(message string) *Spinner { msg := strings.TrimSpace(message) - s := &Spinner{ - enabled: util.StdoutIsTerminal(), - stopCh: make(chan struct{}), - doneCh: make(chan struct{}), - start: time.Now(), + enabled: util.StderrIsTerminal(), } - s.message.Store(msg) if !s.enabled { - close(s.doneCh) return s } - inactive := activeSpinner.Swap(s) - if inactive != nil { + if inactive := activeSpinner.Swap(nil); inactive != nil { inactive.Stop() } - go s.run() + + printer, err := pterm.DefaultSpinner. + WithWriter(os.Stderr). + WithDelay(spinnerDelay). + WithRemoveWhenDone(true). + WithShowTimer(false). + WithStyle(pterm.NewStyle(pterm.FgMagenta)). + WithMessageStyle(pterm.NewStyle(pterm.FgMagenta)). + WithSequence("|", "/", "-", "\\"). + Start(msg) + if err != nil { + s.enabled = false + return s + } + + s.printer = printer + activeSpinner.Store(s) return s } @@ -62,7 +60,7 @@ func WithSpinner(message string, fn func()) { fn() } -func WithSpinnerE(message string, fn func() error) error { +func WithSpinnerR[R any](message string, fn func() R) R { spinner := NewSpinner(message) defer spinner.Stop() return fn() @@ -70,56 +68,30 @@ func WithSpinnerE(message string, fn func() error) error { func (s *Spinner) Stop() { s.stopOnce.Do(func() { - if !s.enabled { + if !s.enabled || s.printer == nil { return } - close(s.stopCh) - <-s.doneCh - if activeSpinner.CompareAndSwap(s, nil) { - fmt.Printf("\r%s", ESCEraseLine) - } + _ = s.printer.Stop() + time.Sleep(s.printer.Delay) + clearSpinnerLine() + activeSpinner.CompareAndSwap(s, nil) }) } -func (s *Spinner) run() { - defer close(s.doneCh) - - if globalPauseDepth.Load() == 0 { - s.render() - } - - timer := time.NewTimer(spinnerRenderInterval) - defer timer.Stop() - - for { - elapsed := time.Since(s.start) - rem := elapsed % spinnerRenderInterval - wait := spinnerRenderInterval - rem - if wait <= 0 { - wait = spinnerRenderInterval - } - timer.Reset(wait) - - select { - case <-s.stopCh: - return - case <-timer.C: - if globalPauseDepth.Load() > 0 { - continue - } - s.render() - } +func clearSpinnerLine() { + if pterm.RawOutput || !pterm.Output { + return } + pterm.Fprinto(os.Stderr, strings.Repeat(" ", pterm.GetTerminalWidth())) + pterm.Fprinto(os.Stderr) } -func (s *Spinner) render() { - elapsed := time.Since(s.start) - step := int(elapsed / spinnerRenderInterval) - frame := spinnerFrames[step%len(spinnerFrames)] - msg := s.message.Load().(string) - - fmt.Printf("\r%s%s %s%s", ColorMagenta, frame, msg, ColorReset) +func (s *Spinner) Refresh() { + if !s.enabled || s.printer == nil || !s.printer.IsActive { + return + } + s.printer.UpdateText(s.printer.Text) } func StopSpinner() { @@ -128,37 +100,16 @@ func StopSpinner() { } } -func PauseSpinner() { - if sp := activeSpinner.Load(); sp == nil || !sp.enabled { - return - } - globalPauseDepth.Add(1) - fmt.Printf("\r%s", ESCEraseLine) -} - -func ResumeSpinner() { - if sp := activeSpinner.Load(); sp == nil || !sp.enabled { - return - } - - for { - d := globalPauseDepth.Load() - if d <= 0 { - return - } - if globalPauseDepth.CompareAndSwap(d, d-1) { - if d-1 == 0 { - if sp := activeSpinner.Load(); sp != nil { - sp.render() - } - } - return - } +func RefreshSpinner() { + if sp := activeSpinner.Load(); sp != nil { + sp.Refresh() } } func WithActiveSpinnerPaused(fn func()) { - PauseSpinner() - defer ResumeSpinner() fn() } + +func WithActiveSpinnerPausedR[R any](fn func() R) R { + return fn() +} diff --git a/mageutil/sys.go b/mageutil/sys.go index 8cce321..744a354 100644 --- a/mageutil/sys.go +++ b/mageutil/sys.go @@ -2,11 +2,11 @@ package mageutil import ( "fmt" - "os" "runtime" "strings" "github.com/openimsdk/gomake/internal/util" + "github.com/openimsdk/tools/utils/datautil" "github.com/shirou/gopsutil/v4/net" "github.com/shirou/gopsutil/v4/process" ) @@ -120,10 +120,7 @@ func PrintBinaryPorts(binaryPath string, pidMap map[string][]int) { if len(portsMap) == 0 { PrintGreen(fmt.Sprintf("Cmdline: %s, PID: %d is not listening on any ports.", cmdline, pid)) } else { - ports := make([]string, 0, len(portsMap)) - for port := range portsMap { - ports = append(ports, port) - } + ports := datautil.Keys(portsMap) PrintGreen(fmt.Sprintf("Cmdline: %s, PID: %d is listening on ports: %s", cmdline, pid, strings.Join(ports, ", "))) } } @@ -217,28 +214,17 @@ func KillExistBinary(binaryPath string) { } // DetectPlatform detects the operating system and architecture. -func DetectPlatform() string { +func DetectPlatform() (string, error) { targetOS, targetArch := runtime.GOOS, runtime.GOARCH switch targetArch { case "amd64", "arm64": default: - PrintRed(fmt.Sprintf("Unsupported architecture: %s", targetArch)) - os.Exit(1) - } - return fmt.Sprintf("%s_%s", targetOS, targetArch) -} - -// rootDir gets the absolute path of the current directory. -func rootDir() string { - dir, err := os.Getwd() - if err != nil { - PrintRed(fmt.Sprintf("Failed to get current directory: %v", err)) - os.Exit(1) + err := fmt.Errorf("unsupported architecture: %s", targetArch) + PrintRed(err.Error()) + return "", err } - return dir + return fmt.Sprintf("%s_%s", targetOS, targetArch), nil } -var rootDirPath = rootDir() - // var platformsOutputBase = filepath.Join(rootDirPath, "_output/bin/platforms") // var toolsOutputBase = filepath.Join(rootDirPath, "_output/bin/tools") From 0531bbc6b5cd4ee5afaff401f2978848a82424ec Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Mon, 1 Jun 2026 18:08:17 +0800 Subject: [PATCH 06/10] refactor(core): extract process utilities --- cmd/microservice-test/main.go | 4 +- internal/util/io.go | 4 + internal/util/process.go | 50 +++++++++ magefile.go | 6 +- mageutil/basic.go | 34 +++--- mageutil/build.go | 18 +-- mageutil/define.go | 4 +- mageutil/export.go | 4 +- mageutil/gen_protocol.go | 11 +- mageutil/logging.go | 195 ++++++++++++++++++--------------- mageutil/process_controller.go | 16 ++- mageutil/spinner.go | 8 -- mageutil/sys.go | 77 ++----------- 13 files changed, 225 insertions(+), 206 deletions(-) create mode 100644 internal/util/process.go diff --git a/cmd/microservice-test/main.go b/cmd/microservice-test/main.go index e29686a..b49bff0 100644 --- a/cmd/microservice-test/main.go +++ b/cmd/microservice-test/main.go @@ -26,7 +26,7 @@ func main() { listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) if err != nil { - mageutil.PrintRed(fmt.Sprintf("Failed to listen on port %d: %v", port, err)) + mageutil.PrintErrRed(fmt.Sprintf("Failed to listen on port %d: %v", port, err)) os.Exit(1) } defer listener.Close() @@ -39,7 +39,7 @@ func main() { // Start serving, using the listener we created if err := http.Serve(listener, nil); err != nil { - mageutil.PrintRed(fmt.Sprintf("HTTP server exited: %v", err)) + mageutil.PrintErrRed(fmt.Sprintf("HTTP server exited: %v", err)) os.Exit(1) } } diff --git a/internal/util/io.go b/internal/util/io.go index 419ca9e..6e763a3 100644 --- a/internal/util/io.go +++ b/internal/util/io.go @@ -9,3 +9,7 @@ import ( func MultiWriter(writers ...io.Writer) io.Writer { return io.MultiWriter(datautil.Filter(writers, func(w io.Writer) (io.Writer, bool) { return w, w != nil })...) } + +type WriterFunc func(p []byte) (n int, err error) + +func (f WriterFunc) Write(p []byte) (n int, err error) { return f(p) } diff --git a/internal/util/process.go b/internal/util/process.go new file mode 100644 index 0000000..80edf4c --- /dev/null +++ b/internal/util/process.go @@ -0,0 +1,50 @@ +package util + +import ( + "github.com/openimsdk/tools/utils/datautil" + "github.com/shirou/gopsutil/v4/process" +) + +func ProcessesByExePath() (map[string][]*process.Process, error) { + processes, err := process.Processes() + if err != nil { + return nil, err + } + + processMap := make(map[string][]*process.Process) + for _, p := range processes { + exePath, err := p.Exe() + if err != nil { + continue + } + exePath = NormalizeExePath(exePath) + processMap[exePath] = append(processMap[exePath], p) + } + return processMap, nil +} + +func ProcessCountByExePath() (map[string]int, error) { + processMap, err := ProcessesByExePath() + if err != nil { + return nil, err + } + + countMap := make(map[string]int, len(processMap)) + for exePath, processes := range processMap { + countMap[exePath] = len(processes) + } + return countMap, nil +} + +func PIDsByExePath() (map[string][]int, error) { + processMap, err := ProcessesByExePath() + if err != nil { + return nil, err + } + + pidMap := make(map[string][]int, len(processMap)) + for exePath, processes := range processMap { + pidMap[exePath] = datautil.Slice(processes, func(e *process.Process) int { return int(e.Pid) }) + } + return pidMap, nil +} diff --git a/magefile.go b/magefile.go index 6d02243..425bf2a 100644 --- a/magefile.go +++ b/magefile.go @@ -67,7 +67,7 @@ func Start() error { } err := setMaxOpenFiles() if err != nil { - mageutil.PrintRed("setMaxOpenFiles failed " + err.Error()) + mageutil.PrintErrRed("setMaxOpenFiles failed " + err.Error()) return err } @@ -88,7 +88,7 @@ func StartWithCustomConfig() error { } err := setMaxOpenFiles() if err != nil { - mageutil.PrintRed("setMaxOpenFiles failed " + err.Error()) + mageutil.PrintErrRed("setMaxOpenFiles failed " + err.Error()) return err } @@ -130,7 +130,7 @@ func Export() error { return mageutil.ExportMageLauncherArchived(nil, exportOpt) }) if err != nil { - mageutil.PrintRed("export failed " + err.Error()) + mageutil.PrintErrRed("export failed " + err.Error()) return err } return nil diff --git a/mageutil/basic.go b/mageutil/basic.go index ac89305..019a5bb 100644 --- a/mageutil/basic.go +++ b/mageutil/basic.go @@ -16,8 +16,8 @@ func CheckAndReportBinariesStatus() error { } err := CheckBinariesRunning() if err != nil { - PrintRed("Some programs are not running properly:") - PrintRedNoTimeStamp(err.Error()) + PrintErrRed("Some programs are not running properly:") + PrintErrRedNoTimeStamp(err.Error()) return err } PrintGreen("All services are running normally.") @@ -25,8 +25,8 @@ func CheckAndReportBinariesStatus() error { time.Sleep(1 * time.Second) err = PrintListenedPortsByBinaries() if err != nil { - PrintRed("PrintListenedPortsByBinaries error") - PrintRedNoTimeStamp(err.Error()) + PrintErrRed("PrintListenedPortsByBinaries error") + PrintErrRedNoTimeStamp(err.Error()) return err } return nil @@ -39,7 +39,7 @@ func StopAndCheckBinaries() error { KillExistBinaries() err := attemptCheckBinaries() if err != nil { - PrintRed(err.Error()) + PrintErrRed(err.Error()) return err } PrintGreen("All services have been stopped") @@ -66,7 +66,7 @@ func attemptCheckBinaries() error { func StartToolsAndServices(binaries []string, pathOpts *PathOptions) error { if pathOpts != nil { if err := UpdateGlobalPaths(pathOpts); err != nil { - PrintRed("Failed to update paths: " + err.Error()) + PrintErrRed("Failed to update paths: " + err.Error()) return err } } @@ -102,8 +102,8 @@ func StartToolsAndServices(binaries []string, pathOpts *PathOptions) error { if len(toolsBinaries) > 0 { PrintBlue("Starting specified tools...") if err := StartTools(toolsBinaries...); err != nil { - PrintRed("Some specified tools failed to start:") - PrintRedNoTimeStamp(err.Error()) + PrintErrRed("Some specified tools failed to start:") + PrintErrRedNoTimeStamp(err.Error()) return err } PrintGreen("Specified tools executed successfully") @@ -113,13 +113,13 @@ func StartToolsAndServices(binaries []string, pathOpts *PathOptions) error { KillExistBinaries() err := attemptCheckBinaries() if err != nil { - PrintRed("Some services running, details are as follows, abort start " + err.Error()) + PrintErrRed("Some services running, details are as follows, abort start " + err.Error()) return err } err = StartBinaries(cmdBinaries...) if err != nil { - PrintRed("Failed to start specified binaries:") - PrintRedNoTimeStamp(err.Error()) + PrintErrRed("Failed to start specified binaries:") + PrintErrRedNoTimeStamp(err.Error()) return err } return CheckAndReportBinariesStatus() @@ -129,8 +129,8 @@ func StartToolsAndServices(binaries []string, pathOpts *PathOptions) error { PrintBlue("Starting tools primarily involves component verification and other preparatory tasks.") if err := StartTools(); err != nil { - PrintRed("Some tools failed to start, details are as follows, abort start") - PrintRedNoTimeStamp(err.Error()) + PrintErrRed("Some tools failed to start, details are as follows, abort start") + PrintErrRedNoTimeStamp(err.Error()) return err } PrintGreen("All tools executed successfully") @@ -138,13 +138,13 @@ func StartToolsAndServices(binaries []string, pathOpts *PathOptions) error { KillExistBinaries() err := attemptCheckBinaries() if err != nil { - PrintRed("Some services running, details are as follows, abort start " + err.Error()) + PrintErrRed("Some services running, details are as follows, abort start " + err.Error()) return err } err = StartBinaries() if err != nil { - PrintRed("Failed to start all binaries") - PrintRedNoTimeStamp(err.Error()) + PrintErrRed("Failed to start all binaries") + PrintErrRedNoTimeStamp(err.Error()) return err } return CheckAndReportBinariesStatus() @@ -187,7 +187,7 @@ func Build(binaries []string, pathOpts *PathOptions, buildOpt *BuildOptions) err if pathOpts != nil { if err := UpdateGlobalPaths(pathOpts); err != nil { - PrintRed("Failed to update paths: " + err.Error()) + PrintErrRed("Failed to update paths: " + err.Error()) return err } } diff --git a/mageutil/build.go b/mageutil/build.go index 536be19..f0c7aa4 100644 --- a/mageutil/build.go +++ b/mageutil/build.go @@ -107,11 +107,11 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, if os.IsNotExist(err) { return nil, nil } - PrintRed(fmt.Sprintf("Failed read directory %s: %v", sourceDir, err)) + PrintErrRed(fmt.Sprintf("Failed read directory %s: %v", sourceDir, err)) return nil, err } else if !info.IsDir() { err := fmt.Errorf("%s is not dir", sourceDir) - PrintRed("Failed " + err.Error()) + PrintErrRed("Failed " + err.Error()) return nil, err } @@ -123,7 +123,7 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, outputDir := filepath.Join(outputBase, targetOS, targetArch) if err := os.MkdirAll(outputDir, 0755); err != nil { - PrintRed(fmt.Sprintf("Failed to create directory %s: %v", outputDir, err)) + PrintErrRed(fmt.Sprintf("Failed to create directory %s: %v", outputDir, err)) return nil, err } @@ -199,7 +199,7 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, relPath, err := filepath.Rel(goModDir, path) if err != nil { - PrintRed(fmt.Sprintf("Failed to get relative path: %v", err)) + PrintErrRed(fmt.Sprintf("Failed to get relative path: %v", err)) errCh <- err return } @@ -220,11 +220,13 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, WithDir(goModDir). WithEnv(env). WithPriority(priority.Low). + WithStdout(GetStdoutInnerLogWriter()). + WithStderr(GetStderrInnerLogWriter()). Run() if err != nil { err = fmt.Errorf("failed to compile %s for %s: %w", dirName, platform, err) - PrintRed("Compilation aborted. " + err.Error()) + PrintErrRed("Compilation aborted. " + err.Error()) errCh <- err return } @@ -235,7 +237,9 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, PrintBlue(fmt.Sprintf("Compressing %s with UPX...", outputFileName)) cmd := NewCmd("upx"). WithArgs("--lzma", outputPath). - WithPriority(priority.Low) + WithPriority(priority.Low). + WithStdout(GetStdoutInnerLogWriter()). + WithStderr(GetStderrInnerLogWriter()) if err := cmd.Run(); err != nil { PrintYellow(fmt.Sprintf("UPX compression failed for %s (non-fatal): %v", outputFileName, err)) } else { @@ -286,7 +290,7 @@ func createStartConfigYML(cmdDirs, toolsDirs []string) error { err := os.WriteFile(configPath, []byte(content.String()), 0644) if err != nil { - PrintRed("Failed to create start-config.yml: " + err.Error()) + PrintErrRed("Failed to create start-config.yml: " + err.Error()) return err } PrintGreen("start-config.yml created successfully.") diff --git a/mageutil/define.go b/mageutil/define.go index d146df3..a2e8734 100644 --- a/mageutil/define.go +++ b/mageutil/define.go @@ -27,14 +27,14 @@ type Config struct { func InitForSSC() error { yamlFile, err := os.ReadFile(StartConfigFile) if err != nil { - PrintRed("error reading YAML file: " + err.Error()) + PrintErrRed("error reading YAML file: " + err.Error()) return fmt.Errorf("error reading YAML file: %w", err) } var config Config err = yaml.Unmarshal(yamlFile, &config) if err != nil { - PrintRed("error unmarshalling YAML: " + err.Error()) + PrintErrRed("error unmarshalling YAML: " + err.Error()) return fmt.Errorf("error unmarshalling YAML: %w", err) } diff --git a/mageutil/export.go b/mageutil/export.go index e104f61..e3295e2 100644 --- a/mageutil/export.go +++ b/mageutil/export.go @@ -78,7 +78,9 @@ func ExportMageLauncherArchived(overrideMappingPaths map[string]string, exportOp PrintBlue(fmt.Sprintf("Compiling mage binary for %s: mage -compile %s", platform, mageBinaryPath)) cmd := NewCmd("mage"). WithArgs("-compile", mageBinaryPath, "-goos", targetOS, "-goarch", targetArch, "-ldflags", "-s -w"). - WithDir(Paths.Root) + WithDir(Paths.Root). + WithStdout(GetStdoutInnerLogWriter()). + WithStderr(GetStderrInnerLogWriter()) if err := cmd.Run(); err != nil { return fmt.Errorf("failed to compile mage for %s: %v", platform, err) } diff --git a/mageutil/gen_protocol.go b/mageutil/gen_protocol.go index 172296e..85104a2 100644 --- a/mageutil/gen_protocol.go +++ b/mageutil/gen_protocol.go @@ -4,7 +4,6 @@ import ( "archive/zip" "bufio" "fmt" - "github.com/magefile/mage/sh" "io" "net/http" "os" @@ -12,6 +11,8 @@ import ( "path/filepath" "runtime" "strings" + + "github.com/magefile/mage/sh" ) func ensureToolsInstalled() error { @@ -133,27 +134,27 @@ func getProtocArch(archMap map[string]string, goArch string) string { func Protocol() error { if err := ensureToolsInstalled(); err != nil { - PrintRed("error " + err.Error()) + PrintErrRed("error " + err.Error()) return err } moduleName, err := getModuleNameFromGoMod() if err != nil { - PrintRed("error fetching module name from go.mod: " + err.Error()) + PrintErrRed("error fetching module name from go.mod: " + err.Error()) return err } protoPath := "./pkg/protocol" dirs, err := os.ReadDir(protoPath) if err != nil { - PrintRed("error " + err.Error()) + PrintErrRed("error " + err.Error()) return err } for _, dir := range dirs { if dir.IsDir() { if err := compileProtoFiles(protoPath, dir.Name(), moduleName); err != nil { - PrintRed("error " + err.Error()) + PrintErrRed("error " + err.Error()) return err } } diff --git a/mageutil/logging.go b/mageutil/logging.go index bb203e2..84d5098 100644 --- a/mageutil/logging.go +++ b/mageutil/logging.go @@ -1,6 +1,7 @@ package mageutil import ( + "errors" "fmt" "io" "os" @@ -9,6 +10,7 @@ import ( "sync" "time" + "github.com/openimsdk/gomake/internal/util" "github.com/pterm/pterm" ) @@ -26,12 +28,94 @@ const defaultTimeFmt = "[2006-01-02 15:04:05 MST]" const defaultLogFileName = "gomake.log" var ( + defaultStdout = os.Stdout + defaultStderr = os.Stderr + logFileStateMu sync.Mutex logWriteMu sync.Mutex sharedLogFile *os.File sharedLogPath string ) +func writeConsoleMessage(writer io.Writer, message string) (int, error) { + if spinner := activeSpinner.Load(); spinner != nil && spinner.enabled && (writer == os.Stdout || writer == os.Stderr) { + pterm.Fprint(writer, message) + spinner.Refresh() + return len(message), nil + } + return io.WriteString(writer, message) +} + +func GetSharedLogFile() (*os.File, error) { + path, err := logFilePath() + if err != nil { + return nil, err + } + + logFileStateMu.Lock() + defer logFileStateMu.Unlock() + + if sharedLogFile != nil && sharedLogPath == path { + return sharedLogFile, nil + } + + if sharedLogFile != nil { + _ = sharedLogFile.Close() + sharedLogFile = nil + sharedLogPath = "" + } + + logFile, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) + if err != nil { + return nil, fmt.Errorf("failed to open log file %s: %w", path, err) + } + + sharedLogFile = logFile + sharedLogPath = path + return sharedLogFile, nil +} + +func GetSharedLogFileWithoutError() *os.File { + logFile, err := GetSharedLogFile() + if err != nil { + _, _ = writeConsoleMessage(defaultStdout, err.Error()) + return nil + } + return logFile +} + +func logFilePath() (string, error) { + if Paths == nil { + return "", fmt.Errorf("paths are not initialized") + } + + logDir := strings.TrimSpace(Paths.OutputLogs) + if logDir == "" { + return "", fmt.Errorf("log directory is empty") + } + + logDir = filepath.Clean(logDir) + if err := os.MkdirAll(logDir, 0755); err != nil { + return "", fmt.Errorf("failed to create log directory %s: %w", logDir, err) + } + + return filepath.Join(logDir, defaultLogFileName), nil +} + +func GetStdoutInnerLogWriter() io.Writer { + return util.MultiWriter( + util.WriterFunc(func(p []byte) (n int, err error) { return writeConsoleMessage(defaultStdout, string(p)) }), + GetSharedLogFileWithoutError(), + ) +} + +func GetStderrInnerLogWriter() io.Writer { + return util.MultiWriter( + util.WriterFunc(func(p []byte) (n int, err error) { return writeConsoleMessage(defaultStderr, string(p)) }), + GetSharedLogFileWithoutError(), + ) +} + type PrintOptions struct { Writer io.Writer Color string @@ -42,51 +126,38 @@ type PrintOptions struct { TimeFmt string } -func Print(opt PrintOptions) (int, error) { +func Print(opt PrintOptions) error { tf := opt.TimeFmt if tf == "" { tf = defaultTimeFmt } var ( - n int err error ) - WithActiveSpinnerPaused(func() { - consoleWriter := opt.Writer - if consoleWriter == nil { - consoleWriter = os.Stdout - } - - consoleMessage := formatPrintMessage(opt, tf, true) - fileMessage := formatPrintMessage(opt, tf, false) + if opt.Writer == nil { + return errors.New("no output writer") + } - logWriteMu.Lock() - defer logWriteMu.Unlock() + consoleMessage := formatPrintMessage(opt, tf, true) + fileMessage := formatPrintMessage(opt, tf, false) - n, err = writeConsoleMessage(consoleWriter, consoleMessage) + logWriteMu.Lock() + defer logWriteMu.Unlock() - logFile, logErr := getSharedLogFile() - if logErr != nil { - return - } + _, err = writeConsoleMessage(opt.Writer, consoleMessage) - if _, logErr = io.WriteString(logFile, fileMessage); err == nil && logErr != nil { - err = logErr - } - }) - - return n, err -} + logFile, logErr := GetSharedLogFile() + if logErr != nil { + return logErr + } -func writeConsoleMessage(writer io.Writer, message string) (int, error) { - if spinner := activeSpinner.Load(); spinner != nil && spinner.enabled && (writer == os.Stdout || writer == os.Stderr) { - pterm.Fprint(writer, message) - spinner.Refresh() - return len(message), nil + if _, logErr = io.WriteString(logFile, fileMessage); err == nil && logErr != nil { + err = logErr } - return io.WriteString(writer, message) + + return err } func formatPrintMessage(opt PrintOptions, timeFmt string, withColor bool) string { @@ -118,66 +189,18 @@ func formatPrintMessage(opt PrintOptions, timeFmt string, withColor bool) string return b.String() } -func getSharedLogFile() (*os.File, error) { - path, err := logFilePath() - if err != nil { - return nil, err - } - - logFileStateMu.Lock() - defer logFileStateMu.Unlock() - - if sharedLogFile != nil && sharedLogPath == path { - return sharedLogFile, nil - } - - if sharedLogFile != nil { - _ = sharedLogFile.Close() - sharedLogFile = nil - sharedLogPath = "" - } - - logFile, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) - if err != nil { - return nil, fmt.Errorf("failed to open log file %s: %w", path, err) - } - - sharedLogFile = logFile - sharedLogPath = path - return sharedLogFile, nil -} - -func logFilePath() (string, error) { - if Paths == nil { - return "", fmt.Errorf("paths are not initialized") - } - - logDir := strings.TrimSpace(Paths.OutputLogs) - if logDir == "" { - return "", fmt.Errorf("log directory is empty") - } - - logDir = filepath.Clean(logDir) - if err := os.MkdirAll(logDir, 0755); err != nil { - return "", fmt.Errorf("failed to create log directory %s: %w", logDir, err) - } - - return filepath.Join(logDir, defaultLogFileName), nil -} - func PrintBlue(message string) { - _, _ = Print(PrintOptions{Color: ColorBlue, Message: message, WithTime: true}) + _ = Print(PrintOptions{Color: ColorBlue, Message: message, WithTime: true, Writer: defaultStdout}) } func PrintGreen(message string) { - _, _ = Print(PrintOptions{Color: ColorGreen, Message: message, WithTime: true}) -} -func PrintRed(message string) { - _, _ = Print(PrintOptions{Color: ColorRed, Message: message, WithTime: true}) + _ = Print(PrintOptions{Color: ColorGreen, Message: message, WithTime: true, Writer: defaultStdout}) } func PrintYellow(message string) { - _, _ = Print(PrintOptions{Color: ColorYellow, Message: message, WithTime: true}) + _ = Print(PrintOptions{Color: ColorYellow, Message: message, WithTime: true, Writer: defaultStdout}) } - -func PrintRedNoTimeStamp(message string) { - _, _ = Print(PrintOptions{Color: ColorRed, Message: message, WithTime: false}) +func PrintErrRed(message string) { + _ = Print(PrintOptions{Color: ColorRed, Message: message, WithTime: true, Writer: defaultStderr}) +} +func PrintErrRedNoTimeStamp(message string) { + _ = Print(PrintOptions{Color: ColorRed, Message: message, WithTime: false, Writer: defaultStderr}) } diff --git a/mageutil/process_controller.go b/mageutil/process_controller.go index e0259d9..0628abf 100644 --- a/mageutil/process_controller.go +++ b/mageutil/process_controller.go @@ -38,7 +38,7 @@ func StartBinaries(specificBinaries ...string) error { binFullPath := filepath.Join(Paths.OutputHostBin, binary) if _, err := os.Stat(binFullPath); err != nil { - PrintRed(fmt.Sprintf("Binary not found: %s. Please build first.", binFullPath)) + PrintErrRed(fmt.Sprintf("Binary not found: %s. Please build first.", binFullPath)) continue } @@ -48,15 +48,11 @@ func StartBinaries(specificBinaries ...string) error { configPath = Paths.K8sConfig } args := []string{"-i", strconv.Itoa(i), "-c", configPath} - logFile, err := getSharedLogFile() - if err != nil { - return err - } cmd := NewCmd(binFullPath). WithArgs(args...). WithDir(Paths.OutputHostBin). - WithStdout(logFile). - WithStderr(logFile) + WithStdout(GetSharedLogFileWithoutError()). + WithStderr(GetSharedLogFileWithoutError()) PrintBlue(fmt.Sprintf("Starting %s", cmd.String())) if err := cmd.Start(); err != nil { return fmt.Errorf("failed to start %s with args %v: %v", binFullPath, args, err) @@ -85,7 +81,7 @@ func StartTools(specificTools ...string) error { toolFullPath := GetBinToolsFullPath(tool) if _, err := os.Stat(toolFullPath); err != nil { - PrintRed(fmt.Sprintf("Tool not found: %s. Please build first.", toolFullPath)) + PrintErrRed(fmt.Sprintf("Tool not found: %s. Please build first.", toolFullPath)) continue } @@ -96,7 +92,9 @@ func StartTools(specificTools ...string) error { cmd := NewCmd(toolFullPath). WithArgs("-c", configPath). - WithDir(Paths.OutputHostBinTools) + WithDir(Paths.OutputHostBinTools). + WithStdout(GetSharedLogFileWithoutError()). + WithStderr(GetSharedLogFileWithoutError()) PrintBlue(fmt.Sprintf("Starting %s", cmd.String())) if err := cmd.Run(); err != nil { diff --git a/mageutil/spinner.go b/mageutil/spinner.go index fcc0a38..4e673aa 100644 --- a/mageutil/spinner.go +++ b/mageutil/spinner.go @@ -105,11 +105,3 @@ func RefreshSpinner() { sp.Refresh() } } - -func WithActiveSpinnerPaused(fn func()) { - fn() -} - -func WithActiveSpinnerPausedR[R any](fn func() R) R { - return fn() -} diff --git a/mageutil/sys.go b/mageutil/sys.go index 744a354..dd3bc80 100644 --- a/mageutil/sys.go +++ b/mageutil/sys.go @@ -37,21 +37,11 @@ func CheckProcessNames(processPath string, expectedCount int, processMap map[str // FetchProcesses returns a map of executable paths to their running count. func FetchProcesses() (map[string]int, error) { - processMap := make(map[string]int) - processes, err := process.Processes() + processMap, err := util.ProcessCountByExePath() if err != nil { return nil, fmt.Errorf("failed to get processes: %v", err) } - for _, p := range processes { - exePath, err := p.Exe() - if err != nil { - continue // Skip processes where the executable path cannot be determined - } - exePath = util.NormalizeExePath(exePath) - processMap[exePath]++ - } - return processMap, nil } @@ -64,23 +54,11 @@ func CheckProcessInMap(processMap map[string]int, processPath string) bool { // FindPIDsByBinaryPath returns a map of executable paths to slices of PIDs. func FindPIDsByBinaryPath() (map[string][]int, error) { - pidMap := make(map[string][]int) - processes, err := process.Processes() + pidMap, err := util.PIDsByExePath() if err != nil { return nil, fmt.Errorf("failed to get processes: %v", err) } - for _, proc := range processes { - exePath, err := proc.Exe() - if err != nil { - // Ignore processes where the executable path cannot be determined - continue - } - - exePath = util.NormalizeExePath(exePath) - pidMap[exePath] = append(pidMap[exePath], int(proc.Pid)) - } - return pidMap, nil } func PrintBinaryPorts(binaryPath string, pidMap map[string][]int) { @@ -127,22 +105,12 @@ func PrintBinaryPorts(binaryPath string, pidMap map[string][]int) { } func BatchKillExistBinaries(binaryPaths []string) { - processes, err := process.Processes() + exePathMap, err := util.ProcessesByExePath() if err != nil { - PrintRed(fmt.Sprintf("Failed to get processes: %v", err)) + PrintErrRed(fmt.Sprintf("Failed to get processes: %v", err)) return } - exePathMap := make(map[string][]*process.Process) - for _, p := range processes { - exePath, err := p.Exe() - if err != nil { - continue // Skip processes where the executable path cannot be determined - } - exePath = util.NormalizeExePath(exePath) - exePathMap[exePath] = append(exePathMap[exePath], p) - } - for _, binaryPath := range binaryPaths { if procs, found := exePathMap[binaryPath]; found { PrintBlue(fmt.Sprintf("binaryPath found %s", binaryPath)) @@ -164,7 +132,7 @@ func terminateAndKillProcess(p *process.Process) { if err != nil { err = p.Kill() // Fallback to kill if terminate fails if err != nil { - PrintRed(fmt.Sprintf("Failed to kill process cmdline: %s, pid: %d, err: %v", cmdline, p.Pid, err)) + PrintErrRed(fmt.Sprintf("Failed to kill process cmdline: %s, pid: %d, err: %v", cmdline, p.Pid, err)) } else { PrintYellow(fmt.Sprintf("Killed process cmdline: %s, pid: %d", cmdline, p.Pid)) } @@ -175,39 +143,16 @@ func terminateAndKillProcess(p *process.Process) { // KillExistBinary kills all processes matching the given binary file path. func KillExistBinary(binaryPath string) { - processes, err := process.Processes() + exePathMap, err := util.ProcessesByExePath() if err != nil { - PrintRed(fmt.Sprintf("Failed to get processes: %v", err)) + PrintErrRed(fmt.Sprintf("Failed to get processes: %v", err)) return } - for _, p := range processes { - exePath, err := p.Exe() - if err != nil { - continue - } - - exePath = util.NormalizeExePath(exePath) + for exePath, procs := range exePathMap { if strings.Contains(exePath, binaryPath) { - - //if strings.EqualFold(exePath, binaryPath) { - cmdline, err := p.Cmdline() - if err != nil { - PrintYellow(fmt.Sprintf("Failed to get command line for process %d: %v", p.Pid, err)) - continue - } - - err = p.Terminate() - if err != nil { - - err = p.Kill() - if err != nil { - PrintRed(fmt.Sprintf("Failed to kill process cmdline: %s, pid: %d, err: %v", cmdline, p.Pid, err)) - } else { - PrintYellow(fmt.Sprintf("Killed process cmdline: %s, pid: %d", cmdline, p.Pid)) - } - } else { - PrintGreen(fmt.Sprintf("Terminated process cmdline: %s, pid: %d", cmdline, p.Pid)) + for _, p := range procs { + terminateAndKillProcess(p) } } } @@ -220,7 +165,7 @@ func DetectPlatform() (string, error) { case "amd64", "arm64": default: err := fmt.Errorf("unsupported architecture: %s", targetArch) - PrintRed(err.Error()) + PrintErrRed(err.Error()) return "", err } return fmt.Sprintf("%s_%s", targetOS, targetArch), nil From 3325bf702b97e34bcdcb44e30ab7d7276202eab2 Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Mon, 1 Jun 2026 18:58:24 +0800 Subject: [PATCH 07/10] fix(core): wait before checking ports --- mageutil/basic.go | 9 ++++++--- mageutil/sys.go | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mageutil/basic.go b/mageutil/basic.go index 019a5bb..a619e5a 100644 --- a/mageutil/basic.go +++ b/mageutil/basic.go @@ -10,6 +10,8 @@ import ( "github.com/openimsdk/gomake/internal/util" ) +const checkDelay = 3 * time.Second + func CheckAndReportBinariesStatus() error { if err := InitForSSC(); err != nil { return err @@ -21,8 +23,9 @@ func CheckAndReportBinariesStatus() error { return err } PrintGreen("All services are running normally.") + PrintGreen(fmt.Sprintf("Waiting for %v to check listened ports...", checkDelay)) + time.Sleep(checkDelay) PrintBlue("Display details of the ports listened to by the service:") - time.Sleep(1 * time.Second) err = PrintListenedPortsByBinaries() if err != nil { PrintErrRed("PrintListenedPortsByBinaries error") @@ -55,9 +58,9 @@ func attemptCheckBinaries() error { return nil } PrintYellow("Some services have not been stopped, details are as follows: " + err.Error()) - PrintYellow("Continue to wait for 1 second before checking again") + PrintYellow(fmt.Sprintf("Continue to wait for %v before checking again", checkDelay)) if i < maxAttempts-1 { - time.Sleep(1 * time.Second) + time.Sleep(checkDelay) } } return fmt.Errorf("already waited for %d seconds, some services have still not stopped", maxAttempts) diff --git a/mageutil/sys.go b/mageutil/sys.go index dd3bc80..b19aea4 100644 --- a/mageutil/sys.go +++ b/mageutil/sys.go @@ -61,6 +61,7 @@ func FindPIDsByBinaryPath() (map[string][]int, error) { return pidMap, nil } + func PrintBinaryPorts(binaryPath string, pidMap map[string][]int) { pids, exists := pidMap[binaryPath] if !exists || len(pids) == 0 { From 004ea38d679d0af16a2a246729b70039b130d0ab Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Tue, 2 Jun 2026 14:19:43 +0800 Subject: [PATCH 08/10] refactor(core): streamline mage build helpers Extract shared argument parsing, simplify build entrypoints, and improve error handling across mage utilities and test server. --- cmd/microservice-test/main.go | 4 +- go.mod | 26 ++++---- go.sum | 106 +++++++++------------------------ magefile.go | 80 +++++++++++-------------- mageutil/arg.go | 18 ++++++ mageutil/basic.go | 43 ++++--------- mageutil/build.go | 12 ++-- mageutil/define.go | 2 - mageutil/gen_protocol.go | 6 +- mageutil/logging.go | 64 +++++++++++--------- mageutil/process_controller.go | 16 +++-- mageutil/sys.go | 17 +++--- 12 files changed, 171 insertions(+), 223 deletions(-) create mode 100644 mageutil/arg.go diff --git a/cmd/microservice-test/main.go b/cmd/microservice-test/main.go index b49bff0..47a8b7d 100644 --- a/cmd/microservice-test/main.go +++ b/cmd/microservice-test/main.go @@ -26,7 +26,7 @@ func main() { listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) if err != nil { - mageutil.PrintErrRed(fmt.Sprintf("Failed to listen on port %d: %v", port, err)) + mageutil.PrintErr(fmt.Errorf("failed to listen on port %d: %w", port, err)) os.Exit(1) } defer listener.Close() @@ -39,7 +39,7 @@ func main() { // Start serving, using the listener we created if err := http.Serve(listener, nil); err != nil { - mageutil.PrintErrRed(fmt.Sprintf("HTTP server exited: %v", err)) + mageutil.PrintErr(fmt.Errorf("HTTP server exited: %w", err)) os.Exit(1) } } diff --git a/go.mod b/go.mod index 7bd1f0e..825188c 100644 --- a/go.mod +++ b/go.mod @@ -1,36 +1,36 @@ module github.com/openimsdk/gomake -go 1.24.0 +go 1.25.0 require ( github.com/bmatcuk/doublestar/v4 v4.10.0 - github.com/magefile/mage v1.15.0 + github.com/magefile/mage v1.17.2 github.com/openimsdk/tools v0.0.49 github.com/pterm/pterm v0.12.83 - github.com/shirou/gopsutil/v4 v4.26.2 - golang.org/x/sys v0.41.0 + github.com/shirou/gopsutil/v4 v4.26.5 + golang.org/x/sys v0.45.0 gopkg.in/yaml.v3 v3.0.1 ) require ( atomicgo.dev/cursor v0.2.0 // indirect - atomicgo.dev/keyboard v0.2.9 // indirect + atomicgo.dev/keyboard v0.2.10 // indirect atomicgo.dev/schedule v0.1.0 // indirect github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/containerd/console v1.0.5 // indirect - github.com/ebitengine/purego v0.10.0 // indirect + github.com/ebitengine/purego v0.10.1 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/gookit/color v1.6.0 // indirect + github.com/gookit/color v1.6.1 // indirect github.com/jinzhu/copier v0.4.0 // indirect github.com/lithammer/fuzzysearch v1.1.8 // indirect - github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect - github.com/mattn/go-runewidth v0.0.20 // indirect + github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e // indirect + github.com/mattn/go-runewidth v0.0.24 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/tklauser/go-sysconf v0.3.16 // indirect - github.com/tklauser/numcpus v0.11.0 // indirect + github.com/tklauser/go-sysconf v0.4.0 // indirect + github.com/tklauser/numcpus v0.12.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - golang.org/x/term v0.40.0 // indirect - golang.org/x/text v0.34.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect ) diff --git a/go.sum b/go.sum index 879d5d2..7fcda7e 100644 --- a/go.sum +++ b/go.sum @@ -2,32 +2,22 @@ atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ= atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw= atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= -atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= -atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= +atomicgo.dev/keyboard v0.2.10 h1:v7mvUKUZLHIggxULEIuWbT+WkkyQSgdbA201EziAhHU= +atomicgo.dev/keyboard v0.2.10/go.mod h1:ap/z5ilnhLqYq852m6kPeTq5Z6aESGWu5mzRpJlC6aI= atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= -github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= -github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= -github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= -github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= -github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI= -github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= -github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= -github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= -github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY= +github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= @@ -35,32 +25,20 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0= github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E= -github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= -github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= -github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA= -github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs= +github.com/gookit/color v1.6.1 h1:KoTnDxJPRgrL0SoX0f8rCFg2zI0t4E3GZZBMo2nN8LU= +github.com/gookit/color v1.6.1/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= -github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= -github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6+8hHTyWBeQ/P4Nb7dd4/0ohEcWQuM= -github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= -github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= -github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ= -github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/magefile/mage v1.17.2 h1:fyXVu1eadI8Ap1HCCNgEhJ5McIWiYhLR8uol64ZZc40= +github.com/magefile/mage v1.17.2/go.mod h1:Yj51kqllmsgFpvvSzgrZPK9WtluG3kUhFaBUVLo4feA= +github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU= +github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/openimsdk/tools v0.0.49 h1:yILTgOCqxlqJMc889fE99E5ZGa70v/E3hkCSeTnWl3s= github.com/openimsdk/tools v0.0.49/go.mod h1:oiSQU5Z6fzjxKFjbqDHImD8EmCIwClU1Rkur1sK12Po= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -69,33 +47,18 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= -github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= -github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= -github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= -github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= -github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= -github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= github.com/pterm/pterm v0.12.83 h1:ie+YmGmA727VuhxBlyGr74Ks+7McV6kT99IB8EU80aA= github.com/pterm/pterm v0.12.83/go.mod h1:xlgc6bFWyJIMtmLJvGim+L7jhSReilOlOnodeIYe4Tk= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI= -github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/shirou/gopsutil/v4 v4.26.5 h1:RPcBXkpz7kOj9PqGFQOlBPZHsyaPvPVQc098y9RmCNM= +github.com/shirou/gopsutil/v4 v4.26.5/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= -github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= -github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= -github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= -github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= +github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= +github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= +github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -118,42 +81,31 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/magefile.go b/magefile.go index 425bf2a..376df61 100644 --- a/magefile.go +++ b/magefile.go @@ -3,12 +3,12 @@ package main import ( - "flag" + "fmt" "github.com/openimsdk/gomake/mageutil" ) -var Default = Build +var Default = BuildAll var Aliases = map[string]any{ "buildcc": BuildWithCustomConfig, @@ -27,27 +27,21 @@ var ( customExportBuildOpt *mageutil.BuildOptions ) +func BuildAll() error { return Build(nil) } + // Build support specifical binary build. // -// Example: `mage build openim-api openim-rpc-user seq` -func Build() error { - flag.Parse() - bin := flag.Args() - if len(bin) != 0 { - bin = bin[1:] - } +// Example: `mage build -bins=openim-api,openim-rpc-user,seq` +func Build(bins *string) (err error) { + defer mageutil.PrintErrPtr(&err) return mageutil.WithSpinnerR("Building binaries...", func() error { - return mageutil.Build(bin, nil, nil) + return mageutil.Build(mageutil.ParseArgList(bins), nil, nil) }) } -func BuildWithCustomConfig() error { - flag.Parse() - bin := flag.Args() - if len(bin) != 0 { - bin = bin[1:] - } +func BuildWithCustomConfig(bins *string) (err error) { + defer mageutil.PrintErrPtr(&err) config := &mageutil.PathOptions{ RootDir: &customRootDir, // default is "."(current directory) @@ -57,45 +51,35 @@ func BuildWithCustomConfig() error { } return mageutil.WithSpinnerR("Building binaries with custom config...", func() error { - return mageutil.Build(bin, config, nil) + return mageutil.Build(mageutil.ParseArgList(bins), config, nil) }) } -func Start() error { +func Start(bins *string) (err error) { + defer mageutil.PrintErrPtr(&err) + if err := mageutil.InitForSSC(); err != nil { return err } - err := setMaxOpenFiles() + err = setMaxOpenFiles() if err != nil { - mageutil.PrintErrRed("setMaxOpenFiles failed " + err.Error()) - return err - } - - flag.Parse() - bin := flag.Args() - if len(bin) != 0 { - bin = bin[1:] + return fmt.Errorf("setMaxOpenFiles failed %w", err) } return mageutil.WithSpinnerR("Starting tools and services...", func() error { - return mageutil.StartToolsAndServices(bin, nil) + return mageutil.StartToolsAndServices(mageutil.ParseArgList(bins), nil) }) } -func StartWithCustomConfig() error { +func StartWithCustomConfig(bins *string) (err error) { + defer mageutil.PrintErrPtr(&err) + if err := mageutil.InitForSSC(); err != nil { return err } - err := setMaxOpenFiles() + err = setMaxOpenFiles() if err != nil { - mageutil.PrintErrRed("setMaxOpenFiles failed " + err.Error()) - return err - } - - flag.Parse() - bin := flag.Args() - if len(bin) != 0 { - bin = bin[1:] + return fmt.Errorf("setMaxOpenFiles failed %w", err) } config := &mageutil.PathOptions{ @@ -105,33 +89,37 @@ func StartWithCustomConfig() error { } return mageutil.WithSpinnerR("Starting tools and services with custom config...", func() error { - return mageutil.StartToolsAndServices(bin, config) + return mageutil.StartToolsAndServices(mageutil.ParseArgList(bins), config) }) } -func Stop() error { +func Stop() (err error) { + defer mageutil.PrintErrPtr(&err) return mageutil.WithSpinnerR("Checking service status...", mageutil.StopAndCheckBinaries) } -func Check() error { +func Check() (err error) { + defer mageutil.PrintErrPtr(&err) return mageutil.WithSpinnerR("Checking service status...", mageutil.CheckAndReportBinariesStatus) } -func Protocol() error { +func Protocol() (err error) { + defer mageutil.PrintErrPtr(&err) return mageutil.WithSpinnerR("Generating protocol artifacts...", mageutil.Protocol) } -func Export() error { +func Export() (err error) { + defer mageutil.PrintErrPtr(&err) + exportOpt := &mageutil.ExportOptions{ ProjectName: &customExportProjectName, BuildOpt: customExportBuildOpt, } - err := mageutil.WithSpinnerR("Exporting launcher archive...", func() error { + err = mageutil.WithSpinnerR("Exporting launcher archive...", func() error { return mageutil.ExportMageLauncherArchived(nil, exportOpt) }) if err != nil { - mageutil.PrintErrRed("export failed " + err.Error()) - return err + return fmt.Errorf("export failed %w", err) } return nil } diff --git a/mageutil/arg.go b/mageutil/arg.go new file mode 100644 index 0000000..7023d2c --- /dev/null +++ b/mageutil/arg.go @@ -0,0 +1,18 @@ +package mageutil + +import ( + "strings" + + "github.com/openimsdk/tools/utils/datautil" +) + +func ParseArgList(arg *string) []string { + if arg == nil || *arg == "" { + return nil + } + + return datautil.Filter(strings.Split(*arg, ","), func(part string) (string, bool) { + part = strings.TrimSpace(part) + return part, part != "" + }) +} diff --git a/mageutil/basic.go b/mageutil/basic.go index a619e5a..44c22ca 100644 --- a/mageutil/basic.go +++ b/mageutil/basic.go @@ -18,9 +18,7 @@ func CheckAndReportBinariesStatus() error { } err := CheckBinariesRunning() if err != nil { - PrintErrRed("Some programs are not running properly:") - PrintErrRedNoTimeStamp(err.Error()) - return err + return fmt.Errorf("some programs are not running properly: %w", err) } PrintGreen("All services are running normally.") PrintGreen(fmt.Sprintf("Waiting for %v to check listened ports...", checkDelay)) @@ -28,9 +26,7 @@ func CheckAndReportBinariesStatus() error { PrintBlue("Display details of the ports listened to by the service:") err = PrintListenedPortsByBinaries() if err != nil { - PrintErrRed("PrintListenedPortsByBinaries error") - PrintErrRedNoTimeStamp(err.Error()) - return err + return fmt.Errorf("PrintListenedPortsByBinaries error: %w", err) } return nil } @@ -39,10 +35,9 @@ func StopAndCheckBinaries() error { if err := InitForSSC(); err != nil { return err } - KillExistBinaries() + PrintErr(KillExistBinaries()) err := attemptCheckBinaries() if err != nil { - PrintErrRed(err.Error()) return err } PrintGreen("All services have been stopped") @@ -69,8 +64,7 @@ func attemptCheckBinaries() error { func StartToolsAndServices(binaries []string, pathOpts *PathOptions) error { if pathOpts != nil { if err := UpdateGlobalPaths(pathOpts); err != nil { - PrintErrRed("Failed to update paths: " + err.Error()) - return err + return fmt.Errorf("failed to update paths: %w", err) } } @@ -105,25 +99,20 @@ func StartToolsAndServices(binaries []string, pathOpts *PathOptions) error { if len(toolsBinaries) > 0 { PrintBlue("Starting specified tools...") if err := StartTools(toolsBinaries...); err != nil { - PrintErrRed("Some specified tools failed to start:") - PrintErrRedNoTimeStamp(err.Error()) - return err + return fmt.Errorf("failed to start specified tools: %w", err) } PrintGreen("Specified tools executed successfully") } if len(cmdBinaries) > 0 { - KillExistBinaries() + PrintErr(KillExistBinaries()) err := attemptCheckBinaries() if err != nil { - PrintErrRed("Some services running, details are as follows, abort start " + err.Error()) - return err + return fmt.Errorf("some services running, details are as follows, abort start %w", err) } err = StartBinaries(cmdBinaries...) if err != nil { - PrintErrRed("Failed to start specified binaries:") - PrintErrRedNoTimeStamp(err.Error()) - return err + return fmt.Errorf("failed to start specified binaries: %w", err) } return CheckAndReportBinariesStatus() } @@ -132,23 +121,18 @@ func StartToolsAndServices(binaries []string, pathOpts *PathOptions) error { PrintBlue("Starting tools primarily involves component verification and other preparatory tasks.") if err := StartTools(); err != nil { - PrintErrRed("Some tools failed to start, details are as follows, abort start") - PrintErrRedNoTimeStamp(err.Error()) - return err + return fmt.Errorf("some tools failed to start, details are as follows, abort start: %w", err) } PrintGreen("All tools executed successfully") - KillExistBinaries() + PrintErr(KillExistBinaries()) err := attemptCheckBinaries() if err != nil { - PrintErrRed("Some services running, details are as follows, abort start " + err.Error()) - return err + return fmt.Errorf("some services running, details are as follows, abort start %w", err) } err = StartBinaries() if err != nil { - PrintErrRed("Failed to start all binaries") - PrintErrRedNoTimeStamp(err.Error()) - return err + return fmt.Errorf("failed to start all binaries %w", err) } return CheckAndReportBinariesStatus() } @@ -190,8 +174,7 @@ func Build(binaries []string, pathOpts *PathOptions, buildOpt *BuildOptions) err if pathOpts != nil { if err := UpdateGlobalPaths(pathOpts); err != nil { - PrintErrRed("Failed to update paths: " + err.Error()) - return err + return fmt.Errorf("failed to update paths: %w", err) } } diff --git a/mageutil/build.go b/mageutil/build.go index f0c7aa4..2d9abb5 100644 --- a/mageutil/build.go +++ b/mageutil/build.go @@ -107,11 +107,11 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, if os.IsNotExist(err) { return nil, nil } - PrintErrRed(fmt.Sprintf("Failed read directory %s: %v", sourceDir, err)) + PrintErr(fmt.Errorf("failed read directory %s: %w", sourceDir, err)) return nil, err } else if !info.IsDir() { err := fmt.Errorf("%s is not dir", sourceDir) - PrintErrRed("Failed " + err.Error()) + PrintErr(fmt.Errorf("failed %w", err)) return nil, err } @@ -123,7 +123,7 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, outputDir := filepath.Join(outputBase, targetOS, targetArch) if err := os.MkdirAll(outputDir, 0755); err != nil { - PrintErrRed(fmt.Sprintf("Failed to create directory %s: %v", outputDir, err)) + PrintErr(fmt.Errorf("failed to create directory %s: %w", outputDir, err)) return nil, err } @@ -199,7 +199,7 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, relPath, err := filepath.Rel(goModDir, path) if err != nil { - PrintErrRed(fmt.Sprintf("Failed to get relative path: %v", err)) + PrintErr(fmt.Errorf("failed to get relative path: %w", err)) errCh <- err return } @@ -226,7 +226,7 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, if err != nil { err = fmt.Errorf("failed to compile %s for %s: %w", dirName, platform, err) - PrintErrRed("Compilation aborted. " + err.Error()) + PrintErr(fmt.Errorf("compilation aborted: %w", err)) errCh <- err return } @@ -290,7 +290,7 @@ func createStartConfigYML(cmdDirs, toolsDirs []string) error { err := os.WriteFile(configPath, []byte(content.String()), 0644) if err != nil { - PrintErrRed("Failed to create start-config.yml: " + err.Error()) + PrintErr(fmt.Errorf("failed to create start-config.yml: %w", err)) return err } PrintGreen("start-config.yml created successfully.") diff --git a/mageutil/define.go b/mageutil/define.go index a2e8734..5c6e345 100644 --- a/mageutil/define.go +++ b/mageutil/define.go @@ -27,14 +27,12 @@ type Config struct { func InitForSSC() error { yamlFile, err := os.ReadFile(StartConfigFile) if err != nil { - PrintErrRed("error reading YAML file: " + err.Error()) return fmt.Errorf("error reading YAML file: %w", err) } var config Config err = yaml.Unmarshal(yamlFile, &config) if err != nil { - PrintErrRed("error unmarshalling YAML: " + err.Error()) return fmt.Errorf("error unmarshalling YAML: %w", err) } diff --git a/mageutil/gen_protocol.go b/mageutil/gen_protocol.go index 85104a2..101b159 100644 --- a/mageutil/gen_protocol.go +++ b/mageutil/gen_protocol.go @@ -134,27 +134,23 @@ func getProtocArch(archMap map[string]string, goArch string) string { func Protocol() error { if err := ensureToolsInstalled(); err != nil { - PrintErrRed("error " + err.Error()) return err } moduleName, err := getModuleNameFromGoMod() if err != nil { - PrintErrRed("error fetching module name from go.mod: " + err.Error()) - return err + return fmt.Errorf("error fetching module name from go.mod: %w", err) } protoPath := "./pkg/protocol" dirs, err := os.ReadDir(protoPath) if err != nil { - PrintErrRed("error " + err.Error()) return err } for _, dir := range dirs { if dir.IsDir() { if err := compileProtoFiles(protoPath, dir.Name(), moduleName); err != nil { - PrintErrRed("error " + err.Error()) return err } } diff --git a/mageutil/logging.go b/mageutil/logging.go index 84d5098..ca7ccd1 100644 --- a/mageutil/logging.go +++ b/mageutil/logging.go @@ -1,7 +1,6 @@ package mageutil import ( - "errors" "fmt" "io" "os" @@ -46,6 +45,24 @@ func writeConsoleMessage(writer io.Writer, message string) (int, error) { return io.WriteString(writer, message) } +func logFilePath() (string, error) { + if Paths == nil { + return "", fmt.Errorf("paths are not initialized") + } + + logDir := strings.TrimSpace(Paths.OutputLogs) + if logDir == "" { + return "", fmt.Errorf("log directory is empty") + } + + logDir = filepath.Clean(logDir) + if err := os.MkdirAll(logDir, 0755); err != nil { + return "", fmt.Errorf("failed to create log directory %s: %w", logDir, err) + } + + return filepath.Join(logDir, defaultLogFileName), nil +} + func GetSharedLogFile() (*os.File, error) { path, err := logFilePath() if err != nil { @@ -84,24 +101,6 @@ func GetSharedLogFileWithoutError() *os.File { return logFile } -func logFilePath() (string, error) { - if Paths == nil { - return "", fmt.Errorf("paths are not initialized") - } - - logDir := strings.TrimSpace(Paths.OutputLogs) - if logDir == "" { - return "", fmt.Errorf("log directory is empty") - } - - logDir = filepath.Clean(logDir) - if err := os.MkdirAll(logDir, 0755); err != nil { - return "", fmt.Errorf("failed to create log directory %s: %w", logDir, err) - } - - return filepath.Join(logDir, defaultLogFileName), nil -} - func GetStdoutInnerLogWriter() io.Writer { return util.MultiWriter( util.WriterFunc(func(p []byte) (n int, err error) { return writeConsoleMessage(defaultStdout, string(p)) }), @@ -136,17 +135,15 @@ func Print(opt PrintOptions) error { err error ) - if opt.Writer == nil { - return errors.New("no output writer") - } - consoleMessage := formatPrintMessage(opt, tf, true) fileMessage := formatPrintMessage(opt, tf, false) logWriteMu.Lock() defer logWriteMu.Unlock() - _, err = writeConsoleMessage(opt.Writer, consoleMessage) + if opt.Writer != nil { + _, err = writeConsoleMessage(opt.Writer, consoleMessage) + } logFile, logErr := GetSharedLogFile() if logErr != nil { @@ -198,9 +195,20 @@ func PrintGreen(message string) { func PrintYellow(message string) { _ = Print(PrintOptions{Color: ColorYellow, Message: message, WithTime: true, Writer: defaultStdout}) } -func PrintErrRed(message string) { - _ = Print(PrintOptions{Color: ColorRed, Message: message, WithTime: true, Writer: defaultStderr}) + +func PrintErr(err error) { + if err == nil { + return + } + _ = Print(PrintOptions{Color: ColorRed, Message: err.Error(), WithTime: true, Writer: defaultStderr}) } -func PrintErrRedNoTimeStamp(message string) { - _ = Print(PrintOptions{Color: ColorRed, Message: message, WithTime: false, Writer: defaultStderr}) +func PrintErrNoTimeStamp(err error) { + if err == nil { + return + } + _ = Print(PrintOptions{Color: ColorRed, Message: err.Error(), WithTime: false, Writer: defaultStderr}) +} + +func PrintErrPtr(err *error) { + PrintErr(*err) } diff --git a/mageutil/process_controller.go b/mageutil/process_controller.go index 0628abf..22ce452 100644 --- a/mageutil/process_controller.go +++ b/mageutil/process_controller.go @@ -1,6 +1,7 @@ package mageutil import ( + "errors" "fmt" "os" "path/filepath" @@ -10,11 +11,14 @@ import ( ) // StopBinaries iterates over all binary files and terminates their corresponding processes. -func StopBinaries() { +func StopBinaries() error { + var errs []error for binary := range serviceBinaries { fullPath := GetBinFullPath(binary) - KillExistBinary(fullPath) + errs = append(errs, KillExistBinary(fullPath)) } + + return errors.Join(errs...) } // StartBinaries Start all binary services or specified ones. @@ -38,7 +42,7 @@ func StartBinaries(specificBinaries ...string) error { binFullPath := filepath.Join(Paths.OutputHostBin, binary) if _, err := os.Stat(binFullPath); err != nil { - PrintErrRed(fmt.Sprintf("Binary not found: %s. Please build first.", binFullPath)) + PrintErr(fmt.Errorf("binary not found: %s. Please build first", binFullPath)) continue } @@ -81,7 +85,7 @@ func StartTools(specificTools ...string) error { toolFullPath := GetBinToolsFullPath(tool) if _, err := os.Stat(toolFullPath); err != nil { - PrintErrRed(fmt.Sprintf("Tool not found: %s. Please build first.", toolFullPath)) + PrintErr(fmt.Errorf("tool not found: %s. please build first", toolFullPath)) continue } @@ -107,13 +111,13 @@ func StartTools(specificTools ...string) error { } // KillExistBinaries iterates over all binary files and kills their corresponding processes. -func KillExistBinaries() { +func KillExistBinaries() error { var paths []string for binary := range serviceBinaries { fullPath := GetBinFullPath(binary) paths = append(paths, fullPath) } - BatchKillExistBinaries(paths) + return BatchKillExistBinaries(paths) } // CheckBinariesStop checks if all binary files have stopped and returns an error if there are any binaries still running. diff --git a/mageutil/sys.go b/mageutil/sys.go index b19aea4..30e6df6 100644 --- a/mageutil/sys.go +++ b/mageutil/sys.go @@ -105,11 +105,10 @@ func PrintBinaryPorts(binaryPath string, pidMap map[string][]int) { } } -func BatchKillExistBinaries(binaryPaths []string) { +func BatchKillExistBinaries(binaryPaths []string) error { exePathMap, err := util.ProcessesByExePath() if err != nil { - PrintErrRed(fmt.Sprintf("Failed to get processes: %v", err)) - return + return fmt.Errorf("failed to get processes: %w", err) } for _, binaryPath := range binaryPaths { @@ -120,6 +119,8 @@ func BatchKillExistBinaries(binaryPaths []string) { } } } + + return nil } func terminateAndKillProcess(p *process.Process) { @@ -133,7 +134,7 @@ func terminateAndKillProcess(p *process.Process) { if err != nil { err = p.Kill() // Fallback to kill if terminate fails if err != nil { - PrintErrRed(fmt.Sprintf("Failed to kill process cmdline: %s, pid: %d, err: %v", cmdline, p.Pid, err)) + PrintErr(fmt.Errorf("failed to kill process cmdline: %s, pid: %d, err: %w", cmdline, p.Pid, err)) } else { PrintYellow(fmt.Sprintf("Killed process cmdline: %s, pid: %d", cmdline, p.Pid)) } @@ -143,11 +144,10 @@ func terminateAndKillProcess(p *process.Process) { } // KillExistBinary kills all processes matching the given binary file path. -func KillExistBinary(binaryPath string) { +func KillExistBinary(binaryPath string) error { exePathMap, err := util.ProcessesByExePath() if err != nil { - PrintErrRed(fmt.Sprintf("Failed to get processes: %v", err)) - return + return fmt.Errorf("failed to get processes: %w", err) } for exePath, procs := range exePathMap { @@ -157,6 +157,8 @@ func KillExistBinary(binaryPath string) { } } } + + return nil } // DetectPlatform detects the operating system and architecture. @@ -166,7 +168,6 @@ func DetectPlatform() (string, error) { case "amd64", "arm64": default: err := fmt.Errorf("unsupported architecture: %s", targetArch) - PrintErrRed(err.Error()) return "", err } return fmt.Sprintf("%s_%s", targetOS, targetArch), nil From db0d2a38606183fb6095524185d42199503e9d5d Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Tue, 2 Jun 2026 15:05:32 +0800 Subject: [PATCH 09/10] feat(core): add archive and path helpers --- mageutil/archive.go | 77 +++++++++++++++++++++++ mageutil/export.go | 148 ++------------------------------------------ mageutil/paths.go | 101 ++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+), 143 deletions(-) create mode 100644 mageutil/archive.go diff --git a/mageutil/archive.go b/mageutil/archive.go new file mode 100644 index 0000000..1e6a784 --- /dev/null +++ b/mageutil/archive.go @@ -0,0 +1,77 @@ +package mageutil + +import ( + "archive/tar" + "compress/gzip" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/openimsdk/gomake/internal/util" +) + +type ArchiveOptions struct { + ProjectName *string +} + +func (opt *ArchiveOptions) GetProjectName() string { + projectName := strings.TrimSpace(util.NilAsZero(util.NilAsZero(opt).ProjectName)) + if projectName == "" { + return "" + } + return strings.NewReplacer("/", "_", "\\", "_").Replace(projectName) +} + +func archive(archivePath string, mappingPaths map[string]string) error { + archivePath = fmt.Sprintf("%s.tar.gz", archivePath) + PrintBlue(fmt.Sprintf("Creating archive: %s", archivePath)) + archiveFile, err := os.Create(archivePath) + if err != nil { + return fmt.Errorf("failed to create archive file %s: %v", archivePath, err) + } + defer archiveFile.Close() + gzipWriter, err := gzip.NewWriterLevel(archiveFile, gzip.BestCompression) + if err != nil { + return fmt.Errorf("failed to create gzip writer: %v", err) + } + defer gzipWriter.Close() + tarWriter := tar.NewWriter(gzipWriter) + defer tarWriter.Close() + + for in, out := range mappingPaths { + err := util.CheckExist(in) + if err != nil { + return err + } + + PrintBlue(fmt.Sprintf("Adding %s to archive", in)) + if err := util.AddToTar(tarWriter, in, out); err != nil { + return fmt.Errorf("failed to add %s to archive: %v", in, err) + } + } + + PrintGreen(fmt.Sprintf("Archive created successfully: %s", archivePath)) + return nil +} + +func ArchiveProject(archiveOptions *ArchiveOptions) error { + archiveDir := Paths.OutputArchive + PrintBlue(fmt.Sprintf("Using archive directory: %s", archiveDir)) + + allFiles, err := GetAllRootFilesExcludeIgnore() + if err != nil { + return err + } + mappingPaths, err := EnsureRootRelPaths(allFiles...) + if err != nil { + return err + } + + archiveName := "archived" + projectName := archiveOptions.GetProjectName() + if projectName != "" { + archiveName = fmt.Sprintf("archived_%s", projectName) + } + return archive(filepath.Join(archiveDir, archiveName), mappingPaths) +} diff --git a/mageutil/export.go b/mageutil/export.go index e3295e2..2f92ab5 100644 --- a/mageutil/export.go +++ b/mageutil/export.go @@ -1,17 +1,12 @@ package mageutil import ( - "archive/tar" - "compress/gzip" - "errors" "fmt" "os" - "os/exec" "path/filepath" "strings" "github.com/openimsdk/gomake/internal/util" - "github.com/openimsdk/tools/utils/datautil" ) type ExportOptions struct { @@ -42,12 +37,6 @@ func ExportMageLauncherArchived(overrideMappingPaths map[string]string, exportOp exportDir := Paths.OutputExport PrintBlue(fmt.Sprintf("Using tmp directory: %s", tmpDir)) PrintBlue(fmt.Sprintf("Using export directory: %s", exportDir)) - if err := os.MkdirAll(tmpDir, 0755); err != nil { - return fmt.Errorf("failed to create tmp directory %s: %v", tmpDir, err) - } - if err := os.MkdirAll(exportDir, 0755); err != nil { - return fmt.Errorf("failed to create export directory %s: %v", exportDir, err) - } platforms := os.Getenv("PLATFORMS") if platforms == "" { @@ -106,142 +95,15 @@ func ExportMageLauncherArchived(overrideMappingPaths map[string]string, exportOp mappingPaths[k] = v } - archiveName := exportArchiveBaseName(platform, exportOpt) - err = archive(filepath.Join(exportDir, archiveName), mappingPaths) - if err != nil { - return err + archiveName := fmt.Sprintf("exported_%s", platform) + projectName := exportOpt.GetProjectName() + if projectName != "" { + archiveName = fmt.Sprintf("exported_%s_%s", projectName, platform) } - } - return nil -} - -func exportArchiveBaseName(platform string, exportOpt *ExportOptions) string { - projectName := exportOpt.GetProjectName() - if projectName == "" { - return fmt.Sprintf("exported_%s", platform) - } - return fmt.Sprintf("exported_%s_%s", projectName, platform) -} - -func archive(archivePath string, mappingPaths map[string]string) error { - archivePath = fmt.Sprintf("%s.tar.gz", archivePath) - PrintBlue(fmt.Sprintf("Creating archive: %s", archivePath)) - archiveFile, err := os.Create(archivePath) - if err != nil { - return fmt.Errorf("failed to create archive file %s: %v", archivePath, err) - } - defer archiveFile.Close() - gzipWriter, err := gzip.NewWriterLevel(archiveFile, gzip.BestCompression) - if err != nil { - return fmt.Errorf("failed to create gzip writer: %v", err) - } - defer gzipWriter.Close() - tarWriter := tar.NewWriter(gzipWriter) - defer tarWriter.Close() - - for in, out := range mappingPaths { - err := util.CheckExist(in) + err = archive(filepath.Join(exportDir, archiveName), mappingPaths) if err != nil { return err } - - PrintBlue(fmt.Sprintf("Adding %s to archive", in)) - if err := util.AddToTar(tarWriter, in, out); err != nil { - return fmt.Errorf("failed to add %s to archive: %v", in, err) - } } - - PrintGreen(fmt.Sprintf("Archive created successfully: %s", archivePath)) return nil } - -func EnsureRootRelPaths(paths ...string) (map[string]string, error) { - root := filepath.Clean(Paths.Root) - if root == "" { - return nil, fmt.Errorf("root path is empty") - } - - relPathMap := make(map[string]string) - for _, path := range paths { - absPath := filepath.Clean(filepath.FromSlash(path)) - if !filepath.IsAbs(absPath) { - absPath = filepath.Join(root, absPath) - } - - relPath, err := filepath.Rel(root, absPath) - if err != nil { - return nil, fmt.Errorf("failed to get relative path for %s: %v", path, err) - } - relPathMap[absPath] = filepath.ToSlash(relPath) - } - - return relPathMap, nil -} - -func GetAllRootFilesExcludeIgnore() ([]string, error) { - root := Paths.Root - if root == "" { - return nil, fmt.Errorf("root path is empty") - } - - cmdOutput, err := NewCmd("git"). - WithArgs("ls-files", "-c", "--exclude-standard", "-z"). - WithDir(root). - Output() - - if err != nil { - var exitErr *exec.ExitError - if errors.As(err, &exitErr) { - return nil, fmt.Errorf("failed to list root files via git ls-files: %s", strings.TrimSpace(string(exitErr.Stderr))) - } - return nil, fmt.Errorf("failed to list root files via git ls-files: %v", err) - } - - relPaths := make([]string, 0) - for _, relPath := range strings.Split(string(cmdOutput), "\x00") { - if relPath == "" { - continue - } - - cleanRelPath := filepath.Clean(filepath.FromSlash(relPath)) - if cleanRelPath == "." { - continue - } - - absPath := filepath.Join(root, cleanRelPath) - info, statErr := os.Stat(absPath) - if statErr != nil { - if os.IsNotExist(statErr) { - continue - } - return nil, fmt.Errorf("failed to stat file %s listed by git: %v", absPath, statErr) - } - if info.IsDir() { - continue - } - - relPaths = append(relPaths, filepath.ToSlash(cleanRelPath)) - } - - if len(relPaths) == 0 { - return nil, fmt.Errorf("no files found under root %s after applying gitignore rules", root) - } - - return relPaths, nil -} - -func GetDefaultExportMappingPaths(exclude []string) (map[string]string, error) { - allFiles, err := GetAllRootFilesExcludeIgnore() - if err != nil { - return nil, err - } - - allFilteredFiles := datautil.Filter(allFiles, func(e string) (string, bool) { - if util.MatchAnyFilepathGlob(e, exclude) { - return "", false - } - return e, true - }) - - return EnsureRootRelPaths(allFilteredFiles...) -} diff --git a/mageutil/paths.go b/mageutil/paths.go index a72b41e..a9b3bce 100644 --- a/mageutil/paths.go +++ b/mageutil/paths.go @@ -1,9 +1,15 @@ package mageutil import ( + "errors" "fmt" "os" + "os/exec" "path/filepath" + "strings" + + "github.com/openimsdk/gomake/internal/util" + "github.com/openimsdk/tools/utils/datautil" ) // Path constants @@ -19,6 +25,7 @@ const ( ToolsDir = "tools" TmpDir = "tmp" ExportDir = "export" + ArchiveDir = "archive" LogsDir = "logs" BinDir = "bin" PlatformsDir = "platforms" @@ -33,6 +40,7 @@ type PathConfig struct { OutputTools string OutputTmp string OutputExport string + OutputArchive string OutputLogs string OutputBin string OutputBinPath string @@ -114,6 +122,7 @@ func NewPathConfig(opts *PathOptions) (*PathConfig, error) { config.OutputTools = config.joinPath(config.Output, ToolsDir) config.OutputTmp = config.joinPath(config.Output, TmpDir) config.OutputExport = config.joinPath(config.Output, ExportDir) + config.OutputArchive = config.joinPath(config.Output, ArchiveDir) config.OutputLogs = config.joinPath(config.Output, LogsDir) config.OutputBin = config.joinPath(config.Output, BinDir) @@ -180,6 +189,7 @@ func (p *PathConfig) createDirectories() error { p.OutputTools, p.OutputTmp, p.OutputExport, + p.OutputArchive, p.OutputLogs, p.OutputBin, p.OutputBinPath, @@ -219,3 +229,94 @@ func GetBinFullPath(binName string) string { func GetBinToolsFullPath(toolName string) string { return Paths.GetBinToolsFullPath(toolName) } + +func EnsureRootRelPaths(paths ...string) (map[string]string, error) { + root := filepath.Clean(Paths.Root) + if root == "" { + return nil, fmt.Errorf("root path is empty") + } + + relPathMap := make(map[string]string) + for _, path := range paths { + absPath := filepath.Clean(filepath.FromSlash(path)) + if !filepath.IsAbs(absPath) { + absPath = filepath.Join(root, absPath) + } + + relPath, err := filepath.Rel(root, absPath) + if err != nil { + return nil, fmt.Errorf("failed to get relative path for %s: %v", path, err) + } + relPathMap[absPath] = filepath.ToSlash(relPath) + } + + return relPathMap, nil +} + +func GetAllRootFilesExcludeIgnore() ([]string, error) { + root := Paths.Root + if root == "" { + return nil, fmt.Errorf("root path is empty") + } + + cmdOutput, err := NewCmd("git"). + WithArgs("ls-files", "-c", "--exclude-standard", "-z"). + WithDir(root). + Output() + + if err != nil { + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { + return nil, fmt.Errorf("failed to list root files via git ls-files: %s", strings.TrimSpace(string(exitErr.Stderr))) + } + return nil, fmt.Errorf("failed to list root files via git ls-files: %v", err) + } + + relPaths := make([]string, 0) + for _, relPath := range strings.Split(string(cmdOutput), "\x00") { + if relPath == "" { + continue + } + + cleanRelPath := filepath.Clean(filepath.FromSlash(relPath)) + if cleanRelPath == "." { + continue + } + + absPath := filepath.Join(root, cleanRelPath) + info, statErr := os.Stat(absPath) + if statErr != nil { + if os.IsNotExist(statErr) { + continue + } + return nil, fmt.Errorf("failed to stat file %s listed by git: %v", absPath, statErr) + } + if info.IsDir() { + continue + } + + relPaths = append(relPaths, filepath.ToSlash(cleanRelPath)) + } + + if len(relPaths) == 0 { + return nil, fmt.Errorf("no files found under root %s after applying gitignore rules", root) + } + + return relPaths, nil +} + +func GetDefaultExportMappingPaths(exclude []string) (map[string]string, error) { + allFiles, err := GetAllRootFilesExcludeIgnore() + if err != nil { + return nil, err + } + + allFilteredFiles := datautil.Filter(allFiles, func(e string) (string, bool) { + if util.MatchAnyFilepathGlob(e, exclude) { + return "", false + } + return e, true + }) + + return EnsureRootRelPaths(allFilteredFiles...) +} From c34002df9d666193eb5490fb0fab7c4c72560fad Mon Sep 17 00:00:00 2001 From: dsx137 <70027572+dsx137@users.noreply.github.com> Date: Wed, 3 Jun 2026 15:05:07 +0800 Subject: [PATCH 10/10] fix(core): simplify process count error return --- mageutil/sys.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mageutil/sys.go b/mageutil/sys.go index 30e6df6..ad4865a 100644 --- a/mageutil/sys.go +++ b/mageutil/sys.go @@ -30,9 +30,9 @@ func CheckProcessNames(processPath string, expectedCount int, processMap map[str if runningCount == expectedCount { return nil - } else { - return fmt.Errorf("%s expected %d processes, but %d running", processPath, expectedCount, runningCount) } + + return fmt.Errorf("%s expected %d processes, but %d running", processPath, expectedCount, runningCount) } // FetchProcesses returns a map of executable paths to their running count.