Skip to content

Commit e2dd66f

Browse files
committed
_content: serve wiki pages on go.dev
The wiki pages have been copied to go.googlesource.com/wiki and will be code reviewed using Gerrit, like other Go repos. Unlike other Go repos, self-review will be permitted, and there is no requirement for Googlers to be involved to make a change. These relaxations are possible because the wiki has no production code. The set of wiki +2'ers is a superset of the usual code +2'ers. Fixes golang/go#61940. Change-Id: I01823720091fbaa24e95e9c82abeaadba867a17c Reviewed-on: https://go-review.googlesource.com/c/website/+/518297 Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 76936b1 commit e2dd66f

File tree

13 files changed

+169
-27
lines changed

13 files changed

+169
-27
lines changed

_content/css/styles.css

+6
Original file line numberDiff line numberDiff line change
@@ -5370,3 +5370,9 @@ a.downloadBox:hover .filename span {
53705370
display: none;
53715371
}
53725372
}
5373+
5374+
hr {
5375+
border: none;
5376+
border-top: 1px solid var(--color-border);
5377+
height: 1px;
5378+
}

_content/site.tmpl

+10-3
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
<a href="{{if .children}}#{{else}}{{.url}}{{end}}" {{if .children}} class="js-desktop-menu-hover"{{end}} aria-label={{.name}} aria-describedby="dropdown-description">
7272
{{.name}} {{if .children}}<i class="material-icons" aria-hidden="true">arrow_drop_down</i>{{end}}
7373
</a>
74-
<div class="screen-reader-only" id="dropdown-description" hidden>
74+
<div class="screen-reader-only" id="dropdown-description" hidden>
7575
Press Enter to activate/deactivate dropdown
7676
</div>
7777
{{- if .children}}
@@ -270,7 +270,7 @@
270270
<script async src="/js/copypaste.js"></script>
271271
</footer>
272272
<section class="Cookie-notice js-cookieNotice">
273-
<div>go.dev uses cookies from Google to deliver and enhance the quality of its services and to
273+
<div>go.dev uses cookies from Google to deliver and enhance the quality of its services and to
274274
analyze traffic. <a target=_blank href="https://policies.google.com/technologies/cookies">Learn more.</a></div>
275275
<div><button class="go-Button">Okay</button></div>
276276
</section>
@@ -438,7 +438,7 @@
438438
{{end}}
439439

440440
{{if .title}}
441-
<h1>{{.title}}</h1>
441+
<h1>{{if strings.HasPrefix .URL "/wiki/"}}Go Wiki: {{end}}{{.title}}</h1>
442442
{{else if eq .layout "error"}}
443443
<h1>Error</h1>
444444
{{else if eq .layout "dir"}}
@@ -471,6 +471,13 @@
471471
{{end}}
472472
</div>
473473
{{end}}
474+
475+
{{if strings.HasPrefix .URL "/wiki/"}}
476+
<hr>
477+
<p>
478+
<i>This content is part of the <a href="/wiki/">Go Wiki</a>.</i>
479+
</p>
480+
{{end}}
474481
</article>
475482

476483
{{end}}

_content/wiki/Comments.md

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
title: Comments
3+
---
4+
5+
<!--
6+
This is just a placeholder page for enabling a test.
7+
In the deployed site it is overwritten with the content of go.googlesource.com/wiki.
8+
-->
9+
10+
Every package should have a package comment. It should immediately precede the ` package ` statement in one of the files in the package. (It only needs to appear in one file.) It should begin with a single sentence that begins "Package _packagename_" and give a concise summary of the package functionality. This introductory sentence will be used in godoc's list of all packages.
11+
12+
Subsequent sentences and/or paragraphs can give more details. Sentences should be properly punctuated.
13+
14+
```go
15+
// Package superman implements methods for saving the world.
16+
//
17+
// Experience has shown that a small number of procedures can prove
18+
// helpful when attempting to save the world.
19+
package superman
20+
```
21+
22+
Nearly every top-level type, const, var and func should have a comment. A comment for bar should be in the form "_bar_ floats on high o'er vales and hills.". The first letter of _bar_ should not be capitalized unless it's capitalized in the code.
23+
24+
```go
25+
// enterOrbit causes Superman to fly into low Earth orbit, a position
26+
// that presents several possibilities for planet salvation.
27+
func enterOrbit() os.Error {
28+
...
29+
}
30+
```
31+
32+
All text that you indent inside a comment, godoc will render as a pre-formatted block. This facilitates code samples.
33+
34+
```go
35+
// fight can be used on any enemy and returns whether Superman won.
36+
//
37+
// Examples:
38+
//
39+
// fight("a random potato")
40+
// fight(LexLuthor{})
41+
//
42+
func fight(enemy interface{}) bool {
43+
// This is testing proper escaping in the wiki.
44+
for i := 0; i < 10; i++ {
45+
println("fight!")
46+
}
47+
}
48+
```
49+
50+

_content/wiki/default.tmpl

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{{define "layout"}}
2+
{{doclayout .}}
3+
{{end}}

_content/wiki/index.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
title: Index
3+
---
4+
5+
The Wiki is still loading...

cmd/golangorg/cloudbuild.yaml

+15
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,36 @@
33
# Do not run directly.
44

55
steps:
6+
# Clone go repo to _goroot.zip for use by uploaded app.
67
- name: gcr.io/cloud-builders/git
78
args: ["clone", "--branch=release-branch.go1.21", "--depth=1", "https://go.googlesource.com/go", "_gotmp"]
89
- name: gcr.io/cloud-builders/git
910
args: ["archive", "--format=zip", "--output=../_goroot.zip", "HEAD"]
1011
dir: _gotmp
1112
- name: golang
1213
args: ["rm", "-rf", "_gotmp"]
14+
# Clone wiki repo into _content/wiki as initial wiki content.
15+
# The server will replace this with the wiki repo when it can,
16+
# but this provides a good fallback.
17+
- name: gcr.io/cloud-builders/git
18+
args: ["clone", "--depth=1", "https://go.googlesource.com/wiki", "_wikitmp"]
19+
- name: golang
20+
args: ["rm", "-rf", "_wikitmp/.git"]
21+
- name: golang
22+
args: ["cp", "-a", "_wikitmp/*", "_content/wiki"]
23+
# Run tests.
1324
- name: golang
1425
args: ["go", "test", "./..."]
26+
# Coordinate with other Cloud Build jobs to deploy only newest commit.
27+
# May abort job here.
1528
- name: golang
1629
args: ["go", "run", "./cmd/locktrigger", "--project=$PROJECT_ID",
1730
"--build=$BUILD_ID", "--repo=https://go.googlesource.com/website"]
31+
# Deploy site and redirect traffic (maybe; tests again in prod first).
1832
- name: gcr.io/cloud-builders/gcloud
1933
entrypoint: bash
2034
args: ["./go-app-deploy.sh", "cmd/golangorg/app.yaml"]
35+
# Clean up stale versions.
2136
- name: golang
2237
args: ["go", "run", "./cmd/versionprune", "--dry_run=false", "--project=$PROJECT_ID", "--service=default"]
2338

cmd/golangorg/server.go

+54-18
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ var (
5959

6060
runningOnAppEngine = os.Getenv("PORT") != ""
6161

62-
tipFlag = flag.Bool("tip", runningOnAppEngine, "load git content for tip.golang.org")
62+
tipFlag = flag.Bool("tip", runningOnAppEngine, "load git content for tip.golang.org")
63+
wikiFlag = flag.Bool("wiki", runningOnAppEngine, "load git content for go.dev/wiki")
6364

6465
googleAnalytics string
6566
)
@@ -157,14 +158,27 @@ func NewHandler(contentDir, goroot string) http.Handler {
157158
gorootFS = os.DirFS(goroot)
158159
}
159160

161+
// go.dev/wiki serves content from the very latest Git commit of the wiki repo.
162+
// Start with the _content/wiki directory as placeholder until Git loads.
163+
var wikiFS atomicFS
164+
wikiDefault, err := fs.Sub(contentFS, "wiki")
165+
if err != nil {
166+
log.Fatalf("loading default wiki content: %v", err)
167+
}
168+
wikiFS.Set(wikiDefault)
169+
if *wikiFlag {
170+
go watchGit(&wikiFS, "https://go.googlesource.com/wiki")
171+
}
172+
contentFS = &mountFS{contentFS, "wiki", &wikiFS}
173+
160174
// tip.golang.org serves content from the very latest Git commit
161175
// of the main Go repo, instead of the one the app is bundled with.
162176
var tipGoroot atomicFS
163177
if _, err := newSite(mux, "tip.golang.org", contentFS, &tipGoroot); err != nil {
164178
log.Fatalf("loading tip site: %v", err)
165179
}
166180
if *tipFlag {
167-
go watchTip(&tipGoroot)
181+
go watchGit(&tipGoroot, "https://go.googlesource.com/go")
168182
}
169183

170184
// beta.golang.org is an old name for tip.
@@ -280,32 +294,32 @@ func parseRFC3339(s string) (time.Time, error) {
280294
return time.Parse(time.RFC3339, s)
281295
}
282296

283-
// watchTip is a background goroutine that watches the main Go repo for updates.
284-
// When a new commit is available, watchTip downloads the new tree and calls
285-
// tipGoroot.Set to install the new file system.
286-
func watchTip(tipGoroot *atomicFS) {
297+
// watchGit is a background goroutine that watches a Git repo for updates.
298+
// When a new commit is available, watchGit downloads the new tree and calls
299+
// fsys.Set to install the new file system.
300+
func watchGit(fsys *atomicFS, repo string) {
287301
for {
288-
// watchTip1 runs until it panics (hopefully never).
302+
// watchGit1 runs until it panics (hopefully never).
289303
// If that happens, sleep 5 minutes and try again.
290-
watchTip1(tipGoroot)
304+
watchGit1(fsys, repo)
291305
time.Sleep(5 * time.Minute)
292306
}
293307
}
294308

295-
// watchTip1 does the actual work of watchTip and recovers from panics.
296-
func watchTip1(tipGoroot *atomicFS) {
309+
// watchGit1 does the actual work of watchGit and recovers from panics.
310+
func watchGit1(afs *atomicFS, repo string) {
297311
defer func() {
298312
if e := recover(); e != nil {
299-
log.Printf("watchTip panic: %v\n%s", e, debug.Stack())
313+
log.Printf("watchGit %s panic: %v\n%s", repo, e, debug.Stack())
300314
}
301315
}()
302316

303317
var r *gitfs.Repo
304318
for {
305319
var err error
306-
r, err = gitfs.NewRepo("https://go.googlesource.com/go")
320+
r, err = gitfs.NewRepo(repo)
307321
if err != nil {
308-
log.Printf("tip: %v", err)
322+
log.Printf("watchGit %s: %v", repo, err)
309323
time.Sleep(1 * time.Minute)
310324
continue
311325
}
@@ -318,29 +332,29 @@ func watchTip1(tipGoroot *atomicFS) {
318332
var err error
319333
h, fsys, err = r.Clone("HEAD")
320334
if err != nil {
321-
log.Printf("tip: %v", err)
335+
log.Printf("watchGit %s: %v", repo, err)
322336
time.Sleep(1 * time.Minute)
323337
continue
324338
}
325-
tipGoroot.Set(fsys)
339+
afs.Set(fsys)
326340
break
327341
}
328342

329343
for {
330344
time.Sleep(5 * time.Minute)
331345
h2, err := r.Resolve("HEAD")
332346
if err != nil {
333-
log.Printf("tip: %v", err)
347+
log.Printf("watchGit %s: %v", repo, err)
334348
continue
335349
}
336350
if h2 != h {
337351
fsys, err := r.CloneHash(h2)
338352
if err != nil {
339-
log.Printf("tip: %v", err)
353+
log.Printf("watchGit %s: %v", repo, err)
340354
time.Sleep(1 * time.Minute)
341355
continue
342356
}
343-
tipGoroot.Set(fsys)
357+
afs.Set(fsys)
344358
h = h2
345359
}
346360
}
@@ -715,6 +729,7 @@ func (fsys fixSpecsFS) Open(name string) (fs.File, error) {
715729
// README.md, and SECURITY.md. The last is particularly problematic
716730
// when running locally on a Mac, because it can be opened as
717731
// security.md, which takes priority over _content/security.html.
732+
// Same for wiki.
718733
type hideRootMDFS struct {
719734
fs fs.FS
720735
}
@@ -723,6 +738,10 @@ func (fsys hideRootMDFS) Open(name string) (fs.File, error) {
723738
if !strings.Contains(name, "/") && strings.HasSuffix(name, ".md") {
724739
return nil, errors.New(".md file not available")
725740
}
741+
switch name {
742+
case "wiki/README.md", "wiki/CONTRIBUTING.md", "wiki/LICENSE", "wiki/PATENTS", "wiki/codereview.cfg":
743+
return nil, errors.New("wiki meta file not available")
744+
}
726745
return fsys.fs.Open(name)
727746
}
728747

@@ -795,6 +814,23 @@ func (a *atomicFS) Set(fsys fs.FS) {
795814
a.v.Store(&fsys)
796815
}
797816

817+
// A mountFS is a root FS with a second FS mounted at a specific location.
818+
type mountFS struct {
819+
old fs.FS // root file system
820+
dir string // mount point
821+
new fs.FS // fs mounted on dir
822+
}
823+
824+
func (m *mountFS) Open(name string) (fs.File, error) {
825+
if name == m.dir {
826+
return m.new.Open(".")
827+
}
828+
if strings.HasPrefix(name, m.dir) && len(name) > len(m.dir) && name[len(m.dir)] == '/' {
829+
return m.new.Open(name[len(m.dir)+1:])
830+
}
831+
return m.old.Open(name)
832+
}
833+
798834
// Open returns fsys.Open(name) where fsys is the file system passed to the most recent call to Set.
799835
// If there has been no call to Set, Open returns an error with text “no file system”.
800836
func (a *atomicFS) Open(name string) (fs.File, error) {

cmd/golangorg/server_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ func TestAll(t *testing.T) {
114114

115115
// Do not process these paths or path prefixes.
116116
ignores := []string{
117+
// Wiki is in a different repo; errors there should not block production push.
118+
"/wiki/",
119+
117120
// Support files not meant to be served directly.
118121
"/doc/articles/wiki/",
119122
"/talks/2013/highperf/",

cmd/golangorg/testdata/godev.txt

+8
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,11 @@ redirect == /doc/security/
109109

110110
GET https://go.dev/doc/security/
111111
body contains Security
112+
113+
GET https://go.dev/wiki/
114+
body contains Go Wiki: Index
115+
body contains <i>This content is part of the <a href="/wiki/">Go Wiki</a>.</i>
116+
117+
GET https://go.dev/wiki/Comments
118+
body contains Go Wiki: Comments
119+
body contains <i>This content is part of the <a href="/wiki/">Go Wiki</a>.</i>

cmd/golangorg/testdata/live.txt

+8
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,11 @@ header Content-Type == text/plain; charset=utf-8
6262
body !contains The Go Playground
6363
body !contains About the Playground
6464
body contains Hello, 世界
65+
66+
GET https://go.dev/wiki/Comments
67+
body contains Go Wiki: Comments
68+
body contains <i>This content is part of the <a href="/wiki/">Go Wiki</a>.</i>
69+
70+
GET https://go.dev/wiki/CommonMistakes
71+
body contains Go Wiki: Common Mistakes
72+
body contains <i>This content is part of the <a href="/wiki/">Go Wiki</a>.</i>

internal/redirect/redirect.go

-3
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,6 @@ var redirects = map[string]string{
111111
"/doc/mem": "/ref/mem",
112112
"/doc/spec": "/ref/spec",
113113

114-
"/wiki": "https://github.com/golang/go/wiki",
115-
116114
"/doc/articles/c_go_cgo.html": "/blog/c-go-cgo",
117115
"/doc/articles/concurrency_patterns.html": "/blog/go-concurrency-patterns-timing-out-and",
118116
"/doc/articles/defer_panic_recover.html": "/blog/defer-panic-and-recover",
@@ -163,7 +161,6 @@ var newIssueRedirects = [...]string{
163161
var prefixHelpers = map[string]string{
164162
"issue": "https://github.com/golang/go/issues/",
165163
"issues": "https://github.com/golang/go/issues/",
166-
"wiki": "https://github.com/golang/go/wiki/",
167164
}
168165

169166
func Handler(target string) http.Handler {

internal/redirect/redirect_test.go

-3
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,6 @@ func TestRedirects(t *testing.T) {
8585
"/issues/new/choose": errorResult(404),
8686
"/issues/1/2/3": errorResult(404),
8787

88-
"/wiki/foo": {302, "https://github.com/golang/go/wiki/foo"},
89-
"/wiki/foo/": {302, "https://github.com/golang/go/wiki/foo/"},
90-
9188
"/design": {301, "https://go.googlesource.com/proposal/+/master/design"},
9289
"/design/": {302, "/design"},
9390
"/design/123-foo": {302, "https://go.googlesource.com/proposal/+/master/design/123-foo.md"},

internal/web/render.go

+7
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ func (site *Site) renderHTML(p Page, tmpl string, r *http.Request) ([]byte, erro
127127
// Either the page explicitly requested templating, or it is markdown,
128128
// which is treated as a template by default.
129129
isTemplate, explicit := p["template"].(bool)
130+
131+
// The wiki is not templated by default.
132+
if !explicit && strings.HasPrefix(file, "wiki/") {
133+
isTemplate, explicit = false, true
134+
}
135+
130136
tdata := data
131137
if !explicit || isTemplate {
132138
// Load content as a template.
@@ -193,6 +199,7 @@ func markdownToHTML(markdown string) (template.HTML, error) {
193199
extension.WithLinkifyEmailRegexp(regexp.MustCompile(`[^\x00-\x{10FFFF}]`)), // impossible
194200
),
195201
extension.DefinitionList,
202+
extension.NewTable(),
196203
),
197204
)
198205
var buf bytes.Buffer

0 commit comments

Comments
 (0)