Skip to content

Commit 95fdbdd

Browse files
committed
cmd/pkgsite: hide irrelevant content in local mode; add quick links
This CL contains several UI improvements when running pkgsite in local mode via cmd/pkgsite: - hide irrelevant or inaccurate content - fix a bug using -gopath_mode (trim the `go env GOPATH` output) - fix panics navigating to vuln pages - link the GO button to the root page, rather than go.dev - add quick-links to the homepage to browse local modules Also: - add -dev and -static flags, to facilitate development - replace TestBuildGetters with more cases in TestServer - fix some redundancy in setting the <title> element for pages; consolidate on using the basePage.HTMLTitle value Updates golang/go#40371 Change-Id: I459a0d0fd39897dd4f902dd023aec653a3fb12cd Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/475755 TryBot-Result: kokoro <noreply+kokoro@google.com> Reviewed-by: Jamal Carvalho <jamal@golang.org> Run-TryBot: Robert Findley <rfindley@google.com>
1 parent b5fdc90 commit 95fdbdd

35 files changed

+347
-201
lines changed

cmd/pkgsite/main.go

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,17 @@
4949
package main
5050

5151
import (
52+
"bytes"
5253
"context"
54+
"encoding/json"
5355
"flag"
5456
"fmt"
57+
"io/fs"
5558
"net/http"
5659
"os"
5760
"os/exec"
5861
"path/filepath"
62+
"sort"
5963
"strings"
6064
"time"
6165

@@ -79,6 +83,8 @@ var (
7983
httpAddr = flag.String("http", defaultAddr, "HTTP service address to listen for incoming requests on")
8084
goRepoPath = flag.String("gorepo", "", "path to Go repo on local filesystem")
8185
useProxy = flag.Bool("proxy", false, "fetch from GOPROXY if not found locally")
86+
devMode = flag.Bool("dev", false, "enable developer mode (reload templates on each page load, serve non-minified JS/CSS, etc.)")
87+
staticFlag = flag.String("static", "static", "path to folder containing static files served")
8288
// other flags are bound to serverConfig below
8389
)
8490

@@ -155,7 +161,7 @@ func buildServer(ctx context.Context, serverCfg serverConfig) (*frontend.Server,
155161
}
156162

157163
cfg := getterConfig{
158-
dirs: serverCfg.paths,
164+
all: serverCfg.useListedMods,
159165
proxy: serverCfg.proxy,
160166
}
161167

@@ -168,11 +174,12 @@ func buildServer(ctx context.Context, serverCfg serverConfig) (*frontend.Server,
168174
if err != nil {
169175
return nil, fmt.Errorf("searching GOPATH: %v", err)
170176
}
171-
}
172-
173-
cfg.pattern = "./..."
174-
if serverCfg.useListedMods {
175-
cfg.pattern = "all"
177+
} else {
178+
var err error
179+
cfg.dirs, err = getModuleDirs(ctx, serverCfg.paths)
180+
if err != nil {
181+
return nil, fmt.Errorf("searching GOPATH: %v", err)
182+
}
176183
}
177184

178185
if serverCfg.useCache {
@@ -193,7 +200,24 @@ func buildServer(ctx context.Context, serverCfg serverConfig) (*frontend.Server,
193200
if err != nil {
194201
return nil, err
195202
}
196-
return newServer(getters, cfg.proxy)
203+
204+
// Collect unique module paths served by this server.
205+
seenModules := make(map[frontend.LocalModule]bool)
206+
var allModules []frontend.LocalModule
207+
for _, modules := range cfg.dirs {
208+
for _, m := range modules {
209+
if seenModules[m] {
210+
continue
211+
}
212+
seenModules[m] = true
213+
allModules = append(allModules, m)
214+
}
215+
}
216+
sort.Slice(allModules, func(i, j int) bool {
217+
return allModules[i].ModulePath < allModules[j].ModulePath
218+
})
219+
220+
return newServer(getters, allModules, cfg.proxy)
197221
}
198222

199223
func collectPaths(args []string) []string {
@@ -204,20 +228,53 @@ func collectPaths(args []string) []string {
204228
return paths
205229
}
206230

207-
// getGOPATHModuleDirs returns the absolute paths to directories in GOPATH
208-
// corresponding to the requested module paths.
231+
// getGOPATHModuleDirs returns the set of workspace modules for each directory,
232+
// determined by running go list -m.
233+
//
234+
// An error is returned if any operations failed unexpectedly, or if no
235+
// requested directories contain any valid modules.
236+
func getModuleDirs(ctx context.Context, dirs []string) (map[string][]frontend.LocalModule, error) {
237+
dirModules := make(map[string][]frontend.LocalModule)
238+
for _, dir := range dirs {
239+
output, err := runGo(dir, "list", "-m", "-json")
240+
if err != nil {
241+
return nil, fmt.Errorf("listing modules in %s: %v", dir, err)
242+
}
243+
var modules []frontend.LocalModule
244+
decoder := json.NewDecoder(bytes.NewBuffer(output))
245+
for decoder.More() {
246+
var m frontend.LocalModule
247+
if err := decoder.Decode(&m); err != nil {
248+
return nil, err
249+
}
250+
if m.ModulePath != "command-line-arguments" {
251+
modules = append(modules, m)
252+
}
253+
}
254+
if len(modules) > 0 {
255+
dirModules[dir] = modules
256+
}
257+
}
258+
if len(dirs) > 0 && len(dirModules) == 0 {
259+
return nil, fmt.Errorf("no modules in any of the requested directories")
260+
}
261+
return dirModules, nil
262+
}
263+
264+
// getGOPATHModuleDirs returns local module information for directories in
265+
// GOPATH corresponding to the requested module paths.
209266
//
210-
// An error is returned if any operations failed unexpectedly. If individual
211-
// module paths are not found, an error is logged and the path skipped. An
212-
// error is returned only if no module paths resolved to a GOPATH directory.
213-
func getGOPATHModuleDirs(ctx context.Context, modulePaths []string) ([]string, error) {
267+
// An error is returned if any operations failed unexpectedly, or if no modules
268+
// were resolved. If individual module paths are not found, an error is logged
269+
// and the path skipped.
270+
func getGOPATHModuleDirs(ctx context.Context, modulePaths []string) (map[string][]frontend.LocalModule, error) {
214271
gopath, err := runGo("", "env", "GOPATH")
215272
if err != nil {
216273
return nil, err
217274
}
218-
gopaths := filepath.SplitList(string(gopath))
275+
gopaths := filepath.SplitList(strings.TrimSpace(string(gopath)))
219276

220-
var dirs []string
277+
dirs := make(map[string][]frontend.LocalModule)
221278
for _, path := range modulePaths {
222279
dir := ""
223280
for _, gopath := range gopaths {
@@ -234,7 +291,7 @@ func getGOPATHModuleDirs(ctx context.Context, modulePaths []string) ([]string, e
234291
if dir == "" {
235292
log.Errorf(ctx, "ERROR: no GOPATH directory contains %q", path)
236293
} else {
237-
dirs = append(dirs, dir)
294+
dirs[dir] = []frontend.LocalModule{{ModulePath: path, Dir: dir}}
238295
}
239296
}
240297

@@ -247,10 +304,10 @@ func getGOPATHModuleDirs(ctx context.Context, modulePaths []string) ([]string, e
247304
// getterConfig defines the set of getters for the server to use.
248305
// See buildGetters.
249306
type getterConfig struct {
250-
dirs []string // local directories to serve
251-
pattern string // go/packages query to load in each directory
252-
modCacheDir string // path to module cache, or ""
253-
proxy *proxy.Client // proxy client, or nil
307+
all bool // if set, request "all" instead of ["<modulePath>/..."]
308+
dirs map[string][]frontend.LocalModule // local modules to serve
309+
modCacheDir string // path to module cache, or ""
310+
proxy *proxy.Client // proxy client, or nil
254311
}
255312

256313
// buildGetters constructs module getters based on the given configuration.
@@ -263,8 +320,16 @@ func buildGetters(ctx context.Context, cfg getterConfig) ([]fetch.ModuleGetter,
263320
var getters []fetch.ModuleGetter
264321

265322
// Load local getters for each directory.
266-
for _, dir := range cfg.dirs {
267-
mg, err := fetch.NewGoPackagesModuleGetter(ctx, dir, cfg.pattern)
323+
for dir, modules := range cfg.dirs {
324+
var patterns []string
325+
if cfg.all {
326+
patterns = append(patterns, "all")
327+
} else {
328+
for _, m := range modules {
329+
patterns = append(patterns, fmt.Sprintf("%s/...", m))
330+
}
331+
}
332+
mg, err := fetch.NewGoPackagesModuleGetter(ctx, dir, patterns...)
268333
if err != nil {
269334
log.Errorf(ctx, "Loading packages from %s: %v", dir, err)
270335
} else {
@@ -291,16 +356,30 @@ func buildGetters(ctx context.Context, cfg getterConfig) ([]fetch.ModuleGetter,
291356
return getters, nil
292357
}
293358

294-
func newServer(getters []fetch.ModuleGetter, prox *proxy.Client) (*frontend.Server, error) {
359+
func newServer(getters []fetch.ModuleGetter, localModules []frontend.LocalModule, prox *proxy.Client) (*frontend.Server, error) {
295360
lds := fetchdatasource.Options{
296361
Getters: getters,
297362
ProxyClientForLatest: prox,
298363
BypassLicenseCheck: true,
299364
}.New()
365+
366+
// In dev mode, use a dirFS to pick up template/JS/CSS changes without
367+
// restarting the server.
368+
var staticFS fs.FS
369+
if *devMode {
370+
staticFS = os.DirFS(*staticFlag)
371+
} else {
372+
staticFS = static.FS
373+
}
374+
300375
server, err := frontend.NewServer(frontend.ServerConfig{
301376
DataSourceGetter: func(context.Context) internal.DataSource { return lds },
302377
TemplateFS: template.TrustedFSFromEmbed(static.FS),
303-
StaticFS: static.FS,
378+
StaticFS: staticFS,
379+
DevMode: *devMode,
380+
LocalMode: true,
381+
LocalModules: localModules,
382+
StaticPath: *staticFlag,
304383
ThirdPartyFS: thirdparty.FS,
305384
})
306385
if err != nil {

0 commit comments

Comments
 (0)