josie / simplegit

web: match README case-insensitively

readme.md (and other casings) now render on the repo home, like
GitHub/Gitea; the matcher compared against exact uppercase
candidates and missed any other spelling. The file read keeps the
actual on-disk path.

LLM Contributor: qwen/qwen3.8-27b

b8622ddd60a831ec5e82333e8c47f4706d65253e
cjosie <administrator@josie-c.com> · 2026-10-05T12:15 · browse files at this commit

parents: 97f81af

diff --git a/internal/web/repohome.go b/internal/web/repohome.go
index a217c93..7e1897d 100644
--- a/internal/web/repohome.go
+++ b/internal/web/repohome.go
@@ -13,7 +13,9 @@ import (
 )
 
 // readmeCandidates are tried in order at the repo root; first hit wins.
-var readmeCandidates = []string{"README.md", "README.markdown", "README.mkd", "README"}
+// Matched case-insensitively (a lowercase readme.md is just as valid as
+// README.md), so candidates stay lowercase for comparison.
+var readmeCandidates = []string{"readme.md", "readme.markdown", "readme.mkd", "readme"}
 
 const (
 	readmeLimit  = 1 << 20
@@ -152,8 +154,8 @@ func (s *Server) attachReadme(data *repoHomeData, repoPath string, entries []git
 	var readme string
 	for _, candidate := range readmeCandidates {
 		for _, entry := range entries {
-			if entry.Type == "blob" && entry.Path == candidate {
-				readme = candidate
+			if entry.Type == "blob" && strings.EqualFold(entry.Path, candidate) {
+				readme = entry.Path
 				break
 			}
 		}
diff --git a/internal/web/repohome_test.go b/internal/web/repohome_test.go
index be2ac6f..a28531f 100644
--- a/internal/web/repohome_test.go
+++ b/internal/web/repohome_test.go
@@ -97,6 +97,43 @@ func TestRepoHomePrivate(t *testing.T) {
 	}
 }
 
+func TestRepoHomeLowercaseReadme(t *testing.T) {
+	httpServer, _, dataDir := newTestServer(t)
+	createRepo(t, newLoggedInClient(t, httpServer), httpServer, "lc", "public")
+	seedFiles(t, dataDir, "lc")
+
+	work := filepath.Join(t.TempDir(), "work")
+	cmd := exec.Command("git", "clone", "-q", filepath.Join(dataDir, "repos", "josie", "lc.git"), work)
+	if out, err := cmd.CombinedOutput(); err != nil {
+		t.Fatalf("clone: %v: %s", err, out)
+	}
+	for _, args := range [][]string{
+		{"mv", "README.md", "readme.md"},
+		{"-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", "lowercase readme"},
+		{"push", "-q", "origin", "main"},
+	} {
+		cmd := exec.Command("git", args...)
+		cmd.Dir = work
+		if out, err := cmd.CombinedOutput(); err != nil {
+			t.Fatalf("git %v: %v: %s", args, err, out)
+		}
+	}
+
+	resp, err := (&http.Client{}).Get(httpServer.URL + "/josie/lc")
+	if err != nil {
+		t.Fatalf("GET /josie/lc: %v", err)
+	}
+	body := readAll(t, resp)
+	if resp.StatusCode != http.StatusOK {
+		t.Fatalf("status = %d, want 200: %q", resp.StatusCode, body)
+	}
+	for _, want := range []string{"readme.md", "<strong>readme</strong>"} {
+		if !strings.Contains(body, want) {
+			t.Errorf("body lacks %q", want)
+		}
+	}
+}
+
 func TestRepoHomeEmptyRepoShowsInstructions(t *testing.T) {
 	httpServer, _, _ := newTestServer(t)
 	loggedIn := newLoggedInClient(t, httpServer)