This repository has no description
0

Configure Feed

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

core / knotmirror / xrpc / git_get_tree.go
3.2 kB 119 lines
1package xrpc 2 3import ( 4 "context" 5 "fmt" 6 "net/http" 7 "path/filepath" 8 "time" 9 "unicode/utf8" 10 11 "github.com/bluesky-social/indigo/atproto/atclient" 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 "tangled.org/core/api/tangled" 14 "tangled.org/core/appview/pages/markup" 15 "tangled.org/core/knotserver/git" 16) 17 18func (x *Xrpc) GetTree(w http.ResponseWriter, r *http.Request) { 19 var ( 20 repoQuery = r.URL.Query().Get("repo") 21 ref = r.URL.Query().Get("ref") // ref can be empty (git.Open handles this) 22 path = r.URL.Query().Get("path") // path can be empty (defaults to root) 23 ) 24 l := x.logger.With("method", "git.getTree", "repo", repoQuery, "ref", ref) 25 26 repo, err := syntax.ParseATURI(repoQuery) 27 if err != nil || repo.RecordKey() == "" { 28 writeJson(w, http.StatusBadRequest, atclient.ErrorBody{Name: "BadRequest", Message: fmt.Sprintf("repo parameter invalid: %s", repoQuery)}) 29 return 30 } 31 32 out, err := x.getTree(r.Context(), repo, ref, path) 33 if err != nil { 34 l.Warn("local mirror failed, trying proxy", "repo", repo, "err", err) 35 if x.proxyToKnot(w, r, repo) { 36 return 37 } 38 writeJson(w, http.StatusInternalServerError, atclient.ErrorBody{Name: "InternalServerError", Message: "failed to get tree"}) 39 return 40 } 41 writeJson(w, http.StatusOK, out) 42} 43 44func (x *Xrpc) getTree(ctx context.Context, repo syntax.ATURI, ref, path string) (*tangled.GitTempGetTree_Output, error) { 45 repoPath, err := x.makeRepoPath(ctx, repo) 46 if err != nil { 47 return nil, fmt.Errorf("failed to resolve repo at-uri: %w", err) 48 } 49 50 gr, err := git.Open(repoPath, ref) 51 if err != nil { 52 return nil, fmt.Errorf("opening git repo: %w", err) 53 } 54 55 files, err := gr.FileTree(ctx, path) 56 if err != nil { 57 return nil, fmt.Errorf("reading file tree: %w", err) 58 } 59 60 // if any of these files are a readme candidate, pass along its blob contents too 61 var readmeFileName string 62 var readmeContents string 63 for _, file := range files { 64 if markup.IsReadmeFile(file.Name) { 65 contents, err := gr.RawContent(filepath.Join(path, file.Name)) 66 if err != nil { 67 x.logger.Error("failed to read contents of file", "path", path, "file", file.Name) 68 } 69 70 if utf8.Valid(contents) { 71 readmeFileName = file.Name 72 readmeContents = string(contents) 73 break 74 } 75 } 76 } 77 78 // convert NiceTree -> tangled.RepoTempGetTree_TreeEntry 79 treeEntries := make([]*tangled.GitTempGetTree_TreeEntry, len(files)) 80 for i, file := range files { 81 entry := &tangled.GitTempGetTree_TreeEntry{ 82 Name: file.Name, 83 Mode: file.Mode, 84 Size: file.Size, 85 } 86 if file.LastCommit != nil { 87 entry.Last_commit = &tangled.GitTempGetTree_LastCommit{ 88 Hash: file.LastCommit.Hash.String(), 89 Message: file.LastCommit.Message, 90 When: file.LastCommit.When.Format(time.RFC3339), 91 } 92 } 93 treeEntries[i] = entry 94 } 95 96 var parentPtr *string 97 if path != "" { 98 parentPtr = &path 99 } 100 101 var dotdotPtr *string 102 if path != "" { 103 dotdot := filepath.Dir(path) 104 if dotdot != "." { 105 dotdotPtr = &dotdot 106 } 107 } 108 109 return &tangled.GitTempGetTree_Output{ 110 Ref: ref, 111 Parent: parentPtr, 112 Dotdot: dotdotPtr, 113 Files: treeEntries, 114 Readme: &tangled.GitTempGetTree_Readme{ 115 Filename: readmeFileName, 116 Contents: readmeContents, 117 }, 118 }, nil 119}