package gomod import ( "encoding/json" "fmt" "io" "net/http" "net/url" "strconv" "strings" "git.yetaga.in/alazyreader/libyear/pkg/cache" "git.yetaga.in/alazyreader/libyear/pkg/libyear" ) // A Queryer holds settings for a type Queryer struct { Root string Client http.Client Cache cache.Cache Logger logger } type majorVersion struct { raw string version int } // GetLatestVersion returns the "latest" major version of a module, // jumping across MVS boundaries: // i.e. github.com/foo/bar => github.com/foo/bar/v2 in the case of one version bump // github.com/foo/baz/v2 => github.com/foo/baz/v6 in the case of a large jump // github.com/foo/quuz/v2 => github.com/foo/quuz/v2 in the case of no jump at all. func (q *Queryer) GetLatestVersion(module string) libyear.Info { latestMod := q.findLatestMajorVersion(module) return q.makeProxyRequest(latestMod + "/@latest") } // GetVersion returns the proxy's information about a given version of a module. // `module` in this case is a fully-disambiguated MVS name (github.com/foo/bar/v2), // and `version` is a version identifier as defined in `https://blog.golang.org/publishing-go-modules#TOC_3.` func (q *Queryer) GetVersion(module string, version string) libyear.Info { if !strings.HasPrefix(version, "v") { return libyear.Info{ Version: version, } } i := q.makeProxyRequest(module + "/@v/" + version + ".info") if i.Version == "" { i.Version = version } return i } // given a module, return the "latest" major version of that module, fully defined func (q *Queryer) findLatestMajorVersion(module string) string { for { nextModule := incrementMajorVersion(module) info := q.makeProxyRequest(nextModule + "/@latest") if info.Version == "" { return module } module = nextModule } } // handle calling the go module proxy func (q *Queryer) makeProxyRequest(mod string) libyear.Info { if q.Root == "" { q.Root = "https://proxy.golang.org/" } q.Logger.Debugf("makeProxyRequest for %s/%s\n", q.Root, mod) u, _ := url.Parse(q.Root) u.Path = mod i := q.Cache.Get(u.String()) if i.Version != "" { q.Logger.Debugf("cache hit for https://proxy.golang.org/%s\n", mod) return i } req, err := http.NewRequest("GET", u.String(), nil) if err != nil { return i } req.Header.Add("Disable-Module-Fetch", "true") resp, err := q.Client.Do(req) if err != nil || resp.StatusCode != 200 { return i } b, _ := io.ReadAll(resp.Body) err = json.Unmarshal(b, &i) q.Cache.Set(u.String(), i) return i } // parses and attempts to add an MVS version to a module func incrementMajorVersion(mod string) string { exp := strings.Split(mod, "/") var module string if exp[len(exp)-1][0] == 'v' { n, err := strconv.Atoi(exp[len(exp)-1][1:]) if err != nil { module = mod + "/v2" } else { module = strings.Join(exp[:len(exp)-1], "/") + "/v" + fmt.Sprint(n+1) } } else { module = mod + "/v2" } return module }