Monorepo for Tangled tangled.org

appview,knotserver: immutable nix flakeref link header

Close: #231

Signed-off-by: Seongmin Lee <git@boltless.me>

boltless.me f81257f0 1b21764c

verified
Changed files
+129 -1
api
appview
repo
knotserver
lexicons
+33
api/tangled/reporesolveRef.go
··· 1 + // Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT. 2 + 3 + package tangled 4 + 5 + // schema: sh.tangled.repo.resolveRef 6 + 7 + import ( 8 + "bytes" 9 + "context" 10 + 11 + "github.com/bluesky-social/indigo/lex/util" 12 + ) 13 + 14 + const ( 15 + RepoResolveRefNSID = "sh.tangled.repo.resolveRef" 16 + ) 17 + 18 + // RepoResolveRef calls the XRPC method "sh.tangled.repo.resolveRef". 19 + // 20 + // ref: Reference name (branch, tag or other references) 21 + // repo: Repository identifier in format 'did:plc:.../repoName' 22 + func RepoResolveRef(ctx context.Context, c util.LexClient, ref string, repo string) ([]byte, error) { 23 + buf := new(bytes.Buffer) 24 + 25 + params := map[string]interface{}{} 26 + params["ref"] = ref 27 + params["repo"] = repo 28 + if err := c.LexDo(ctx, util.Query, "", "sh.tangled.repo.resolveRef", params, nil, buf); err != nil { 29 + return nil, err 30 + } 31 + 32 + return buf.Bytes(), nil 33 + }
+18 -1
appview/repo/repo.go
··· 110 110 } 111 111 112 112 repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name) 113 - archiveBytes, err := tangled.RepoArchive(r.Context(), xrpcc, "tar.gz", "", ref, repo) 113 + // TODO: we are requesting the knot twice here to get permanent commit-hash. 114 + // This should purely handled from knot instead. 115 + rawHash, err := tangled.RepoResolveRef(r.Context(), xrpcc, ref, repo) 116 + if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 117 + l.Error("failed to call XRPC repo.archive", "err", xrpcerr) 118 + rp.pages.Error503(w) 119 + return 120 + } 121 + hash := string(rawHash) 122 + immutableLink := fmt.Sprintf( 123 + "%s/%s/archive/%s", 124 + rp.config.Core.AppviewHost, 125 + repo, 126 + hash, 127 + ) 128 + 129 + archiveBytes, err := tangled.RepoArchive(r.Context(), xrpcc, "tar.gz", "", hash, repo) 114 130 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 115 131 l.Error("failed to call XRPC repo.archive", "err", xrpcerr) 116 132 rp.pages.Error503(w) ··· 123 139 w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) 124 140 w.Header().Set("Content-Type", "application/gzip") 125 141 w.Header().Set("Content-Length", fmt.Sprintf("%d", len(archiveBytes))) 142 + w.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"immutable\"", immutableLink)) 126 143 127 144 // Write the archive data directly 128 145 w.Write(archiveBytes)
+4
knotserver/git/git.go
··· 71 71 return &g, nil 72 72 } 73 73 74 + func (g *GitRepo) Hash() plumbing.Hash { 75 + return g.h 76 + } 77 + 74 78 // re-open a repository and update references 75 79 func (g *GitRepo) Refresh() error { 76 80 refreshed, err := PlainOpen(g.path)
+31
knotserver/xrpc/repo_resolve_ref.go
··· 1 + package xrpc 2 + 3 + import ( 4 + "fmt" 5 + "net/http" 6 + 7 + "tangled.org/core/knotserver/git" 8 + xrpcerr "tangled.org/core/xrpc/errors" 9 + ) 10 + 11 + func (x *Xrpc) RepoResolveRef(w http.ResponseWriter, r *http.Request) { 12 + repo := r.URL.Query().Get("repo") 13 + repoPath, err := x.parseRepoParam(repo) 14 + if err != nil { 15 + writeError(w, err.(xrpcerr.XrpcError), http.StatusBadRequest) 16 + return 17 + } 18 + 19 + ref := r.URL.Query().Get("ref") 20 + // ref can be empty (git.Open handles this) 21 + 22 + gr, err := git.Open(repoPath, ref) 23 + if err != nil { 24 + x.Logger.Error("failed to open", "error", err) 25 + writeError(w, xrpcerr.RefNotFoundError, http.StatusNotFound) 26 + return 27 + } 28 + 29 + w.Header().Set("Content-Type", "text/plain") 30 + fmt.Fprint(w, gr.Hash().String()) 31 + }
+1
knotserver/xrpc/xrpc.go
··· 66 66 r.Get("/"+tangled.RepoBranchNSID, x.RepoBranch) 67 67 r.Get("/"+tangled.RepoArchiveNSID, x.RepoArchive) 68 68 r.Get("/"+tangled.RepoLanguagesNSID, x.RepoLanguages) 69 + r.Get("/"+tangled.RepoResolveRefNSID, x.RepoResolveRef) 69 70 70 71 // knot query endpoints (no auth required) 71 72 r.Get("/"+tangled.KnotListKeysNSID, x.ListKeys)
+42
lexicons/repo/resolveRef.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "sh.tangled.repo.resolveRef", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "Resolve a ref to its corresponding commit hash", 8 + "parameters": { 9 + "type": "params", 10 + "required": ["repo", "ref"], 11 + "properties": { 12 + "repo": { 13 + "type": "string", 14 + "description": "Repository identifier in format 'did:plc:.../repoName'" 15 + }, 16 + "ref": { 17 + "type": "string", 18 + "description": "Reference name (branch, tag or other references)" 19 + } 20 + } 21 + }, 22 + "output": { 23 + "encoding": "*/*", 24 + "description": "Resolved hash" 25 + }, 26 + "errors": [ 27 + { 28 + "name": "RepoNotFound", 29 + "description": "Repository not found or access denied" 30 + }, 31 + { 32 + "name": "RefNotFound", 33 + "description": "Ref not found" 34 + }, 35 + { 36 + "name": "InvalidRequest", 37 + "description": "Invalid request parameters" 38 + } 39 + ] 40 + } 41 + } 42 + }