diff --git a/cmd/golangorg/handlers.go b/cmd/golangorg/handlers.go index d40c486a..1cafdcbd 100644 --- a/cmd/golangorg/handlers.go +++ b/cmd/golangorg/handlers.go @@ -87,6 +87,7 @@ func registerHandlers(pres *godoc.Presentation) *http.ServeMux { mux.Handle("/pkg/C/", redirect.Handler("/cmd/cgo/")) mux.HandleFunc("/fmt", fmtHandler) mux.Handle("/doc/devel/release.html", releaseHandler{ReleaseHistory: sortReleases(history.Releases)}) + handleRootAndSubtree(mux, "/project/", projectHandler{ReleaseHistory: sortMajorReleases(history.Releases)}, pres) redirect.Register(mux) http.Handle("/", hostEnforcerHandler{mux}) @@ -94,6 +95,26 @@ func registerHandlers(pres *godoc.Presentation) *http.ServeMux { return mux } +// handleRootAndSubtree registers a handler for the given pattern in mux. +// The handler selects between root or subtree handlers to handle requests. +// +// The root handler is used for requests with URL path equal to the pattern, +// and the subtree handler is used for all other requests matched by pattern. +// +// The pattern must have a trailing slash ('/'), otherwise handleRoot panics. +func handleRootAndSubtree(mux *http.ServeMux, path string, root, subtree http.Handler) { + if !strings.HasSuffix(path, "/") { + panic("handleRootAndSubtree must be used on patterns with a trailing slash ('/')") + } + mux.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) { + if req.URL.Path == path { + root.ServeHTTP(w, req) + } else { + subtree.ServeHTTP(w, req) + } + }) +} + func readTemplate(name string) *template.Template { if pres == nil { panic("no global Presentation set yet") diff --git a/cmd/golangorg/project.go b/cmd/golangorg/project.go new file mode 100644 index 00000000..dd5e8d36 --- /dev/null +++ b/cmd/golangorg/project.go @@ -0,0 +1,114 @@ +// Copyright 2020 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 ( + "bytes" + "fmt" + "html/template" + "log" + "net/http" + "sort" + + "golang.org/x/tools/godoc" + "golang.org/x/tools/godoc/vfs" + "golang.org/x/website/internal/history" +) + +// projectHandler serves The Go Project page. +type projectHandler struct { + ReleaseHistory []MajorRelease // Pre-computed release history to display. +} + +func (h projectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + const relPath = "doc/contrib.html" + + src, err := vfs.ReadFile(fs, relPath) + if err != nil { + log.Printf("reading template %s: %v", relPath, err) + pres.ServeError(w, req, relPath, err) + return + } + + meta, src, err := extractMetadata(src) + if err != nil { + log.Printf("decoding metadata %s: %v", relPath, err) + pres.ServeError(w, req, relPath, err) + return + } + if !meta.Template { + err := fmt.Errorf("got non-template, want template") + log.Printf("unexpected metadata %s: %v", relPath, err) + pres.ServeError(w, req, relPath, err) + return + } + + page := godoc.Page{ + Title: meta.Title, + Subtitle: meta.Subtitle, + GoogleCN: googleCN(req), + } + data := projectTemplateData{ + Major: h.ReleaseHistory, + } + + // Evaluate as HTML template. + tmpl, err := template.New("").Parse(string(src)) + if err != nil { + log.Printf("parsing template %s: %v", relPath, err) + pres.ServeError(w, req, relPath, err) + return + } + var buf bytes.Buffer + if err := tmpl.Execute(&buf, data); err != nil { + log.Printf("executing template %s: %v", relPath, err) + pres.ServeError(w, req, relPath, err) + return + } + src = buf.Bytes() + + page.Body = src + pres.ServePage(w, page) +} + +// sortMajorReleases returns a sorted list of major Go releases, +// suitable to be displayed on the Go project page. +func sortMajorReleases(rs map[history.GoVer]history.Release) []MajorRelease { + var major []MajorRelease + for v, r := range rs { + if !v.IsMajor() { + continue + } + major = append(major, MajorRelease{ver: v, rel: r}) + } + sort.Slice(major, func(i, j int) bool { + if major[i].ver.X != major[j].ver.X { + return major[i].ver.X > major[j].ver.X + } + return major[i].ver.Y > major[j].ver.Y + }) + return major +} + +type projectTemplateData struct { + Major []MajorRelease +} + +// MajorRelease represents a major Go release entry as displayed on the Go project page. +type MajorRelease struct { + ver history.GoVer + rel history.Release +} + +// V returns the Go release version string, like "1.14", "1.14.1", "1.14.2", etc. +func (r MajorRelease) V() string { + return r.ver.String() +} + +// Date returns the date of the release, formatted for display on the Go project page. +func (r MajorRelease) Date() string { + d := r.rel.Date + return fmt.Sprintf("%s %d", d.Month, d.Year) +} diff --git a/cmd/golangorg/regtest_test.go b/cmd/golangorg/regtest_test.go index 58127d15..e2359905 100644 --- a/cmd/golangorg/regtest_test.go +++ b/cmd/golangorg/regtest_test.go @@ -128,6 +128,16 @@ func TestLiveServer(t *testing.T) { Path: "/doc/devel/release.html", Regexp: `go1\.14\.2\s+\(released 2020/04/08\)\s+includes\s+fixes to cgo, the go command, the runtime,`, }, + { + Message: "Go project page has an entry for Go 1.14", + Path: "/project/", + Substring: `
A summary of the changes between Go releases. Notes for the major releases:
\x0aOnline\x20communities\x20include\x20people\x20from\x20many\x20different\x20backgrounds.\x0aThe\x20Go\x20contributors\x20are\x20committed\x20to\x20providing\x20a\x20friendly,\x20safe\x20and\x20welcoming\x0aenvironment\x20for\x20all,\x20regardless\x20of\x20gender\x20identity\x20and\x20expression,\x20sexual\x20orientation,\x0adisabilities,\x20neurodiversity,\x20physical\x20appearance,\x20body\x20size,\x20ethnicity,\x20nationality,\x0arace,\x20age,\x20religion,\x20or\x20similar\x20personal\x20characteristics.\x0a
\x0a\x0a\x0aThe\x20first\x20goal\x20of\x20the\x20Code\x20of\x20Conduct\x20is\x20to\x20specify\x20a\x20baseline\x20standard\x0aof\x20behavior\x20so\x20that\x20people\x20with\x20different\x20social\x20values\x20and\x20communication\x0astyles\x20can\x20talk\x20about\x20Go\x20effectively,\x20productively,\x20and\x20respectfully.\x0a
\x0a\x0a\x0aThe\x20second\x20goal\x20is\x20to\x20provide\x20a\x20mechanism\x20for\x20resolving\x20conflicts\x20in\x20the\x0acommunity\x20when\x20they\x20arise.\x0a
\x0a\x0a\x0aThe\x20third\x20goal\x20of\x20the\x20Code\x20of\x20Conduct\x20is\x20to\x20make\x20our\x20community\x20welcoming\x20to\x0apeople\x20from\x20different\x20backgrounds.\x0aDiversity\x20is\x20critical\x20to\x20the\x20project;\x20for\x20Go\x20to\x20be\x20successful,\x20it\x20needs\x0acontributors\x20and\x20users\x20from\x20all\x20backgrounds.\x0a(See\x20Go,\x20Open\x20Source,\x20Community.)\x0a
\x0a\x0a\x0aWe\x20believe\x20that\x20healthy\x20debate\x20and\x20disagreement\x20are\x20essential\x20to\x20a\x20healthy\x20project\x20and\x20community.\x0aHowever,\x20it\x20is\x20never\x20ok\x20to\x20be\x20disrespectful.\x0aWe\x20value\x20diverse\x20opinions,\x20but\x20we\x20value\x20respectful\x20behavior\x20more.\x0a
\x0a\x0a\x0aThese\x20are\x20the\x20values\x20to\x20which\x20people\x20in\x20the\x20Go\x20community\x20(\xe2\x80\x9cGophers\xe2\x80\x9d)\x20should\x20aspire.\x0a
\x0a\x0a\x0aPeople\x20are\x20complicated.\x0aYou\x20should\x20expect\x20to\x20be\x20misunderstood\x20and\x20to\x20misunderstand\x20others;\x0awhen\x20this\x20inevitably\x20occurs,\x20resist\x20the\x20urge\x20to\x20be\x20defensive\x20or\x20assign\x20blame.\x0aTry\x20not\x20to\x20take\x20offense\x20where\x20no\x20offense\x20was\x20intended.\x0aGive\x20people\x20the\x20benefit\x20of\x20the\x20doubt.\x0aEven\x20if\x20the\x20intent\x20was\x20to\x20provoke,\x20do\x20not\x20rise\x20to\x20it.\x0aIt\x20is\x20the\x20responsibility\x20of\x20all\x20parties\x20to\x20de-escalate\x20conflict\x20when\x20it\x20arises.\x0a
\x0a\x0aIn\x20the\x20interest\x20of\x20fostering\x20an\x20open\x20and\x20welcoming\x20environment,\x20we\x20as\x0acontributors\x20and\x20maintainers\x20pledge\x20to\x20making\x20participation\x20in\x20our\x20project\x20and\x0aour\x20community\x20a\x20harassment-free\x20experience\x20for\x20everyone,\x20regardless\x20of\x20age,\x20body\x0asize,\x20disability,\x20ethnicity,\x20gender\x20identity\x20and\x20expression,\x20level\x20of\x0aexperience,\x20education,\x20socio-economic\x20status,\x20nationality,\x20personal\x20appearance,\x0arace,\x20religion,\x20or\x20sexual\x20identity\x20and\x20orientation.
\x0a\x0aExamples\x20of\x20behavior\x20that\x20contributes\x20to\x20creating\x20a\x20positive\x20environment\x0ainclude:
\x0a\x0aExamples\x20of\x20unacceptable\x20behavior\x20by\x20participants\x20include:
\x0a\x0aProject\x20maintainers\x20are\x20responsible\x20for\x20clarifying\x20the\x20standards\x20of\x20acceptable\x0abehavior\x20and\x20are\x20expected\x20to\x20take\x20appropriate\x20and\x20fair\x20corrective\x20action\x20in\x0aresponse\x20to\x20any\x20instances\x20of\x20unacceptable\x20behavior.
\x0a\x0aProject\x20maintainers\x20have\x20the\x20right\x20and\x20responsibility\x20to\x20remove,\x20edit,\x20or\x20reject\x0acomments,\x20commits,\x20code,\x20wiki\x20edits,\x20issues,\x20and\x20other\x20contributions\x20that\x20are\x0anot\x20aligned\x20to\x20this\x20Code\x20of\x20Conduct,\x20or\x20to\x20ban\x20temporarily\x20or\x20permanently\x20any\x0acontributor\x20for\x20other\x20behaviors\x20that\x20they\x20deem\x20inappropriate,\x20threatening,\x0aoffensive,\x20or\x20harmful.
\x0a\x0aThis\x20Code\x20of\x20Conduct\x20applies\x20both\x20within\x20project\x20spaces\x20and\x20in\x20public\x20spaces\x0awhen\x20an\x20individual\x20is\x20representing\x20the\x20project\x20or\x20its\x20community.\x20Examples\x20of\x0arepresenting\x20a\x20project\x20or\x20community\x20include\x20using\x20an\x20official\x20project\x20e-mail\x0aaddress,\x20posting\x20via\x20an\x20official\x20social\x20media\x20account,\x20or\x20acting\x20as\x20an\x20appointed\x0arepresentative\x20at\x20an\x20online\x20or\x20offline\x20event.\x20Representation\x20of\x20a\x20project\x20may\x20be\x0afurther\x20defined\x20and\x20clarified\x20by\x20project\x20maintainers.
\x0a\x0aThis\x20Code\x20of\x20Conduct\x20also\x20applies\x20outside\x20the\x20project\x20spaces\x20when\x20the\x20Project\x0aStewards\x20have\x20a\x20reasonable\x20belief\x20that\x20an\x20individual’s\x20behavior\x20may\x20have\x20a\x0anegative\x20impact\x20on\x20the\x20project\x20or\x20its\x20community.
\x0a\x0aWe\x20do\x20not\x20believe\x20that\x20all\x20conflict\x20is\x20bad;\x20healthy\x20debate\x20and\x20disagreement\x0aoften\x20yield\x20positive\x20results.\x20However,\x20it\x20is\x20never\x20okay\x20to\x20be\x20disrespectful\x20or\x0ato\x20engage\x20in\x20behavior\x20that\x20violates\x20the\x20project\xe2\x80\x99s\x20code\x20of\x20conduct.
\x0a\x0aIf\x20you\x20see\x20someone\x20violating\x20the\x20code\x20of\x20conduct,\x20you\x20are\x20encouraged\x20to\x20address\x0athe\x20behavior\x20directly\x20with\x20those\x20involved.\x20Many\x20issues\x20can\x20be\x20resolved\x20quickly\x0aand\x20easily,\x20and\x20this\x20gives\x20people\x20more\x20control\x20over\x20the\x20outcome\x20of\x20their\x0adispute.\x20If\x20you\x20are\x20unable\x20to\x20resolve\x20the\x20matter\x20for\x20any\x20reason,\x20or\x20if\x20the\x0abehavior\x20is\x20threatening\x20or\x20harassing,\x20report\x20it.\x20We\x20are\x20dedicated\x20to\x20providing\x0aan\x20environment\x20where\x20participants\x20feel\x20welcome\x20and\x20safe.
\x0a\x0aReports\x20should\x20be\x20directed\x20to\x20Carmen\x20Andoh\x20and\x20Van\x20Riper,\x20the\x0aGo\x20Project\x20Stewards,\x20at\x20conduct@golang.org.\x0aIt\x20is\x20the\x20Project\x20Stewards\xe2\x80\x99\x20duty\x20to\x0areceive\x20and\x20address\x20reported\x20violations\x20of\x20the\x20code\x20of\x20conduct.\x20They\x20will\x20then\x0awork\x20with\x20a\x20committee\x20consisting\x20of\x20representatives\x20from\x20the\x20Open\x20Source\x0aPrograms\x20Office\x20and\x20the\x20Google\x20Open\x20Source\x20Strategy\x20team.\x20If\x20for\x20any\x20reason\x20you\x0aare\x20uncomfortable\x20reaching\x20out\x20the\x20Project\x20Stewards,\x20please\x20email\x0athe\x20Google\x20Open\x20Source\x20Programs\x20Office\x20at\x20opensource@google.com.
\x0a\x0aWe\x20will\x20investigate\x20every\x20complaint,\x20but\x20you\x20may\x20not\x20receive\x20a\x20direct\x20response.\x0aWe\x20will\x20use\x20our\x20discretion\x20in\x20determining\x20when\x20and\x20how\x20to\x20follow\x20up\x20on\x20reported\x0aincidents,\x20which\x20may\x20range\x20from\x20not\x20taking\x20action\x20to\x20permanent\x20expulsion\x20from\x0athe\x20project\x20and\x20project-sponsored\x20spaces.\x20We\x20will\x20notify\x20the\x20accused\x20of\x20the\x0areport\x20and\x20provide\x20them\x20an\x20opportunity\x20to\x20discuss\x20it\x20before\x20any\x20action\x20is\x20taken.\x0aThe\x20identity\x20of\x20the\x20reporter\x20will\x20be\x20omitted\x20from\x20the\x20details\x20of\x20the\x20report\x0asupplied\x20to\x20the\x20accused.\x20In\x20potentially\x20harmful\x20situations,\x20such\x20as\x20ongoing\x0aharassment\x20or\x20threats\x20to\x20anyone’s\x20safety,\x20we\x20may\x20take\x20action\x20without\x20notice.
\x0a\x0aThis\x20Code\x20of\x20Conduct\x20is\x20adapted\x20from\x20the\x20Contributor\x20Covenant,\x20version\x201.4,\x0aavailable\x20at\x0ahttps://www.contributor-covenant.org/version/1/4/code-of-conduct.html
\x0a\x0a\x0aGo\x20is\x20an\x20open\x20source\x20project\x20developed\x20by\x20a\x20team\x20at\x0aGoogle\x20and\x20many\x0acontributors\x20from\x20the\x20open\x20source\x20community.\x0a
\x0a\x0a\x0aGo\x20is\x20distributed\x20under\x20a\x20BSD-style\x20license.\x0a
\x0a\x0a\x0aA\x20low\x20traffic\x20mailing\x20list\x20for\x20important\x20announcements,\x20such\x20as\x20new\x20releases.\x0a
\x0a\x0aWe\x20encourage\x20all\x20Go\x20users\x20to\x20subscribe\x20to\x0agolang-announce.\x0a
\x0a\x0a\x0aA\x20summary\x20of\x20the\x20changes\x20between\x20Go\x20releases.\x20Notes\x20for\x20the\x20major\x20releases:
\x0a\x0a\x0aWhat\x20Go\x201\x20defines\x20and\x20the\x20backwards-compatibility\x20guarantees\x20one\x20can\x20expect\x20as\x0aGo\x201\x20matures.\x0a
\x0a\x0a\x0aCheck\x20out\x20the\x20Go\x20source\x20code.
\x0a\x0a\x0aA\x20mailing\x20list\x20for\x20general\x20discussion\x20of\x20Go\x20programming.\x0a
\x0a\x0aQuestions\x20about\x20using\x20Go\x20or\x20announcements\x20relevant\x20to\x20other\x20Go\x20users\x20should\x20be\x20sent\x20to\x0agolang-nuts.\x0a
\x0a\x0aThe\x20golang-dev\x0amailing\x20list\x20is\x20for\x20discussing\x20code\x20changes\x20to\x20the\x20Go\x20project.\x0aThe\x20golang-codereviews\x0amailing\x20list\x20is\x20for\x20actual\x20reviewing\x20of\x20the\x20code\x20changes\x20(CLs).
\x0a\x0aA\x20mailing\x20list\x20that\x20receives\x20a\x20message\x20summarizing\x20each\x20checkin\x20to\x20the\x20Go\x20repository.
\x0a\x0aView\x20the\x20status\x20of\x20Go\x20builds\x20across\x20the\x20supported\x20operating\x0asystems\x20and\x20architectures.
\x0a\x0a\x0a\x0aIf\x20you\x20spot\x20bugs,\x20mistakes,\x20or\x20inconsistencies\x20in\x20the\x20Go\x20project's\x20code\x20or\x0adocumentation,\x20please\x20let\x20us\x20know\x20by\x0afiling\x20a\x20ticket\x0aon\x20our\x20issue\x20tracker.\x0a(Of\x20course,\x20you\x20should\x20check\x20it's\x20not\x20an\x20existing\x20issue\x20before\x20creating\x0aa\x20new\x20one.)\x0a
\x0a\x0a\x0aWe\x20pride\x20ourselves\x20on\x20being\x20meticulous;\x20no\x20issue\x20is\x20too\x20small.\x0a
\x0a\x0a\x0aSecurity-related\x20issues\x20should\x20be\x20reported\x20to\x0asecurity@golang.org.
\x0aSee\x20the\x20security\x20policy\x20for\x20more\x20details.\x0a
\x0aCommunity-related\x20issues\x20should\x20be\x20reported\x20to\x0aconduct@golang.org.
\x0aSee\x20the\x20Code\x20of\x20Conduct\x20for\x20more\x20details.\x0a
\x0aGo\x20is\x20an\x20open\x20source\x20project\x20and\x20we\x20welcome\x20contributions\x20from\x20the\x20community.\x0a
\x0a\x0aTo\x20get\x20started,\x20read\x20these\x20contribution\x0aguidelines\x20for\x20information\x20on\x20design,\x20testing,\x20and\x20our\x20code\x20review\x20process.\x0a
\x0a\x0aCheck\x20the\x20tracker\x20for\x0aopen\x20issues\x20that\x20interest\x20you.\x20Those\x20labeled\x0ahelp\x20wanted\x0aare\x20particularly\x20in\x20need\x20of\x20outside\x20help.\x0a
\x0a", + "doc/contrib.html": "\x0a\x0a\x0a\x0a\x0a\x0a\x0aGo\x20is\x20an\x20open\x20source\x20project\x20developed\x20by\x20a\x20team\x20at\x0aGoogle\x20and\x20many\x0acontributors\x20from\x20the\x20open\x20source\x20community.\x0a
\x0a\x0a\x0aGo\x20is\x20distributed\x20under\x20a\x20BSD-style\x20license.\x0a
\x0a\x0a\x0aA\x20low\x20traffic\x20mailing\x20list\x20for\x20important\x20announcements,\x20such\x20as\x20new\x20releases.\x0a
\x0a\x0aWe\x20encourage\x20all\x20Go\x20users\x20to\x20subscribe\x20to\x0agolang-announce.\x0a
\x0a\x0a\x0aA\x20summary\x20of\x20the\x20changes\x20between\x20Go\x20releases.\x20Notes\x20for\x20the\x20major\x20releases:
\x0a\x0a\x0aWhat\x20Go\x201\x20defines\x20and\x20the\x20backwards-compatibility\x20guarantees\x20one\x20can\x20expect\x20as\x0aGo\x201\x20matures.\x0a
\x0a\x0a\x0aCheck\x20out\x20the\x20Go\x20source\x20code.
\x0a\x0a\x0aA\x20mailing\x20list\x20for\x20general\x20discussion\x20of\x20Go\x20programming.\x0a
\x0a\x0aQuestions\x20about\x20using\x20Go\x20or\x20announcements\x20relevant\x20to\x20other\x20Go\x20users\x20should\x20be\x20sent\x20to\x0agolang-nuts.\x0a
\x0a\x0aThe\x20golang-dev\x0amailing\x20list\x20is\x20for\x20discussing\x20code\x20changes\x20to\x20the\x20Go\x20project.\x0aThe\x20golang-codereviews\x0amailing\x20list\x20is\x20for\x20actual\x20reviewing\x20of\x20the\x20code\x20changes\x20(CLs).
\x0a\x0aA\x20mailing\x20list\x20that\x20receives\x20a\x20message\x20summarizing\x20each\x20checkin\x20to\x20the\x20Go\x20repository.
\x0a\x0aView\x20the\x20status\x20of\x20Go\x20builds\x20across\x20the\x20supported\x20operating\x0asystems\x20and\x20architectures.
\x0a\x0a\x0a\x0aIf\x20you\x20spot\x20bugs,\x20mistakes,\x20or\x20inconsistencies\x20in\x20the\x20Go\x20project's\x20code\x20or\x0adocumentation,\x20please\x20let\x20us\x20know\x20by\x0afiling\x20a\x20ticket\x0aon\x20our\x20issue\x20tracker.\x0a(Of\x20course,\x20you\x20should\x20check\x20it's\x20not\x20an\x20existing\x20issue\x20before\x20creating\x0aa\x20new\x20one.)\x0a
\x0a\x0a\x0aWe\x20pride\x20ourselves\x20on\x20being\x20meticulous;\x20no\x20issue\x20is\x20too\x20small.\x0a
\x0a\x0a\x0aSecurity-related\x20issues\x20should\x20be\x20reported\x20to\x0asecurity@golang.org.
\x0aSee\x20the\x20security\x20policy\x20for\x20more\x20details.\x0a
\x0aCommunity-related\x20issues\x20should\x20be\x20reported\x20to\x0aconduct@golang.org.
\x0aSee\x20the\x20Code\x20of\x20Conduct\x20for\x20more\x20details.\x0a
\x0aGo\x20is\x20an\x20open\x20source\x20project\x20and\x20we\x20welcome\x20contributions\x20from\x20the\x20community.\x0a
\x0a\x0aTo\x20get\x20started,\x20read\x20these\x20contribution\x0aguidelines\x20for\x20information\x20on\x20design,\x20testing,\x20and\x20our\x20code\x20review\x20process.\x0a
\x0a\x0aCheck\x20the\x20tracker\x20for\x0aopen\x20issues\x20that\x20interest\x20you.\x20Those\x20labeled\x0ahelp\x20wanted\x0aare\x20particularly\x20in\x20need\x20of\x20outside\x20help.\x0a
\x0a", "doc/copyright.html": "\x0a\x0a\x0a\x20\x20Except\x20as\x0a\x20\x20noted,\x20the\x20contents\x20of\x20this\x0a\x20\x20site\x20are\x20licensed\x20under\x20the\x0a\x20\x20Creative\x20Commons\x20Attribution\x203.0\x20License,\x0a\x20\x20and\x20code\x20is\x20licensed\x20under\x20a\x20BSD\x20license.\x0a
\x0a",