This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

gitutil,knotmirror/xrpc: match tree/branch responses that knot serves

Lewis: May this revision serve well! <did:plc:3fwecdnvtcscjnrx2p4n7alz>

author did:plc:3fwecdnvtcscjnrx2p4n7a… committer
Tangled
date (Aug 1, 2026, 7:17 PM UTC) commit d3d6a888 parent 8e58c343 change-id vkqlrryt
+119 -79
-17
appview/pages/markup/format.go
··· 2 2 3 3 import ( 4 4 "regexp" 5 - 6 - "github.com/go-git/go-git/v5/plumbing/filemode" 7 5 ) 8 6 9 7 type Format string ··· 15 13 16 14 var FileTypePatterns = map[Format]*regexp.Regexp{ 17 15 FormatMarkdown: regexp.MustCompile(`(?i)\.(md|markdown|mdown|mkdn|mkd)$`), 18 - } 19 - 20 - var ReadmePattern = regexp.MustCompile(`(?i)^readme(?:\.[^.]+)?$`) 21 - 22 - // IsReadmeFile reports whether name/mode identifies a readme blob. The git 23 - // mode is checked so directories or symlinks named "readme" are filtered out. 24 - func IsReadmeFile(name, mode string) bool { 25 - if !ReadmePattern.MatchString(name) { 26 - return false 27 - } 28 - m, err := filemode.New(mode) 29 - if err != nil { 30 - return false 31 - } 32 - return m == filemode.Regular || m == filemode.Executable 33 16 } 34 17 35 18 // GetFormat returns the Format whose extension list matches filename,
-53
appview/pages/markup/format_test.go
··· 2 2 3 3 import "testing" 4 4 5 - func TestIsReadmeFile(t *testing.T) { 6 - const ( 7 - fileMode = "100644" 8 - execMode = "100755" 9 - dirMode = "040000" 10 - ) 11 - 12 - cases := []struct { 13 - name string 14 - mode string 15 - want bool 16 - }{ 17 - {"README.md", fileMode, true}, 18 - {"readme.md", fileMode, true}, 19 - {"ReadMe.MD", fileMode, true}, 20 - {"README.markdown", fileMode, true}, 21 - {"README.mdown", fileMode, true}, 22 - {"README.mkdn", fileMode, true}, 23 - {"README.mkd", fileMode, true}, 24 - {"README.txt", fileMode, true}, 25 - {"readme", fileMode, true}, 26 - {"README", execMode, true}, 27 - 28 - // regression: a directory named "readme" must not be picked up 29 - // as the README blob; tree handlers used to fetch it and 503. 30 - {"readme", dirMode, false}, 31 - {"README.md", dirMode, false}, 32 - 33 - // readme is matched by convention, not by renderable format; 34 - // unsupported markup falls through to plaintext in GetFormat. 35 - {"README.rst", fileMode, true}, 36 - {"README.org", fileMode, true}, 37 - {"README.asciidoc", fileMode, true}, 38 - {"README.foo", fileMode, true}, 39 - 40 - {"README.Music.md", fileMode, false}, 41 - {"readme-old", fileMode, false}, 42 - {"readme_legacy", fileMode, false}, 43 - {"notreadme.md", fileMode, false}, 44 - {"READMEISH", fileMode, false}, 45 - {"README.md", "", false}, 46 - {"README.md", "120000", false}, // symlink 47 - } 48 - 49 - for _, c := range cases { 50 - t.Run(c.name+"/"+c.mode, func(t *testing.T) { 51 - if got := IsReadmeFile(c.name, c.mode); got != c.want { 52 - t.Errorf("IsReadmeFile(%q, %q) = %v, want %v", c.name, c.mode, got, c.want) 53 - } 54 - }) 55 - } 56 - } 57 - 58 5 func TestFileTypePatterns(t *testing.T) { 59 6 cases := []struct { 60 7 format Format
+2 -2
appview/repo/index.go
··· 22 22 "tangled.org/core/appview/db" 23 23 "tangled.org/core/appview/models" 24 24 "tangled.org/core/appview/pages" 25 - "tangled.org/core/appview/pages/markup" 26 25 "tangled.org/core/appview/pipelines" 26 + "tangled.org/core/gitutil" 27 27 "tangled.org/core/types" 28 28 29 29 "github.com/go-chi/chi/v5" ··· 322 322 treeResp = resp 323 323 324 324 for _, file := range resp.Files { 325 - if markup.IsReadmeFile(file.Name, file.Mode) { 325 + if gitutil.IsReadmeFile(file.Name, file.Mode) { 326 326 readmeFileName = file.Name 327 327 break 328 328 }
+2 -2
appview/repo/tree.go
··· 11 11 "tangled.org/core/api/tangled" 12 12 "tangled.org/core/appview/db" 13 13 "tangled.org/core/appview/pages" 14 - "tangled.org/core/appview/pages/markup" 15 14 "tangled.org/core/appview/reporesolver" 15 + "tangled.org/core/gitutil" 16 16 "tangled.org/core/types" 17 17 xrpcclient "tangled.org/core/xrpc/xrpcclient" 18 18 ··· 71 71 } 72 72 } 73 73 files[i] = file 74 - if markup.IsReadmeFile(xrpcFile.Name, xrpcFile.Mode) { 74 + if gitutil.IsReadmeFile(xrpcFile.Name, xrpcFile.Mode) { 75 75 readmeFile = xrpcFile 76 76 } 77 77 }
+22
gitutil/readme.go
··· 1 + package gitutil 2 + 3 + import ( 4 + "regexp" 5 + 6 + "github.com/go-git/go-git/v5/plumbing/filemode" 7 + ) 8 + 9 + var readmePattern = regexp.MustCompile(`(?i)^readme(?:\.[^.]+)?$`) 10 + 11 + // IsReadmeFile reports whether name/mode identifies a readme blob. The git 12 + // mode is checked so directories or symlinks named "readme" are filtered out. 13 + func IsReadmeFile(name, mode string) bool { 14 + if !readmePattern.MatchString(name) { 15 + return false 16 + } 17 + m, err := filemode.New(mode) 18 + if err != nil { 19 + return false 20 + } 21 + return m == filemode.Regular || m == filemode.Executable 22 + }
+48
gitutil/readme_test.go
··· 1 + package gitutil 2 + 3 + import "testing" 4 + 5 + func TestIsReadmeFile(t *testing.T) { 6 + const ( 7 + fileMode = "100644" 8 + execMode = "100755" 9 + dirMode = "040000" 10 + ) 11 + 12 + cases := []struct { 13 + name string 14 + mode string 15 + want bool 16 + }{ 17 + {"README.md", fileMode, true}, 18 + {"ReadMe.MD", fileMode, true}, 19 + {"readme", fileMode, true}, 20 + {"README", execMode, true}, 21 + 22 + // regression: a directory named "readme" must not be picked up 23 + // as the README blob; tree handlers used to fetch it and 503. 24 + {"readme", dirMode, false}, 25 + {"README.md", dirMode, false}, 26 + 27 + // readme is matched by convention, not by renderable format; 28 + // unsupported markup falls through to plaintext in GetFormat. 29 + {"README.rst", fileMode, true}, 30 + {"README.foo", fileMode, true}, 31 + 32 + {"README.Music.md", fileMode, false}, 33 + {"readme-old", fileMode, false}, 34 + {"readme_legacy", fileMode, false}, 35 + {"notreadme.md", fileMode, false}, 36 + {"READMEISH", fileMode, false}, 37 + {"README.md", "", false}, 38 + {"README.md", "120000", false}, // symlink 39 + } 40 + 41 + for _, c := range cases { 42 + t.Run(c.name+"/"+c.mode, func(t *testing.T) { 43 + if got := IsReadmeFile(c.name, c.mode); got != c.want { 44 + t.Errorf("IsReadmeFile(%q, %q) = %v, want %v", c.name, c.mode, got, c.want) 45 + } 46 + }) 47 + } 48 + }
+41 -3
knotmirror/xrpc/git_get_tree.go
··· 7 7 "net/http" 8 8 "path/filepath" 9 9 "time" 10 + "unicode/utf8" 10 11 11 12 "github.com/bluesky-social/indigo/atproto/atclient" 12 13 "github.com/bluesky-social/indigo/atproto/syntax" ··· 14 15 "github.com/go-git/go-git/v5/plumbing" 15 16 "github.com/go-git/go-git/v5/plumbing/object" 16 17 "tangled.org/core/api/tangled" 18 + "tangled.org/core/gitutil" 17 19 "tangled.org/core/knotmirror/xrpc/gitea" 18 20 ) 19 21 20 22 const ( 21 23 LastCommitCache = "last_commit:%s:%s" 22 24 LastCommitCacheTTL = 30 * 24 * time.Hour 25 + MaxReadmeBytes = 1 << 20 23 26 ) 24 27 25 28 func (x *Xrpc) GetTree(w http.ResponseWriter, r *http.Request) { ··· 170 173 return nil, err 171 174 } 172 175 176 + sizes, err := gitea.EntrySizes(ctx, repoPath, subTree.Entries) 177 + if err != nil { 178 + x.logger.Warn("tree entry size read failed", "err", err) 179 + } 180 + 173 181 outEntries := make([]*tangled.GitTempGetTree_TreeEntry, len(subTree.Entries)) 174 182 for i, entry := range subTree.Entries { 175 183 var entryLastCommit *tangled.GitTempGetTree_LastCommit ··· 187 195 outEntries[i] = &tangled.GitTempGetTree_TreeEntry{ 188 196 Name: entry.Name, 189 197 Mode: entry.Mode.String(), 198 + Size: sizes[i], 190 199 Last_commit: entryLastCommit, 191 200 } 192 201 } ··· 195 204 var dotdot *string 196 205 if treePath != "" { 197 206 parent = &treePath 198 - if dir := filepath.Dir(treePath); dir != "" { 207 + if dir := filepath.Dir(treePath); dir != "." { 199 208 dotdot = &dir 200 209 } 201 210 } ··· 213 222 } 214 223 } 215 224 225 + readmeName, readmeContents := x.readme(ctx, repoPath, subTree.Entries, sizes) 226 + 216 227 return &tangled.GitTempGetTree_Output{ 217 228 Ref: ref, 218 229 Parent: parent, ··· 221 232 LastCommit: outLastCommit, 222 233 // TODO: remove this field entirely 223 234 Readme: &tangled.GitTempGetTree_Readme{ 224 - Filename: "", 225 - Contents: "", 235 + Filename: readmeName, 236 + Contents: readmeContents, 226 237 }, 227 238 }, nil 228 239 } 240 + 241 + func (x *Xrpc) readme(ctx context.Context, repoPath string, entries []object.TreeEntry, sizes []int64) (string, string) { 242 + for i, entry := range entries { 243 + if !gitutil.IsReadmeFile(entry.Name, entry.Mode.String()) || sizes[i] > MaxReadmeBytes { 244 + continue 245 + } 246 + size, reader, err := gitea.ReadBlob(ctx, repoPath, entry.Hash) 247 + if err != nil { 248 + x.logger.Warn("readme blob open failed", "file", entry.Name, "err", err) 249 + continue 250 + } 251 + if size > MaxReadmeBytes { 252 + reader.Close() 253 + continue 254 + } 255 + contents, err := io.ReadAll(reader) 256 + reader.Close() 257 + if err != nil { 258 + x.logger.Warn("readme blob read failed", "file", entry.Name, "err", err) 259 + continue 260 + } 261 + if utf8.Valid(contents) { 262 + return entry.Name, string(contents) 263 + } 264 + } 265 + return "", "" 266 + }
+2
knotmirror/xrpc/git_list_branches.go
··· 9 9 "os" 10 10 "os/exec" 11 11 "path/filepath" 12 + "slices" 12 13 "strconv" 13 14 "strings" 14 15 ··· 163 164 if err != nil { 164 165 return nil, fmt.Errorf("hydrating branch commits: %w", err) 165 166 } 167 + slices.Reverse(branches) 166 168 167 169 // -> total 168 170 total, err := func(repoPath string) (int, error) {
+2 -2
knotserver/xrpc/repo_tree.go
··· 7 7 "unicode/utf8" 8 8 9 9 "tangled.org/core/api/tangled" 10 - "tangled.org/core/appview/pages/markup" 10 + "tangled.org/core/gitutil" 11 11 "tangled.org/core/knotserver/git" 12 12 "tangled.org/core/types" 13 13 xrpcerr "tangled.org/core/xrpc/errors" ··· 50 50 var readmeFileName string 51 51 var readmeContents string 52 52 for _, file := range files { 53 - if markup.IsReadmeFile(file.Name, file.Mode) { 53 + if gitutil.IsReadmeFile(file.Name, file.Mode) { 54 54 contents, err := gr.RawContent(filepath.Join(path, file.Name)) 55 55 if err != nil { 56 56 x.Logger.Error("failed to read contents of file", "path", path, "file", file.Name)