diff --git a/pkg/environment/env.go b/pkg/environment/env.go index 8ea388cc3e..c88150823a 100644 --- a/pkg/environment/env.go +++ b/pkg/environment/env.go @@ -14,6 +14,7 @@ package environment import ( + "errors" "fmt" "os" "path/filepath" @@ -21,7 +22,7 @@ import ( "strings" "time" - "github.com/pingcap/errors" + perrs "github.com/pingcap/errors" "github.com/pingcap/tiup/pkg/localdata" "github.com/pingcap/tiup/pkg/repository" "github.com/pingcap/tiup/pkg/repository/v1manifest" @@ -32,7 +33,7 @@ import ( var ( // ErrInstallFirst indicates that a component/version is not installed - ErrInstallFirst = errors.New("component not installed") + ErrInstallFirst = perrs.New("component not installed") ) // EnvList is the canonical allowlist of environment variables TiUP will print or expose. @@ -144,7 +145,22 @@ func InitEnv(options repository.Options, mOpt repository.MirrorOptions) (*Enviro var local v1manifest.LocalManifests local, err = v1manifest.NewManifests(profile) if err != nil { - return nil, errors.Annotatef(err, "initial repository from mirror(%s) failed", mirrorAddr) + if errors.Is(err, v1manifest.ErrLoadManifest) { + // Only bootstrap root.json when the file is actually missing. + // If it exists but can't be read (e.g. permissions), preserve the + // original error rather than overwriting it. + rootPath := profile.Path("bin", "root.json") + if _, statErr := os.Stat(rootPath); os.IsNotExist(statErr) { + // Use the configured mirrorAddr so that custom/test mirrors are respected. + if err := profile.ResetMirror(mirrorAddr, ""); err != nil { + return nil, perrs.Annotatef(err, "initial repository from mirror(%s) failed", mirrorAddr) + } + local, err = v1manifest.NewManifests(profile) + } + } + if err != nil { + return nil, perrs.Annotatef(err, "initial repository from mirror(%s) failed", mirrorAddr) + } } v1repo = repository.NewV1Repo(mirror, options, local) @@ -222,7 +238,7 @@ func (env *Environment) SelectInstalledVersion(component string, ver utils.Versi versions := []string{} for _, v := range installed { vi, err := env.v1Repo.LocalComponentVersion(component, v, true) - if errors.Cause(err) == repository.ErrUnknownVersion { + if perrs.Cause(err) == repository.ErrUnknownVersion { continue } if err != nil { @@ -238,9 +254,9 @@ func (env *Environment) SelectInstalledVersion(component string, ver utils.Versi return semver.Compare(versions[i], versions[j]) > 0 }) - errInstallFirst := errors.Annotatef(ErrInstallFirst, "use `tiup install %s` to install component `%s` first", component, component) + errInstallFirst := perrs.Annotatef(ErrInstallFirst, "use `tiup install %s` to install component `%s` first", component, component) if !ver.IsEmpty() { - errInstallFirst = errors.Annotatef(ErrInstallFirst, "use `tiup install %s:%s` to install specified version", component, ver.String()) + errInstallFirst = perrs.Annotatef(ErrInstallFirst, "use `tiup install %s:%s` to install specified version", component, ver.String()) } if ver.IsEmpty() || string(ver) == utils.NightlyVersionAlias { diff --git a/pkg/localdata/profile.go b/pkg/localdata/profile.go index 1712536fb5..0d29550c01 100644 --- a/pkg/localdata/profile.go +++ b/pkg/localdata/profile.go @@ -224,6 +224,11 @@ func (p *Profile) VersionIsInstalled(component, version string) (bool, error) { // ResetMirror reset root.json and cleanup manifests directory func (p *Profile) ResetMirror(addr, root string) error { + // Ensure bin directory exists + if err := os.MkdirAll(p.Path("bin"), 0755); err != nil { + return err + } + // Calculating root.json path shaWriter := sha256.New() if _, err := io.Copy(shaWriter, strings.NewReader(addr)); err != nil {