Commit 55d70f99b2

55d70f99b2841a1f97936a5f6c4607ddce0c5da9

parent: 9def4a7f1d

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-18 04:19 UTC

httpd, e2e: the tree root of the default branch is the repo home

layout.html's own comment says the repohead is identical on every page
within a tab, but /owner/repo and /owner/repo/tree/<default>/ rendered
different identity markup (h1 vs p+link) because only the repoHome
handler set RepoHome; the tree handler never did, even when it served
the exact same content by a more explicit URL. tree() now sets
RepoHome when the ref is the default branch and the path is empty.

TestReadmeRelativeLinks gains a check that the repohead is
byte-identical between an issue list and a single issue, and between
the repo home and its tree root, since inst.get is anonymous and both
pairs share a tab.

Ref #221
e2e/design_test.go +24
@@ -149,6 +149,30 @@ func TestReadmeRelativeLinks(t *testing.T) {
149149 if _, body := inst.get(t, "/alice/site"); !strings.Contains(body, `class="chip topic"`) {
150150 t.Error("repo home lost its topics")
151151 }
152 // The repohead is the same markup on every tab within a repository —
153 // aria-current on the tab link and the watch/bookmark toggles are the
154 // only things that could vary, and they depend on who is signed in,
155 // not which tab is shown. inst.get is anonymous, so two tabs' headers
156 // must be byte-identical.
157 inst.ssh(t, aliceKey, "", "issue", "create", "alice/site", "--title", "one")
158 header := func(body string) string {
159 i := strings.Index(body, `<header class="repohead">`)
160 j := strings.Index(body, "</header>")
161 if i < 0 || j < 0 || j < i {
162 t.Fatalf("repohead not found:\n%.2000s", body)
163 }
164 return body[i:j]
165 }
166 _, issuesBody := inst.get(t, "/alice/site/issues")
167 _, issueBody := inst.get(t, "/alice/site/issues/1")
168 if a, b := header(issuesBody), header(issueBody); a != b {
169 t.Errorf("issues list and issue page headers differ:\n%s\nvs\n%s", a, b)
170 }
171 _, homeBody := inst.get(t, "/alice/site")
172 _, treeBody := inst.get(t, "/alice/site/tree/main/")
173 if a, b := header(homeBody), header(treeBody); a != b {
174 t.Errorf("repo home and tree headers differ:\n%s\nvs\n%s", a, b)
175 }
152176}
153177
154178// TestLandingRoutes checks the landing page's copy and the two routes.
internal/httpd/web.go +6 −1
@@ -481,7 +481,12 @@ func (s *Server) tree(w http.ResponseWriter, r *http.Request) {
481481 return
482482 }
483483 p.Tab = "files"
484 s.renderTree(w, r, p, strings.Trim(r.PathValue("path"), "/"))
484 path := strings.Trim(r.PathValue("path"), "/")
485 // The root of the default branch is the same page as the bare repo
486 // URL, so its header must match: RepoHome is what picks the h1 over
487 // the p+link identity, not which route was typed.
488 p.RepoHome = path == "" && p.Ref == p.Repo.DefaultBranch
489 s.renderTree(w, r, p, path)
485490}
486491
487492// treePage is shared by the populated and empty-repository renders: two