github.com/julianthome/gore@v0.0.0-20231109011145-b3a6bbe6fe55/gomod.go (about) 1 package gore 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "go/build" 7 "io" 8 "net" 9 "net/url" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "strconv" 14 "strings" 15 "time" 16 ) 17 18 func (s *Session) initGoMod() error { 19 tempModule := filepath.Base(s.tempDir) 20 goModPath := filepath.Join(s.tempDir, "go.mod") 21 directives := s.listModuleDirectives() 22 mod := "module " + tempModule + "\n" + strings.Join(directives, "\n") 23 return os.WriteFile(goModPath, []byte(mod), 0o644) 24 } 25 26 func (s *Session) listModuleDirectives() []string { 27 var directives []string 28 for i, pp := range printerPkgs { 29 if pp.path == "fmt" { 30 continue 31 } 32 // Check local module caches. 33 found := lookupGoModule(pp.path, pp.version) 34 if found { 35 for _, r := range pp.requires { 36 if !lookupGoModule(r.path, r.version) { 37 found = false 38 break 39 } 40 } 41 } 42 if found || canAccessGoproxy() { 43 // Specifying the version of the printer package improves startup 44 // performance by skipping module version fetching. Also allows to 45 // use gore in offline environment. 46 directives = append(directives, "require "+pp.path+" "+pp.version) 47 for _, r := range pp.requires { 48 directives = append(directives, "require "+r.path+" "+r.version) 49 } 50 } else { 51 // If there is no module cache and no network connection, use fmt package. 52 printerPkgs = printerPkgs[i+1:] 53 } 54 // only the first printer is checked (assuming printerPkgs[1] is fmt) 55 break 56 } 57 modules, err := goListAll() 58 if err != nil { 59 return directives 60 } 61 for _, m := range modules { 62 if m.Main || m.Replace != nil { 63 directives = append(directives, "replace "+m.Path+" => "+strconv.Quote(m.Dir)) 64 s.requiredModules = append(s.requiredModules, m.Path) 65 } 66 } 67 return directives 68 } 69 70 type goModule struct { 71 Path, Dir, Version string 72 Main bool 73 Replace *goModule 74 } 75 76 func goListAll() ([]*goModule, error) { 77 cmd := exec.Command("go", "list", "-json", "-m", "all") 78 out, err := cmd.Output() 79 if err != nil { 80 return nil, err 81 } 82 d := json.NewDecoder(bytes.NewReader(out)) 83 var ms []*goModule 84 for { 85 m := new(goModule) 86 if err := d.Decode(m); err != nil { 87 if err == io.EOF { 88 return ms, nil 89 } 90 return nil, err 91 } 92 ms = append(ms, m) 93 } 94 } 95 96 func lookupGoModule(pkg, version string) bool { 97 modDir := filepath.Join(build.Default.GOPATH, "pkg/mod", pkg+"@"+version) 98 fi, err := os.Stat(modDir) 99 return err == nil && fi.IsDir() 100 } 101 102 func canAccessGoproxy() bool { 103 var host string 104 if u, err := url.Parse(getGoproxy()); err != nil { 105 host = "proxy.golang.org" 106 } else { 107 host = u.Hostname() 108 } 109 addr := net.JoinHostPort(host, "80") 110 dialer := net.Dialer{Timeout: 5 * time.Second} 111 conn, err := dialer.Dial("tcp", addr) 112 if err != nil { 113 return false 114 } 115 defer conn.Close() 116 return true 117 } 118 119 func getGoproxy() string { 120 if goproxy := os.Getenv("GOPROXY"); goproxy != "" { 121 return goproxy 122 } 123 return "https://proxy.golang.org/" 124 }