提交
This commit is contained in:
14
cool-tools/internal/cmd/cmd.go
Normal file
14
cool-tools/internal/cmd/cmd.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
)
|
||||
|
||||
var (
|
||||
Main = gcmd.Command{
|
||||
Name: "cool-tools",
|
||||
Usage: "cool-tools [command] [args...]",
|
||||
Brief: "cool-tools is a collection of tools for cool people.",
|
||||
Description: `cool-tools is a collection of tools for cool people.`,
|
||||
}
|
||||
)
|
||||
328
cool-tools/internal/cmd/cmd_build.go
Normal file
328
cool-tools/internal/cmd/cmd_build.go
Normal file
@@ -0,0 +1,328 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gbase64"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/mlog"
|
||||
)
|
||||
|
||||
var (
|
||||
Build = cBuild{
|
||||
nodeNameInConfigFile: "gfcli.build",
|
||||
packedGoFileName: "internal/packed/build_pack_data.go",
|
||||
}
|
||||
)
|
||||
|
||||
type cBuild struct {
|
||||
g.Meta `name:"build" brief:"{cBuildBrief}" dc:"{cBuildDc}" eg:"{cBuildEg}" ad:"{cBuildAd}"`
|
||||
nodeNameInConfigFile string // nodeNameInConfigFile is the node name for compiler configurations in configuration file.
|
||||
packedGoFileName string // packedGoFileName specifies the file name for packing common folders into one single go file.
|
||||
}
|
||||
|
||||
const (
|
||||
cBuildBrief = `cross-building go project for lots of platforms`
|
||||
cBuildEg = `
|
||||
cool-tools build main.go
|
||||
cool-tools build main.go --pack public,template
|
||||
cool-tools build main.go --cgo
|
||||
cool-tools build main.go -m none
|
||||
cool-tools build main.go -n my-app -a all -s all
|
||||
cool-tools build main.go -n my-app -a amd64,386 -s linux -p .
|
||||
cool-tools build main.go -n my-app -v 1.0 -a amd64,386 -s linux,windows,darwin -p ./docker/bin
|
||||
`
|
||||
cBuildDc = `
|
||||
The "build" command is most commonly used command, which is designed as a powerful wrapper for
|
||||
"go build" command for convenience cross-compiling usage.
|
||||
It provides much more features for building binary:
|
||||
1. Cross-Compiling for many platforms and architectures.
|
||||
2. Configuration file support for compiling.
|
||||
3. Build-In Variables.
|
||||
`
|
||||
cBuildAd = `
|
||||
PLATFORMS
|
||||
darwin amd64,arm64
|
||||
freebsd 386,amd64,arm
|
||||
linux 386,amd64,arm,arm64,ppc64,ppc64le,mips,mipsle,mips64,mips64le
|
||||
netbsd 386,amd64,arm
|
||||
openbsd 386,amd64,arm
|
||||
windows 386,amd64
|
||||
`
|
||||
// https://golang.google.cn/doc/install/source
|
||||
cBuildPlatforms = `
|
||||
darwin amd64
|
||||
darwin arm64
|
||||
ios amd64
|
||||
ios arm64
|
||||
freebsd 386
|
||||
freebsd amd64
|
||||
freebsd arm
|
||||
linux 386
|
||||
linux amd64
|
||||
linux arm
|
||||
linux arm64
|
||||
linux ppc64
|
||||
linux ppc64le
|
||||
linux mips
|
||||
linux mipsle
|
||||
linux mips64
|
||||
linux mips64le
|
||||
netbsd 386
|
||||
netbsd amd64
|
||||
netbsd arm
|
||||
openbsd 386
|
||||
openbsd amd64
|
||||
openbsd arm
|
||||
windows 386
|
||||
windows amd64
|
||||
android arm
|
||||
dragonfly amd64
|
||||
plan9 386
|
||||
plan9 amd64
|
||||
solaris amd64
|
||||
`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cBuildBrief`: cBuildBrief,
|
||||
`cBuildDc`: cBuildDc,
|
||||
`cBuildEg`: cBuildEg,
|
||||
`cBuildAd`: cBuildAd,
|
||||
})
|
||||
Main.AddObject(Build)
|
||||
}
|
||||
|
||||
type cBuildInput struct {
|
||||
g.Meta `name:"build" config:"gfcli.build"`
|
||||
File string `name:"FILE" arg:"true" brief:"building file path"`
|
||||
Name string `short:"n" name:"name" brief:"output binary name"`
|
||||
Version string `short:"v" name:"version" brief:"output binary version"`
|
||||
Arch string `short:"a" name:"arch" brief:"output binary architecture, multiple arch separated with ','"`
|
||||
System string `short:"s" name:"system" brief:"output binary system, multiple os separated with ','"`
|
||||
Output string `short:"o" name:"output" brief:"output binary path, used when building single binary file"`
|
||||
Path string `short:"p" name:"path" brief:"output binary directory path, default is './temp'" d:"./temp"`
|
||||
Extra string `short:"e" name:"extra" brief:"extra custom \"go build\" options"`
|
||||
Mod string `short:"m" name:"mod" brief:"like \"-mod\" option of \"go build\", use \"-m none\" to disable go module"`
|
||||
Cgo bool `short:"c" name:"cgo" brief:"enable or disable cgo feature, it's disabled in default" orphan:"true"`
|
||||
VarMap g.Map `short:"r" name:"varMap" brief:"custom built embedded variable into binary"`
|
||||
PackSrc string `short:"ps" name:"packSrc" brief:"pack one or more folders into one go file before building"`
|
||||
PackDst string `short:"pd" name:"packDst" brief:"temporary go file path for pack, this go file will be automatically removed after built" d:"internal/packed/build_pack_data.go"`
|
||||
ExitWhenError bool `short:"ew" name:"exitWhenError" brief:"exit building when any error occurs, default is false" orphan:"true"`
|
||||
}
|
||||
|
||||
type cBuildOutput struct{}
|
||||
|
||||
func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, err error) {
|
||||
mlog.SetHeaderPrint(true)
|
||||
|
||||
mlog.Debugf(`build input: %+v`, in)
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("go") == "" {
|
||||
mlog.Fatalf(`command "go" not found in your environment, please install golang first to proceed this command`)
|
||||
}
|
||||
|
||||
var (
|
||||
parser = gcmd.ParserFromCtx(ctx)
|
||||
file = parser.GetArg(2).String()
|
||||
)
|
||||
if len(file) < 1 {
|
||||
// Check and use the main.go file.
|
||||
if gfile.Exists("main.go") {
|
||||
file = "main.go"
|
||||
} else {
|
||||
mlog.Fatal("build file path cannot be empty")
|
||||
}
|
||||
}
|
||||
if in.Name == "" {
|
||||
in.Name = gfile.Name(file)
|
||||
}
|
||||
if len(in.Name) < 1 || in.Name == "*" {
|
||||
mlog.Fatal("name cannot be empty")
|
||||
}
|
||||
if in.Mod != "" && in.Mod != "none" {
|
||||
mlog.Debugf(`mod is %s`, in.Mod)
|
||||
if in.Extra == "" {
|
||||
in.Extra = fmt.Sprintf(`-mod=%s`, in.Mod)
|
||||
} else {
|
||||
in.Extra = fmt.Sprintf(`-mod=%s %s`, in.Mod, in.Extra)
|
||||
}
|
||||
}
|
||||
if in.Extra != "" {
|
||||
in.Extra += " "
|
||||
}
|
||||
var (
|
||||
customSystems = gstr.SplitAndTrim(in.System, ",")
|
||||
customArches = gstr.SplitAndTrim(in.Arch, ",")
|
||||
)
|
||||
if len(in.Version) > 0 {
|
||||
in.Path += "/" + in.Version
|
||||
}
|
||||
// System and arch checks.
|
||||
var (
|
||||
spaceRegex = regexp.MustCompile(`\s+`)
|
||||
platformMap = make(map[string]map[string]bool)
|
||||
)
|
||||
for _, line := range strings.Split(strings.TrimSpace(cBuildPlatforms), "\n") {
|
||||
line = gstr.Trim(line)
|
||||
line = spaceRegex.ReplaceAllString(line, " ")
|
||||
var (
|
||||
array = strings.Split(line, " ")
|
||||
system = strings.TrimSpace(array[0])
|
||||
arch = strings.TrimSpace(array[1])
|
||||
)
|
||||
if platformMap[system] == nil {
|
||||
platformMap[system] = make(map[string]bool)
|
||||
}
|
||||
platformMap[system][arch] = true
|
||||
}
|
||||
// Auto packing.
|
||||
if in.PackSrc != "" {
|
||||
if in.PackDst == "" {
|
||||
mlog.Fatal(`parameter "packDst" should not be empty when "packSrc" is used`)
|
||||
}
|
||||
if gfile.Exists(in.PackDst) && !gfile.IsFile(in.PackDst) {
|
||||
mlog.Fatalf(`parameter "packDst" path "%s" should be type of file not directory`, in.PackDst)
|
||||
}
|
||||
if !gfile.Exists(in.PackDst) {
|
||||
// Remove the go file that is automatically packed resource.
|
||||
defer func() {
|
||||
_ = gfile.Remove(in.PackDst)
|
||||
mlog.Printf(`remove the automatically generated resource go file: %s`, in.PackDst)
|
||||
}()
|
||||
}
|
||||
// remove black space in separator.
|
||||
in.PackSrc, _ = gregex.ReplaceString(`,\s+`, `,`, in.PackSrc)
|
||||
packCmd := fmt.Sprintf(`gf pack %s %s --keepPath=true`, in.PackSrc, in.PackDst)
|
||||
mlog.Print(packCmd)
|
||||
gproc.MustShellRun(ctx, packCmd)
|
||||
}
|
||||
|
||||
// Injected information by building flags.
|
||||
ldFlags := fmt.Sprintf(
|
||||
`-X 'github.com/gogf/gf/v2/os/gbuild.builtInVarStr=%v'`,
|
||||
c.getBuildInVarStr(ctx, in),
|
||||
)
|
||||
|
||||
// start building
|
||||
mlog.Print("start building...")
|
||||
if in.Cgo {
|
||||
genv.MustSet("CGO_ENABLED", "1")
|
||||
} else {
|
||||
genv.MustSet("CGO_ENABLED", "0")
|
||||
}
|
||||
var (
|
||||
cmd = ""
|
||||
ext = ""
|
||||
)
|
||||
for system, item := range platformMap {
|
||||
cmd = ""
|
||||
ext = ""
|
||||
if len(customSystems) > 0 && customSystems[0] != "all" && !gstr.InArray(customSystems, system) {
|
||||
continue
|
||||
}
|
||||
for arch := range item {
|
||||
if len(customArches) > 0 && customArches[0] != "all" && !gstr.InArray(customArches, arch) {
|
||||
continue
|
||||
}
|
||||
if len(customSystems) == 0 && len(customArches) == 0 {
|
||||
if runtime.GOOS == "windows" {
|
||||
ext = ".exe"
|
||||
}
|
||||
// Single binary building, output the binary to current working folder.
|
||||
output := ""
|
||||
if len(in.Output) > 0 {
|
||||
output = "-o " + in.Output + ext
|
||||
} else {
|
||||
output = "-o " + in.Name + ext
|
||||
}
|
||||
cmd = fmt.Sprintf(`go build %s -ldflags "%s" %s %s`, output, ldFlags, in.Extra, file)
|
||||
} else {
|
||||
// Cross-building, output the compiled binary to specified path.
|
||||
if system == "windows" {
|
||||
ext = ".exe"
|
||||
}
|
||||
genv.MustSet("GOOS", system)
|
||||
genv.MustSet("GOARCH", arch)
|
||||
cmd = fmt.Sprintf(
|
||||
`go build -o %s/%s/%s%s -ldflags "%s" %s%s`,
|
||||
in.Path, system+"_"+arch, in.Name, ext, ldFlags, in.Extra, file,
|
||||
)
|
||||
}
|
||||
mlog.Debug(cmd)
|
||||
// It's not necessary printing the complete command string.
|
||||
cmdShow, _ := gregex.ReplaceString(`\s+(-ldflags ".+?")\s+`, " ", cmd)
|
||||
mlog.Print(cmdShow)
|
||||
if result, err := gproc.ShellExec(ctx, cmd); err != nil {
|
||||
mlog.Printf(
|
||||
"failed to build, os:%s, arch:%s, error:\n%s\n\n%s\n",
|
||||
system, arch, gstr.Trim(result),
|
||||
`you may use command option "--debug" to enable debug info and check the details`,
|
||||
)
|
||||
if in.ExitWhenError {
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
mlog.Debug(gstr.Trim(result))
|
||||
}
|
||||
// single binary building.
|
||||
if len(customSystems) == 0 && len(customArches) == 0 {
|
||||
goto buildDone
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildDone:
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
// getBuildInVarMapJson retrieves and returns the custom build-in variables in configuration
|
||||
// file as json.
|
||||
func (c cBuild) getBuildInVarStr(ctx context.Context, in cBuildInput) string {
|
||||
buildInVarMap := in.VarMap
|
||||
if buildInVarMap == nil {
|
||||
buildInVarMap = make(g.Map)
|
||||
}
|
||||
buildInVarMap["builtGit"] = c.getGitCommit(ctx)
|
||||
buildInVarMap["builtTime"] = gtime.Now().String()
|
||||
b, err := json.Marshal(buildInVarMap)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
return gbase64.EncodeToString(b)
|
||||
}
|
||||
|
||||
// getGitCommit retrieves and returns the latest git commit hash string if present.
|
||||
func (c cBuild) getGitCommit(ctx context.Context) string {
|
||||
if gproc.SearchBinary("git") == "" {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
cmd = `git log -1 --format="%cd %H" --date=format:"%Y-%m-%d %H:%M:%S"`
|
||||
s, _ = gproc.ShellExec(ctx, cmd)
|
||||
)
|
||||
mlog.Debug(cmd)
|
||||
if s != "" {
|
||||
if !gstr.Contains(s, "fatal") {
|
||||
return gstr.Trim(s)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
99
cool-tools/internal/cmd/cmd_pack.go
Normal file
99
cool-tools/internal/cmd/cmd_pack.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/allyes"
|
||||
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
var (
|
||||
Pack = cPack{}
|
||||
)
|
||||
|
||||
type cPack struct {
|
||||
g.Meta `name:"pack" usage:"{cPackUsage}" brief:"{cPackBrief}" eg:"{cPackEg}"`
|
||||
}
|
||||
|
||||
const (
|
||||
cPackUsage = `cool-tools pack SRC DST`
|
||||
cPackBrief = `packing any file/directory to a resource file, or a go file`
|
||||
cPackEg = `
|
||||
cool-tools pack public data.bin
|
||||
cool-tools pack public,template data.bin
|
||||
cool-tools pack public,template packed/data.go
|
||||
cool-tools pack public,template,config packed/data.go
|
||||
cool-tools pack public,template,config packed/data.go -n=packed -p=/var/www/my-app
|
||||
cool-tools pack /var/www/public packed/data.go -n=packed
|
||||
`
|
||||
cPackSrcBrief = `source path for packing, which can be multiple source paths.`
|
||||
cPackDstBrief = `
|
||||
destination file path for packed file. if extension of the filename is ".go" and "-n" option is given,
|
||||
it enables packing SRC to go file, or else it packs SRC into a binary file.
|
||||
`
|
||||
cPackNameBrief = `package name for output go file, it's set as its directory name if no name passed`
|
||||
cPackPrefixBrief = `prefix for each file packed into the resource file`
|
||||
cPackKeepPathBrief = `keep the source path from system to resource file, usually for relative path`
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cPackUsage`: cPackUsage,
|
||||
`cPackBrief`: cPackBrief,
|
||||
`cPackEg`: cPackEg,
|
||||
`cPackSrcBrief`: cPackSrcBrief,
|
||||
`cPackDstBrief`: cPackDstBrief,
|
||||
`cPackNameBrief`: cPackNameBrief,
|
||||
`cPackPrefixBrief`: cPackPrefixBrief,
|
||||
`cPackKeepPathBrief`: cPackKeepPathBrief,
|
||||
})
|
||||
Main.AddObject(Pack)
|
||||
|
||||
}
|
||||
|
||||
type cPackInput struct {
|
||||
g.Meta `name:"pack"`
|
||||
Src string `name:"SRC" arg:"true" v:"required" brief:"{cPackSrcBrief}"`
|
||||
Dst string `name:"DST" arg:"true" v:"required" brief:"{cPackDstBrief}"`
|
||||
Name string `name:"name" short:"n" brief:"{cPackNameBrief}"`
|
||||
Prefix string `name:"prefix" short:"p" brief:"{cPackPrefixBrief}"`
|
||||
KeepPath bool `name:"keepPath" short:"k" brief:"{cPackKeepPathBrief}" orphan:"true"`
|
||||
}
|
||||
|
||||
type cPackOutput struct{}
|
||||
|
||||
func (c cPack) Index(ctx context.Context, in cPackInput) (out *cPackOutput, err error) {
|
||||
if gfile.Exists(in.Dst) && gfile.IsDir(in.Dst) {
|
||||
mlog.Fatalf("DST path '%s' cannot be a directory", in.Dst)
|
||||
}
|
||||
if !gfile.IsEmpty(in.Dst) && !allyes.Check() {
|
||||
s := gcmd.Scanf("path '%s' is not empty, files might be overwrote, continue? [y/n]: ", in.Dst)
|
||||
if strings.EqualFold(s, "n") {
|
||||
return
|
||||
}
|
||||
}
|
||||
if in.Name == "" && gfile.ExtName(in.Dst) == "go" {
|
||||
in.Name = gfile.Basename(gfile.Dir(in.Dst))
|
||||
}
|
||||
var option = gres.Option{
|
||||
Prefix: in.Prefix,
|
||||
KeepPath: in.KeepPath,
|
||||
}
|
||||
if in.Name != "" {
|
||||
if err = gres.PackToGoFileWithOption(in.Src, in.Dst, in.Name, option); err != nil {
|
||||
mlog.Fatalf("pack failed: %v", err)
|
||||
}
|
||||
} else {
|
||||
if err = gres.PackToFileWithOption(in.Src, in.Dst, option); err != nil {
|
||||
mlog.Fatalf("pack failed: %v", err)
|
||||
}
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
170
cool-tools/internal/cmd/cmd_run.go
Normal file
170
cool-tools/internal/cmd/cmd_run.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package cmd
|
||||
|
||||
// 同步自 gf v2.2.2 https://github.com/gogf/gf/blob/60d828397149ed281111a7530074ce20f997224a/cmd/gf/internal/cmd/cmd_build.go
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gtype"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gfsnotify"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/os/gtimer"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
|
||||
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/mlog"
|
||||
)
|
||||
|
||||
var (
|
||||
Run = cRun{}
|
||||
)
|
||||
|
||||
type cRun struct {
|
||||
g.Meta `name:"run" usage:"{cRunUsage}" brief:"{cRunBrief}" eg:"{cRunEg}" dc:"{cRunDc}"`
|
||||
}
|
||||
|
||||
type cRunApp struct {
|
||||
File string // Go run file name.
|
||||
Path string // Directory storing built binary.
|
||||
Options string // Extra "go run" options.
|
||||
Args string // Custom arguments.
|
||||
}
|
||||
|
||||
const (
|
||||
cRunUsage = `cool-tools run FILE [OPTION]`
|
||||
cRunBrief = `running go codes with hot-compiled-like feature`
|
||||
cRunEg = `
|
||||
cool-tools run main.go
|
||||
cool-tools run main.go --args "server -p 8080"
|
||||
cool-tools run main.go -mod=vendor
|
||||
`
|
||||
cRunDc = `
|
||||
The "run" command is used for running go codes with hot-compiled-like feature,
|
||||
which compiles and runs the go codes asynchronously when codes change.
|
||||
`
|
||||
cRunFileBrief = `building file path.`
|
||||
cRunPathBrief = `output directory path for built binary file. it's "manifest/output" in default`
|
||||
cRunExtraBrief = `the same options as "go run"/"go build" except some options as follows defined`
|
||||
cRunArgsBrief = `custom arguments for your process`
|
||||
)
|
||||
|
||||
var (
|
||||
process *gproc.Process
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cRunUsage`: cRunUsage,
|
||||
`cRunBrief`: cRunBrief,
|
||||
`cRunEg`: cRunEg,
|
||||
`cRunDc`: cRunDc,
|
||||
`cRunFileBrief`: cRunFileBrief,
|
||||
`cRunPathBrief`: cRunPathBrief,
|
||||
`cRunExtraBrief`: cRunExtraBrief,
|
||||
`cRunArgsBrief`: cRunArgsBrief,
|
||||
})
|
||||
// 注册命令
|
||||
Main.AddObject(Run)
|
||||
}
|
||||
|
||||
type (
|
||||
cRunInput struct {
|
||||
g.Meta `name:"run"`
|
||||
File string `name:"FILE" arg:"true" brief:"{cRunFileBrief}" v:"required"`
|
||||
Path string `name:"path" short:"p" brief:"{cRunPathBrief}" d:"./"`
|
||||
Extra string `name:"extra" short:"e" brief:"{cRunExtraBrief}"`
|
||||
Args string `name:"args" short:"a" brief:"{cRunArgsBrief}"`
|
||||
}
|
||||
cRunOutput struct{}
|
||||
)
|
||||
|
||||
func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err error) {
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("go") == "" {
|
||||
mlog.Fatalf(`command "go" not found in your environment, please install golang first to proceed this command`)
|
||||
}
|
||||
|
||||
app := &cRunApp{
|
||||
File: in.File,
|
||||
Path: in.Path,
|
||||
Options: in.Extra,
|
||||
Args: in.Args,
|
||||
}
|
||||
dirty := gtype.NewBool()
|
||||
_, err = gfsnotify.Add(gfile.RealPath("."), func(event *gfsnotify.Event) {
|
||||
if gfile.ExtName(event.Path) != "go" {
|
||||
return
|
||||
}
|
||||
// Variable `dirty` is used for running the changes only one in one second.
|
||||
if !dirty.Cas(false, true) {
|
||||
return
|
||||
}
|
||||
// With some delay in case of multiple code changes in very short interval.
|
||||
gtimer.SetTimeout(ctx, 1500*gtime.MS, func(ctx context.Context) {
|
||||
defer dirty.Set(false)
|
||||
mlog.Printf(`go file changes: %s`, event.String())
|
||||
app.Run(ctx)
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
go app.Run(ctx)
|
||||
select {}
|
||||
}
|
||||
|
||||
func (app *cRunApp) Run(ctx context.Context) {
|
||||
// Rebuild and run the codes.
|
||||
renamePath := ""
|
||||
mlog.Printf("build: %s", app.File)
|
||||
outputPath := gfile.Join(app.Path, gfile.Name(app.File))
|
||||
if runtime.GOOS == "windows" {
|
||||
outputPath += ".exe"
|
||||
if gfile.Exists(outputPath) {
|
||||
renamePath = outputPath + "~"
|
||||
if err := gfile.Rename(outputPath, renamePath); err != nil {
|
||||
mlog.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// In case of `pipe: too many open files` error.
|
||||
// Build the app.
|
||||
buildCommand := fmt.Sprintf(
|
||||
`go build -o %s %s %s`,
|
||||
outputPath,
|
||||
app.Options,
|
||||
app.File,
|
||||
)
|
||||
mlog.Print(buildCommand)
|
||||
result, err := gproc.ShellExec(ctx, buildCommand)
|
||||
if err != nil {
|
||||
mlog.Printf("build error: \n%s%s", result, err.Error())
|
||||
return
|
||||
}
|
||||
// Kill the old process if build successfully.
|
||||
if process != nil {
|
||||
if err := process.Kill(); err != nil {
|
||||
mlog.Debugf("kill process error: %s", err.Error())
|
||||
//return
|
||||
}
|
||||
}
|
||||
// Run the binary file.
|
||||
runCommand := fmt.Sprintf(`%s %s`, outputPath, app.Args)
|
||||
mlog.Print(runCommand)
|
||||
if runtime.GOOS == "windows" {
|
||||
// Special handling for windows platform.
|
||||
// DO NOT USE "cmd /c" command.
|
||||
process = gproc.NewProcess(runCommand, nil)
|
||||
} else {
|
||||
process = gproc.NewProcessCmd(runCommand, nil)
|
||||
}
|
||||
if pid, err := process.Start(ctx); err != nil {
|
||||
mlog.Printf("build running error: %s", err.Error())
|
||||
} else {
|
||||
mlog.Printf("build running pid: %d", pid)
|
||||
}
|
||||
}
|
||||
80
cool-tools/internal/cmd/docs.go
Normal file
80
cool-tools/internal/cmd/docs.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var (
|
||||
Docs = gcmd.Command{
|
||||
Name: "docs",
|
||||
Usage: "cool-tools docs",
|
||||
Brief: "查看帮助文档",
|
||||
Description: "查看帮助文档",
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) error {
|
||||
s := g.Server("docs")
|
||||
// 获取本机未占用的端口
|
||||
port, err := getfreeport()
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
return err
|
||||
}
|
||||
// 获取本机ip
|
||||
// ip, err := getlocalip()
|
||||
// if err != nil {
|
||||
// mlog.Fatal(err)
|
||||
// return err
|
||||
// }
|
||||
s.SetServerRoot("docs")
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.RedirectTo("/cool-admin-go/")
|
||||
})
|
||||
// 设置端口
|
||||
s.SetPort(gconv.Int(port))
|
||||
mlog.Printf("CoolAdminGo docs server is running at %s", "http://"+"127.0.0.1"+":"+gconv.String(port)+"/cool-admin-go/")
|
||||
s.Run()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
Main.AddCommand(&Docs)
|
||||
}
|
||||
|
||||
// getfreeport 获取本机未占用的端口
|
||||
func getfreeport() (int, error) {
|
||||
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
l, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer l.Close()
|
||||
return l.Addr().(*net.TCPAddr).Port, nil
|
||||
}
|
||||
|
||||
// getlocalip 获取本机ip
|
||||
// func getlocalip() (string, error) {
|
||||
// addrs, err := net.InterfaceAddrs()
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// for _, address := range addrs {
|
||||
// // 检查ip地址判断是否回环地址
|
||||
// if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
// if ipnet.IP.To4() != nil {
|
||||
// return ipnet.IP.String(), nil
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return "", nil
|
||||
// }
|
||||
27
cool-tools/internal/cmd/dump.go
Normal file
27
cool-tools/internal/cmd/dump.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
)
|
||||
|
||||
var (
|
||||
Dump = &gcmd.Command{
|
||||
Name: "dump",
|
||||
Usage: "cool-tools dump",
|
||||
Brief: "查看打包的资源文件",
|
||||
Description: "查看打包的资源文件",
|
||||
Arguments: []gcmd.Argument{},
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||
gres.Dump()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// init
|
||||
func init() {
|
||||
Main.AddCommand(Dump)
|
||||
}
|
||||
18
cool-tools/internal/cmd/gen.go.bak
Normal file
18
cool-tools/internal/cmd/gen.go.bak
Normal file
@@ -0,0 +1,18 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
var (
|
||||
Gen = cGen{}
|
||||
)
|
||||
|
||||
type cGen struct {
|
||||
g.Meta `name:"gen" brief:"生成代码" description:"生成代码, 例如: cool-tools gen model"`
|
||||
cGenModel
|
||||
}
|
||||
|
||||
func init() {
|
||||
Main.AddObject(Gen)
|
||||
}
|
||||
46
cool-tools/internal/cmd/gen_model.go.bak
Normal file
46
cool-tools/internal/cmd/gen_model.go.bak
Normal file
@@ -0,0 +1,46 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/cool-team-official/cool-admin-go/cool"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"gorm.io/gen"
|
||||
)
|
||||
|
||||
type cGenModel struct {
|
||||
g.Meta `name:"model" brief:"生成模型代码" description:"生成模型代码, 例如: cool-tools gen model"`
|
||||
}
|
||||
|
||||
var (
|
||||
GenModel = cGenModel{}
|
||||
)
|
||||
|
||||
type cGenModelInput struct {
|
||||
g.Meta `name:"model" brief:"生成模型代码" description:"生成模型代码, 例如: cool-tools gen model"`
|
||||
Database string `v:"required#请输入数据库名称" arg:"true" name:"database" brief:"数据库名称" description:"数据库名称"`
|
||||
}
|
||||
type cGenModelOutput struct{}
|
||||
|
||||
func (c *cGenModel) Index(ctx g.Ctx, in cGenModelInput) (out cGenModelOutput, err error) {
|
||||
g.Log().Print(ctx, in.Database)
|
||||
// 获取数据库
|
||||
db, err := cool.InitDB(in.Database)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
generator := gen.NewGenerator(gen.Config{
|
||||
OutPath: "temp/gen/model",
|
||||
OutFile: "",
|
||||
ModelPkgPath: "",
|
||||
WithUnitTest: false,
|
||||
FieldNullable: true,
|
||||
FieldCoverable: true,
|
||||
FieldSignable: true,
|
||||
FieldWithIndexTag: true,
|
||||
FieldWithTypeTag: true,
|
||||
Mode: 0,
|
||||
})
|
||||
generator.UseDB(db)
|
||||
generator.GenerateAllTable()
|
||||
generator.Execute()
|
||||
return
|
||||
}
|
||||
68
cool-tools/internal/cmd/init.go
Normal file
68
cool-tools/internal/cmd/init.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
)
|
||||
|
||||
var (
|
||||
Init = gcmd.Command{
|
||||
Name: "init",
|
||||
Usage: "cool-tools init [dst]",
|
||||
Brief: "创建一个新的cool-admin-go项目",
|
||||
Arguments: []gcmd.Argument{
|
||||
{
|
||||
Name: "dst",
|
||||
// Short: "m",
|
||||
Brief: "the destination path",
|
||||
IsArg: true,
|
||||
},
|
||||
},
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||
dst := parser.GetArg(2).String()
|
||||
if dst == "" {
|
||||
dst = "."
|
||||
}
|
||||
// 如果目标路径不存在,则创建目标路径
|
||||
if gfile.IsEmpty(dst) {
|
||||
if err = gfile.Mkdir(dst); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if !gfile.IsDir(dst) {
|
||||
g.Log().Panicf(ctx, "%s is not a directory", dst)
|
||||
} else {
|
||||
s := gcmd.Scanf(`the folder "%s" is not empty, files might be overwrote, continue? [y/n]: `, dst)
|
||||
if strings.EqualFold(s, "n") {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
err = gres.Export("cool-admin-go-simple", dst, gres.ExportOption{
|
||||
RemovePrefix: "cool-admin-go-simple",
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = gfile.ReplaceDir("cool-admin-go-simple", gfile.Basename(gfile.RealPath(dst)), dst, "*", true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
g.Log().Infof(ctx, "init success")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// init 初始化模块
|
||||
func init() {
|
||||
Main.AddCommand(&Init)
|
||||
}
|
||||
25
cool-tools/internal/cmd/install.go
Normal file
25
cool-tools/internal/cmd/install.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/service"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
)
|
||||
|
||||
var (
|
||||
Install = gcmd.Command{
|
||||
Name: "install",
|
||||
Usage: "cool-tools install",
|
||||
Brief: "Install cool-tools to the system.",
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||
err = service.Install.Run(ctx)
|
||||
return
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// init
|
||||
func init() {
|
||||
Main.AddCommand(&Install)
|
||||
}
|
||||
61
cool-tools/internal/cmd/module.go
Normal file
61
cool-tools/internal/cmd/module.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cool-team-official/cool-admin-go/cool-tools/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
)
|
||||
|
||||
var (
|
||||
Module = &gcmd.Command{
|
||||
Name: "module",
|
||||
Usage: "cool-tools module moduleName",
|
||||
Brief: "在modules目录下创建模块",
|
||||
Description: "在modules目录下创建模块, 并且创建相应的目录结构,注意: 如果模块已经存在, 则会覆盖原有的模块,本命令需在项目根目录下执行.",
|
||||
Arguments: []gcmd.Argument{
|
||||
{
|
||||
Name: "moduleName",
|
||||
IsArg: true,
|
||||
Orphan: false,
|
||||
},
|
||||
},
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||
moduleName := parser.GetArg(2).String()
|
||||
if moduleName == "" {
|
||||
println("moduleName is empty")
|
||||
return nil
|
||||
}
|
||||
err = service.CreatModule(ctx, moduleName)
|
||||
return
|
||||
},
|
||||
}
|
||||
M = &gcmd.Command{
|
||||
Name: "m",
|
||||
Usage: "cool-tools module moduleName",
|
||||
Brief: "在modules目录下创建模块,为module的简写模式",
|
||||
Description: "在modules目录下创建模块, 并且创建相应的目录结构,注意: 如果模块已经存在, 则会覆盖原有的模块,本命令需在项目根目录下执行.",
|
||||
Arguments: []gcmd.Argument{
|
||||
{
|
||||
Name: "moduleName",
|
||||
IsArg: true,
|
||||
Orphan: false,
|
||||
},
|
||||
},
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||
moduleName := parser.GetArg(2).String()
|
||||
if moduleName == "" {
|
||||
println("moduleName is empty")
|
||||
return nil
|
||||
}
|
||||
err = service.CreatModule(ctx, moduleName)
|
||||
return
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
Main.AddCommand(Module)
|
||||
Main.AddCommand(M)
|
||||
}
|
||||
61
cool-tools/internal/cmd/snippetsmaker.go
Normal file
61
cool-tools/internal/cmd/snippetsmaker.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
)
|
||||
|
||||
var (
|
||||
SnippetsMaker = gcmd.Command{
|
||||
Name: "snippetsmaker",
|
||||
Usage: "snippetsmaker",
|
||||
Brief: "代码片段生成器",
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||
g.Log().Debug(ctx, "snippetsmaker,生成工具^.^")
|
||||
files := garray.New(true)
|
||||
files.Append("modules/demo/model/demo_sample.go")
|
||||
files.Append("modules/demo/service/demo_sample.go")
|
||||
files.Append("modules/demo/controller/admin/demo_sample.go")
|
||||
// 遍历files
|
||||
for _, file := range files.Slice() {
|
||||
sArray := garray.NewStrArray()
|
||||
gfile.ReadLines(file.(string), func(line string) error {
|
||||
// g.Log().Debug(ctx, line)
|
||||
// println(line)
|
||||
// search := `Sample`
|
||||
// replace := `${TM_FILENAME_BASE/(.*)/${1:/capitalize}/}`
|
||||
// replaceArray := []string{"Sample", "${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}", "sample", "${TM_FILENAME_BASE/(.*)/${1:/downcase}/}", "demo", "${2:模块名称}", "app", "${TM_DIRECTORY/^.+[\\/\\\\]+(.*)$/$1/}"}
|
||||
replaceArray := []string{"DemoSample", "${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}", "demo_sample", "${TM_FILENAME_BASE/(.*)/${1:/downcase}/}"}
|
||||
|
||||
result := gstr.ReplaceByArray(line, replaceArray)
|
||||
sArray.Append(gstr.AddSlashes(result))
|
||||
|
||||
return nil
|
||||
})
|
||||
// g.Dump(sArray)
|
||||
println(file.(string))
|
||||
println("--------------------------------------code start------------------------------------------")
|
||||
println(`"body":[`)
|
||||
sArray.Iterator(
|
||||
func(index int, value string) bool {
|
||||
println("\"" + value + "\",")
|
||||
return true
|
||||
},
|
||||
)
|
||||
println("]")
|
||||
println("--------------------------------------code end------------------------------------------")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
Main.AddCommand(&SnippetsMaker)
|
||||
}
|
||||
53
cool-tools/internal/cmd/version.go
Normal file
53
cool-tools/internal/cmd/version.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gbuild"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
|
||||
type sVersion struct {
|
||||
Name string //程序名称
|
||||
Homepage string //程序主页
|
||||
Version string //程序版本
|
||||
GoFrame string //goframe version
|
||||
Golang string //golang version
|
||||
Git string //git commit id
|
||||
Time string //build datetime
|
||||
InstallPath string //安装路径
|
||||
}
|
||||
|
||||
var (
|
||||
Version = gcmd.Command{
|
||||
Name: "version",
|
||||
Usage: "cool-tools version",
|
||||
Brief: "查看版本信息",
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||
info := gbuild.Info()
|
||||
binVersion := "v1.5.10"
|
||||
|
||||
// 生成sVersion结构体
|
||||
res := sVersion{
|
||||
Name: "cool-tools",
|
||||
Homepage: "https://cool-js.com",
|
||||
Version: binVersion,
|
||||
GoFrame: info.GoFrame,
|
||||
Golang: info.Golang,
|
||||
Git: info.Git,
|
||||
Time: info.Time,
|
||||
InstallPath: gfile.SelfDir(),
|
||||
}
|
||||
// mlog.Printf(`CLI Installed At: %s`, gfile.SelfPath())
|
||||
gutil.Dump(res)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// init 初始化模块
|
||||
func init() {
|
||||
Main.AddCommand(&Version)
|
||||
}
|
||||
Reference in New Issue
Block a user