Skip to content

Commit dff2e8c

Browse files
committed
feat: supports installation of target versions via wildcards. #103 #108
1 parent 6c2799b commit dff2e8c

File tree

5 files changed

+92
-92
lines changed

5 files changed

+92
-92
lines changed

cli/install.go

+11-7
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ func install(ctx *cli.Context) (err error) {
2222
if vname == "" {
2323
return cli.ShowSubcommandHelp(ctx)
2424
}
25-
targetV := filepath.Join(versionsDir, vname)
26-
27-
// 检查版本是否已经安装
28-
if finfo, err := os.Stat(targetV); err == nil && finfo.IsDir() {
29-
return cli.Exit(fmt.Sprintf("[g] %q version has been installed.", vname), 1)
30-
}
3125

3226
// 查找版本
3327
c, err := collector.NewCollector(strings.Split(os.Getenv(mirrorEnv), ",")...)
@@ -38,10 +32,20 @@ func install(ctx *cli.Context) (err error) {
3832
if err != nil {
3933
return cli.Exit(errstring(err), 1)
4034
}
41-
v, err := version.FindVersion(items, vname)
35+
36+
v, err := version.NewFinder(items).Find(vname)
4237
if err != nil {
4338
return cli.Exit(errstring(err), 1)
4439
}
40+
41+
vname = v.Name()
42+
targetV := filepath.Join(versionsDir, vname)
43+
44+
// 检查版本是否已经安装
45+
if finfo, err := os.Stat(targetV); err == nil && finfo.IsDir() {
46+
return cli.Exit(fmt.Sprintf("[g] %q version has been installed.", vname), 1)
47+
}
48+
4549
// 查找版本下当前平台的安装包
4650
pkgs, err := v.FindPackages(version.ArchiveKind, runtime.GOOS, runtime.GOARCH)
4751
if err != nil {

pkg/errs/error.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ var (
2323
// VersionNotFoundError 版本不存在错误
2424
type VersionNotFoundError struct {
2525
version string
26+
goos string
27+
goarch string
2628
}
2729

2830
func IsVersionNotFound(err error) bool {
@@ -33,14 +35,16 @@ func IsVersionNotFound(err error) bool {
3335
return ok
3436
}
3537

36-
func NewVersionNotFoundError(version string) error {
38+
func NewVersionNotFoundError(version, goos, goarch string) error {
3739
return &VersionNotFoundError{
3840
version: version,
41+
goos: goos,
42+
goarch: goarch,
3943
}
4044
}
4145

4246
func (e VersionNotFoundError) Error() string {
43-
return fmt.Sprintf("Version not found %q", e.version)
47+
return fmt.Sprintf("Version not found %q [%s,%s]", e.version, e.goos, e.goarch)
4448
}
4549

4650
func (e VersionNotFoundError) Version() string {

version/finder.go

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package version
2+
3+
import (
4+
"runtime"
5+
"sort"
6+
7+
"github.com/Masterminds/semver/v3"
8+
"github.com/voidint/g/pkg/errs"
9+
)
10+
11+
type Finder struct {
12+
goos string
13+
goarch string
14+
items []*Version
15+
}
16+
17+
func NewFinder(items []*Version) *Finder {
18+
sort.Sort(Collection(items)) // 升序
19+
20+
return &Finder{
21+
goos: runtime.GOOS,
22+
goarch: runtime.GOARCH,
23+
items: items,
24+
}
25+
}
26+
27+
// Find 返回满足条件的语义化版本号。版本格式:主版本号.次版本号.修订号。
28+
// vname 支持以下几类版本标识:
29+
// 1、具体版本号:如'1.21.4'
30+
// 2、最新版本:latest
31+
// 3、通配符:如'1.21.x'、'1.x'、'1.18.*'等
32+
// 4、兼容某个主版本号:如'^1'、'^1.18'、'^1.18.10'等,在主版本号保持一致的前提下,次版本号和修订号均保持最新。
33+
// 5、匹配某个主次版本号:如'~1.18',在主次版本号保持一致的前提下,修订号保持最新。
34+
// 6、大于某个版本:如'>1.18',大于该版本的前提下,匹配最大的版本号。
35+
// 7、小于某个版本:如'<1.16',小于该版本的前提下,匹配最大的版本号。
36+
// 8、版本区间:如'1.18 - 1.20',匹配该区间范围内的最大版本。
37+
func (fdr *Finder) Find(vname string) (*Version, error) {
38+
if vname == latest {
39+
return fdr.findLatest()
40+
}
41+
42+
for i := len(fdr.items) - 1; i > 0; i-- {
43+
if fdr.items[i].name == vname && fdr.items[i].match(fdr.goos, fdr.goarch) {
44+
return fdr.items[i], nil
45+
}
46+
}
47+
48+
cs, err := semver.NewConstraint(vname)
49+
if err != nil {
50+
return nil, errs.NewVersionNotFoundError(vname, fdr.goos, fdr.goarch)
51+
}
52+
53+
for i := len(fdr.items) - 1; i > 0; i-- { // 优先匹配高版本
54+
if fdr.items[i].match(fdr.goos, fdr.goarch) && cs.Check(fdr.items[i].sv) {
55+
return fdr.items[i], nil
56+
}
57+
}
58+
return nil, errs.NewVersionNotFoundError(vname, fdr.goos, fdr.goarch)
59+
}
60+
61+
const latest = "latest"
62+
63+
func (fdr *Finder) findLatest() (*Version, error) {
64+
for i := len(fdr.items) - 1; i > 0; i-- {
65+
if fdr.items[i].match(fdr.goos, fdr.goarch) {
66+
return fdr.items[i], nil
67+
}
68+
}
69+
return nil, errs.NewVersionNotFoundError(latest, fdr.goos, fdr.goarch)
70+
}

version/version.go

+5-19
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,6 @@ func Semantify(vname string) (*semver.Version, error) {
3434
return sv, nil
3535
}
3636

37-
// FindVersion 返回指定名称的版本
38-
func FindVersion(all []*Version, name string) (*Version, error) {
39-
for i := range all {
40-
if all[i].name == name {
41-
return all[i], nil
42-
}
43-
}
44-
return nil, errs.NewVersionNotFoundError(name)
45-
}
46-
4737
// Version go版本
4838
type Version struct {
4939
name string // 源版本名,如'1.12.4',不完全等于sv.Original()
@@ -106,17 +96,13 @@ func (v *Version) SemanticVersion() semver.Version {
10696
return *v.sv
10797
}
10898

109-
// FindPackage 返回指定操作系统和硬件架构的版本包
110-
func (v *Version) FindPackage(kind, goos, goarch string) (*Package, error) {
111-
prefix := fmt.Sprintf("go%s.%s-%s", v.name, goos, goarch)
112-
for i := range v.pkgs {
113-
if v.pkgs[i] == nil || !strings.EqualFold(v.pkgs[i].Kind, kind) || !strings.HasPrefix(v.pkgs[i].FileName, prefix) {
114-
continue
99+
func (v *Version) match(goos, goarch string) bool {
100+
for _, pkg := range v.pkgs {
101+
if strings.Contains(pkg.FileName, goos) && strings.Contains(pkg.FileName, goarch) { // TODO 不够严谨
102+
return true
115103
}
116-
return v.pkgs[i], nil
117104
}
118-
119-
return nil, errs.ErrPackageNotFound
105+
return false
120106
}
121107

122108
// FindPackages 返回指定操作系统和硬件架构的版本包

version/version_test.go

-64
Original file line numberDiff line numberDiff line change
@@ -13,70 +13,6 @@ import (
1313
"github.com/voidint/g/pkg/errs"
1414
)
1515

16-
func TestFindVersion(t *testing.T) {
17-
t.Run("查找指定名称的版本", func(t *testing.T) {
18-
v0 := MustNew("1.12.5")
19-
v1 := MustNew("1.11.10")
20-
v2 := MustNew("1.9.7")
21-
v3 := MustNew("1.20")
22-
23-
items := []*Version{v0, v1, v2, v3}
24-
25-
v, err := FindVersion(items, "1.11.10")
26-
assert.Nil(t, err)
27-
assert.NotNil(t, v)
28-
assert.Equal(t, "1.11.10", v.Name())
29-
30-
v, err = FindVersion(items, "1.11.11")
31-
assert.True(t, errs.IsVersionNotFound(err))
32-
assert.Nil(t, v)
33-
34-
v, err = FindVersion(items, "1.20")
35-
assert.Nil(t, err)
36-
assert.NotNil(t, v)
37-
assert.Equal(t, "1.20", v.Name())
38-
})
39-
}
40-
41-
func TestFindPackage(t *testing.T) {
42-
t.Run("查询版本下的安装包", func(t *testing.T) {
43-
pkgs := []*Package{
44-
{
45-
FileName: "go1.12.4.src.tar.gz",
46-
Kind: SourceKind,
47-
Size: "21MB",
48-
},
49-
{
50-
FileName: "go1.12.4.darwin-amd64.tar.gz",
51-
Kind: ArchiveKind,
52-
OS: "macOS",
53-
Arch: "x86-64",
54-
Size: "122MB",
55-
},
56-
{
57-
FileName: "go1.12.4.windows-386.msi",
58-
Kind: InstallerKind,
59-
OS: "Windows",
60-
Arch: "x86",
61-
Size: "102MB",
62-
},
63-
}
64-
v := MustNew("1.12.4", WithPackages(pkgs))
65-
66-
pkg, err := v.FindPackage(ArchiveKind, "darwin", "amd64")
67-
assert.Nil(t, err)
68-
assert.NotNil(t, pkg)
69-
assert.Equal(t, "go1.12.4.darwin-amd64.tar.gz", pkg.FileName)
70-
assert.Equal(t, ArchiveKind, pkg.Kind)
71-
assert.Equal(t, "macOS", pkg.OS)
72-
assert.Equal(t, "x86-64", pkg.Arch)
73-
74-
pkg, err = v.FindPackage(ArchiveKind, "darwin", "386")
75-
assert.Equal(t, errs.ErrPackageNotFound, err)
76-
assert.Nil(t, pkg)
77-
})
78-
}
79-
8016
func TestVerifyChecksum(t *testing.T) {
8117
t.Run("检查安装包校验和", func(t *testing.T) {
8218
filename := fmt.Sprintf("%d.txt", time.Now().Unix())

0 commit comments

Comments
 (0)