diff --git a/cmd/help.go b/cmd/help.go index 3ee820415b..601fb18d74 100644 --- a/cmd/help.go +++ b/cmd/help.go @@ -48,7 +48,7 @@ Simply type tiup help | for full details.`, func externalHelp(env *environment.Environment, spec string, args ...string) { profile := env.Profile() component, version := environment.ParseCompVersion(spec) - selectVer, err := profile.SelectInstalledVersion(component, version) + selectVer, err := env.SelectInstalledVersion(component, version) if err != nil { fmt.Println(err) return diff --git a/pkg/environment/env.go b/pkg/environment/env.go index 9983edeb86..8e8c1a7f41 100644 --- a/pkg/environment/env.go +++ b/pkg/environment/env.go @@ -36,6 +36,11 @@ const ( tiupName = "tiup" ) +var ( + // ErrInstallFirst indicates that a component/version is not installed + ErrInstallFirst = errors.New("component not installed") +) + // Mirror return mirror of tiup. // If it's not defined, it will use "https://tiup-mirrors.pingcap.com/". func Mirror() string { @@ -179,17 +184,64 @@ func (env *Environment) downloadComponent(component string, version pkgver.Versi // SelectInstalledVersion selects the installed versions and the latest release version // will be chosen if there is an empty version -func (env *Environment) SelectInstalledVersion(component string, version pkgver.Version) (pkgver.Version, error) { - return env.profile.SelectInstalledVersion(component, version) +func (env *Environment) SelectInstalledVersion(component string, ver pkgver.Version) (pkgver.Version, error) { + installed, err := env.Profile().InstalledVersions(component) + if err != nil { + return ver, err + } + + versions := []string{} + for _, v := range installed { + vi, err := env.v1Repo.ComponentVersion(component, v, true) + if err != nil { + return ver, err + } + if vi.Yanked { + continue + } + versions = append(versions, v) + } + + errInstallFirst := errors.Annotatef(ErrInstallFirst, "use `tiup install %s` to install component `%s` first", component, component) + if len(installed) == 0 { + return ver, errInstallFirst + } + + if !ver.IsEmpty() { + for _, v := range versions { + if pkgver.Version(v) == ver { + return ver, nil + } + } + return ver, errInstallFirst + } + + sort.Slice(versions, func(i, j int) bool { + // Reverse sort: v5.0.0-rc,v5.0.0-nightly-20210305,v4.0.11 + return semver.Compare(versions[i], versions[j]) > 0 + }) + + for _, v := range versions { + if pkgver.Version(v).IsNightly() { + continue + } + if semver.Prerelease(v) == "" { + ver = pkgver.Version(v) + break + } else if ver.IsEmpty() { + ver = pkgver.Version(v) + } + } + + if ver.IsEmpty() { + return ver, errInstallFirst + } + return ver, nil } // DownloadComponentIfMissing downloads the specific version of a component if it is missing func (env *Environment) DownloadComponentIfMissing(component string, ver pkgver.Version) (pkgver.Version, error) { - versions, err := env.profile.InstalledVersions(component) - if err != nil { - return "", err - } - + var err error if ver.String() == version.NightlyVersion { if ver, _, err = env.v1Repo.LatestNightlyVersion(component); err != nil { return "", err @@ -200,23 +252,10 @@ func (env *Environment) DownloadComponentIfMissing(component string, ver pkgver. // download the latest version if the specific component doesn't be installed // Check whether the specific version exist in local - if ver.IsEmpty() && len(versions) > 0 { - sort.Slice(versions, func(i, j int) bool { - return semver.Compare(versions[i], versions[j]) < 0 - }) - ver = pkgver.Version(versions[len(versions)-1]) - } - - needDownload := ver.IsEmpty() - if !ver.IsEmpty() { - installed := false - for _, v := range versions { - if pkgver.Version(v) == ver { - installed = true - break - } - } - needDownload = !installed + ver, err = env.SelectInstalledVersion(component, ver) + needDownload := errors.Cause(err) == ErrInstallFirst + if err != nil && !needDownload { + return "", err } if needDownload { diff --git a/pkg/localdata/profile.go b/pkg/localdata/profile.go index b7b5b0963b..faa603cf06 100644 --- a/pkg/localdata/profile.go +++ b/pkg/localdata/profile.go @@ -224,38 +224,6 @@ func (p *Profile) VersionIsInstalled(component, version string) (bool, error) { return false, nil } -// SelectInstalledVersion selects the installed versions and the latest release version -// will be chosen if there is an empty version -func (p *Profile) SelectInstalledVersion(component string, version pkgver.Version) (pkgver.Version, error) { - installed, err := p.InstalledVersions(component) - if err != nil { - return "", err - } - - errInstallFirst := fmt.Errorf("use `tiup install %[1]s` to install `%[1]s` first", component) - if len(installed) < 1 { - return "", errInstallFirst - } - - if version.IsEmpty() { - sort.Slice(installed, func(i, j int) bool { - return semver.Compare(installed[i], installed[j]) < 0 - }) - version = pkgver.Version(installed[len(installed)-1]) - } - found := false - for _, v := range installed { - if pkgver.Version(v) == version { - found = true - break - } - } - if !found { - return "", errInstallFirst - } - return version, nil -} - // ResetMirror reset root.json and cleanup manifests directory func (p *Profile) ResetMirror(addr, root string) error { // Calculating root.json path