Skip to content

Commit 8515cf0

Browse files
jbagopherbot
authored andcommitted
devtools/cmd/evaldoc: support fetching modules from proxy
This CL augments the evaldoc program by taking a module@version argument and fetching it from the proxy. I tested this manually. The final version will have more extensive tests. For golang/go#80385. Change-Id: I26f413abad242378eb45b5abf52fba7f0c2ddeed Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/811080 LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Ethan Lee <ethanalee@google.com> Auto-Submit: Jonathan Amsterdam <jba@google.com> kokoro-CI: kokoro <noreply+kokoro@google.com>
1 parent c739841 commit 8515cf0

1 file changed

Lines changed: 74 additions & 15 deletions

File tree

devtools/cmd/evaldoc/main.go

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// The evaldoc command takes a local directory path, loads the module,
6-
// and prints symbols and whether they have documentation to standard output.
5+
// The evaldoc command takes a module_path@version or a local directory
6+
// path, loads the module, and prints symbols and whether they have
7+
// documentation to standard output.
78
//
89
// It can be used to better understand the "documentation coverage" score
910
// on a package's evaluations page (pkg.go.dev/IMPORT/PATH?tab=evals).
@@ -12,6 +13,7 @@
1213
package main
1314

1415
import (
16+
"context"
1517
"errors"
1618
"flag"
1719
"fmt"
@@ -20,18 +22,25 @@ import (
2022
"go/token"
2123
"io/fs"
2224
"log"
25+
"net/http"
2326
"os"
2427
"path"
2528
"path/filepath"
2629
"strings"
2730

2831
"golang.org/x/mod/modfile"
2932
"golang.org/x/pkgsite/internal/frontend"
33+
"golang.org/x/pkgsite/internal/proxy"
34+
)
35+
36+
var (
37+
proxyURL = flag.String("proxy", "", "module proxy URL (defaults to GOPROXY or https://proxy.golang.org)")
3038
)
3139

3240
func main() {
3341
flag.Usage = func() {
34-
fmt.Fprintf(flag.CommandLine.Output(), "usage: %s local_dir_path\n", os.Args[0])
42+
fmt.Fprintf(flag.CommandLine.Output(), "usage: %s [flags] (module_path@version | local_dir_path)\n", os.Args[0])
43+
fmt.Fprintln(flag.CommandLine.Output(), "local_dir_path must start with one of: / . ~")
3544
flag.PrintDefaults()
3645
}
3746
flag.Parse()
@@ -65,29 +74,79 @@ func resolveLocalPath(arg string) (string, error) {
6574
return filepath.Abs(arg)
6675
}
6776

68-
// getContentDir uses arg to find a module at localPath, and returns an fs.FS whose root is the
77+
// getContentDir uses arg to find a module, and returns an fs.FS whose root is the
6978
// content directory of that module (the directory containing the go.mod file).
79+
// It also returns the module path.
7080
func getContentDir(arg string) (modulePath string, contentDir fs.FS, err error) {
7181
if len(arg) == 0 {
7282
return "", nil, errors.New("empty argument")
7383
}
74-
localPath, err := resolveLocalPath(arg)
84+
if arg[0] == '/' || arg[0] == '.' || arg[0] == '~' {
85+
localPath, err := resolveLocalPath(arg)
86+
if err != nil {
87+
return "", nil, fmt.Errorf("failed to resolve path %s: %w", arg, err)
88+
}
89+
fi, err := os.Stat(localPath)
90+
if err != nil || !fi.IsDir() {
91+
return "", nil, fmt.Errorf("%s is not a directory", localPath)
92+
}
93+
modBytes, err := os.ReadFile(filepath.Join(localPath, "go.mod"))
94+
if err != nil {
95+
return "", nil, fmt.Errorf("failed to read go.mod in %s: %w", localPath, err)
96+
}
97+
modulePath = modfile.ModulePath(modBytes)
98+
if modulePath == "" {
99+
return "", nil, fmt.Errorf("go.mod in %s contains no module path", localPath)
100+
}
101+
return modulePath, os.DirFS(localPath), nil
102+
}
103+
104+
var reqVer string
105+
var found bool
106+
modulePath, reqVer, found = strings.Cut(arg, "@")
107+
if !found || reqVer == "" {
108+
reqVer = "latest"
109+
}
110+
111+
pURL := *proxyURL
112+
if pURL == "" {
113+
pURL = os.Getenv("GOPROXY")
114+
}
115+
if pURL == "off" {
116+
return "", nil, errors.New("GOPROXY is off")
117+
}
118+
if pURL == "" {
119+
pURL = "https://proxy.golang.org"
120+
}
121+
// Take the first URL from the GOPROXY list.
122+
if idx := strings.IndexAny(pURL, ",|"); idx != -1 {
123+
pURL = pURL[:idx]
124+
}
125+
126+
ctx := context.Background()
127+
proxyClient, err := proxy.New(pURL, http.DefaultTransport)
75128
if err != nil {
76-
return "", nil, fmt.Errorf("failed to resolve path %s: %w", arg, err)
129+
return "", nil, fmt.Errorf("proxy.New(%q): %w", pURL, err)
77130
}
78-
fi, err := os.Stat(localPath)
79-
if err != nil || !fi.IsDir() {
80-
return "", nil, fmt.Errorf("%s is not a directory", localPath)
131+
proxyClient = proxyClient.WithFetchDisabled()
132+
133+
verInfo, err := proxyClient.Info(ctx, modulePath, reqVer)
134+
if err != nil {
135+
return "", nil, fmt.Errorf("proxyClient.Info(%q, %q): %w", modulePath, reqVer, err)
81136
}
82-
modBytes, err := os.ReadFile(filepath.Join(localPath, "go.mod"))
137+
resolvedVersion := verInfo.Version
138+
139+
zipReader, err := proxyClient.Zip(ctx, modulePath, resolvedVersion)
83140
if err != nil {
84-
return "", nil, fmt.Errorf("failed to read go.mod in %s: %w", localPath, err)
141+
return "", nil, fmt.Errorf("proxyClient.Zip(%q, %q): %w", modulePath, resolvedVersion, err)
85142
}
86-
modulePath = modfile.ModulePath(modBytes)
87-
if modulePath == "" {
88-
return "", nil, fmt.Errorf("go.mod in %s contains no module path", localPath)
143+
144+
contentDir, err = fs.Sub(zipReader, modulePath+"@"+resolvedVersion)
145+
if err != nil {
146+
return "", nil, fmt.Errorf("fs.Sub: %w", err)
89147
}
90-
return modulePath, os.DirFS(localPath), nil
148+
149+
return modulePath, contentDir, nil
91150
}
92151

93152
// packageDirs walks contentDir to find directories corresponding to valid import paths,

0 commit comments

Comments
 (0)