From f17742409aac95f64ca95f8f2f089d5c046fbf0f Mon Sep 17 00:00:00 2001 From: 15128022404 <1421485150@qq.com> Date: Sun, 26 Mar 2023 22:49:38 +0800 Subject: [PATCH] init --- .gitignore | 4 +- cli/cli.go | 156 ++++++++++++++++++++++++++++++++++++++++ cli/commands.go | 20 ++++++ cli/ls.go | 47 ++++++++++++ cli/use.go | 33 +++++++++ go.mod | 19 +++++ go.sum | 22 ++++++ internal/build/build.go | 41 +++++++++++ main.go | 14 ++++ 9 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 cli/cli.go create mode 100644 cli/commands.go create mode 100644 cli/ls.go create mode 100644 cli/use.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/build/build.go create mode 100644 main.go diff --git a/.gitignore b/.gitignore index 66fd13c..4ac68d8 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out - +downloads +go +versions # Dependency directories (remove the comment below to include it) # vendor/ diff --git a/cli/cli.go b/cli/cli.go new file mode 100644 index 0000000..723f71d --- /dev/null +++ b/cli/cli.go @@ -0,0 +1,156 @@ +package cli + +import ( + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "runtime" + "sort" + "strings" + "time" + + "github.com/Masterminds/semver" + "github.com/fatih/color" + "github.com/forget-the-bright/j/internal/build" + "github.com/urfave/cli/v2" +) + +var ( + ghomeDir string + downloadsDir string + versionsDir string + goroot string +) + +func init() { + /* ghomeDir, _ = os.Getwd() + goroot = filepath.Join(ghomeDir, "go") + downloadsDir = filepath.Join(ghomeDir, "downloads") + os.MkdirAll(downloadsDir, 0755) + versionsDir = filepath.Join(ghomeDir, "versions") + os.MkdirAll(versionsDir, 0755) */ + cli.AppHelpTemplate = fmt.Sprintf(`NAME: + {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} + + USAGE: + {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .Commands}} command{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} + + VERSION: + %s{{end}}{{end}}{{if .Description}} + + DESCRIPTION: + {{.Description}}{{end}}{{if len .Authors}} + + AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: + {{range $index, $author := .Authors}}{{if $index}} + {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} + + COMMANDS:{{range .VisibleCategories}}{{if .Name}} + + {{.Name}}:{{end}}{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + + GLOBAL OPTIONS: + {{range $index, $option := .VisibleFlags}}{{if $index}} + {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} + + COPYRIGHT: + {{.Copyright}}{{end}} +`, build.ShortVersion) +} + +// Run 运行g命令行 +func Run() { + app := cli.NewApp() + app.Name = "g" + app.Usage = "Golang Version Manager" + app.Version = build.Version() + app.Copyright = fmt.Sprintf("Copyright (c) 2019-%d, voidint. All rights reserved.", time.Now().Year()) + app.Authors = []*cli.Author{ + {Name: "wh", Email: "helloworldwh@163.com"}, + } + + app.Before = func(ctx *cli.Context) (err error) { + ghomeDir = ghome() + goroot = filepath.Join(ghomeDir, "go") + downloadsDir = filepath.Join(ghomeDir, "downloads") + if err = os.MkdirAll(downloadsDir, 0755); err != nil { + return err + } + versionsDir = filepath.Join(ghomeDir, "versions") + return os.MkdirAll(versionsDir, 0755) + } + app.Commands = commands + + if err := app.Run(os.Args); err != nil { + os.Exit(1) + } +} + +// ghome 返回g根目录 +func ghome() (dir string) { + /* if experimental := os.Getenv(experimentalEnv); experimental == "true" { + if dir = os.Getenv(homeEnv); dir != "" { + return dir + } + } + homeDir, _ := os.UserHomeDir() + return filepath.Join(homeDir, ".g") */ + path, _ := os.Getwd() + return path +} + +// inuse 返回当前的go版本号 +func inuse(goroot string) (version string) { + p, _ := os.Readlink(goroot) + return filepath.Base(p) +} + +// render 渲染go版本列表 +func render(curV string, items []*semver.Version, out io.Writer) { + sort.Sort(semver.Collection(items)) + + for i := range items { + fields := strings.SplitN(items[i].String(), "-", 2) + v := strings.TrimSuffix(strings.TrimSuffix(fields[0], ".0"), ".0") + if len(fields) > 1 { + v += fields[1] + } + if v == curV { + color.New(color.FgGreen).Fprintf(out, "* %s\n", v) + } else { + fmt.Fprintf(out, " %s\n", v) + } + } +} + +// errstring 返回统一格式的错误信息 +func errstring(err error) string { + if err == nil { + return "" + } + return wrapstring(err.Error()) +} + +func wrapstring(str string) string { + if str == "" { + return str + } + words := strings.Fields(str) + if len(words) > 0 { + words[0] = strings.Title(words[0]) + } + return fmt.Sprintf("[g] %s", strings.Join(words, " ")) +} + +func mkSymlink(oldname, newname string) (err error) { + if runtime.GOOS == "windows" { + // Windows 10下无特权用户无法创建符号链接,优先调用mklink /j创建'目录联接' + if err = exec.Command("cmd", "/c", "mklink", "/j", newname, oldname).Run(); err == nil { + return nil + } + } + return os.Symlink(oldname, newname) +} diff --git a/cli/commands.go b/cli/commands.go new file mode 100644 index 0000000..6c6227a --- /dev/null +++ b/cli/commands.go @@ -0,0 +1,20 @@ +package cli + +import "github.com/urfave/cli/v2" + +var ( + commands = []*cli.Command{ + { + Name: "ls", + Usage: "List installed versions", + UsageText: "g ls", + Action: list, + }, + { + Name: "use", + Usage: "Switch to specified version", + UsageText: "g use ", + Action: use, + }, + } +) diff --git a/cli/ls.go b/cli/ls.go new file mode 100644 index 0000000..ecfc406 --- /dev/null +++ b/cli/ls.go @@ -0,0 +1,47 @@ +package cli + +import ( + "fmt" + "io/ioutil" + "strings" + + "github.com/Masterminds/semver" + "github.com/k0kubun/go-ansi" + "github.com/urfave/cli/v2" +) + +func list(*cli.Context) (err error) { + infos, err := ioutil.ReadDir(versionsDir) + if err != nil || len(infos) <= 0 { + fmt.Printf("No version installed yet\n\n") + return nil + } + items := make([]*semver.Version, 0, len(infos)) + for i := range infos { + if !infos[i].IsDir() { + continue + } + vname := infos[i].Name() + var idx int + if strings.Contains(vname, "alpha") { + idx = strings.Index(vname, "alpha") + + } else if strings.Contains(vname, "beta") { + idx = strings.Index(vname, "beta") + + } else if strings.Contains(vname, "rc") { + idx = strings.Index(vname, "rc") + } + if idx > 0 { + vname = vname[:idx] + "-" + vname[idx:] + } + v, err := semver.NewVersion(vname) + if err != nil || v == nil { + continue + } + items = append(items, v) + } + + render(inuse(goroot), items, ansi.NewAnsiStdout()) + return nil +} diff --git a/cli/use.go b/cli/use.go new file mode 100644 index 0000000..6d4fab7 --- /dev/null +++ b/cli/use.go @@ -0,0 +1,33 @@ +package cli + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/urfave/cli/v2" +) + +func use(ctx *cli.Context) (err error) { + vname := ctx.Args().First() + if vname == "" { + return nil + //return cli.ShowSubcommandHelp(ctx) + } + targetV := filepath.Join(versionsDir, vname) + + if finfo, err := os.Stat(targetV); err != nil || !finfo.IsDir() { + return cli.Exit(fmt.Sprintf("[g] The %q version does not exist, please install it first.", vname), 1) + } + + _ = os.Remove(goroot) + + if err = mkSymlink(targetV, goroot); err != nil { + return cli.Exit(errstring(err), 1) + } + if output, err := exec.Command(filepath.Join(goroot, "bin", "go"), "version").Output(); err == nil { + fmt.Print(string(output)) + } + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d73a9cb --- /dev/null +++ b/go.mod @@ -0,0 +1,19 @@ +module github.com/forget-the-bright/j + +go 1.20 + +require ( + github.com/Masterminds/semver v1.5.0 + github.com/fatih/color v1.15.0 + github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 + github.com/urfave/cli/v2 v2.25.0 +) + +require ( + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + golang.org/x/sys v0.6.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c5075b7 --- /dev/null +++ b/go.sum @@ -0,0 +1,22 @@ +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/urfave/cli/v2 v2.25.0 h1:ykdZKuQey2zq0yin/l7JOm9Mh+pg72ngYMeB0ABn6q8= +github.com/urfave/cli/v2 v2.25.0/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/build/build.go b/internal/build/build.go new file mode 100644 index 0000000..59ddb47 --- /dev/null +++ b/internal/build/build.go @@ -0,0 +1,41 @@ +package build + +import "strings" + +const ( + // ShortVersion 短版本号 + ShortVersion = "1.0.0" +) + +// The value of variables come form `gb build -ldflags '-X "build.Build=xxxxx" -X "build.CommitID=xxxx"' ` +var ( + // Build build time + Build string + // Branch current git branch + Branch string + // Commit git commit id + Commit string +) + +// Version 生成版本信息 +func Version() string { + var buf strings.Builder + buf.WriteString(ShortVersion) + + if Build != "" { + buf.WriteByte('\n') + buf.WriteString("build: ") + buf.WriteString(Build) + } + if Branch != "" { + buf.WriteByte('\n') + buf.WriteString("branch: ") + buf.WriteString(Branch) + } + if Commit != "" { + buf.WriteByte('\n') + buf.WriteString("commit: ") + buf.WriteString(Commit) + } + return buf.String() +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..2deac15 --- /dev/null +++ b/main.go @@ -0,0 +1,14 @@ +package main + +import "github.com/forget-the-bright/j/cli" + +func main() { + /* dir, _ := os.Getwd() + fmt.Println(dir) + cli.List(dir) + if len(os.Args) > 1 { + fmt.Println(os.Args[1]) + cli.Use(os.Args[1]) //"1.17.9" + } */ + cli.Run() +}