diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f44c4df --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +downloads +java +versions +bin +j +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..95af279 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}", + "args": ["ls-remote" ] + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d8d1dd8 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# j +Gradle_Version_Manager + + + + +## 安装 + [下载](https://github.com/forget-the-bright/j/releases) 下载自己需要的版本, 到自己自定义的目录 修改可执行文件名称为j + + 默认文件下载安装在用户目录下 ```.grvm```目录,目录下 ```versions```, ```downloads```, ```gradle``` 分别是本地安装目录,安装包下载目录,当前使用的java版本目录 + + 将 JAVA_HOME 配置为 ```USER_HOME\.grvm\gradle``` + + 指定安装目录需要 添加环境变量 ```GRVM_HOME``` +## 命令 + +### 列出 + +列出所有可安装版本 +``` +grvm ls-remote +``` + + +列出本地安装版本 +``` +grvm ls +``` + +### 下载 +``` +grvm install 8 +``` + + +### 切换版本 +``` +grvm use 17 +``` + + + + + diff --git a/cli/clean.go b/cli/clean.go new file mode 100644 index 0000000..1fd272c --- /dev/null +++ b/cli/clean.go @@ -0,0 +1,24 @@ +package cli + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/urfave/cli/v2" +) + +func clean(ctx *cli.Context) (err error) { + entries, err := ioutil.ReadDir(downloadsDir) + if err != nil { + return cli.Exit(errstring(err), 1) + } + + for i := range entries { + if err = os.RemoveAll(filepath.Join(downloadsDir, entries[i].Name())); err == nil { + fmt.Println("Remove", entries[i].Name()) + } + } + return nil +} diff --git a/cli/cli.go b/cli/cli.go new file mode 100644 index 0000000..7d2c815 --- /dev/null +++ b/cli/cli.go @@ -0,0 +1,164 @@ +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() + fmt.Println(ghomeDir) + goroot = filepath.Join(ghomeDir, "java") + fmt.Println(goroot) + 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 = "grvm" + app.Usage = "Gradle Version Manager" + app.Version = build.Version() + app.Copyright = fmt.Sprintf("Copyright (c) 2019-%d, forget-the-bright. 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, "gradle") + 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) + } +} + +const ( + homeEnv = "GRVM_HOME" + mirrorEnv = "GRVM_MIRROR" +) + +// ghome 返回g根目录 +func ghome() (dir string) { + //fmt.Println(os.Getenv(homeEnv)) + /* path, _ := os.Getwd() + return path */ + if dir = os.Getenv(homeEnv); dir != "" { + return dir + } + homeDir, _ := os.UserHomeDir() + return filepath.Join(homeDir, ".j") + +} + +// 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("[grvm] %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..766d72c --- /dev/null +++ b/cli/commands.go @@ -0,0 +1,50 @@ +package cli + +import "github.com/urfave/cli/v2" + +var ( + commands = []*cli.Command{ + { + Name: "ls", + Usage: "List installed versions", + UsageText: "grvm ls", + Action: list, + }, + /* { + Name: "ls-all", + Usage: "List All versions", + UsageText: "j ls-all", + Action: listAll, + }, */ + { + Name: "ls-remote", + Usage: "List Remote versions", + UsageText: "grvm ls-remote", + Action: listRemote, + }, + { + Name: "install", + Usage: "install versions", + UsageText: "grvm install ", + Action: install, + }, + { + Name: "use", + Usage: "Switch to specified version", + UsageText: "grvm use ", + Action: use, + }, + { + Name: "uninstall", + Usage: "Uninstall a version", + UsageText: "grvm uninstall ", + Action: uninstall, + }, + { + Name: "clean", + Usage: "Remove files from the package download directory", + UsageText: "grvm clean", + Action: clean, + }, + } +) diff --git a/cli/install.go b/cli/install.go new file mode 100644 index 0000000..f9ea508 --- /dev/null +++ b/cli/install.go @@ -0,0 +1,95 @@ +package cli + +import ( + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/forget-the-bright/j/internal/pkg/archiver" + "github.com/forget-the-bright/j/internal/pkg/check" + "github.com/forget-the-bright/j/internal/pkg/collector" + "github.com/forget-the-bright/j/internal/pkg/config" + "github.com/forget-the-bright/j/internal/pkg/download" + + //"github.com/mholt/archiver/v3" + "github.com/urfave/cli/v2" +) + +func fundVersion(version string) *config.UrlItem { + config.Url_Items = collector.ConvertCollectorToUrlItem(collector.GetOpenJDKArchiveReleasesInfo(), false) + for _, v := range config.Url_Items { + if v.SimpleName == version { //strings.Contains(v.SimpleName, version) + if version != "8" { + v.In.Sha256 = collector.GetSha256ByUrl(v.In.Sha256, true) + } + return v + } + } + return nil +} + +func downloadAndInstall(version string) (err error) { + ui := fundVersion(version) + if ui == nil { + return cli.Exit(errors.New(version+" version is not supported"), 1) + } + + filename := filepath.Join(downloadsDir, ui.In.FileName) + //判断本地有没有安装包 没有就进入下载 + if _, err := os.Stat(filename); err != nil { + DownloadWithProgress(ui.In.URL, filename) + } else { + if ui.In.Sha256 != check.PrintSha256(filename) { + DownloadWithProgress(ui.In.URL, filename) + } + } + + //获取解压目标目录 + targetV := filepath.Join(versionsDir, ui.SimpleName) + + // 检查版本是否已经安装 + if finfo, err := os.Stat(targetV); err == nil && finfo.IsDir() { + return cli.Exit(fmt.Sprintf("[grvm] %q version has been installed.", version), 1) + } + // 解压安装包 + if err = archiver.Unarchive(filename, targetV, true); err != nil { + return cli.Exit(errstring(err), 1) + } + /* // 解压安装包 + if err = archiver.Unarchive(filename, versionsDir); err != nil { + fmt.Println(err.Error()) + return cli.Exit(errstring(err), 1) + } + // 目录重命名 + if err = os.Rename(filepath.Join(versionsDir, ui.Expected), targetV); err != nil { + fmt.Println(err.Error()) + return cli.Exit(errstring(err), 1) + } */ + + // 重新建立软链接 + _ = os.Remove(goroot) + if err = mkSymlink(targetV, goroot); err != nil { + return cli.Exit(errstring(err), 1) + } + fmt.Printf("Now using %s\n", ui.Expected) + return nil +} + +func install(ctx *cli.Context) (err error) { + version := ctx.Args().First() + if version == "" { + return cli.ShowSubcommandHelp(ctx) + } + return downloadAndInstall(version) + +} + +func Install(version string) (err error) { + return downloadAndInstall(version) +} + +// DownloadWithProgress 下载版本另存为指定文件且显示下载进度 +func DownloadWithProgress(url, dst string) (size int64, err error) { + return download.Download(url, dst, os.O_CREATE|os.O_WRONLY, 0644, true) +} diff --git a/cli/ls-remote.go b/cli/ls-remote.go new file mode 100644 index 0000000..625321d --- /dev/null +++ b/cli/ls-remote.go @@ -0,0 +1,33 @@ +package cli + +import ( + "fmt" + + "github.com/fatih/color" + "github.com/forget-the-bright/j/internal/pkg/collector" + "github.com/k0kubun/go-ansi" + "github.com/urfave/cli/v2" +) + +func remoteVersionLength(version string) string { + yu := 8 - len(version) + for i := 0; i < yu; i++ { + version += " " + } + return version +} + +func listRemote(*cli.Context) (err error) { + use_version := inuse(goroot) + out := ansi.NewAnsiStdout() + rs := collector.ConvertCollectorToUrlItem(collector.GetOpenJDKArchiveReleasesInfo(), false) + color.New(color.FgGreen).Fprintf(out, " %s\n", " version info") + for _, v := range rs { + if v.SimpleName == use_version { //strings.Contains(v.SimpleName, version) + color.New(color.FgGreen).Fprintf(out, "* %s\n", remoteVersionLength(v.SimpleName)+" "+v.Expected) + } else { + fmt.Fprintf(out, " %s\n", remoteVersionLength(v.SimpleName)+" "+v.Expected) + } + } + return nil +} 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/ls_all.go b/cli/ls_all.go new file mode 100644 index 0000000..1d4c017 --- /dev/null +++ b/cli/ls_all.go @@ -0,0 +1,30 @@ +package cli + +import ( + "fmt" + + "github.com/fatih/color" + "github.com/forget-the-bright/j/internal/pkg/config" + "github.com/k0kubun/go-ansi" + "github.com/urfave/cli/v2" +) + +func mathVersionLength(version string) string { + if len(version) <= 1 { + return version + " " + } + return version +} +func listAll(*cli.Context) (err error) { + use_version := inuse(goroot) + out := ansi.NewAnsiStdout() + color.New(color.FgGreen).Fprintf(out, " %s\n", "version info") + for _, v := range config.ReverseArray(config.Url_Items) { + if v.SimpleName == use_version { //strings.Contains(v.SimpleName, version) + color.New(color.FgGreen).Fprintf(out, "* %s\n", mathVersionLength(v.SimpleName)+" "+v.Expected) + } else { + fmt.Fprintf(out, " %s\n", mathVersionLength(v.SimpleName)+" "+v.Expected) + } + } + return nil +} diff --git a/cli/uninstall.go b/cli/uninstall.go new file mode 100644 index 0000000..300deff --- /dev/null +++ b/cli/uninstall.go @@ -0,0 +1,27 @@ +package cli + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/urfave/cli/v2" +) + +func uninstall(ctx *cli.Context) (err error) { + vname := ctx.Args().First() + if vname == "" { + return cli.ShowSubcommandHelp(ctx) + } + targetV := filepath.Join(versionsDir, vname) + + if finfo, err := os.Stat(targetV); err != nil || !finfo.IsDir() { + return cli.Exit(fmt.Sprintf("[j] %q version is not installed", vname), 1) + } + + if err = os.RemoveAll(targetV); err != nil { + return cli.Exit(wrapstring(fmt.Sprintf("Uninstall failed: %s", err.Error())), 1) + } + fmt.Printf("Uninstalled java %s\n", vname) + return nil +} diff --git a/cli/use.go b/cli/use.go new file mode 100644 index 0000000..c8387c9 --- /dev/null +++ b/cli/use.go @@ -0,0 +1,32 @@ +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 cli.ShowSubcommandHelp(ctx) + } + targetV := filepath.Join(versionsDir, vname) + + if finfo, err := os.Stat(targetV); err != nil || !finfo.IsDir() { + return cli.Exit(fmt.Sprintf("[j] 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", "java"), "--version").Output(); err == nil { + fmt.Print(string(output)) + } + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f2c8281 --- /dev/null +++ b/go.mod @@ -0,0 +1,29 @@ +module github.com/forget-the-bright/j + +go 1.20 + +require ( + github.com/Masterminds/semver v1.5.0 + github.com/PuerkitoBio/goquery v1.8.1 + github.com/fatih/color v1.15.0 + github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 + github.com/schollz/progressbar/v3 v3.13.1 + github.com/urfave/cli/v2 v2.25.0 + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 +) + +require ( + github.com/andybalholm/cascadia v1.3.1 // indirect + 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/mattn/go-runewidth v0.0.14 // indirect + github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect + github.com/rivo/uniseg v0.4.3 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/stretchr/testify v1.8.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/term v0.6.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b11bbff --- /dev/null +++ b/go.sum @@ -0,0 +1,85 @@ +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= +github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= +github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= +github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= +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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/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/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= +github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +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/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE= +github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +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/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +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= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +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-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/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= +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.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +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.6/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/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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/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/internal/pkg/archiver/archiver.go b/internal/pkg/archiver/archiver.go new file mode 100644 index 0000000..1358f66 --- /dev/null +++ b/internal/pkg/archiver/archiver.go @@ -0,0 +1,29 @@ +package archiver + +import ( + "errors" + "strings" +) + +func Run_unzip(src string, dst string, strip bool) error { + return Unarchive(src, dst, strip) +} + +func Unarchive(src string, dst string, strip bool) (err error) { + fileTypes := strings.Split(src, ".") + if len(fileTypes) == 0 { + return errors.New("fileType is not supported") + } + //fmt.Println(fileTypes[len(fileTypes)-1]) + switch fileTypes[len(fileTypes)-1] { + case "gz": + err = untgz(src, dst, strip) + case "gx": + err = untgx(src, dst, strip) + case "zip": + err = unzip(src, dst, strip) + default: + err = errors.New(fileTypes[len(fileTypes)-1] + " is not supported") + } + return err +} diff --git a/internal/pkg/archiver/tgx.go b/internal/pkg/archiver/tgx.go new file mode 100644 index 0000000..7c2c5ac --- /dev/null +++ b/internal/pkg/archiver/tgx.go @@ -0,0 +1,117 @@ +package archiver + +import ( + "archive/tar" + "io" + "os" + "path/filepath" + "strings" + + "github.com/xi2/xz" +) + +func untgx(src string, dst string, strip bool) error { + xzFile, err := os.Open(src) + if err != nil { + return err + } + defer xzFile.Close() + var prefixToStrip string + if strip { + xzr, err := xz.NewReader(xzFile, 0) + if err != nil { + return err + } + r := tar.NewReader(xzr) + var prefix []string + for { + header, err := r.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + var dir string + if header.Typeflag != tar.TypeDir { + dir = filepath.Dir(header.Name) + } else { + continue + } + if prefix != nil { + dirSplit := strings.Split(dir, string(filepath.Separator)) + i, e, dse := 0, len(prefix), len(dirSplit) + if dse < e { + e = dse + } + for i < e { + if prefix[i] != dirSplit[i] { + prefix = prefix[0:i] + break + } + i++ + } + } else { + prefix = strings.Split(dir, string(filepath.Separator)) + } + } + prefixToStrip = strings.Join(prefix, string(filepath.Separator)) + } + xzFile.Seek(0, 0) + xzr, err := xz.NewReader(xzFile, 0) + if err != nil { + return err + } + r := tar.NewReader(xzr) + dirCache := make(map[string]bool) // todo: radix tree would perform better here + if err := os.MkdirAll(dst, 0755); err != nil { + return err + } + for { + header, err := r.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + var dir string + if header.Typeflag != tar.TypeDir { + dir = filepath.Dir(header.Name) + } else { + dir = filepath.Clean(header.Name) + if !strings.HasPrefix(dir, prefixToStrip) { + continue + } + } + dir = strings.TrimPrefix(dir, prefixToStrip) + if dir != "" && dir != "." { + cached := dirCache[dir] + if !cached { + if err := os.MkdirAll(filepath.Join(dst, dir), 0755); err != nil { + return err + } + dirCache[dir] = true + } + } + target := filepath.Join(dst, dir, filepath.Base(header.Name)) + switch header.Typeflag { + case tar.TypeReg: + d, err := os.OpenFile(target, + os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode|0600)&0777) + if err != nil { + return err + } + _, err = io.Copy(d, r) + d.Close() + if err != nil { + return err + } + case tar.TypeSymlink: + if err = os.Symlink(header.Linkname, target); err != nil { + return err + } + } + } + return nil +} diff --git a/internal/pkg/archiver/tgz.go b/internal/pkg/archiver/tgz.go new file mode 100644 index 0000000..e1981fa --- /dev/null +++ b/internal/pkg/archiver/tgz.go @@ -0,0 +1,118 @@ +package archiver + +import ( + "archive/tar" + "compress/gzip" + "io" + "os" + "path/filepath" + "strings" +) + +func untgz(src string, dst string, strip bool) error { + gzFile, err := os.Open(src) + if err != nil { + return err + } + defer gzFile.Close() + var prefixToStrip string + if strip { + gzr, err := gzip.NewReader(gzFile) + if err != nil { + return err + } + defer gzr.Close() + r := tar.NewReader(gzr) + var prefix []string + for { + header, err := r.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + var dir string + if header.Typeflag != tar.TypeDir { + dir = filepath.Dir(header.Name) + } else { + continue + } + if prefix != nil { + dirSplit := strings.Split(dir, string(filepath.Separator)) + i, e, dse := 0, len(prefix), len(dirSplit) + if dse < e { + e = dse + } + for i < e { + if prefix[i] != dirSplit[i] { + prefix = prefix[0:i] + break + } + i++ + } + } else { + prefix = strings.Split(dir, string(filepath.Separator)) + } + } + prefixToStrip = strings.Join(prefix, string(filepath.Separator)) + } + gzFile.Seek(0, 0) + gzr, err := gzip.NewReader(gzFile) + if err != nil { + return err + } + defer gzr.Close() + r := tar.NewReader(gzr) + dirCache := make(map[string]bool) // todo: radix tree would perform better here + if err := os.MkdirAll(dst, 0755); err != nil { + return err + } + for { + header, err := r.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + var dir string + if header.Typeflag != tar.TypeDir { + dir = filepath.Dir(header.Name) + } else { + dir = filepath.Clean(header.Name) + if !strings.HasPrefix(dir, prefixToStrip) { + continue + } + } + dir = strings.TrimPrefix(dir, prefixToStrip) + if dir != "" && dir != "." { + cached := dirCache[dir] + if !cached { + if err := os.MkdirAll(filepath.Join(dst, dir), 0755); err != nil { + return err + } + dirCache[dir] = true + } + } + target := filepath.Join(dst, dir, filepath.Base(header.Name)) + switch header.Typeflag { + case tar.TypeReg: + d, err := os.OpenFile(target, + os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode|0600)&0777) + if err != nil { + return err + } + _, err = io.Copy(d, r) + d.Close() + if err != nil { + return err + } + case tar.TypeSymlink: + if err = os.Symlink(header.Linkname, target); err != nil { + return err + } + } + } + return nil +} diff --git a/internal/pkg/archiver/zip.go b/internal/pkg/archiver/zip.go new file mode 100644 index 0000000..bc3b526 --- /dev/null +++ b/internal/pkg/archiver/zip.go @@ -0,0 +1,89 @@ +package archiver + +import ( + "archive/zip" + "io" + "os" + "path/filepath" + "strings" +) + +func unzip(src string, dst string, strip bool) error { + r, err := zip.OpenReader(src) + if err != nil { + return err + } + defer r.Close() + var prefixToStrip string + if strip { + var prefix []string + for _, f := range r.File { + var dir string + if !f.Mode().IsDir() { + dir = filepath.Dir(f.Name) + } else { + continue + } + if prefix != nil { + dirSplit := strings.Split(dir, string(filepath.Separator)) + i, e, dse := 0, len(prefix), len(dirSplit) + if dse < e { + e = dse + } + for i < e { + if prefix[i] != dirSplit[i] { + prefix = prefix[0:i] + break + } + i++ + } + } else { + prefix = strings.Split(dir, string(filepath.Separator)) + } + } + prefixToStrip = strings.Join(prefix, string(filepath.Separator)) + } + dirCache := make(map[string]bool) // todo: radix tree would perform better here + if err := os.MkdirAll(dst, 0755); err != nil { + return err + } + for _, f := range r.File { + var dir string + if !f.Mode().IsDir() { + dir = filepath.Dir(f.Name) + } else { + dir = filepath.Clean(f.Name) + if !strings.HasPrefix(dir, prefixToStrip) { + continue + } + } + dir = strings.TrimPrefix(dir, prefixToStrip) + if dir != "" && dir != "." { + cached := dirCache[dir] + if !cached { + if err := os.MkdirAll(filepath.Join(dst, dir), 0755); err != nil { + return err + } + dirCache[dir] = true + } + } + if !f.Mode().IsDir() { + name := filepath.Base(f.Name) + fr, err := f.Open() + if err != nil { + return err + } + d, err := os.OpenFile(filepath.Join(dst, dir, name), + os.O_WRONLY|os.O_CREATE|os.O_TRUNC, (f.Mode()|0600)&0777) + if err != nil { + return err + } + _, err = io.Copy(d, fr) + d.Close() + if err != nil { + return err + } + } + } + return nil +} diff --git a/internal/pkg/check/sha256sum.go b/internal/pkg/check/sha256sum.go new file mode 100644 index 0000000..957b860 --- /dev/null +++ b/internal/pkg/check/sha256sum.go @@ -0,0 +1,24 @@ +package check + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "io/ioutil" +) + +func PrintSha256(path string) string { + strContent := readFileToStr(path) + sum := sha256.Sum256([]byte(strContent)) + //fmt.Printf("sha256: %x\n", sum) + return hex.EncodeToString(sum[:]) +} + +func readFileToStr(path string) string { + content, err := ioutil.ReadFile(path) + if err != nil { + fmt.Println("read file failed, err:", err) + return "" + } + return string(content) +} diff --git a/internal/pkg/collector/collector.go b/internal/pkg/collector/collector.go new file mode 100644 index 0000000..3435f9c --- /dev/null +++ b/internal/pkg/collector/collector.go @@ -0,0 +1,72 @@ +package collector + +import ( + "io/ioutil" + "net/http" + "strings" +) + +type Collector struct { + Version string + Windows_X64 *Op_Item + Linux_X64 *Op_Item + Linux_AArch64 *Op_Item + Mac_X64 *Op_Item + Mac_AArch64 *Op_Item +} + +type Op_Item struct { + FileType string + Arch string + Url string + Sha256Url string + FileName string +} + +var Archive_Releases_Collectors []*Collector +var Collectors []*Collector + +var Collector_Archive_Url string = Collector_Url + "/archive/" +var Collector_Url string = "https://jdk.java.net" + +func build_Op_Item(file_type, arch, download_url, sha256_url, file_name string) *Op_Item { + return &Op_Item{ + FileType: file_type, + Arch: arch, + Url: download_url, + Sha256Url: sha256_url, + FileName: file_name, + } +} + +func getFileNameByDownLoadUrl(url string) string { + downloads := strings.Split(url, "/") + file_name := downloads[len(downloads)-1] + return file_name +} +func getFileNameNoSuffix(file_name string) string { + return strings.ReplaceAll(file_name, "."+getFileTypeByFileName(file_name), "") +} + +func GetSha256ByUrl(url string, isGetSha256 bool) string { + if isGetSha256 { + resp, _ := http.Get(url) + defer resp.Body.Close() + bytes, _ := ioutil.ReadAll(resp.Body) + return string(bytes) + } else { + return url + } +} + +func getFileTypeByFileName(filename string) string { + filenames := strings.Split(filename, ".") + switch filenames[len(filenames)-1] { + case "zip": + return "zip" + case "gz": + return "tar.gz" + default: + return filenames[len(filenames)-1] + } +} diff --git a/internal/pkg/collector/gradle_collector_test.go b/internal/pkg/collector/gradle_collector_test.go new file mode 100644 index 0000000..8947398 --- /dev/null +++ b/internal/pkg/collector/gradle_collector_test.go @@ -0,0 +1,87 @@ +package collector + +import ( + "fmt" + "io/ioutil" + "net/http" + "net/url" + "testing" + + "github.com/PuerkitoBio/goquery" +) + +func Test_GetGradleSha256ByUrl(t *testing.T) { + resp, _ := httpGetByProxy("https://downloads.gradle-dn.com/distributions/gradle-8.0.2-bin.zip.sha256") + defer resp.Body.Close() + bytes, _ := ioutil.ReadAll(resp.Body) + t.Run("", func(t *testing.T) { + fmt.Println(string(bytes)) + }) + +} + +func Test_getCollectorVersionShaSum(t *testing.T) { + resp, _ := httpGetByProxy("https://gradle.org/release-checksums/") + + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + fmt.Println("false") + } + versions := make([]*string, 0) + maps := make(map[string]string) + doc_selector, _ := goquery.NewDocumentFromReader(resp.Body) + t.Run("", func(t *testing.T) { + docs := doc_selector.Find(".layout__main") + a_docs := docs.Find("a[name]") + a_docs.Each(func(j int, a_doc *goquery.Selection) { + version := a_doc.AttrOr("name", "") + if version != "" { + versions = append(versions, &version) + + } + }) + lis := docs.Find("ul[style]") + + for i := 0; i < len(versions); i++ { + sha256 := lis.Eq(i).Find("li").Eq(i).Find("code").Text() + maps[*versions[i]] = sha256 + fmt.Printf("version: %v , sha256: %v\n", *versions[i], sha256) + } + /* fmt.Printf("len(versions): %v\n", len(versions)) + s := lis.Eq(0).Find("li").Eq(0).Find("code") + fmt.Printf("lis: %v\n", s.Text()) + docs.Each(func(j int, doc *goquery.Selection) { + + }) */ + }) +} + +func Test_getCollectorVersion(t *testing.T) { + resp, _ := httpGetByProxy("https://gradle.org/releases/") + //resp, _ := http.Get("https://gradle.org/releases/") + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + fmt.Println("false") + } + doc_selector, _ := goquery.NewDocumentFromReader(resp.Body) + t.Run("", func(t *testing.T) { + a_docs := doc_selector.Find(".resources-contents").Find("a[name]") + a_docs.Each(func(j int, a_doc *goquery.Selection) { + version := a_doc.AttrOr("name", "") + fmt.Printf("version: %v\n", version) + }) + }) +} + +func httpGetByProxy(url string) (*http.Response, error) { + request, _ := http.NewRequest("GET", url, nil) + return getClientProxy().Do(request) +} + +func getClientProxy() *http.Client { + proxyUrl, err := url.Parse("http://127.0.0.1:8000") + if err != nil { + panic(err) + } + return &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)}} +} diff --git a/internal/pkg/collector/openjdk_collector.go b/internal/pkg/collector/openjdk_collector.go new file mode 100644 index 0000000..5372ec3 --- /dev/null +++ b/internal/pkg/collector/openjdk_collector.go @@ -0,0 +1,215 @@ +package collector + +import ( + "fmt" + "net/http" + "runtime" + "strings" + + "github.com/PuerkitoBio/goquery" + "github.com/forget-the-bright/j/internal/pkg/config" +) + +func ConvertCollectorToUrlItem(colls []*Collector, isGetSha256 bool) []*config.UrlItem { + var rs = make([]*config.UrlItem, 0) + for _, coll := range colls { + var item *Op_Item + switch runtime.GOOS { + case "linux": + if runtime.GOARCH == "aarch64" { + item = coll.Linux_AArch64 + } else { + item = coll.Linux_X64 + } + case "windows": + item = coll.Windows_X64 + case "darwin": + if runtime.GOARCH == "aarch64" { + item = coll.Mac_AArch64 + } else { + item = coll.Mac_X64 + } + default: + item = nil + } + if item != nil { + rs = append(rs, &config.UrlItem{ + In: &config.JavaFileItem{ + FileName: item.FileName, + URL: item.Url, + Sha256: GetSha256ByUrl(item.Sha256Url, isGetSha256), + }, + SimpleName: coll.Version, + Expected: getFileNameNoSuffix(item.FileName), + }) + } + } + switch runtime.GOOS { + case "windows": + rs = append(rs, &config.UrlItem{ + In: &config.JavaFileItem{ + FileName: "openjdk-8u42-b03-windows-i586-14_jul_2022.zip", + URL: "https://download.java.net/openjdk/jdk8u42/ri/openjdk-8u42-b03-windows-i586-14_jul_2022.zip", + Sha256: "0314134bd981db63c7ca68d262ef896383b5694307a14bac81af88b5ad926279", + }, + Expected: "openjdk-8u42-b03-windows-i586-14_jul_2022", + SimpleName: "8", + }) + case "linux": + rs = append(rs, &config.UrlItem{ + In: &config.JavaFileItem{ + FileName: "openjdk-8u42-b03-linux-x64-14_jul_2022.tar.gz", + URL: "https://download.java.net/openjdk/jdk8u42/ri/openjdk-8u42-b03-linux-x64-14_jul_2022.tar.gz", + Sha256: "dd5fc6ef5ebffb88cd66af5258226c31f6c719fdcd855d95464fdb2cab051baa", + }, + Expected: "openjdk-8u42-b03-linux-x64-14_jul_2022", + SimpleName: "8", + }) + + } + return config.ReverseArray(rs) +} + +func GetOpenJDKVesionUrlInfo() []*Collector { + resp, _ := http.Get(Collector_Archive_Url) + Collectors = make([]*Collector, 0) + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + fmt.Println("false") + } + doc_selector, _ := goquery.NewDocumentFromReader(resp.Body) + divs := doc_selector.Find("#sidebar").Find(".links") + divs.Each(func(j int, div *goquery.Selection) { + link_about := div.Find(".about").Text() + if link_about == "Reference Implementations" { + a_docs := div.Find("a") + a_docs.Each(func(j int, a_doc *goquery.Selection) { + info_url := a_doc.AttrOr("href", "") + versions := strings.Split(a_doc.Text(), " ") + version := versions[len(versions)-1] + version_url := Collector_Url + strings.ReplaceAll(info_url, ".", "") + Collectors = append(Collectors, getVersionByUrl(version, version_url)) + //fmt.Println(version) + //fmt.Println(version_url) + }) + } + //fmt.Printf("link_about: %v\n", link_about) + }) + return Collectors +} + +func GetOpenJDKArchiveReleasesInfo() []*Collector { + Archive_Releases_Collectors = make([]*Collector, 0) + resp, _ := http.Get(Collector_Archive_Url) + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + fmt.Println("false") + } + docs, _ := goquery.NewDocumentFromReader(resp.Body) + tbody_docs := docs.Find(".builds").Find("tbody").Children() + var collector_Item *Collector + tbody_docs.Each(func(j int, tr *goquery.Selection) { + th := tr.Find("th") + if th.Length() == 1 { + val := th.Text() + if val != "Source" { + s2 := strings.Split(val, "(build ") + version_str := strings.Trim(strings.Trim(s2[0], " "), " GA") + //fmt.Println("\n" + version_str) + collector_Item = &Collector{ + Version: version_str, + } + Archive_Releases_Collectors = append(Archive_Releases_Collectors, collector_Item) + } + } + if th.Length() == 2 { + a_item := tr.Find("td").Find("a") + file_type := strings.Trim(a_item.First().Text(), "\n") + download_url := a_item.First().AttrOr("href", "") + file_name := getFileNameByDownLoadUrl(download_url) + sha256_url := a_item.Last().AttrOr("href", "") + releases := strings.Split(th.First().Text(), "/") + + switch releases[0] { + case "Windows": + collector_Item.Windows_X64 = build_Op_Item(file_type, "x64", download_url, sha256_url, file_name) + case "Mac": + if len(releases) == 1 || releases[1] == "x64" { + collector_Item.Mac_X64 = build_Op_Item(file_type, "x64", download_url, sha256_url, file_name) + } else { + collector_Item.Mac_AArch64 = build_Op_Item(file_type, releases[1], download_url, sha256_url, file_name) + } + case "Linux": + if len(releases) == 1 || releases[1] == "x64" { + collector_Item.Linux_X64 = build_Op_Item(file_type, "x64", download_url, sha256_url, file_name) + } else { + collector_Item.Linux_X64 = build_Op_Item(file_type, releases[1], download_url, sha256_url, file_name) + } + } + } + }) + return Archive_Releases_Collectors +} + +func getVersionByUrl(version, url string) *Collector { + var coll = Collector{ + Version: version, + } + resp, _ := http.Get(url) + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + fmt.Println("false") + } + doc_selector, _ := goquery.NewDocumentFromReader(resp.Body) + + divs := doc_selector.Find("#main").Find("ul") + + if divs.Length() <= 1 || version == "8" { + li_docs := divs.Find("li") + linux_x64_a := li_docs.Eq(0).Find("a") + windows_x64_a := li_docs.Eq(1).Find("a") + + linux_url := linux_x64_a.Eq(0).AttrOr("href", "") + linux_file_name := getFileNameByDownLoadUrl(linux_url) + linux_file_type := getFileTypeByFileName(linux_file_name) + linux_sha256_url := linux_x64_a.Eq(1).AttrOr("href", "") + + windows_url := windows_x64_a.Eq(0).AttrOr("href", "") + windows_file_name := getFileNameByDownLoadUrl(windows_url) + windows_file_type := getFileTypeByFileName(windows_file_name) + windows_sha256_url := windows_x64_a.Eq(1).AttrOr("href", "") + + coll.Linux_X64 = &Op_Item{ + Arch: "x64", + Url: linux_url, + Sha256Url: linux_sha256_url, + FileName: linux_file_name, + FileType: linux_file_type, + } + coll.Windows_X64 = &Op_Item{ + Arch: "x64", + Url: windows_url, + Sha256Url: windows_sha256_url, + FileName: windows_file_name, + FileType: windows_file_type, + } + } else { + divs_eq0 := divs.Eq(0) + li_docs := divs_eq0.Find("li") + linux_x64_a := li_docs.Eq(0).Find("a") + + linux_url := linux_x64_a.Eq(0).AttrOr("href", "") + linux_file_name := getFileNameByDownLoadUrl(linux_url) + linux_file_type := getFileTypeByFileName(linux_file_name) + linux_sha256_url := linux_x64_a.Eq(1).AttrOr("href", "") + coll.Linux_X64 = &Op_Item{ + Arch: "x64", + Url: linux_url, + Sha256Url: linux_sha256_url, + FileName: linux_file_name, + FileType: linux_file_type, + } + + } + return &coll +} diff --git a/internal/pkg/collector/openjdk_collector_test.go b/internal/pkg/collector/openjdk_collector_test.go new file mode 100644 index 0000000..c6392a1 --- /dev/null +++ b/internal/pkg/collector/openjdk_collector_test.go @@ -0,0 +1,188 @@ +package collector + +import ( + "fmt" + "io/ioutil" + "net/http" + "strings" + "testing" + + "github.com/PuerkitoBio/goquery" +) + +func Test_GetSha256ByUrl(t *testing.T) { + resp, _ := http.Get("https://download.java.net/java/GA/jdk19.0.1/afdd2e245b014143b62ccb916125e3ce/10/GPL/openjdk-19.0.1_windows-x64_bin.zip.sha256") + defer resp.Body.Close() + bytes, _ := ioutil.ReadAll(resp.Body) + t.Run("", func(t *testing.T) { + fmt.Println(string(bytes)) + }) + +} + +func Test_getOpenJdkVersion(t *testing.T) { + resp, _ := http.Get(Collector_Archive_Url) + colls := make([]*Collector, 0) + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + fmt.Println("false") + } + doc_selector, _ := goquery.NewDocumentFromReader(resp.Body) + t.Run("", func(t *testing.T) { + divs := doc_selector.Find("#sidebar").Find(".links") + divs.Each(func(j int, div *goquery.Selection) { + link_about := div.Find(".about").Text() + if link_about == "Reference Implementations" { + a_docs := div.Find("a") + a_docs.Each(func(j int, a_doc *goquery.Selection) { + info_url := a_doc.AttrOr("href", "") + versions := strings.Split(a_doc.Text(), " ") + version := versions[len(versions)-1] + version_url := Collector_Url + strings.ReplaceAll(info_url, ".", "") + colls = append(colls, test_getVersionByUrl(version, version_url)) + //fmt.Println(version) + //fmt.Println(version_url) + }) + } + fmt.Printf("link_about: %v\n", link_about) + }) + temp := colls + fmt.Printf("len(temp): %v\n", len(temp)) + //fmt.Printf("divs.Length(): %v\n", divs.Length()) + }) +} + +func test_getVersionByUrl(version, url string) *Collector { + var coll = Collector{ + Version: version, + } + resp, _ := http.Get(url) + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + fmt.Println("false") + } + doc_selector, _ := goquery.NewDocumentFromReader(resp.Body) + + divs := doc_selector.Find("#main").Find("ul") + + if divs.Length() <= 1 || version == "8" { + li_docs := divs.Find("li") + linux_x64_a := li_docs.Eq(0).Find("a") + windows_x64_a := li_docs.Eq(1).Find("a") + + linux_url := linux_x64_a.Eq(0).AttrOr("href", "") + linux_file_name := getFileNameByDownLoadUrl(linux_url) + linux_file_type := getFileTypeByFileName(linux_file_name) + linux_sha256_url := linux_x64_a.Eq(1).AttrOr("href", "") + + windows_url := windows_x64_a.Eq(0).AttrOr("href", "") + windows_file_name := getFileNameByDownLoadUrl(windows_url) + windows_file_type := getFileTypeByFileName(windows_file_name) + windows_sha256_url := windows_x64_a.Eq(1).AttrOr("href", "") + + coll.Linux_X64 = &Op_Item{ + Arch: "x64", + Url: linux_url, + Sha256Url: linux_sha256_url, + FileName: linux_file_name, + FileType: linux_file_type, + } + coll.Windows_X64 = &Op_Item{ + Arch: "x64", + Url: windows_url, + Sha256Url: windows_sha256_url, + FileName: windows_file_name, + FileType: windows_file_type, + } + } else { + divs_eq0 := divs.Eq(0) + li_docs := divs_eq0.Find("li") + linux_x64_a := li_docs.Eq(0).Find("a") + + linux_url := linux_x64_a.Eq(0).AttrOr("href", "") + linux_file_name := getFileNameByDownLoadUrl(linux_url) + linux_file_type := getFileTypeByFileName(linux_file_name) + linux_sha256_url := linux_x64_a.Eq(1).AttrOr("href", "") + coll.Linux_X64 = &Op_Item{ + Arch: "x64", + Url: linux_url, + Sha256Url: linux_sha256_url, + FileName: linux_file_name, + FileType: linux_file_type, + } + + } + return &coll +} + +func Test_getArchiveVersion(t *testing.T) { + Archive_Releases_Collectors = make([]*Collector, 0) + resp, _ := http.Get(Collector_Archive_Url) + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + fmt.Println("false") + } + c, _ := goquery.NewDocumentFromReader(resp.Body) + t.Run("", func(t *testing.T) { + s := c.Find(".builds").Find("tbody").Children() + /* s.Each(func(j int, tr *goquery.Selection) { + th := tr.Find("th") + if th.Length() == 1 { + val := th.Text() + if val != "Source" { + s2 := strings.Split(val, "(build ") + fmt.Println(strings.Trim(strings.Trim(s2[0], " "), " GA")) + } + } + }) */ + var collector_Item *Collector + //flag := false + s.Each(func(j int, tr *goquery.Selection) { + th := tr.Find("th") + if th.Length() == 1 { + val := th.Text() + if val != "Source" { + s2 := strings.Split(val, "(build ") + version_str := strings.Trim(strings.Trim(s2[0], " "), " GA") + fmt.Println("\n" + version_str) + collector_Item = &Collector{ + Version: version_str, + } + Archive_Releases_Collectors = append(Archive_Releases_Collectors, collector_Item) + } + } + if th.Length() == 2 { + a_item := tr.Find("td").Find("a") + file_type := strings.Trim(a_item.First().Text(), "\n") + download_url := a_item.First().AttrOr("href", "") + file_name := getFileNameByDownLoadUrl(download_url) + sha256_url := a_item.Last().AttrOr("href", "") + releases := strings.Split(th.First().Text(), "/") + + switch releases[0] { + case "Windows": + collector_Item.Mac_X64 = build_Op_Item(file_type, "x64", download_url, sha256_url, file_name) + case "Mac": + if len(releases) == 1 || releases[1] == "x64" { + collector_Item.Mac_X64 = build_Op_Item(file_type, "x64", download_url, sha256_url, file_name) + } else { + collector_Item.Mac_AArch64 = build_Op_Item(file_type, "x64", download_url, sha256_url, file_name) + } + case "Linux": + if len(releases) == 1 || releases[1] == "x64" { + collector_Item.Linux_X64 = build_Op_Item(file_type, "x64", download_url, sha256_url, file_name) + } else { + collector_Item.Linux_X64 = build_Op_Item(file_type, "x64", download_url, sha256_url, file_name) + } + } + //fmt.Printf("%v %v\n", strings.Trim(a_item.First().Text(), "\n"), a_item.Last().Text()) + //fmt.Printf("%v %v\n", th.First().Text(), th.Last().Text()) + //fmt.Printf("%v\n%v\n", a_item.First().AttrOr("href", ""), a_item.Last().AttrOr("href", "")) + //fmt.Printf("th.Text(): %v th.len:%v\n", th.Text(), thLen) + } + }) + }) + c2 := Archive_Releases_Collectors + fmt.Printf("len(c2): %v\n", len(c2)) + fmt.Println(c2) +} diff --git a/internal/pkg/config/linux_url.go b/internal/pkg/config/linux_url.go new file mode 100644 index 0000000..3667771 --- /dev/null +++ b/internal/pkg/config/linux_url.go @@ -0,0 +1,112 @@ +package config + +var linux_Url_Items = []*UrlItem{ + { + In: &JavaFileItem{ + FileName: "openjdk-19+36_linux-x64_bin.tar.gz", + URL: "https://download.java.net/openjdk/jdk19/ri/openjdk-19+36_linux-x64_bin.tar.gz", + Sha256: "f47aba585cfc9ecff1ed8e023524e8309f4315ed8b80100b40c7dcc232c12f96", + }, + Expected: "jdk-19", + SimpleName: "19", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-18+36_linux-x64_bin.tar.gz", + URL: "https://download.java.net/openjdk/jdk18/ri/openjdk-18+36_linux-x64_bin.tar.gz", + Sha256: "0f60aef7b8504983d6e374fe94d09a7bedcf05ec559e812d801a33bd4ebd23d0", + }, + Expected: "jdk-18", + SimpleName: "18", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-17+35_linux-x64_bin.tar.gz", + URL: "https://download.java.net/openjdk/jdk17/ri/openjdk-17+35_linux-x64_bin.tar.gz", + Sha256: "aef49cc7aa606de2044302e757fa94c8e144818e93487081c4fd319ca858134b", + }, + Expected: "jdk-17", + SimpleName: "17", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-16+36_linux-x64_bin.tar.gz", + URL: "https://download.java.net/openjdk/jdk16/ri/openjdk-16+36_linux-x64_bin.tar.gz", + Sha256: "e952958f16797ad7dc7cd8b724edd69ec7e0e0434537d80d6b5165193e33b931", + }, + Expected: "jdk-16", + SimpleName: "16", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-15+36_linux-x64_bin.tar.gz", + URL: "https://download.java.net/openjdk/jdk15/ri/openjdk-15+36_linux-x64_bin.tar.gz", + Sha256: "bb67cadee687d7b486583d03c9850342afea4593be4f436044d785fba9508fb7", + }, + Expected: "jdk-15", + SimpleName: "15", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-14+36_linux-x64_bin.tar.gz", + URL: "https://download.java.net/openjdk/jdk14/ri/openjdk-14+36_linux-x64_bin.tar.gz", + Sha256: "c7006154dfb8b66328c6475447a396feb0042608ee07a96956547f574a911c09", + }, + Expected: "jdk-14", + SimpleName: "14", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-13+33_linux-x64_bin.tar.gz", + URL: "https://download.java.net/openjdk/jdk13/ri/openjdk-13+33_linux-x64_bin.tar.gz", + Sha256: "5f547b8f0ffa7da517223f6f929a5055d749776b1878ccedbd6cc1334f4d6f4d", + }, + Expected: "jdk-13", + SimpleName: "13", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-12+32_linux-x64_bin.tar.gz", + URL: "https://download.java.net/openjdk/jdk12/ri/openjdk-12+32_linux-x64_bin.tar.gz", + Sha256: "4bf3d9f4bbbb8cb9a0d96ceade42df8b2ca85f7853fbcd08274df2b7d2cef074", + }, + Expected: "jdk-12", + SimpleName: "12", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-11+28_linux-x64_bin.tar.gz", + URL: "https://download.java.net/openjdk/jdk11/ri/openjdk-11+28_linux-x64_bin.tar.gz", + Sha256: "3784cfc4670f0d4c5482604c7c513beb1a92b005f569df9bf100e8bef6610f2e", + }, + Expected: "jdk-11", + SimpleName: "11", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-10+44_linux-x64_bin_ri.tar.gz", + URL: "https://download.java.net/openjdk/jdk10/ri/openjdk-10+44_linux-x64_bin_ri.tar.gz", + Sha256: "0b0e778f2c935dae32c71dd78f3ad921e0972ffc536e64703f0e9855e570abe2", + }, + Expected: "jdk-10", + SimpleName: "10", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-9+181_linux-x64_ri.zip", + URL: "https://download.java.net/openjdk/jdk9/ri/openjdk-9+181_linux-x64_ri.zip", + Sha256: "f4f9c75f9f1f2d1888b9be013c96210e017fd22547366c8e0929085dfe8e38aa", + }, + Expected: "jdk-9", + SimpleName: "9", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-8u42-b03-linux-x64-14_jul_2022.tar.gz", + URL: "https://download.java.net/openjdk/jdk8u42/ri/openjdk-8u42-b03-linux-x64-14_jul_2022.tar.gz", + Sha256: "dd5fc6ef5ebffb88cd66af5258226c31f6c719fdcd855d95464fdb2cab051baa", + }, + Expected: "jdk-8", + SimpleName: "8", + }, +} diff --git a/internal/pkg/config/urls.go b/internal/pkg/config/urls.go new file mode 100644 index 0000000..666f185 --- /dev/null +++ b/internal/pkg/config/urls.go @@ -0,0 +1,39 @@ +package config + +import ( + "errors" + "runtime" + + cli "github.com/urfave/cli/v2" +) + +type JavaFileItem struct { + FileName string + URL string + Sha256 string +} +type UrlItem struct { + In *JavaFileItem + Expected string + SimpleName string +} + +var Url_Items []*UrlItem + +func ReverseArray(arr []*UrlItem) []*UrlItem { + for i, j := 0, len(arr)-1; i < j; i, j = i+1, j-1 { + arr[i], arr[j] = arr[j], arr[i] + } + return arr +} + +func init() { + switch runtime.GOOS { + case "linux": + Url_Items = linux_Url_Items + case "windows": + Url_Items = windows_Url_Items + default: + cli.Exit(errors.New(runtime.GOOS+" OS is not supported"), 1) + } +} diff --git a/internal/pkg/config/windows_url.go b/internal/pkg/config/windows_url.go new file mode 100644 index 0000000..e7e5baf --- /dev/null +++ b/internal/pkg/config/windows_url.go @@ -0,0 +1,103 @@ +package config + +var windows_Url_Items = []*UrlItem{ + { + In: &JavaFileItem{ + FileName: "openjdk-19+36_windows-x64_bin.zip", + URL: "https://download.java.net/openjdk/jdk19/ri/openjdk-19+36_windows-x64_bin.zip", + Sha256: "8fabcee7c4e8d3b53486777ecd27bb906d67d7c1efd1bf22a8290cf659afa487", + }, + Expected: "jdk-19", + SimpleName: "19", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-18+36_windows-x64_bin.zip", + URL: "https://download.java.net/openjdk/jdk18/ri/openjdk-18+36_windows-x64_bin.zip", + Sha256: "a5b91d4c12752d44aa75df70ae3e2311287b3e60c288b07dade106376c688277", + }, + Expected: "jdk-18", + SimpleName: "18", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-17+35_windows-x64_bin.zip", + URL: "https://download.java.net/openjdk/jdk17/ri/openjdk-17+35_windows-x64_bin.zip", + Sha256: "e88b0df00021c9d266bb435c9a95fdc67d1948cce4518daf85c234907bd393c5", + }, + Expected: "jdk-17", + SimpleName: "17", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-16+36_windows-x64_bin.zip", + URL: "https://download.java.net/openjdk/jdk16/ri/openjdk-16+36_windows-x64_bin.zip", + Sha256: "a78bdeaad186297601edac6772d931224d7af6f682a43372e693c37020bd37d6", + }, + Expected: "jdk-16", + SimpleName: "16", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-15+36_windows-x64_bin.zip", + URL: "https://download.java.net/openjdk/jdk15/ri/openjdk-15+36_windows-x64_bin.zip", + Sha256: "764e39a71252a9791118a31ae56a4247c049463bda5eb72497122ec50b1d07f8", + }, + Expected: "jdk-15", + SimpleName: "15", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-14+36_windows-x64_bin.zip", + URL: "https://download.java.net/openjdk/jdk14/ri/openjdk-14+36_windows-x64_bin.zip", + Sha256: "6b56c65c2ebb89eb361f47370359f88c4b87234dc073988a2c33e7d75c01e488", + }, + Expected: "jdk-14", + SimpleName: "14", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-13+33_windows-x64_bin.zip", + URL: "https://download.java.net/openjdk/jdk13/ri/openjdk-13+33_windows-x64_bin.zip", + Sha256: "053d8c87bb34347478512911a6218a389720bffcde4e496be5a54d51ad7c9c2f", + }, + Expected: "jdk-13", + SimpleName: "13", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-12+32_windows-x64_bin.zip", + URL: "https://download.java.net/openjdk/jdk12/ri/openjdk-12+32_windows-x64_bin.zip", + Sha256: "d6a550477754289e5bc0a635974b40bf5bc0515db441381414303ae954d8d6b8", + }, + Expected: "jdk-12", + SimpleName: "12", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-11+28_windows-x64_bin.zip", + URL: "https://download.java.net/openjdk/jdk11/ri/openjdk-11+28_windows-x64_bin.zip", + Sha256: "fde3b28ca31b86a889c37528f17411cd0b9651beb6fa76cac89a223417910f4b", + }, + Expected: "jdk-11", + SimpleName: "11", + }, + { + In: &JavaFileItem{ + FileName: "jdk-9+181_windows-x64_ri.zip", + URL: "https://download.java.net/openjdk/jdk9/ri/jdk-9+181_windows-x64_ri.zip", + Sha256: "51948d69c7b770b376162ec5b88f6ec8a266bd3c9e6da21c4e834b6d0d661897", + }, + Expected: "jdk-9", + SimpleName: "9", + }, + { + In: &JavaFileItem{ + FileName: "openjdk-8u42-b03-windows-i586-14_jul_2022.zip", + URL: "https://download.java.net/openjdk/jdk8u42/ri/openjdk-8u42-b03-windows-i586-14_jul_2022.zip", + Sha256: "0314134bd981db63c7ca68d262ef896383b5694307a14bac81af88b5ad926279", + }, + Expected: "java-se-8u42-ri", + SimpleName: "8", + }, +} diff --git a/internal/pkg/download/download.go b/internal/pkg/download/download.go new file mode 100644 index 0000000..08ad9cf --- /dev/null +++ b/internal/pkg/download/download.go @@ -0,0 +1,72 @@ +package download + +import ( + "fmt" + "io" + "io/fs" + "io/ioutil" + "net/http" + "os" + "time" + + "github.com/forget-the-bright/j/internal/pkg/errs" + "github.com/k0kubun/go-ansi" + "github.com/schollz/progressbar/v3" +) + +// Download 下载资源并另存为 +func Download(srcURL string, filename string, flag int, perm fs.FileMode, withProgress bool) (size int64, err error) { + resp, err := http.Get(srcURL) + if err != nil { + return 0, errs.NewDownloadError(srcURL, err) + } + defer resp.Body.Close() + + f, err := os.OpenFile(filename, flag, perm) + if err != nil { + return 0, errs.NewDownloadError(srcURL, err) + } + defer f.Close() + + var dst io.Writer + if withProgress { + bar := progressbar.NewOptions64( + resp.ContentLength, + progressbar.OptionEnableColorCodes(true), + progressbar.OptionSetTheme(progressbar.Theme{ + Saucer: "=", + SaucerHead: ">", + SaucerPadding: " ", + BarStart: "[", + BarEnd: "]", + }), + progressbar.OptionSetWidth(15), + progressbar.OptionSetDescription("Downloading"), + progressbar.OptionSetWriter(ansi.NewAnsiStdout()), + progressbar.OptionShowBytes(true), + progressbar.OptionThrottle(65*time.Millisecond), + progressbar.OptionShowCount(), + progressbar.OptionOnCompletion(func() { + _, _ = fmt.Fprint(ansi.NewAnsiStdout(), "\n") + }), + // progressbar.OptionSpinnerType(35), + // progressbar.OptionFullWidth(), + ) + _ = bar.RenderBlank() + dst = io.MultiWriter(f, bar) + + } else { + dst = f + } + return io.Copy(dst, resp.Body) +} + +// DownloadAsBytes 返回下载资源的原始字节切片 +func DownloadAsBytes(srcURL string) (data []byte, err error) { + resp, err := http.Get(srcURL) + if err != nil { + return nil, err + } + defer resp.Body.Close() + return ioutil.ReadAll(resp.Body) +} diff --git a/internal/pkg/errs/error.go b/internal/pkg/errs/error.go new file mode 100644 index 0000000..9dcbc57 --- /dev/null +++ b/internal/pkg/errs/error.go @@ -0,0 +1,88 @@ +package errs + +import ( + "errors" + "fmt" + "strings" +) + +var ( + // ErrVersionNotFound 版本不存在 + ErrVersionNotFound = errors.New("version not found") + // ErrPackageNotFound 版本包不存在 + ErrPackageNotFound = errors.New("installation package not found") +) + +var ( + // ErrUnsupportedChecksumAlgorithm 不支持的校验和算法 + ErrUnsupportedChecksumAlgorithm = errors.New("unsupported checksum algorithm") + // ErrChecksumNotMatched 校验和不匹配 + ErrChecksumNotMatched = errors.New("file checksum does not match the computed checksum") + // ErrChecksumFileNotFound 校验和文件不存在 + ErrChecksumFileNotFound = errors.New("checksum file not found") +) + +// URLUnreachableError URL不可达错误 +type URLUnreachableError struct { + err error + url string +} + +// NewURLUnreachableError 返回URL不可达错误实例 +func NewURLUnreachableError(url string, err error) error { + return &URLUnreachableError{ + err: err, + url: url, + } +} + +func (e URLUnreachableError) Error() string { + var buf strings.Builder + buf.WriteString(fmt.Sprintf("URL %q is unreachable", e.url)) + if e.err != nil { + buf.WriteString(" ==> " + e.err.Error()) + } + return buf.String() +} + +func (e URLUnreachableError) Err() error { + return e.err +} + +func (e URLUnreachableError) URL() string { + return e.url +} + +// DownloadError 下载失败错误 +type DownloadError struct { + url string + err error +} + +// NewDownloadError 返回下载失败错误实例 +func NewDownloadError(url string, err error) error { + return &DownloadError{ + url: url, + err: err, + } +} + +// Error 返回错误字符串 +func (e DownloadError) Error() string { + var buf strings.Builder + buf.WriteString(fmt.Sprintf("Resource(%s) download failed", e.url)) + if e.err != nil { + buf.WriteString(" ==> " + e.err.Error()) + } + return buf.String() +} + +// Err 返回错误对象 +func (e DownloadError) Err() error { + return e.err +} + +// URL 返回资源URL +func (e DownloadError) URL() string { + return e.url +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..817cb7c --- /dev/null +++ b/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/forget-the-bright/j/cli" + +func main() { + cli.Run() +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..9123296 --- /dev/null +++ b/makefile @@ -0,0 +1,59 @@ +GO = CGO_ENABLED=0 GO111MODULE=on GOPROXY=https://goproxy.cn,direct go +BUILD_DATE := $(shell date '+%Y-%m-%d %H:%M:%S') +GIT_BRANCH := $(shell git symbolic-ref --short -q HEAD) +GIT_COMMIT_HASH := $(shell git rev-parse HEAD|cut -c 1-8) +GO_FLAGS := -v -ldflags="-X 'github.com/forget-the-bright/j/internal/build.Build=$(BUILD_DATE)' -X 'github.com/forget-the-bright/j/internal/build.Commit=$(GIT_COMMIT_HASH)' -X 'github.com/forget-the-bright/j/internal/build.Branch=$(GIT_BRANCH)'" + + +all: install test clean + +build: + $(GO) build $(GO_FLAGS) + +install: build + $(GO) install $(GO_FLAGS) +build-myall: build-linux-amd64 build-windows-amd64 +build-all: build-linux build-darwin build-windows + +build-linux: build-linux-386 build-linux-amd64 build-linux-arm build-linux-arm64 build-linux-s390x +build-linux-386: + GOOS=linux GOARCH=386 $(GO) build $(GO_FLAGS) -o bin/linux-386/j +build-linux-amd64: + GOOS=linux GOARCH=amd64 $(GO) build $(GO_FLAGS) -o bin/linux-amd64/linux_amd_x86_64_j +build-linux-arm: + GOOS=linux GOARCH=arm $(GO) build $(GO_FLAGS) -o bin/linux-arm/j +build-linux-arm64: + GOOS=linux GOARCH=arm64 $(GO) build $(GO_FLAGS) -o bin/linux-arm64/j +build-linux-s390x: + GOOS=linux GOARCH=s390x $(GO) build $(GO_FLAGS) -o bin/linux-s390x/j + + +build-darwin: build-darwin-amd64 build-darwin-arm64 +build-darwin-amd64: + GOOS=darwin GOARCH=amd64 $(GO) build $(GO_FLAGS) -o bin/darwin-amd64/j +build-darwin-arm64: + GOOS=darwin GOARCH=arm64 $(GO) build $(GO_FLAGS) -o bin/darwin-arm64/j + + +build-windows: build-windows-386 build-windows-amd64 build-windows-arm build-windows-arm64 +build-windows-386: + GOOS=windows GOARCH=386 $(GO) build $(GO_FLAGS) -o bin/windows-386/j.exe +build-windows-amd64: + GOOS=windows GOARCH=amd64 $(GO) build $(GO_FLAGS) -o bin/windows-amd64/windows_amd_x86_64_j.exe +build-windows-arm: + GOOS=windows GOARCH=arm $(GO) build $(GO_FLAGS) -o bin/windows-arm/j.exe +build-windows-arm64: + GOOS=windows GOARCH=arm64 $(GO) build $(GO_FLAGS) -o bin/windows-arm64/j.exe + +package: + sh ./package.sh + +test: + $(GO) test -v ./... + +clean: + $(GO) clean -x + rm -f sha256sum.txt + rm -rf bin + +.PHONY: all build install test package clean build-linux build-darwin build-windows build-linux-386 build-linux-amd64 build-linux-arm build-linux-arm64 build-linux-s390x build-darwin-amd64 build-darwin-arm64 build-windows-386 build-windows-amd64 build-windows-arm build-windows-arm64