+++ /dev/null
-package main
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net/http"
- "os"
- "path"
- "path/filepath"
- "strings"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/google/renameio"
- "github.com/rogpeppe/go-internal/modfile"
- "golang.org/x/mod/module"
-)
-
-/*
-Q: which versions of our module are being used
-A: find the latest version of every Go module, find the dependency on our module
-
-Q: what modules have stopped using our module
-A: find every module where a version [0..N) uses us, but version N doesn't.
-*/
-
-func Fetch(since time.Time) ([]module.Version, time.Time, error) {
- var out []module.Version
- for {
- out2, since2, err := fetch(since, out)
- if err != nil {
- return nil, since, err
- }
- if len(out) == len(out2) {
- break
- }
- out = out2
- since = since2
- }
- return out, since, nil
-}
-
-func fetch(since time.Time, out []module.Version) ([]module.Version, time.Time, error) {
- // +1µs because of bug in index.golang.org that returns results
- // >=since instead of >since
- ts := since.Add(1 * time.Microsecond)
- u := `https://index.golang.org/index?since=` + ts.Format(time.RFC3339Nano)
- resp, err := http.Get(u)
- if err != nil {
- return nil, since, err
- }
- defer resp.Body.Close()
- dec := json.NewDecoder(resp.Body)
-
- var entry struct {
- module.Version
- Timestamp time.Time
- }
- for {
- if err := dec.Decode(&entry); err != nil {
- if err == io.EOF {
- break
- }
- return out, since, err
- }
-
- out = append(out, entry.Version)
- since = entry.Timestamp
- }
-
- return out, since, nil
-}
-
-func main() {
- cache, err := os.UserCacheDir()
- if err != nil {
- log.Fatal(err)
- }
-
- var since time.Time
- b, err := ioutil.ReadFile(filepath.Join(cache, "go-module-query", "last"))
- if err == nil {
- t, err := time.Parse(time.RFC3339Nano, string(b))
- if err != nil {
- log.Fatal(err)
- }
- since = t
- log.Println("Resuming at", since)
- } else if !os.IsNotExist(err) {
- log.Fatal(err)
- }
-
- out, since, err := Fetch(since)
- if err != nil {
- log.Fatal(err)
- }
-
- sem := make(chan struct{}, 8)
- var wg sync.WaitGroup
- var errs uint64
- for _, v := range out {
- mpath, _ := module.EscapePath(v.Path)
- p := filepath.Join(cache, "go-module-query", mpath, "@v", v.Version+".mod")
- // XXX is this atomic?
- if err := os.MkdirAll(filepath.Join(cache, "go-module-query", mpath, "@v"), 0777); err != nil {
- log.Println(err)
- continue
- }
- if _, err := os.Stat(p); os.IsNotExist(err) {
- fmt.Println("Fetching", v)
- sem <- struct{}{}
- wg.Add(1)
- go func(p string, v module.Version) {
- defer wg.Done()
- defer func() { <-sem }()
- resp, err := http.Get("https://proxy.golang.org/" + path.Join(mpath, "@v", v.Version+".mod"))
- if err != nil {
- atomic.AddUint64(&errs, 1)
- log.Println(err)
- return
- }
- defer resp.Body.Close()
- // XXX handle response code
- pf, err := renameio.TempFile("", p)
- if err != nil {
- atomic.AddUint64(&errs, 1)
- log.Println(err)
- return
- }
- defer pf.Cleanup()
- if _, err := io.Copy(pf, resp.Body); err != nil {
- atomic.AddUint64(&errs, 1)
- log.Println(err)
- return
- }
- if err := pf.CloseAtomicallyReplace(); err != nil {
- atomic.AddUint64(&errs, 1)
- log.Println("Couldn't store go.mod:", err)
- }
- }(p, v)
- }
- }
-
- wg.Wait()
-
- if errs > 0 {
- log.Println("Couldn't download all go.mod, not storing timestamp")
- return
- }
-
- if err := renameio.WriteFile(filepath.Join(cache, "go-module-query", "last"), []byte(since.Format(time.RFC3339Nano)), 0666); err != nil {
- log.Println("Couldn't store timestamp:", err)
- }
-}
-
-func printGraph() {
- cache, err := os.UserCacheDir()
- if err != nil {
- log.Fatal(err)
- }
- filepath.Walk(filepath.Join(cache, "go-module-query"), func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return nil
- }
- if strings.HasSuffix(path, ".mod") {
- name := filepath.Base(path)
- name = name[:len(name)-4]
- b, err := ioutil.ReadFile(path)
- if err != nil {
- log.Println(err)
- return nil
- }
- f, err := modfile.Parse(path, b, nil)
- if err != nil {
- log.Println(err)
- return nil
- }
- f.Module.Mod.Version = name
- for _, dep := range f.Require {
- fmt.Printf("%s@%s %s@%s\n", f.Module.Mod.Path, f.Module.Mod.Version, dep.Mod.Path, dep.Mod.Version)
- }
- }
- return nil
- })
-}