appview,knotserver: immutable nix flakeref link header #741

open
opened by boltless.me targeting master from push-ptrrwwvnkmxq
Changed files
+129 -1
api
appview
repo
knotserver
lexicons
+33
api/tangled/reporesolveRef.go
···
+
// Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT.
+
+
package tangled
+
+
// schema: sh.tangled.repo.resolveRef
+
+
import (
+
"bytes"
+
"context"
+
+
"github.com/bluesky-social/indigo/lex/util"
+
)
+
+
const (
+
RepoResolveRefNSID = "sh.tangled.repo.resolveRef"
+
)
+
+
// RepoResolveRef calls the XRPC method "sh.tangled.repo.resolveRef".
+
//
+
// ref: Reference name (branch, tag or other references)
+
// repo: Repository identifier in format 'did:plc:.../repoName'
+
func RepoResolveRef(ctx context.Context, c util.LexClient, ref string, repo string) ([]byte, error) {
+
buf := new(bytes.Buffer)
+
+
params := map[string]interface{}{}
+
params["ref"] = ref
+
params["repo"] = repo
+
if err := c.LexDo(ctx, util.Query, "", "sh.tangled.repo.resolveRef", params, nil, buf); err != nil {
+
return nil, err
+
}
+
+
return buf.Bytes(), nil
+
}
+18 -1
appview/repo/repo.go
···
}
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
-
archiveBytes, err := tangled.RepoArchive(r.Context(), xrpcc, "tar.gz", "", ref, repo)
+
// TODO: we are requesting the knot twice here to get permanent commit-hash.
+
// This should purely handled from knot instead.
+
rawHash, err := tangled.RepoResolveRef(r.Context(), xrpcc, ref, repo)
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
+
l.Error("failed to call XRPC repo.archive", "err", xrpcerr)
+
rp.pages.Error503(w)
+
return
+
}
+
hash := string(rawHash)
+
immutableLink := fmt.Sprintf(
+
"%s/%s/archive/%s",
+
rp.config.Core.AppviewHost,
+
repo,
+
hash,
+
)
+
+
archiveBytes, err := tangled.RepoArchive(r.Context(), xrpcc, "tar.gz", "", hash, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.archive", "err", xrpcerr)
rp.pages.Error503(w)
···
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
w.Header().Set("Content-Type", "application/gzip")
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(archiveBytes)))
+
w.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"immutable\"", immutableLink))
// Write the archive data directly
w.Write(archiveBytes)
+4
knotserver/git/git.go
···
return &g, nil
}
+
func (g *GitRepo) Hash() plumbing.Hash {
+
return g.h
+
}
+
// re-open a repository and update references
func (g *GitRepo) Refresh() error {
refreshed, err := PlainOpen(g.path)
+31
knotserver/xrpc/repo_resolve_ref.go
···
+
package xrpc
+
+
import (
+
"fmt"
+
"net/http"
+
+
"tangled.org/core/knotserver/git"
+
xrpcerr "tangled.org/core/xrpc/errors"
+
)
+
+
func (x *Xrpc) RepoResolveRef(w http.ResponseWriter, r *http.Request) {
+
repo := r.URL.Query().Get("repo")
+
repoPath, err := x.parseRepoParam(repo)
+
if err != nil {
+
writeError(w, err.(xrpcerr.XrpcError), http.StatusBadRequest)
+
return
+
}
+
+
ref := r.URL.Query().Get("ref")
+
// ref can be empty (git.Open handles this)
+
+
gr, err := git.Open(repoPath, ref)
+
if err != nil {
+
x.Logger.Error("failed to open", "error", err)
+
writeError(w, xrpcerr.RefNotFoundError, http.StatusNotFound)
+
return
+
}
+
+
w.Header().Set("Content-Type", "text/plain")
+
fmt.Fprint(w, gr.Hash().String())
+
}
+1
knotserver/xrpc/xrpc.go
···
r.Get("/"+tangled.RepoBranchNSID, x.RepoBranch)
r.Get("/"+tangled.RepoArchiveNSID, x.RepoArchive)
r.Get("/"+tangled.RepoLanguagesNSID, x.RepoLanguages)
+
r.Get("/"+tangled.RepoResolveRefNSID, x.RepoResolveRef)
// knot query endpoints (no auth required)
r.Get("/"+tangled.KnotListKeysNSID, x.ListKeys)
+42
lexicons/repo/resolveRef.json
···
+
{
+
"lexicon": 1,
+
"id": "sh.tangled.repo.resolveRef",
+
"defs": {
+
"main": {
+
"type": "query",
+
"description": "Resolve a ref to its corresponding commit hash",
+
"parameters": {
+
"type": "params",
+
"required": ["repo", "ref"],
+
"properties": {
+
"repo": {
+
"type": "string",
+
"description": "Repository identifier in format 'did:plc:.../repoName'"
+
},
+
"ref": {
+
"type": "string",
+
"description": "Reference name (branch, tag or other references)"
+
}
+
}
+
},
+
"output": {
+
"encoding": "*/*",
+
"description": "Resolved hash"
+
},
+
"errors": [
+
{
+
"name": "RepoNotFound",
+
"description": "Repository not found or access denied"
+
},
+
{
+
"name": "RefNotFound",
+
"description": "Ref not found"
+
},
+
{
+
"name": "InvalidRequest",
+
"description": "Invalid request parameters"
+
}
+
]
+
}
+
}
+
}