in internal/fetch/latest.go [48:147]
func LatestModuleVersions(ctx context.Context, modulePath string, prox *proxy.Client, hasGoMod func(v string) (bool, error)) (info *internal.LatestModuleVersions, err error) {
defer derrors.WrapStack(&err, "LatestModuleVersions(%q)", modulePath)
defer func() {
if info != nil {
log.Debugf(ctx, "LatestModuleVersions(%q) => (raw=%q cooked=%q, %v)", modulePath, info.RawVersion, info.CookedVersion, err)
}
}()
if modulePath == stdlib.ModulePath {
vs, err := stdlib.Versions()
if err != nil {
return nil, err
}
latest := version.LatestOf(vs)
if latest == "" {
return nil, errors.New("no versions for stdlib")
}
return internal.NewLatestModuleVersions(modulePath, latest, latest, "", []byte("module std"))
}
// Remember calls to hasGoMod because they can be expensive.
hasGoModResults := map[string]bool{}
hasGoModFunc := func(v string) (bool, error) {
result, ok := hasGoModResults[v]
if ok {
return result, nil
}
err := derrors.NotFound
if hasGoMod != nil {
result, err = hasGoMod(v)
}
if err != nil && !errors.Is(err, derrors.NotFound) {
return false, err
}
if err != nil {
// hasGoMod doesn't know; download the zip.
zr, err := prox.Zip(ctx, modulePath, v)
if err != nil {
return false, err
}
contentsDir, err := fs.Sub(zr, modulePath+"@"+v)
if err != nil {
return false, err
}
result = hasGoModFile(contentsDir)
}
hasGoModResults[v] = result
return result, nil
}
// Get the raw latest version.
versions, err := prox.Versions(ctx, modulePath)
if err != nil {
return nil, err
}
latestInfo, err := prox.Info(ctx, modulePath, version.Latest)
if errors.Is(err, derrors.NotFound) || errors.Is(err, derrors.NotFetched) {
// No information from the proxy, but not a showstopper either; we can
// proceed with the result of the list endpoint.
} else if err != nil {
return nil, err
} else {
versions = append(versions, latestInfo.Version)
}
if len(versions) == 0 {
// No tagged versions, and nothing from @latest: no version information.
return nil, nil
}
rawLatest, err := version.LatestVersion(versions, hasGoModFunc)
if err != nil {
return nil, err
}
// Get the go.mod file at the raw latest version.
modBytes, err := prox.Mod(ctx, modulePath, rawLatest)
if err != nil {
// Something's wrong with the go.mod file, so assume a minimal one instead of failing.
log.Warningf(ctx, "proxy.Mod(%q, %q): %v; using minimal go.mod for latest version info",
modulePath, rawLatest)
modBytes = []byte(fmt.Sprintf("module %s", modulePath))
}
lmv, err := internal.NewLatestModuleVersions(modulePath, rawLatest, "", "", modBytes)
if err != nil {
// An error here means a bad go.mod file.
return nil, fmt.Errorf("%v: %w", err, derrors.BadModule)
}
// Get the cooked latest version by disallowing retracted versions.
unretractedVersions := version.RemoveIf(versions, lmv.IsRetracted)
if len(versions) == len(unretractedVersions) {
lmv.CookedVersion = lmv.RawVersion
} else {
lmv.CookedVersion, err = version.LatestVersion(unretractedVersions, hasGoModFunc)
if err != nil {
return nil, err
}
}
return lmv, nil
}