This repository has no description
0

Configure Feed

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

3.9 kB 149 lines
1package main 2 3import ( 4 "context" 5 "crypto/sha1" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io" 10 "net/url" 11 "os" 12 "path/filepath" 13 14 "github.com/bluesky-social/indigo/atproto/identity" 15 "github.com/bluesky-social/indigo/atproto/syntax" 16 "github.com/samber/lo" 17 "tangled.org/core/repoident" 18 "tangled.org/core/repoverify" 19) 20 21// 1 MB; match https://sourcegraph.sourcegraph.com/r/github.com/sourcegraph/sourcegraph/-/blob/cmd/searcher/internal/search/store.go?L32 22const MaxFileSize = 1 << 20 23 24func gitIndex(ctx context.Context, cfg *Config, dir identity.Directory, req indexRequest) error { 25 ctx, cancel := context.WithTimeout(ctx, cfg.IndexTimeout) 26 defer cancel() 27 28 repo, err := loadRepo(ctx, cfg, dir, req.Repo) 29 if err != nil { 30 return fmt.Errorf("loading repo %s: %w", req.Repo, err) 31 } 32 repo.Branches = req.Branches 33 34 gitDir, err := tmpGitDir(repo.Did.String()) 35 if err != nil { 36 return err 37 } 38 defer os.RemoveAll(gitDir) // best-effort cleanup 39 40 if err := fetchRepo(ctx, gitDir, repo.CloneURL(), req.Branches); err != nil { 41 return err 42 } 43 44 if err := indexRepo(ctx, cfg, gitDir, *repo); err != nil { 45 return err 46 } 47 48 return nil 49} 50 51func loadRepo(ctx context.Context, cfg *Config, dir identity.Directory, repoDID repoident.RepoDid) (*Repo, error) { 52 ident, err := dir.LookupDID(ctx, syntax.DID(repoDID)) 53 if err != nil { 54 return nil, err 55 } 56 57 knot, err := repoident.KnotURLFromIdentity(ident, cfg.KnotScheme) 58 if err != nil { 59 return nil, fmt.Errorf("repoDid %s: %w", repoDID, err) 60 } 61 62 described, err := repoverify.Describe(ctx, nil, knot, repoDID) 63 if err != nil { 64 return nil, err 65 } 66 67 return &Repo{ 68 Did: repoDID, 69 Owner: described.OwnerDid, 70 Slug: described.Rkey, 71 Knot: knot, 72 }, nil 73} 74 75func fetchRepo(ctx context.Context, gitDir, cloneUrl string, branches []indexBranch) error { 76 // Create a repo to fetch into 77 if err := executeCmd(ctx, 78 "git", 79 // use a random default branch. This is so that HEAD isn't a symref to a 80 // branch that is indexed. For example if you are indexing 81 // HEAD,master. Then HEAD would be pointing to master by default. 82 "-c", "init.defaultBranch=nonExistentBranchBB0FOFCH32", 83 "init", 84 // we don't need a working copy 85 "--bare", 86 gitDir, 87 ); err != nil { 88 return err 89 } 90 91 fetchArgs := append([]string{ 92 "-C", gitDir, 93 "-c", "protocol.version=2", 94 "fetch", "--depth=1", "--no-tags", 95 // Git's blob:limit filter excludes blobs whose size is >= the given limit, 96 // while zoekt indexes files up to and including FileLimit bytes. 97 fmt.Sprintf("--filter=blob:limit=%d", int64(MaxFileSize)+1), 98 cloneUrl, 99 }, lo.Map(branches, func(b indexBranch, _ int) string { return string(b.Version) })...) 100 101 if err := executeCmd(ctx, "git", fetchArgs...); err != nil { 102 return err 103 } 104 105 return errors.Join(lo.FilterMap(branches, func(b indexBranch, _ int) (error, bool) { 106 err := executeCmd(ctx, "git", "-C", gitDir, "update-ref", b.Name.Ref(), string(b.Version)) 107 if err == nil { 108 return nil, false 109 } 110 return fmt.Errorf("failed update-ref %s to %s: %w", b.Name.Ref(), b.Version, err), true 111 })...) 112} 113 114func indexRepo(ctx context.Context, cfg *Config, gitDir string, repo Repo) error { 115 executablePath, err := os.Executable() 116 if err != nil { 117 return err 118 } 119 120 repoJson, err := json.Marshal(repo) 121 if err != nil { 122 return err 123 } 124 125 args := []string{"index"} 126 args = append(args, "-index-dir", cfg.IndexDir) 127 args = append(args, "-appview-url", cfg.AppviewUrl) 128 args = append(args, gitDir, string(repoJson)) 129 if err := executeCmd(ctx, executablePath, args...); err != nil { 130 return err 131 } 132 return nil 133} 134 135func tmpGitDir(name string) (string, error) { 136 abs := url.QueryEscape(name) 137 if len(abs) > 200 { 138 h := sha1.New() 139 _, _ = io.WriteString(h, abs) 140 abs = abs[:200] + fmt.Sprintf("%x", h.Sum(nil))[:8] 141 } 142 dir := filepath.Join(os.TempDir(), abs+".git") 143 if _, err := os.Stat(dir); err == nil { 144 if err := os.RemoveAll(dir); err != nil { 145 return "", err 146 } 147 } 148 return dir, nil 149}