1
2
3
4
5
6
7 package doc
8
9 import (
10 "errors"
11 "fmt"
12 "net"
13 "net/url"
14 "os"
15 "os/exec"
16 "os/signal"
17 "path/filepath"
18 "strings"
19 )
20
21
22
23
24
25 func pickUnusedPort() (int, error) {
26 l, err := net.Listen("tcp", "localhost:0")
27 if err != nil {
28 return 0, err
29 }
30 port := l.Addr().(*net.TCPAddr).Port
31 if err := l.Close(); err != nil {
32 return 0, err
33 }
34 return port, nil
35 }
36
37 func doPkgsite(urlPath, fragment string) error {
38 port, err := pickUnusedPort()
39 if err != nil {
40 return fmt.Errorf("failed to find port for documentation server: %v", err)
41 }
42 addr := fmt.Sprintf("localhost:%d", port)
43 path, err := url.JoinPath("http://"+addr, urlPath)
44 if err != nil {
45 return fmt.Errorf("internal error: failed to construct url: %v", err)
46 }
47 if fragment != "" {
48 path += "#" + fragment
49 }
50
51
52
53
54 signal.Ignore(signalsToIgnore...)
55
56
57 env := os.Environ()
58 vars, err := runCmd(env, goCmd(), "env", "GOPROXY", "GOMODCACHE")
59 fields := strings.Fields(vars)
60 if err == nil && len(fields) == 2 {
61 goproxy, gomodcache := fields[0], fields[1]
62 gomodcache = filepath.Join(gomodcache, "cache", "download")
63
64
65
66 if strings.HasPrefix(gomodcache, "/") {
67 gomodcache = "file://" + gomodcache
68 } else {
69 gomodcache = "file:///" + filepath.ToSlash(gomodcache)
70 }
71 env = append(env, "GOPROXY="+gomodcache+","+goproxy)
72 }
73
74 const version = "v0.0.0-20250714212547-01b046e81fe7"
75 cmd := exec.Command(goCmd(), "run", "golang.org/x/pkgsite/cmd/internal/doc@"+version,
76 "-gorepo", buildCtx.GOROOT,
77 "-http", addr,
78 "-open", path)
79 cmd.Env = env
80 cmd.Stdout = os.Stderr
81 cmd.Stderr = os.Stderr
82
83 if err := cmd.Run(); err != nil {
84 var ee *exec.ExitError
85 if errors.As(err, &ee) {
86
87
88
89
90 os.Exit(ee.ExitCode())
91 }
92 return err
93 }
94
95 return nil
96 }
97
View as plain text