2021-03-02 16:53:14 +03:00
|
|
|
// Copyright 2021 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package internal
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2021-03-03 18:07:46 +03:00
|
|
|
"strings"
|
2021-03-02 16:53:14 +03:00
|
|
|
|
|
|
|
"golang.org/x/mod/modfile"
|
|
|
|
"golang.org/x/mod/semver"
|
|
|
|
)
|
|
|
|
|
|
|
|
// LatestModuleVersions describes the latest versions of a module. It also holds the
|
|
|
|
// go.mod file of the raw latest version, which establishes whether the module
|
|
|
|
// is deprecated, and what versions are retracted.
|
|
|
|
type LatestModuleVersions struct {
|
|
|
|
ModulePath string
|
|
|
|
RawVersion string // ignoring retractions
|
|
|
|
CookedVersion string // considering retractions
|
|
|
|
GoodVersion string // successfully processed
|
|
|
|
GoModFile *modfile.File // of raw
|
2021-04-06 17:14:58 +03:00
|
|
|
Deprecated bool
|
2021-03-02 16:53:14 +03:00
|
|
|
deprecationComment string
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewLatestModuleVersions(modulePath, raw, cooked, good string, modBytes []byte) (*LatestModuleVersions, error) {
|
|
|
|
modFile, err := modfile.ParseLax(fmt.Sprintf("%s@%s/go.mod", modulePath, raw), modBytes, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dep, comment := isDeprecated(modFile)
|
|
|
|
return &LatestModuleVersions{
|
|
|
|
ModulePath: modulePath,
|
|
|
|
RawVersion: raw,
|
|
|
|
CookedVersion: cooked,
|
|
|
|
GoodVersion: good,
|
|
|
|
GoModFile: modFile,
|
2021-04-06 17:14:58 +03:00
|
|
|
Deprecated: dep,
|
2021-03-02 16:53:14 +03:00
|
|
|
deprecationComment: comment,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-03-03 18:07:46 +03:00
|
|
|
// isDeprecated reports whether the go.mod deprecates this module.
|
|
|
|
// It looks for "Deprecated" comments in the line comments before and next to
|
|
|
|
// the module declaration. If it finds one, it returns true along with the text
|
|
|
|
// after "Deprecated:". Otherwise it returns false, "".
|
|
|
|
func isDeprecated(mf *modfile.File) (bool, string) {
|
|
|
|
const prefix = "Deprecated:"
|
|
|
|
|
|
|
|
if mf.Module == nil {
|
|
|
|
return false, ""
|
|
|
|
}
|
|
|
|
for _, comment := range append(mf.Module.Syntax.Before, mf.Module.Syntax.Suffix...) {
|
|
|
|
text := strings.TrimSpace(strings.TrimPrefix(comment.Token, "//"))
|
|
|
|
if strings.HasPrefix(text, prefix) {
|
|
|
|
return true, strings.TrimSpace(text[len(prefix):])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false, ""
|
|
|
|
}
|
|
|
|
|
2021-03-02 16:53:14 +03:00
|
|
|
// PopulateModuleInfo uses the LatestModuleVersions to populate fields of the given module.
|
|
|
|
func (li *LatestModuleVersions) PopulateModuleInfo(mi *ModuleInfo) {
|
2021-04-06 17:14:58 +03:00
|
|
|
mi.Deprecated = li.Deprecated
|
2021-03-02 16:53:14 +03:00
|
|
|
mi.DeprecationComment = li.deprecationComment
|
2021-03-04 19:17:20 +03:00
|
|
|
mi.Retracted, mi.RetractionRationale = isRetracted(li.GoModFile, mi.Version)
|
2021-03-02 16:53:14 +03:00
|
|
|
}
|
|
|
|
|
2021-03-04 19:17:20 +03:00
|
|
|
// IsRetracted reports whether the version is retracted according to the go.mod
|
|
|
|
// file in the receiver.
|
|
|
|
func (li *LatestModuleVersions) IsRetracted(version string) bool {
|
|
|
|
r, _ := isRetracted(li.GoModFile, version)
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
// isRetracted reports whether the go.mod file retracts the version.
|
2021-03-02 16:53:14 +03:00
|
|
|
// If so, it returns true along with the rationale for the retraction.
|
2021-03-04 19:17:20 +03:00
|
|
|
func isRetracted(mf *modfile.File, resolvedVersion string) (bool, string) {
|
2021-03-02 16:53:14 +03:00
|
|
|
for _, r := range mf.Retract {
|
|
|
|
if semver.Compare(resolvedVersion, r.Low) >= 0 && semver.Compare(resolvedVersion, r.High) <= 0 {
|
|
|
|
return true, r.Rationale
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false, ""
|
|
|
|
}
|