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_archive.go
3.1 kB 113 lines
1package xrpc 2 3import ( 4 "fmt" 5 "net/http" 6 "net/url" 7 "os/exec" 8 "strings" 9 10 "github.com/bluesky-social/indigo/atproto/atclient" 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 "github.com/go-git/go-git/v5/plumbing" 13 "tangled.org/core/api/tangled" 14 "tangled.org/core/knotmirror/db" 15 "tangled.org/core/knotmirror/xrpc/gitea" 16) 17 18func (x *Xrpc) GetArchive(w http.ResponseWriter, r *http.Request) { 19 var ( 20 repoQuery = r.URL.Query().Get("repo") 21 ref = r.URL.Query().Get("ref") 22 format = r.URL.Query().Get("format") 23 prefix = r.URL.Query().Get("prefix") 24 ) 25 26 repo, err := syntax.ParseDID(repoQuery) 27 if err != nil { 28 writeJson(w, http.StatusBadRequest, atclient.ErrorBody{Name: "BadRequest", Message: fmt.Sprintf("repo parameter invalid: %s", repoQuery)}) 29 return 30 } 31 32 if format != "tar.gz" { 33 writeJson(w, http.StatusBadRequest, atclient.ErrorBody{Name: "BadRequest", Message: "only tar.gz format is supported"}) 34 return 35 } 36 if format == "" { 37 format = "tar.gz" 38 } 39 40 l := x.logger.With("repo", repo, "ref", ref, "format", format, "prefix", prefix) 41 l.Debug("request") 42 43 ctx := r.Context() 44 45 repoPath, err := x.makeRepoPath(ctx, repo) 46 if err != nil { 47 l.Warn("local mirror failed, trying proxy", "err", err) 48 if x.proxyToKnot(w, r, repo) { 49 return 50 } 51 writeJson(w, http.StatusInternalServerError, atclient.ErrorBody{Name: "InternalServerError", Message: "failed to resolve repo"}) 52 return 53 } 54 55 rev := ref 56 if rev == "" { 57 rev = "HEAD" 58 } 59 commit, err := gitea.GetCommit(ctx, repoPath, rev) 60 61 repoName, err := func() (string, error) { 62 r, err := db.GetRepoByRepoDid(ctx, x.db, repo) 63 if err != nil { 64 return "", err 65 } 66 if r == nil { 67 return "", fmt.Errorf("repo not found: %s", repo) 68 } 69 return r.Name, nil 70 }() 71 if err != nil { 72 l.Warn("local mirror failed, trying proxy", "err", err) 73 if x.proxyToKnot(w, r, repo) { 74 return 75 } 76 writeJson(w, http.StatusInternalServerError, atclient.ErrorBody{Name: "InternalServerError", Message: "failed to retrieve repo name"}) 77 return 78 } 79 80 safeRefFilename := strings.ReplaceAll(plumbing.ReferenceName(ref).Short(), "/", "-") 81 immutableLink := func() string { 82 params := url.Values{} 83 params.Set("repo", repo.String()) 84 params.Set("ref", commit.Hash.String()) 85 params.Set("format", format) 86 params.Set("prefix", prefix) 87 return fmt.Sprintf("%s/xrpc/%s?%s", x.cfg.BaseUrl(), tangled.GitTempGetArchiveNSID, params.Encode()) 88 }() 89 90 filename := fmt.Sprintf("%s-%s.tar.gz", repoName, safeRefFilename) 91 w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) 92 w.Header().Set("Content-Type", "application/gzip") 93 w.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"immutable\"", immutableLink)) 94 95 cmd := exec.Command( 96 "git", 97 "archive", 98 fmt.Sprintf("--prefix=%s", prefix), 99 "--format=tar.gz", 100 commit.Hash.String(), 101 ) 102 103 var stderr strings.Builder 104 cmd.Dir = repoPath 105 cmd.Stdout = w 106 cmd.Stderr = &stderr 107 108 if err := cmd.Run(); err != nil { 109 err = fmt.Errorf("%w\n%s", err, stderr.String()) 110 l.Error("failed to archive", "err", err) 111 w.WriteHeader(http.StatusInternalServerError) 112 } 113}