Skip to content

Commit c53e698

Browse files
committed
merged www separate site into the same project
1 parent 1f3e624 commit c53e698

14 files changed

Lines changed: 2660 additions & 0 deletions

File tree

.github/workflows/pages.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Deploy to GitHub Pages
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
paths:
7+
- "www/public/**"
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
pages: write
13+
id-token: write
14+
15+
concurrency:
16+
group: "pages"
17+
cancel-in-progress: false
18+
19+
jobs:
20+
deploy:
21+
environment:
22+
name: github-pages
23+
url: ${{ steps.deployment.outputs.page_url }}
24+
runs-on: ubuntu-latest
25+
steps:
26+
- name: Checkout
27+
uses: actions/checkout@v4
28+
29+
- name: Setup Pages
30+
uses: actions/configure-pages@v5
31+
32+
- name: Upload artifact
33+
uses: actions/upload-pages-artifact@v3
34+
with:
35+
path: www/public
36+
37+
- name: Deploy to GitHub Pages
38+
id: deployment
39+
uses: actions/deploy-pages@v4

www/.gitignore

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
firebase-debug.log*
8+
firebase-debug.*.log*
9+
10+
# Firebase cache
11+
.firebase/
12+
13+
# Firebase config
14+
15+
# Uncomment this if you'd like others to create their own Firebase project.
16+
# For a team working on the same Firebase project(s), it is recommended to leave
17+
# it commented so all members can deploy to the same project(s) in .firebaserc.
18+
# .firebaserc
19+
20+
# Runtime data
21+
pids
22+
*.pid
23+
*.seed
24+
*.pid.lock
25+
26+
# Directory for instrumented libs generated by jscoverage/JSCover
27+
lib-cov
28+
29+
# Coverage directory used by tools like istanbul
30+
coverage
31+
32+
# nyc test coverage
33+
.nyc_output
34+
35+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
36+
.grunt
37+
38+
# Bower dependency directory (https://bower.io/)
39+
bower_components
40+
41+
# node-waf configuration
42+
.lock-wscript
43+
44+
# Compiled binary addons (http://nodejs.org/api/addons.html)
45+
build/Release
46+
47+
# Dependency directories
48+
node_modules/
49+
50+
# Optional npm cache directory
51+
.npm
52+
53+
# Optional eslint cache
54+
.eslintcache
55+
56+
# Optional REPL history
57+
.node_repl_history
58+
59+
# Output of 'npm pack'
60+
*.tgz
61+
62+
# Yarn Integrity file
63+
.yarn-integrity
64+
65+
# dotenv environment variables file
66+
.env
67+
68+
# dataconnect generated files
69+
.dataconnect

www/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This is a web server that announces whether or not a particular Go version has been tagged.

www/go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module mymodule
2+
3+
go 1.20
4+

www/main.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Outyet is a web server that announces whether or not a particular Go version
6+
// has been tagged.
7+
package main
8+
9+
import (
10+
"expvar"
11+
"flag"
12+
"fmt"
13+
"html/template"
14+
"log"
15+
"net/http"
16+
"strings"
17+
"sync"
18+
"time"
19+
)
20+
21+
// Command-line flags.
22+
var (
23+
httpAddr = flag.String("addr", "0.0.0.0:8080", "Listen address")
24+
pollPeriod = flag.Duration("poll", 5*time.Second, "Poll period")
25+
version = flag.String("version", "1.20", "Go version")
26+
)
27+
28+
const baseChangeURL = "https://go.googlesource.com/go/+/"
29+
30+
func main() {
31+
flag.Parse()
32+
changeURL := fmt.Sprintf("%sgo%s", baseChangeURL, *version)
33+
34+
// Serve the images directory just like Firebase Hosting would
35+
fs := http.FileServer(http.Dir("public/images"))
36+
http.Handle("/images/", http.StripPrefix("/images/", fs))
37+
38+
// Serve the docs directory
39+
docsFs := http.FileServer(http.Dir("public/docs"))
40+
http.Handle("/docs/", http.StripPrefix("/docs/", docsFs))
41+
42+
// Serve the manual HTML viewer
43+
http.HandleFunc("/manual.html", func(w http.ResponseWriter, r *http.Request) {
44+
http.ServeFile(w, r, "public/manual.html")
45+
})
46+
47+
http.Handle("/", NewServer(*version, changeURL, *pollPeriod))
48+
49+
addr := *httpAddr
50+
port := addr
51+
if idx := strings.LastIndex(addr, ":"); idx != -1 {
52+
port = addr[idx:]
53+
}
54+
55+
log.Printf("serving http://%s", addr)
56+
log.Printf("local access: http://localhost%s", port)
57+
log.Fatal(http.ListenAndServe(addr, nil))
58+
}
59+
60+
// Exported variables for monitoring the server.
61+
// These are exported via HTTP as a JSON object at /debug/vars.
62+
var (
63+
hitCount = expvar.NewInt("hitCount")
64+
pollCount = expvar.NewInt("pollCount")
65+
pollError = expvar.NewString("pollError")
66+
pollErrorCount = expvar.NewInt("pollErrorCount")
67+
)
68+
69+
// Server implements the outyet server.
70+
// It serves the user interface (it's an http.Handler)
71+
// and polls the remote repository for changes.
72+
type Server struct {
73+
version string
74+
url string
75+
period time.Duration
76+
77+
mu sync.RWMutex // protects the yes variable
78+
yes bool
79+
}
80+
81+
// NewServer returns an initialized outyet server.
82+
func NewServer(version, url string, period time.Duration) *Server {
83+
s := &Server{version: version, url: url, period: period}
84+
go s.poll()
85+
return s
86+
}
87+
88+
// poll polls the change URL for the specified period until the tag exists.
89+
// Then it sets the Server's yes field true and exits.
90+
func (s *Server) poll() {
91+
for !isTagged(s.url) {
92+
pollSleep(s.period)
93+
}
94+
s.mu.Lock()
95+
s.yes = true
96+
s.mu.Unlock()
97+
pollDone()
98+
}
99+
100+
// Hooks that may be overridden for integration tests.
101+
var (
102+
pollSleep = time.Sleep
103+
pollDone = func() {}
104+
)
105+
106+
// isTagged makes an HTTP HEAD request to the given URL and reports whether it
107+
// returned a 200 OK response.
108+
func isTagged(url string) bool {
109+
pollCount.Add(1)
110+
r, err := http.Head(url)
111+
if err != nil {
112+
log.Print(err)
113+
pollError.Set(err.Error())
114+
pollErrorCount.Add(1)
115+
return false
116+
}
117+
return r.StatusCode == http.StatusOK
118+
}
119+
120+
// ServeHTTP implements the HTTP user interface.
121+
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
122+
hitCount.Add(1)
123+
s.mu.RLock()
124+
data := struct {
125+
URL string
126+
Version string
127+
Yes bool
128+
}{
129+
s.url,
130+
s.version,
131+
s.yes,
132+
}
133+
s.mu.RUnlock()
134+
err := tmpl.Execute(w, data)
135+
if err != nil {
136+
log.Print(err)
137+
}
138+
}
139+
140+
// tmpl is the HTML template that drives the user interface.
141+
var tmpl = template.Must(template.ParseFiles("public/index.html"))

www/main_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"net/http"
9+
"net/http/httptest"
10+
"strings"
11+
"testing"
12+
"time"
13+
)
14+
15+
// statusHandler is an http.Handler that writes an empty response using itself
16+
// as the response status code.
17+
type statusHandler int
18+
19+
func (h *statusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
20+
w.WriteHeader(int(*h))
21+
}
22+
23+
func TestIsTagged(t *testing.T) {
24+
// Set up a fake "Google Code" web server reporting 404 not found.
25+
status := statusHandler(http.StatusNotFound)
26+
s := httptest.NewServer(&status)
27+
defer s.Close()
28+
29+
if isTagged(s.URL) {
30+
t.Fatal("isTagged == true, want false")
31+
}
32+
33+
// Change fake server status to 200 OK and try again.
34+
status = http.StatusOK
35+
36+
if !isTagged(s.URL) {
37+
t.Fatal("isTagged == false, want true")
38+
}
39+
}
40+
41+
func TestIntegration(t *testing.T) {
42+
status := statusHandler(http.StatusNotFound)
43+
ts := httptest.NewServer(&status)
44+
defer ts.Close()
45+
46+
// Replace the pollSleep with a closure that we can block and unblock.
47+
sleep := make(chan bool)
48+
pollSleep = func(time.Duration) {
49+
sleep <- true
50+
sleep <- true
51+
}
52+
53+
// Replace pollDone with a closure that will tell us when the poller is
54+
// exiting.
55+
done := make(chan bool)
56+
pollDone = func() { done <- true }
57+
58+
// Put things as they were when the test finishes.
59+
defer func() {
60+
pollSleep = time.Sleep
61+
pollDone = func() {}
62+
}()
63+
64+
s := NewServer("1.x", ts.URL, 1*time.Millisecond)
65+
66+
<-sleep // Wait for poll loop to start sleeping.
67+
68+
// Make first request to the server.
69+
r, _ := http.NewRequest("GET", "/", nil)
70+
w := httptest.NewRecorder()
71+
s.ServeHTTP(w, r)
72+
if b := w.Body.String(); !strings.Contains(b, "No.") {
73+
t.Fatalf("body = %s, want no", b)
74+
}
75+
76+
status = http.StatusOK
77+
78+
<-sleep // Permit poll loop to stop sleeping.
79+
<-done // Wait for poller to see the "OK" status and exit.
80+
81+
// Make second request to the server.
82+
w = httptest.NewRecorder()
83+
s.ServeHTTP(w, r)
84+
if b := w.Body.String(); !strings.Contains(b, "YES!") {
85+
t.Fatalf("body = %q, want yes", b)
86+
}
87+
}

0 commit comments

Comments
 (0)