M README.md => README.md +1 -1
@@ 223,7 223,7 @@ compare.sr.ht deploys like any other SourceHut web service. On the instance:
```sh
make install PREFIX=/usr/local
# → /usr/local/bin/comparesrht
- # → /usr/share/sourcehut/compare.sr.ht/static/{bundle.<hash>.js,main.min.<hash>.css,logo.svg}
+ # → /usr/share/sourcehut/compare.sr.ht/static/{bundle.<hash>.js,main.min.<hash>.css}
```
2. **Config.** Append the `[compare.sr.ht]` section (`origin=` and
D web/static/logo.svg => web/static/logo.svg +0 -14
@@ 1,14 0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128">
- <style>
- @media (prefers-color-scheme: light) {
- #outline { display: none }
- #logo { stroke: black }
- }
- @media (prefers-color-scheme: dark) {
- #outline { display: none }
- #logo { stroke: white }
- }
- </style>
- <circle id="outline" cx="64" cy="64" r="50" fill="none" stroke-width="14" stroke="#888" />
- <circle id="logo" cx="64" cy="64" r="50" fill="none" stroke-width="10" stroke="white" />
-</svg>
M web/templates.go => web/templates.go +3 -2
@@ 19,8 19,9 @@ import (
//go:embed templates/*.html
var tmplFS embed.FS
-// staticFS holds the built front-end assets (the hashed bundle.<hash>.js, the
-// hashed stylesheet, logo.svg). It is served read-only under /static/.
+// staticFS holds the built front-end assets: the hashed bundle.<hash>.js and
+// the hashed stylesheet, and nothing else since the favicon became chrome's
+// inlined one. It is served read-only under /static/.
//
//go:embed static
var staticFS embed.FS
M web/templates/layout.html => web/templates/layout.html +8 -7
@@ 19,13 19,14 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{.Title}}</title>
- <link rel="icon" type="image/svg+xml" href="/static/logo.svg">
- {{/* Guarded rather than emitted empty: <link href=""> re-requests the page
- it is on, which is a page load per page load. assets.Resolve answers ""
- for a binary built without `make css`, which is a checkout run from
- source; a deployed build always has the artefact and always takes
- this. */}}
- {{if .StyleHref}}<link rel="stylesheet" href="{{.StyleHref}}">{{end}}
+ {{/* The stylesheet and the icon, both guarded rather than emitted empty:
+ <link href=""> re-requests the page it is on, which is a page load per
+ page load. assets.Resolve answers "" for a binary built without `make
+ css`, which is a checkout run from source; a deployed build always has
+ the artefact. The icon is chrome's own brand circle, inlined as a
+ data: URI — it cannot 404 and costs no request, which the copy of the
+ same circle this service shipped under /static/logo.svg did. */}}
+ {{template "srht-head-links" .}}
<style>
/* Semantic status badge for the changed-files table. The default table
cell colour is too dim on the dark chrome, so each git status gets an
M web/web_test.go => web/web_test.go +20 -5
@@ 451,12 451,27 @@ func TestStaticBundleAndCSS(t *testing.T) {
"a hashed stylesheet must be cacheable forever")
assert.Empty(t, rec.Header().Get("Vary"),
"an asset served identically to everybody must not be declared to vary on Cookie")
+}
+
+// TestFaviconIsInline pins that the icon costs no request and no file. It used
+// to be /static/logo.svg — this service's copy of the brand circle chrome now
+// inlines as a data: URI, which is also the only value html/template will let
+// through a href without rewriting it to #ZgotmplZ.
+func TestFaviconIsInline(t *testing.T) {
+ root, _ := gitFixture(t)
+ h := testServer(t, root, demoAuthorizer())
- // An asset whose name carries no hash cannot be immutable: the next deploy
- // serves different bytes at the same URL.
- rec = get(t, h, "/static/logo.svg", "")
+ rec := get(t, h, "/", "")
require.Equal(t, http.StatusOK, rec.Code)
- assert.Equal(t, "public, max-age=3600", rec.Header().Get("Cache-Control"))
+ // html/template writes the '+' of "svg+xml" as +, which is why the
+ // prefix this looks for stops short of it.
+ assert.Contains(t, rec.Body.String(), `<link rel="icon" href="data:image/svg`)
+ assert.NotContains(t, rec.Body.String(), "#ZgotmplZ",
+ "a data: URI that arrived as a plain string would have been rewritten")
+ assert.NotContains(t, rec.Body.String(), "logo.svg")
+
+ assert.Equal(t, http.StatusNotFound, get(t, h, "/static/logo.svg", "").Code,
+ "the shipped copy of the icon is gone")
}
// TestStaticListingRefused pins what the hand-rolled static route published
@@ 475,7 490,7 @@ func TestStaticListingRefused(t *testing.T) {
// A Go file-server listing is a list of <a href="name">name</a>; the
// stylesheet's own <link> in the chrome is not, which is why the
// entry and not the name is what this looks for.
- assert.NotContains(t, rec.Body.String(), `<a href="logo.svg">`,
+ assert.NotContains(t, rec.Body.String(), `<a href="`+bundleName(t)+`">`,
"the response is a directory listing")
// And it is a page, not net/http's plain-text dead end.
assert.Contains(t, rec.Body.String(), "navbar-brand")