From 66996d0d915b7617315a40a98cc7c546362dc95e Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 16 Feb 2021 21:26:23 -0500 Subject: [PATCH] cmd/golangorg: simplify local vs prod programs There was too much duplicated code between main.go and appinit.go and too many build-tagged-out files. Make main.go the func main for both prod and local. Introduce local.go, merging dl.go and play.go. Introduce prod.go, holding the prod-specific bits of appinit.go (the rest are in main.go). Rename the build tag to prod instead of golangorg (the whole program is golangorg; it's very confusing). Fixes golang/go#41102. Change-Id: I261ce8e9171110f01798025f8218ce9f8253af81 Reviewed-on: https://go-review.googlesource.com/c/website/+/293413 Trust: Russ Cox Reviewed-by: Dmitri Shuralyov --- README.md | 2 +- cmd/golangorg/Dockerfile.prod | 2 +- cmd/golangorg/README.md | 5 +- cmd/golangorg/blog.go | 3 + cmd/golangorg/codewalk.go | 3 + cmd/golangorg/dl.go | 16 ----- cmd/golangorg/go115.go | 1 + cmd/golangorg/godoc.go | 3 + cmd/golangorg/godoc_test.go | 9 +-- cmd/golangorg/goroot.go | 74 -------------------- cmd/golangorg/handlers.go | 9 +-- cmd/golangorg/local.go | 40 +++++++++++ cmd/golangorg/main.go | 97 ++++++--------------------- cmd/golangorg/play.go | 11 --- cmd/golangorg/{appinit.go => prod.go} | 72 +++----------------- cmd/golangorg/project.go | 3 + cmd/golangorg/regtest_test.go | 3 + cmd/golangorg/release.go | 3 + cmd/golangorg/x.go | 3 + cmd/golangorg/x_test.go | 3 + content.go | 5 ++ 21 files changed, 107 insertions(+), 260 deletions(-) delete mode 100644 cmd/golangorg/dl.go delete mode 100644 cmd/golangorg/goroot.go create mode 100644 cmd/golangorg/local.go delete mode 100644 cmd/golangorg/play.go rename cmd/golangorg/{appinit.go => prod.go} (53%) diff --git a/README.md b/README.md index 17d62766..ec3d08e1 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Content is in _content/. Server code is in cmd/ and internal/. To run the server to preview local content changes, use: - go run ./cmd/golangorg -a + go run ./cmd/golangorg The supporting programs cmd/admingolangorg and cmd/googlegolangorg are the servers for admin.golang.org and google.golang.org. diff --git a/cmd/golangorg/Dockerfile.prod b/cmd/golangorg/Dockerfile.prod index 6c229807..e60c99ef 100644 --- a/cmd/golangorg/Dockerfile.prod +++ b/cmd/golangorg/Dockerfile.prod @@ -21,7 +21,7 @@ COPY . /website WORKDIR /website/cmd/golangorg -RUN go build -o /golangorg -tags=golangorg golang.org/x/website/cmd/golangorg +RUN go build -o /golangorg -tags=prod golang.org/x/website/cmd/golangorg # Clean up goroot for the final image. RUN cd /goroot && git clean -xdf diff --git a/cmd/golangorg/README.md b/cmd/golangorg/README.md index 228b62f9..233e6051 100644 --- a/cmd/golangorg/README.md +++ b/cmd/golangorg/README.md @@ -16,10 +16,9 @@ To run in production mode locally, you need: * Godoc sources inside $GOPATH (`go get -d golang.org/x/website/cmd/golangorg`) -Build with the `golangorg` tag and run: +Run with the `prod` tag: - go build -tags golangorg - ./golangorg + go run -tags prod . In production mode it serves on localhost:8080 (not 6060). The port is controlled by $PORT, as in: diff --git a/cmd/golangorg/blog.go b/cmd/golangorg/blog.go index 219fec82..e9edade6 100644 --- a/cmd/golangorg/blog.go +++ b/cmd/golangorg/blog.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.16 +// +build go1.16 + package main import ( diff --git a/cmd/golangorg/codewalk.go b/cmd/golangorg/codewalk.go index 91c8cdc2..961b0088 100644 --- a/cmd/golangorg/codewalk.go +++ b/cmd/golangorg/codewalk.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.16 +// +build go1.16 + // The /doc/codewalk/ tree is synthesized from codewalk descriptions, // files named $GOROOT/doc/codewalk/*.xml. // For an example and a description of the format, see diff --git a/cmd/golangorg/dl.go b/cmd/golangorg/dl.go deleted file mode 100644 index edeecb8a..00000000 --- a/cmd/golangorg/dl.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !golangorg - -package main - -import "net/http" - -// Register a redirect handler for /dl/ to the golang.org download page. -// This file will not be included when deploying godoc to golang.org. - -func init() { - http.Handle("/dl/", http.RedirectHandler("https://golang.org/dl/", http.StatusFound)) -} diff --git a/cmd/golangorg/go115.go b/cmd/golangorg/go115.go index c53a808d..38174dc7 100644 --- a/cmd/golangorg/go115.go +++ b/cmd/golangorg/go115.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !go1.16 // +build !go1.16 package main diff --git a/cmd/golangorg/godoc.go b/cmd/golangorg/godoc.go index 74a89786..54b71a37 100644 --- a/cmd/golangorg/godoc.go +++ b/cmd/golangorg/godoc.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.16 +// +build go1.16 + package main import ( diff --git a/cmd/golangorg/godoc_test.go b/cmd/golangorg/godoc_test.go index 6afe551b..1e184368 100644 --- a/cmd/golangorg/godoc_test.go +++ b/cmd/golangorg/godoc_test.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.16 // +build go1.16 package main_test @@ -77,14 +78,6 @@ func waitForServerReady(t *testing.T, addr string) { false) } -func waitForSearchReady(t *testing.T, addr string) { - waitForServer(t, - fmt.Sprintf("http://%v/search?q=FALLTHROUGH", addr), - "The list of tokens.", - 2*time.Minute, - false) -} - func waitUntilScanComplete(t *testing.T, addr string) { waitForServer(t, fmt.Sprintf("http://%v/pkg", addr), diff --git a/cmd/golangorg/goroot.go b/cmd/golangorg/goroot.go deleted file mode 100644 index 998e8693..00000000 --- a/cmd/golangorg/goroot.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "os" - "path/filepath" - "runtime" -) - -// Copies of functions from src/cmd/go/internal/cfg/cfg.go for -// finding the GOROOT. -// Keep them in sync until support is moved to a common place, if ever. - -func findGOROOT() string { - if env := os.Getenv("GOROOT"); env != "" { - return filepath.Clean(env) - } - def := filepath.Clean(runtime.GOROOT()) - if runtime.Compiler == "gccgo" { - // gccgo has no real GOROOT, and it certainly doesn't - // depend on the executable's location. - return def - } - exe, err := os.Executable() - if err == nil { - exe, err = filepath.Abs(exe) - if err == nil { - if dir := filepath.Join(exe, "../.."); isGOROOT(dir) { - // If def (runtime.GOROOT()) and dir are the same - // directory, prefer the spelling used in def. - if isSameDir(def, dir) { - return def - } - return dir - } - exe, err = filepath.EvalSymlinks(exe) - if err == nil { - if dir := filepath.Join(exe, "../.."); isGOROOT(dir) { - if isSameDir(def, dir) { - return def - } - return dir - } - } - } - } - return def -} - -// isGOROOT reports whether path looks like a GOROOT. -// -// It does this by looking for the path/pkg/tool directory, -// which is necessary for useful operation of the cmd/go tool, -// and is not typically present in a GOPATH. -func isGOROOT(path string) bool { - stat, err := os.Stat(filepath.Join(path, "pkg", "tool")) - if err != nil { - return false - } - return stat.IsDir() -} - -// isSameDir reports whether dir1 and dir2 are the same directory. -func isSameDir(dir1, dir2 string) bool { - if dir1 == dir2 { - return true - } - info1, err1 := os.Stat(dir1) - info2, err2 := os.Stat(dir2) - return err1 == nil && err2 == nil && os.SameFile(info1, info2) -} diff --git a/cmd/golangorg/handlers.go b/cmd/golangorg/handlers.go index 1cafdcbd..57c6cc53 100644 --- a/cmd/golangorg/handlers.go +++ b/cmd/golangorg/handlers.go @@ -2,13 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// The /doc/codewalk/ tree is synthesized from codewalk descriptions, -// files named $GOROOT/doc/codewalk/*.xml. -// For an example and a description of the format, see -// http://golang.org/doc/codewalk/codewalk or run godoc -http=:6060 -// and see http://localhost:6060/doc/codewalk/codewalk . -// That page is itself a codewalk; the source code for it is -// $GOROOT/doc/codewalk/codewalk.xml. +//go:build go1.16 +// +build go1.16 package main diff --git a/cmd/golangorg/local.go b/cmd/golangorg/local.go new file mode 100644 index 00000000..ea32c54c --- /dev/null +++ b/cmd/golangorg/local.go @@ -0,0 +1,40 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.16 && !prod +// +build go1.16,!prod + +package main + +import ( + "fmt" + "log" + "net/http" + "os" + "path/filepath" + "runtime" + + // This package registers "/compile" and "/share" handlers + // that redirect to the golang.org playground. + _ "golang.org/x/tools/playground" +) + +func earlySetup() { + _, file, _, ok := runtime.Caller(0) + if !ok { + fmt.Fprintln(os.Stderr, "runtime.Caller failed: cannot find templates for -a mode.") + os.Exit(2) + } + dir := filepath.Join(file, "../../../_content") + if _, err := os.Stat(filepath.Join(dir, "godoc.html")); err != nil { + log.Printf("warning: cannot find template dir; using embedded copy") + return + } + *templateDir = dir +} + +func lateSetup(mux *http.ServeMux) { + // Register a redirect handler for /dl/ to the golang.org download page. + http.Handle("/dl/", http.RedirectHandler("https://golang.org/dl/", http.StatusFound)) +} diff --git a/cmd/golangorg/main.go b/cmd/golangorg/main.go index d6534fe8..3faf6634 100644 --- a/cmd/golangorg/main.go +++ b/cmd/golangorg/main.go @@ -15,22 +15,16 @@ // https://golang.org/pkg/compress/zlib) // -// Some pages are being transitioned from $GOROOT to content/doc. -// See golang.org/issue/29206 and golang.org/issue/33637. - +//go:build go1.16 // +build go1.16 -// +build !golangorg package main import ( - _ "expvar" // to serve /debug/vars "flag" "fmt" - "go/build" "log" "net/http" - _ "net/http/pprof" // to serve /debug/pprof/* "os" "path/filepath" "regexp" @@ -42,39 +36,19 @@ import ( "golang.org/x/website" ) -const defaultAddr = "localhost:6060" // default webserver address - var ( - // network - httpAddr = flag.String("http", defaultAddr, "HTTP service address") - - verbose = flag.Bool("v", false, "verbose mode") - - // file system roots - // TODO(gri) consider the invariant that goroot always end in '/' - goroot = flag.String("goroot", findGOROOT(), "Go root directory") - - // layout control - autoFlag = flag.Bool("a", false, "update templates automatically") + httpAddr = flag.String("http", "localhost:6060", "HTTP service address") + verbose = flag.Bool("v", false, "verbose mode") + goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory") showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings") templateDir = flag.String("templates", "", "load templates/JS/CSS from disk in this directory (usually /path-to-website/content)") - showPlayground = flag.Bool("play", false, "enable playground") + showPlayground = flag.Bool("play", true, "enable playground") declLinks = flag.Bool("links", true, "link identifiers to their declarations") - - // source code notes - notesRx = flag.String("notes", "BUG", "regular expression matching note markers to show") + notesRx = flag.String("notes", "BUG", "regular expression matching note markers to show") ) -func getFullPath(relPath string) string { - gopath := os.Getenv("GOPATH") - if gopath == "" { - gopath = build.Default.GOPATH - } - return gopath + relPath -} - func usage() { - fmt.Fprintf(os.Stderr, "usage: golangorg -http="+defaultAddr+"\n") + fmt.Fprintf(os.Stderr, "usage: golangorg\n") flag.PrintDefaults() os.Exit(2) } @@ -86,37 +60,12 @@ func loggingHandler(h http.Handler) http.Handler { }) } -func initCorpus(corpus *godoc.Corpus) { - err := corpus.Init() - if err != nil { - log.Fatal(err) - } -} - func main() { + earlySetup() + flag.Usage = usage flag.Parse() - // Find templates in -a mode. - if *autoFlag { - if *templateDir != "" { - fmt.Fprintln(os.Stderr, "Cannot use -a and -templates together.") - usage() - } - _, file, _, ok := runtime.Caller(0) - if !ok { - fmt.Fprintln(os.Stderr, "runtime.Caller failed: cannot find templates for -a mode.") - os.Exit(2) - } - dir := filepath.Join(file, "../../../_content") - if _, err := os.Stat(filepath.Join(dir, "godoc.html")); err != nil { - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, "Cannot find templates for -a mode.") - os.Exit(2) - } - *templateDir = dir - } - playEnabled = *showPlayground // Check usage. @@ -129,14 +78,12 @@ func main() { usage() } - // Set the resolved goroot. - vfs.GOROOT = *goroot - fsGate := make(chan bool, 20) // Determine file system to use. rootfs := gatefs.New(vfs.OS(*goroot), fsGate) fs.Bind("/", rootfs, "/", vfs.BindReplace) + // Try serving files in /doc from a local copy before trying the main // go repository. This lets us update some documentation outside the // Go release cycle. This includes root.html, which redirects to "/". @@ -144,25 +91,22 @@ func main() { if *templateDir != "" { fs.Bind("/doc", vfs.OS(*templateDir), "/doc", vfs.BindBefore) fs.Bind("/lib/godoc", vfs.OS(*templateDir), "/", vfs.BindBefore) + root := filepath.Join(*templateDir, "..") + fs.Bind("/robots.txt", vfs.OS(root), "/robots.txt", vfs.BindBefore) + fs.Bind("/favicon.ico", vfs.OS(root), "/favicon.ico", vfs.BindBefore) } else { fs.Bind("/doc", vfs.FromFS(website.Content), "/doc", vfs.BindBefore) fs.Bind("/lib/godoc", vfs.FromFS(website.Content), "/", vfs.BindReplace) + fs.Bind("/robots.txt", vfs.FromFS(website.Root), "/robots.txt", vfs.BindBefore) + fs.Bind("/favicon.ico", vfs.FromFS(website.Root), "/favicon.ico", vfs.BindBefore) } - // Bind $GOPATH trees into Go root. - for _, p := range filepath.SplitList(build.Default.GOPATH) { - fs.Bind("/src", gatefs.New(vfs.OS(p), fsGate), "/src", vfs.BindAfter) - } - - webroot := getFullPath("/src/golang.org/x/website") - fs.Bind("/robots.txt", gatefs.New(vfs.OS(webroot), fsGate), "/robots.txt", vfs.BindBefore) - fs.Bind("/favicon.ico", gatefs.New(vfs.OS(webroot), fsGate), "/favicon.ico", vfs.BindBefore) - corpus := godoc.NewCorpus(fs) corpus.Verbose = *verbose - - go initCorpus(corpus) - + corpus.IndexEnabled = false + if err := corpus.Init(); err != nil { + log.Fatal(err) + } // Initialize the version info before readTemplates, which saves // the map value in a method value. corpus.InitVersionInfo() @@ -176,7 +120,8 @@ func main() { } readTemplates(pres) - registerHandlers(pres) + mux := registerHandlers(pres) + lateSetup(mux) var handler http.Handler = http.DefaultServeMux if *verbose { diff --git a/cmd/golangorg/play.go b/cmd/golangorg/play.go deleted file mode 100644 index f44a3ccc..00000000 --- a/cmd/golangorg/play.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !golangorg - -package main - -// This package registers "/compile" and "/share" handlers -// that redirect to the golang.org playground. -import _ "golang.org/x/tools/playground" diff --git a/cmd/golangorg/appinit.go b/cmd/golangorg/prod.go similarity index 53% rename from cmd/golangorg/appinit.go rename to cmd/golangorg/prod.go index 9dc40763..e2b83281 100644 --- a/cmd/golangorg/appinit.go +++ b/cmd/golangorg/prod.go @@ -2,29 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build go1.16 -// +build golangorg +//go:build go1.16 && prod +// +build go1.16,prod package main -// This file replaces main.go when running golangorg under App Engine. -// See README.md for details. - import ( "context" - "go/build" "io" "log" "net/http" "os" - "regexp" - "runtime" "strings" - "golang.org/x/tools/godoc" - "golang.org/x/tools/godoc/vfs" - "golang.org/x/tools/godoc/vfs/gatefs" - "golang.org/x/website" "golang.org/x/website/internal/dl" "golang.org/x/website/internal/proxy" "golang.org/x/website/internal/redirect" @@ -34,51 +24,22 @@ import ( "golang.org/x/website/internal/memcache" ) -func main() { +func earlySetup() { log.SetFlags(log.Lshortfile | log.LstdFlags) - - playEnabled = true - log.Println("initializing golang.org server ...") - fsGate := make(chan bool, 20) - - rootfs := gatefs.New(vfs.OS(runtime.GOROOT()), fsGate) - fs.Bind("/", rootfs, "/", vfs.BindReplace) - - // Try serving files in /doc from a local copy before trying the main - // go repository. This lets us update some documentation outside the - // Go release cycle. This includes root.html, which redirects to "/". - // See golang.org/issue/29206. - fs.Bind("/doc", vfs.FromFS(website.Content), "/doc", vfs.BindBefore) - fs.Bind("/lib/godoc", vfs.FromFS(website.Content), "/", vfs.BindReplace) - - webroot := getFullPath("/src/golang.org/x/website") - fs.Bind("/favicon.ico", gatefs.New(vfs.OS(webroot), fsGate), "/favicon.ico", vfs.BindBefore) - - corpus := godoc.NewCorpus(fs) - corpus.Verbose = false - corpus.MaxResults = 10000 // matches flag default in main.go - corpus.IndexEnabled = false - if err := corpus.Init(); err != nil { - log.Fatal(err) + port := "8080" + if p := os.Getenv("PORT"); p != "" { + port = p } - corpus.InitVersionInfo() + *httpAddr = ":" + port +} - pres = godoc.NewPresentation(corpus) - pres.ShowPlayground = true - pres.DeclLinks = true - pres.NotesRx = regexp.MustCompile("BUG") +func lateSetup(mux *http.ServeMux) { pres.GoogleAnalytics = os.Getenv("GOLANGORG_ANALYTICS") - readTemplates(pres) - datastoreClient, memcacheClient := getClients() - // NOTE(cbro): registerHandlers registers itself against DefaultServeMux. - // The mux returned has host enforcement, so it's important to register - // against this mux and not DefaultServeMux. - mux := registerHandlers(pres) dl.RegisterHandlers(mux, datastoreClient, memcacheClient) short.RegisterHandlers(mux, datastoreClient, memcacheClient) @@ -100,21 +61,6 @@ func main() { } log.Println("godoc initialization complete") - - // TODO(cbro): add instrumentation via opencensus. - port := "8080" - if p := os.Getenv("PORT"); p != "" { // PORT is set by GAE flex. - port = p - } - log.Fatal(http.ListenAndServe(":"+port, nil)) -} - -func getFullPath(relPath string) string { - gopath := os.Getenv("GOPATH") - if gopath == "" { - gopath = build.Default.GOPATH - } - return gopath + relPath } func getClients() (*datastore.Client, *memcache.Client) { diff --git a/cmd/golangorg/project.go b/cmd/golangorg/project.go index dd5e8d36..ca573828 100644 --- a/cmd/golangorg/project.go +++ b/cmd/golangorg/project.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.16 +// +build go1.16 + package main import ( diff --git a/cmd/golangorg/regtest_test.go b/cmd/golangorg/regtest_test.go index 84e83a5a..28c40f1b 100644 --- a/cmd/golangorg/regtest_test.go +++ b/cmd/golangorg/regtest_test.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.16 +// +build go1.16 + // Regression tests to run against a production instance of godoc. package main_test diff --git a/cmd/golangorg/release.go b/cmd/golangorg/release.go index ea62317c..0c5d9d19 100644 --- a/cmd/golangorg/release.go +++ b/cmd/golangorg/release.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.16 +// +build go1.16 + package main import ( diff --git a/cmd/golangorg/x.go b/cmd/golangorg/x.go index 1b3de3fe..af2a0686 100644 --- a/cmd/golangorg/x.go +++ b/cmd/golangorg/x.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.16 +// +build go1.16 + // This file contains the handlers that serve go-import redirects for Go // sub-repositories. It specifies the mapping from import paths like // "golang.org/x/tools" to the actual repository locations. diff --git a/cmd/golangorg/x_test.go b/cmd/golangorg/x_test.go index a97dda95..1cd5afb3 100644 --- a/cmd/golangorg/x_test.go +++ b/cmd/golangorg/x_test.go @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.16 +// +build go1.16 + package main import ( diff --git a/content.go b/content.go index b3b72454..58646fb1 100644 --- a/content.go +++ b/content.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.16 // +build go1.16 // Package website exports the static content as an embed.FS. @@ -25,3 +26,7 @@ func subdir(fsys fs.FS, path string) fs.FS { } return s } + +// Root is the website root files: favicon.ico and robots.txt. +//go:embed favicon.ico robots.txt +var Root embed.FS