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/cmd/microservice-test/main.go b/cmd/microservice-test/main.go index e05471d..47a8b7d 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.PrintErr(fmt.Errorf("failed to listen on port %d: %w", 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.PrintErr(fmt.Errorf("HTTP server exited: %w", err)) + os.Exit(1) + } } diff --git a/go.mod b/go.mod index b7a2a5e..825188c 100644 --- a/go.mod +++ b/go.mod @@ -1,24 +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/shirou/gopsutil/v4 v4.26.2 - golang.org/x/sys v0.41.0 + github.com/pterm/pterm v0.12.83 + github.com/shirou/gopsutil/v4 v4.26.5 + golang.org/x/sys v0.45.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/ebitengine/purego v0.10.0 // indirect + atomicgo.dev/cursor v0.2.0 // 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.1 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/gookit/color v1.6.1 // indirect github.com/jinzhu/copier v0.4.0 // indirect - github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect + github.com/lithammer/fuzzysearch v1.1.8 // 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.43.0 // indirect + golang.org/x/text v0.37.0 // indirect ) diff --git a/go.sum b/go.sum index 23997b8..7fcda7e 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,44 @@ +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.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.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= +github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= 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.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.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= +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.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/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/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-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= @@ -21,23 +47,64 @@ 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/pterm/pterm v0.12.83 h1:ie+YmGmA727VuhxBlyGr74Ks+7McV6kT99IB8EU80aA= +github.com/pterm/pterm v0.12.83/go.mod h1:xlgc6bFWyJIMtmLJvGim+L7jhSReilOlOnodeIYe4Tk= +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/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= 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-20210615035016-665e8c7367d1/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.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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-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.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.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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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/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/io.go b/internal/util/io.go new file mode 100644 index 0000000..6e763a3 --- /dev/null +++ b/internal/util/io.go @@ -0,0 +1,15 @@ +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 })...) +} + +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/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/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/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..376df61 100644 --- a/magefile.go +++ b/magefile.go @@ -1,16 +1,14 @@ //go:build mage -// +build mage package main import ( - "flag" - "os" + "fmt" "github.com/openimsdk/gomake/mageutil" ) -var Default = Build +var Default = BuildAll var Aliases = map[string]any{ "buildcc": BuildWithCustomConfig, @@ -29,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() { - 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) - mageutil.WithSpinner("Building binaries...", func() { - mageutil.Build(bin, nil, nil) + return mageutil.WithSpinnerR("Building binaries...", func() error { + return mageutil.Build(mageutil.ParseArgList(bins), nil, nil) }) } -func BuildWithCustomConfig() { - 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) @@ -58,42 +50,36 @@ 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(mageutil.ParseArgList(bins), config, nil) }) } -func Start() { - mageutil.InitForSSC() - err := setMaxOpenFiles() - if err != nil { - mageutil.PrintRed("setMaxOpenFiles failed " + err.Error()) - os.Exit(1) - } +func Start(bins *string) (err error) { + defer mageutil.PrintErrPtr(&err) - flag.Parse() - bin := flag.Args() - if len(bin) != 0 { - bin = bin[1:] + if err := mageutil.InitForSSC(); err != nil { + return err + } + err = setMaxOpenFiles() + if err != nil { + return fmt.Errorf("setMaxOpenFiles failed %w", err) } - mageutil.WithSpinner("Starting tools and services...", func() { - mageutil.StartToolsAndServices(bin, nil) + return mageutil.WithSpinnerR("Starting tools and services...", func() error { + return mageutil.StartToolsAndServices(mageutil.ParseArgList(bins), nil) }) } -func StartWithCustomConfig() { - mageutil.InitForSSC() - err := setMaxOpenFiles() - if err != nil { - mageutil.PrintRed("setMaxOpenFiles failed " + err.Error()) - os.Exit(1) - } +func StartWithCustomConfig(bins *string) (err error) { + defer mageutil.PrintErrPtr(&err) - flag.Parse() - bin := flag.Args() - if len(bin) != 0 { - bin = bin[1:] + if err := mageutil.InitForSSC(); err != nil { + return err + } + err = setMaxOpenFiles() + if err != nil { + return fmt.Errorf("setMaxOpenFiles failed %w", err) } config := &mageutil.PathOptions{ @@ -102,33 +88,38 @@ 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(mageutil.ParseArgList(bins), config) }) } -func Stop() { - mageutil.WithSpinner("Checking service status...", mageutil.StopAndCheckBinaries) +func Stop() (err error) { + defer mageutil.PrintErrPtr(&err) + return mageutil.WithSpinnerR("Checking service status...", mageutil.StopAndCheckBinaries) } -func Check() { - mageutil.WithSpinner("Checking service status...", mageutil.CheckAndReportBinariesStatus) +func Check() (err error) { + defer mageutil.PrintErrPtr(&err) + return mageutil.WithSpinnerR("Checking service status...", mageutil.CheckAndReportBinariesStatus) } -func Protocol() { - mageutil.WithSpinnerE("Generating protocol artifacts...", mageutil.Protocol) +func Protocol() (err error) { + defer mageutil.PrintErrPtr(&err) + return mageutil.WithSpinnerR("Generating protocol artifacts...", mageutil.Protocol) } -func Export() { +func Export() (err error) { + defer mageutil.PrintErrPtr(&err) + 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 fmt.Errorf("export failed %w", err) } + return nil } 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/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 2ce734b..44c22ca 100644 --- a/mageutil/basic.go +++ b/mageutil/basic.go @@ -10,34 +10,38 @@ import ( "github.com/openimsdk/gomake/internal/util" ) -func CheckAndReportBinariesStatus() { - InitForSSC() +const checkDelay = 3 * time.Second + +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 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)) + time.Sleep(checkDelay) PrintBlue("Display details of the ports listened to by the service:") - time.Sleep(1 * time.Second) err = PrintListenedPortsByBinaries() if err != nil { - PrintRed("PrintListenedPortsByBinaries error") - PrintRedNoTimeStamp(err.Error()) - os.Exit(1) + return fmt.Errorf("PrintListenedPortsByBinaries error: %w", err) } + return nil } -func StopAndCheckBinaries() { - InitForSSC() - KillExistBinaries() +func StopAndCheckBinaries() error { + if err := InitForSSC(); err != nil { + return err + } + PrintErr(KillExistBinaries()) err := attemptCheckBinaries() if err != nil { - PrintRed(err.Error()) - return + return err } PrintGreen("All services have been stopped") + return nil } func attemptCheckBinaries() error { @@ -49,19 +53,18 @@ 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) } -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 fmt.Errorf("failed to update paths: %w", err) } } @@ -87,7 +90,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)) @@ -96,52 +99,42 @@ func StartToolsAndServices(binaries []string, pathOpts *PathOptions) { if len(toolsBinaries) > 0 { PrintBlue("Starting specified tools...") if err := StartTools(toolsBinaries...); err != nil { - PrintRed("Some specified tools failed to start:") - PrintRedNoTimeStamp(err.Error()) - return + 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 { - PrintRed("Some services running, details are as follows, abort start " + err.Error()) - return + return fmt.Errorf("some services running, details are as follows, abort start %w", err) } err = StartBinaries(cmdBinaries...) if err != nil { - PrintRed("Failed to start specified binaries:") - PrintRedNoTimeStamp(err.Error()) - return + return fmt.Errorf("failed to start specified binaries: %w", 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 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 { - PrintRed("Some services running, details are as follows, abort start " + err.Error()) - return + return fmt.Errorf("some services running, details are as follows, abort start %w", err) } err = StartBinaries() if err != nil { - PrintRed("Failed to start all binaries") - PrintRedNoTimeStamp(err.Error()) - return + return fmt.Errorf("failed to start all binaries %w", err) } - CheckAndReportBinariesStatus() + return CheckAndReportBinariesStatus() } func isExecutableFile(filePath string) bool { @@ -165,22 +158,23 @@ 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.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 { - 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 fmt.Errorf("failed to update paths: %w", err) } } @@ -190,10 +184,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 d3831f1..2d9abb5 100644 --- a/mageutil/build.go +++ b/mageutil/build.go @@ -6,8 +6,9 @@ import ( "path/filepath" "runtime" "strings" - "sync/atomic" + "sync" + "github.com/openimsdk/gomake/internal/priority" "github.com/openimsdk/gomake/internal/util" ) @@ -34,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 @@ -76,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() @@ -96,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 } - fmt.Printf("Failed read directory %s: %v\n", sourceDir, err) - os.Exit(1) + PrintErr(fmt.Errorf("failed read directory %s: %w", sourceDir, err)) + return nil, err } else if !info.IsDir() { - fmt.Printf("Failed %s is not dir\n", sourceDir) - os.Exit(1) + err := fmt.Errorf("%s is not dir", sourceDir) + PrintErr(fmt.Errorf("failed %w", err)) + 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 { - fmt.Printf("Failed to create directory %s: %v\n", outputDir, err) - os.Exit(1) + PrintErr(fmt.Errorf("failed to create directory %s: %w", outputDir, err)) + return nil, err } cpuNum := runtime.GOMAXPROCS(0) @@ -132,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, @@ -151,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 @@ -192,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) + PrintErr(fmt.Errorf("failed to get relative path: %w", err)) + errCh <- err + return } buildTarget := relPath @@ -217,20 +215,32 @@ func compileDir(buildOpt *BuildOptions, sourceDir, outputBase, platform string, } buildArgs = append(buildArgs, buildTarget) - err = RunWithPriority(PriorityLow, env, "go", buildArgs...) - - os.Chdir(originalDir) + err = NewCmd("go"). + WithArgs(buildArgs...). + WithDir(goModDir). + WithEnv(env). + WithPriority(priority.Low). + WithStdout(GetStdoutInnerLogWriter()). + WithStderr(GetStderrInnerLogWriter()). + Run() 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) + PrintErr(fmt.Errorf("compilation aborted: %w", err)) + 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 := RunWithPriority(PriorityLow, nil, "upx", "--lzma", outputPath); err != nil { + cmd := NewCmd("upx"). + WithArgs("--lzma", outputPath). + 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 { PrintGreen(fmt.Sprintf("Successfully compressed with UPX: %s", outputFileName)) @@ -241,20 +251,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 @@ -270,10 +290,11 @@ 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 + PrintErr(fmt.Errorf("failed to create start-config.yml: %w", err)) + return err } PrintGreen("start-config.yml created successfully.") + return nil } func ResolveBuildOptions(codeOpt *BuildOptions, envOpt *BuildOptions) *BuildOptions { @@ -387,7 +408,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/cmd.go b/mageutil/cmd.go new file mode 100644 index 0000000..f179a3a --- /dev/null +++ b/mageutil/cmd.go @@ -0,0 +1,123 @@ +package mageutil + +import ( + "bytes" + "fmt" + "io" + "os" + "os/exec" + "strings" + + "github.com/openimsdk/gomake/internal/priority" + "github.com/openimsdk/gomake/internal/util" +) + +type Cmd struct { + execCmd *exec.Cmd + + env map[string]string + + priority *priority.Level +} + +func NewCmd(command string) *Cmd { + cmd := &Cmd{execCmd: exec.Command(command)} + return cmd +} + +func (c *Cmd) WithArgs(args ...string) *Cmd { + c.execCmd.Args = append(c.execCmd.Args, 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.execCmd.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.execCmd.Stdin = stdin + return c +} + +func (c *Cmd) WithStdout(stdout io.Writer) *Cmd { + c.execCmd.Stdout = stdout + return c +} + +func (c *Cmd) WithStderr(stderr io.Writer) *Cmd { + c.execCmd.Stderr = stderr + return c +} + +func (c *Cmd) Start() error { + c.execCmd.Env = append(os.Environ(), util.FlattenEnvs(c.env)...) + + if err := c.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)) + } + } + + return nil +} + +func (c *Cmd) Wait() error { + return c.execCmd.Wait() +} + +func (c *Cmd) Run() error { + err := c.Start() + if err != nil { + return err + } + + return c.Wait() +} + +func (c *Cmd) String() string { + return c.execCmd.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() + return buf.Bytes(), err +} + +func (c *Cmd) CombinedOutput() ([]byte, error) { + if c.execCmd.Stdout != nil || c.execCmd.Stderr != nil { + return nil, fmt.Errorf("stdout or stderr already set") + } + 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 a839241..5c6e345 100644 --- a/mageutil/define.go +++ b/mageutil/define.go @@ -24,18 +24,16 @@ type Config struct { MaxFileDescriptors int `yaml:"maxFileDescriptors"` } -func InitForSSC() { +func InitForSSC() error { yamlFile, err := os.ReadFile(StartConfigFile) if err != nil { - fmt.Printf("error reading YAML file: %v", err) - os.Exit(1) + return fmt.Errorf("error reading YAML file: %w", err) } var config Config err = yaml.Unmarshal(yamlFile, &config) if err != nil { - fmt.Printf("error unmarshalling YAML: %v", err) - os.Exit(1) + return fmt.Errorf("error unmarshalling YAML: %w", err) } adjustedBinaries := make(map[string]int) @@ -56,4 +54,5 @@ func InitForSSC() { serviceBinaries = adjustedBinaries toolBinaries = adjustedToolsBinaries MaxFileDescriptors = config.MaxFileDescriptors + return nil } diff --git a/mageutil/export.go b/mageutil/export.go index ce233b4..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 { @@ -34,22 +29,22 @@ 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 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 == "" { - platforms = DetectPlatform() + platform, err := DetectPlatform() + if err != nil { + return err + } + platforms = platform } platformList := strings.Fields(platforms) @@ -70,10 +65,11 @@ 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). + WithStdout(GetStdoutInnerLogWriter()). + WithStderr(GetStderrInnerLogWriter()) if err := cmd.Run(); err != nil { return fmt.Errorf("failed to compile mage for %s: %v", platform, err) } @@ -99,141 +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") - } - - cmd := exec.Command("git", "ls-files", "-c", "--exclude-standard", "-z") - cmd.Dir = root - - output, err := cmd.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(output), "\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/gen_protocol.go b/mageutil/gen_protocol.go index 51482aa..101b159 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 { @@ -31,21 +32,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 +67,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 +86,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,28 +134,24 @@ func getProtocArch(archMap map[string]string, goArch string) string { func Protocol() error { if err := ensureToolsInstalled(); err != nil { - fmt.Println("error ", err.Error()) - os.Exit(1) + return err } moduleName, err := getModuleNameFromGoMod() if err != nil { - fmt.Println("error fetching module name from go.mod: ", err.Error()) - os.Exit(1) + return fmt.Errorf("error fetching module name from go.mod: %w", err) } protoPath := "./pkg/protocol" dirs, err := os.ReadDir(protoPath) if err != nil { - fmt.Println("error ", err.Error()) - os.Exit(1) + return err } for _, dir := range dirs { if dir.IsDir() { if err := compileProtoFiles(protoPath, dir.Name(), moduleName); err != nil { - fmt.Println("error ", err.Error()) - os.Exit(1) + return err } } } @@ -180,7 +177,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 +193,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/logging.go b/mageutil/logging.go index c7b1683..ca7ccd1 100644 --- a/mageutil/logging.go +++ b/mageutil/logging.go @@ -4,8 +4,13 @@ import ( "fmt" "io" "os" + "path/filepath" "strings" + "sync" "time" + + "github.com/openimsdk/gomake/internal/util" + "github.com/pterm/pterm" ) const ( @@ -19,6 +24,97 @@ const ( 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 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 { + 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 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 @@ -29,99 +125,90 @@ type PrintOptions struct { TimeFmt string } -func Print(opt PrintOptions) (int, error) { - w := opt.Writer - if w == nil { - w = os.Stdout - } +func Print(opt PrintOptions) error { tf := opt.TimeFmt if tf == "" { tf = defaultTimeFmt } var ( - n int err 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(' ') - } - } + consoleMessage := formatPrintMessage(opt, tf, true) + fileMessage := formatPrintMessage(opt, tf, false) - if opt.Color != "" { - b.WriteString(opt.Color) - } - b.WriteString(opt.Message) - if opt.Color != "" { - b.WriteString(ColorReset) - } + logWriteMu.Lock() + defer logWriteMu.Unlock() + + if opt.Writer != nil { + _, err = writeConsoleMessage(opt.Writer, consoleMessage) + } + + logFile, logErr := GetSharedLogFile() + if logErr != nil { + return logErr + } + + if _, logErr = io.WriteString(logFile, fileMessage); err == nil && logErr != nil { + err = logErr + } + + return err +} - if !opt.NoNewLine { +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(' ') } + } + + if withColor && opt.Color != "" { + b.WriteString(opt.Color) + } + b.WriteString(opt.Message) + if withColor && opt.Color != "" { + b.WriteString(ColorReset) + } - nLocal, errLocal := io.WriteString(w, b.String()) - n, err = nLocal, errLocal - }) + if !opt.NoNewLine { + b.WriteByte('\n') + } - return n, err + return b.String() } 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}) -} - -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}) + _ = Print(PrintOptions{Color: ColorYellow, Message: message, WithTime: true, Writer: defaultStdout}) } -func PrintGreenNoTimeStamp(message string) { - _, _ = Print(PrintOptions{Color: ColorGreen, Message: message, WithTime: false}) +func PrintErr(err error) { + if err == nil { + return + } + _ = Print(PrintOptions{Color: ColorRed, Message: err.Error(), WithTime: true, Writer: defaultStderr}) } - -func PrintRedToStdErr(a ...any) { - _, _ = Print(PrintOptions{ - Writer: os.Stderr, - Color: ColorRed, - Message: fmt.Sprint(a...), - WithTime: false, - NoNewLine: true, - }) +func PrintErrNoTimeStamp(err error) { + if err == nil { + return + } + _ = Print(PrintOptions{Color: ColorRed, Message: err.Error(), WithTime: false, Writer: defaultStderr}) } -func PrintGreenToStdOut(a ...any) { - _, _ = Print(PrintOptions{ - Writer: os.Stdout, - Color: ColorGreen, - Message: fmt.Sprint(a...), - WithTime: false, - NoNewLine: true, - }) +func PrintErrPtr(err *error) { + PrintErr(*err) } diff --git a/mageutil/paths.go b/mageutil/paths.go index bec6a92..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 @@ -12,13 +18,14 @@ const ( DeploymentType = "DEPLOYMENT_TYPE" KUBERNETES = "kubernetes" - // Directory name constants + // Directory command constants ConfigDir = "config" OutputDir = "_output" SrcDir = "cmd" 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 @@ -46,11 +54,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 @@ -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, @@ -206,12 +216,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) } @@ -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...) +} 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() -} diff --git a/mageutil/process_controller.go b/mageutil/process_controller.go index 1b8ea10..22ce452 100644 --- a/mageutil/process_controller.go +++ b/mageutil/process_controller.go @@ -1,9 +1,9 @@ package mageutil import ( + "errors" "fmt" "os" - "os/exec" "path/filepath" "slices" "strconv" @@ -11,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. @@ -39,7 +42,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)) + PrintErr(fmt.Errorf("binary not found: %s. Please build first", binFullPath)) continue } @@ -49,11 +52,12 @@ func StartBinaries(specificBinaries ...string) error { configPath = Paths.K8sConfig } args := []string{"-i", strconv.Itoa(i), "-c", configPath} - cmd := exec.Command(binFullPath, args...) - fmt.Printf("Starting %s\n", cmd.String()) - cmd.Dir = Paths.OutputHostBin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + cmd := NewCmd(binFullPath). + WithArgs(args...). + WithDir(Paths.OutputHostBin). + 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) } @@ -81,7 +85,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)) + PrintErr(fmt.Errorf("tool not found: %s. please build first", toolFullPath)) continue } @@ -90,32 +94,30 @@ func StartTools(specificTools ...string) error { configPath = Paths.K8sConfig } - cmd := exec.Command(toolFullPath, "-c", configPath) - fmt.Printf("Starting %s\n", cmd.String()) - cmd.Dir = Paths.OutputHostBinTools - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + cmd := NewCmd(toolFullPath). + WithArgs("-c", configPath). + WithDir(Paths.OutputHostBinTools). + 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 error: %v", toolFullPath, err) + if err := cmd.Run(); err != nil { + 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) - } - fmt.Printf("Starting %s successfully \n", cmd.String()) + PrintGreen(fmt.Sprintf("Starting %s successfully", cmd.String())) } return nil } // 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/spinner.go b/mageutil/spinner.go index aec3d23..4e673aa 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,8 @@ 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() -} diff --git a/mageutil/sys.go b/mageutil/sys.go index 3a958a7..ad4865a 100644 --- a/mageutil/sys.go +++ b/mageutil/sys.go @@ -2,22 +2,22 @@ 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" ) 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. @@ -30,28 +30,18 @@ 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. 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,48 +54,37 @@ 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) { 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 } @@ -120,46 +99,34 @@ 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, ", "))) } } } -func BatchKillExistBinaries(binaryPaths []string) { - processes, err := process.Processes() +func BatchKillExistBinaries(binaryPaths []string) error { + exePathMap, err := util.ProcessesByExePath() if err != nil { - fmt.Printf("Failed to get processes: %v\n", 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) + return fmt.Errorf("failed to get processes: %w", err) } 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) } } } + + return nil } 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,78 +134,44 @@ 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) + PrintErr(fmt.Errorf("failed to kill process cmdline: %s, pid: %d, err: %w", 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)) } } // KillExistBinary kills all processes matching the given binary file path. -func KillExistBinary(binaryPath string) { - processes, err := process.Processes() +func KillExistBinary(binaryPath string) error { + exePathMap, err := util.ProcessesByExePath() if err != nil { - fmt.Printf("Failed to get processes: %v\n", err) - return + return fmt.Errorf("failed to get processes: %w", err) } - 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 { - fmt.Printf("Failed to get command line for process %d: %v\n", p.Pid, err) - continue - } - - err = p.Terminate() - if err != nil { - - err = p.Kill() - if err != nil { - fmt.Printf("Failed to kill process cmdline: %s, pid: %d, err: %v\n", cmdline, p.Pid, err) - } else { - fmt.Printf("Killed process cmdline: %s, pid: %d\n", cmdline, p.Pid) - } - } else { - fmt.Printf("Terminated process cmdline: %s, pid: %d\n", cmdline, p.Pid) + for _, p := range procs { + terminateAndKillProcess(p) } } } + + return nil } // 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: - fmt.Printf("Unsupported architecture: %s\n", targetArch) - os.Exit(1) + err := fmt.Errorf("unsupported architecture: %s", targetArch) + return "", err } - return fmt.Sprintf("%s_%s", targetOS, targetArch) + return fmt.Sprintf("%s_%s", targetOS, targetArch), nil } -// rootDir gets the absolute path of the current directory. -func rootDir() string { - dir, err := os.Getwd() - if err != nil { - fmt.Println("Failed to get current directory:", err) - os.Exit(1) - } - return dir -} - -var rootDirPath = rootDir() - // var platformsOutputBase = filepath.Join(rootDirPath, "_output/bin/platforms") // var toolsOutputBase = filepath.Join(rootDirPath, "_output/bin/tools") 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)) }