cmd/relui: use go:embed for templates and content
This changes relui to use embed from go1.16 for managing content. This
drastically simplifies the deployment steps, in preparation for
containerizing.
For golang/go#47401
Change-Id: I1eb9f6f63fa490ef73ed454ddecd1fd99db4f960
Reviewed-on: https://go-review.googlesource.com/c/build/+/340429
Trust: Alexander Rakoczy <alex@golang.org>
Run-TryBot: Alexander Rakoczy <alex@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/cmd/relui/content.go b/cmd/relui/content.go
new file mode 100644
index 0000000..ce02062
--- /dev/null
+++ b/cmd/relui/content.go
@@ -0,0 +1,18 @@
+// Copyright 2021 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
+// +build go1.16
+
+package main
+
+import "embed"
+
+// static is our static web server content.
+//go:embed static
+var static embed.FS
+
+// templates are our template files.
+//go:embed templates
+var templates embed.FS
diff --git a/cmd/relui/main.go b/cmd/relui/main.go
index b04013b..945f96a 100644
--- a/cmd/relui/main.go
+++ b/cmd/relui/main.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
+
// relui is a web interface for managing the release process of Go.
package main
@@ -44,7 +47,7 @@
http.Handle("/workflows/create", http.HandlerFunc(s.createWorkflowHandler))
http.Handle("/workflows/new", http.HandlerFunc(s.newWorkflowHandler))
http.Handle("/tasks/start", http.HandlerFunc(s.startTaskHandler))
- http.Handle("/", fileServerHandler(relativeFile("./static"), http.HandlerFunc(s.homeHandler)))
+ http.Handle("/", fileServerHandler(static, http.HandlerFunc(s.homeHandler)))
port := os.Getenv("PORT")
if port == "" {
port = "8080"
diff --git a/cmd/relui/store.go b/cmd/relui/store.go
index 26edf32..84db0af 100644
--- a/cmd/relui/store.go
+++ b/cmd/relui/store.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/relui/templates/layout.html b/cmd/relui/templates/layout.html
index 03b892b..bf0a4aa 100644
--- a/cmd/relui/templates/layout.html
+++ b/cmd/relui/templates/layout.html
@@ -7,7 +7,7 @@
<html lang="en">
<title>Go Releases</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
- <link rel="stylesheet" href="/styles.css" />
+ <link rel="stylesheet" href="/static/styles.css" />
<body class="Site">
<header class="Site-header">
<div class="Header">
diff --git a/cmd/relui/web.go b/cmd/relui/web.go
index ae32970..2ff9ad1 100644
--- a/cmd/relui/web.go
+++ b/cmd/relui/web.go
@@ -2,12 +2,16 @@
// 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 (
"bytes"
"html/template"
"io"
+ "io/fs"
"log"
"mime"
"net/http"
@@ -21,33 +25,28 @@
reluipb "golang.org/x/build/cmd/relui/protos"
)
-// fileServerHandler returns a http.Handler rooted at root. It will call the next handler provided for requests to "/".
+// fileServerHandler returns a http.Handler rooted at root. It will
+// call the next handler provided for requests to "/".
//
-// The returned handler sets the appropriate Content-Type and Cache-Control headers for the returned file.
-func fileServerHandler(root string, next http.Handler) http.Handler {
+// The returned handler sets the appropriate Content-Type and
+// Cache-Control headers for the returned file.
+func fileServerHandler(fs fs.FS, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
next.ServeHTTP(w, r)
return
}
- // http.FileServer would correctly return a 404, but we need to check that the file exists
- // before calculating the Content-Type header.
- if _, err := os.Stat(path.Join(root, r.URL.Path)); os.IsNotExist(err) {
- http.NotFound(w, r)
- return
- }
w.Header().Set("Content-Type", mime.TypeByExtension(path.Ext(r.URL.Path)))
w.Header().Set("Cache-Control", "no-cache, private, max-age=0")
-
- fs := http.FileServer(http.Dir(root))
- fs.ServeHTTP(w, r)
+ s := http.FileServer(http.FS(fs))
+ s.ServeHTTP(w, r)
})
}
var (
- homeTmpl = template.Must(template.Must(layoutTmpl.Clone()).ParseFiles(relativeFile("templates/home.html")))
- layoutTmpl = template.Must(template.ParseFiles(relativeFile("templates/layout.html")))
- newWorkflowTmpl = template.Must(template.Must(layoutTmpl.Clone()).ParseFiles(relativeFile("templates/new_workflow.html")))
+ homeTmpl = template.Must(template.Must(layoutTmpl.Clone()).ParseFS(templates, "templates/home.html"))
+ layoutTmpl = template.Must(template.ParseFS(templates, "templates/layout.html"))
+ newWorkflowTmpl = template.Must(template.Must(layoutTmpl.Clone()).ParseFS(templates, "templates/new_workflow.html"))
)
// server implements the http handlers for relui.
diff --git a/cmd/relui/web_test.go b/cmd/relui/web_test.go
index 145ad5b..dcd18e6 100644
--- a/cmd/relui/web_test.go
+++ b/cmd/relui/web_test.go
@@ -2,10 +2,14 @@
// 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 (
"context"
+ "embed"
"io/ioutil"
"net/http"
"net/http/httptest"
@@ -21,8 +25,12 @@
"google.golang.org/grpc"
)
+// testStatic is our static web server content.
+//go:embed testing
+var testStatic embed.FS
+
func TestFileServerHandler(t *testing.T) {
- h := fileServerHandler("./testing", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ h := fileServerHandler(testStatic, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Home"))
}))
@@ -41,7 +49,7 @@
},
{
desc: "sets headers and returns file",
- path: "/test.css",
+ path: "/testing/test.css",
wantCode: http.StatusOK,
wantBody: ".Header { font-size: 10rem; }\n",
wantHeaders: map[string]string{
@@ -54,6 +62,9 @@
path: "/foo.js",
wantCode: http.StatusNotFound,
wantBody: "404 page not found\n",
+ wantHeaders: map[string]string{
+ "Content-Type": "text/plain; charset=utf-8",
+ },
},
}
for _, c := range cases {