tangled
alpha
login
or
join now
back
round
1
view raw
appview,knotserver: immutable nix flakeref link header
#741
open
opened by
boltless.me
2 months ago
targeting
master
from
push-ptrrwwvnkmxq
Close: #231
Signed-off-by: Seongmin Lee
git@boltless.me
options
unified
split
Changed files
+129
-1
api
tangled
reporesolveRef.go
appview
repo
repo.go
knotserver
git
git.go
xrpc
repo_resolve_ref.go
xrpc.go
lexicons
repo
resolveRef.json
+33
api/tangled/reporesolveRef.go
···
1
1
+
// Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT.
2
2
+
3
3
+
package tangled
4
4
+
5
5
+
// schema: sh.tangled.repo.resolveRef
6
6
+
7
7
+
import (
8
8
+
"bytes"
9
9
+
"context"
10
10
+
11
11
+
"github.com/bluesky-social/indigo/lex/util"
12
12
+
)
13
13
+
14
14
+
const (
15
15
+
RepoResolveRefNSID = "sh.tangled.repo.resolveRef"
16
16
+
)
17
17
+
18
18
+
// RepoResolveRef calls the XRPC method "sh.tangled.repo.resolveRef".
19
19
+
//
20
20
+
// ref: Reference name (branch, tag or other references)
21
21
+
// repo: Repository identifier in format 'did:plc:.../repoName'
22
22
+
func RepoResolveRef(ctx context.Context, c util.LexClient, ref string, repo string) ([]byte, error) {
23
23
+
buf := new(bytes.Buffer)
24
24
+
25
25
+
params := map[string]interface{}{}
26
26
+
params["ref"] = ref
27
27
+
params["repo"] = repo
28
28
+
if err := c.LexDo(ctx, util.Query, "", "sh.tangled.repo.resolveRef", params, nil, buf); err != nil {
29
29
+
return nil, err
30
30
+
}
31
31
+
32
32
+
return buf.Bytes(), nil
33
33
+
}
+18
-1
appview/repo/repo.go
···
110
110
}
111
111
112
112
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
113
113
-
archiveBytes, err := tangled.RepoArchive(r.Context(), xrpcc, "tar.gz", "", ref, repo)
113
113
+
// TODO: we are requesting the knot twice here to get permanent commit-hash.
114
114
+
// This should purely handled from knot instead.
115
115
+
rawHash, err := tangled.RepoResolveRef(r.Context(), xrpcc, ref, repo)
116
116
+
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
117
117
+
l.Error("failed to call XRPC repo.archive", "err", xrpcerr)
118
118
+
rp.pages.Error503(w)
119
119
+
return
120
120
+
}
121
121
+
hash := string(rawHash)
122
122
+
immutableLink := fmt.Sprintf(
123
123
+
"%s/%s/archive/%s",
124
124
+
rp.config.Core.AppviewHost,
125
125
+
repo,
126
126
+
hash,
127
127
+
)
128
128
+
129
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
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
74
+
func (g *GitRepo) Hash() plumbing.Hash {
75
75
+
return g.h
76
76
+
}
77
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
1
+
package xrpc
2
2
+
3
3
+
import (
4
4
+
"fmt"
5
5
+
"net/http"
6
6
+
7
7
+
"tangled.org/core/knotserver/git"
8
8
+
xrpcerr "tangled.org/core/xrpc/errors"
9
9
+
)
10
10
+
11
11
+
func (x *Xrpc) RepoResolveRef(w http.ResponseWriter, r *http.Request) {
12
12
+
repo := r.URL.Query().Get("repo")
13
13
+
repoPath, err := x.parseRepoParam(repo)
14
14
+
if err != nil {
15
15
+
writeError(w, err.(xrpcerr.XrpcError), http.StatusBadRequest)
16
16
+
return
17
17
+
}
18
18
+
19
19
+
ref := r.URL.Query().Get("ref")
20
20
+
// ref can be empty (git.Open handles this)
21
21
+
22
22
+
gr, err := git.Open(repoPath, ref)
23
23
+
if err != nil {
24
24
+
x.Logger.Error("failed to open", "error", err)
25
25
+
writeError(w, xrpcerr.RefNotFoundError, http.StatusNotFound)
26
26
+
return
27
27
+
}
28
28
+
29
29
+
w.Header().Set("Content-Type", "text/plain")
30
30
+
fmt.Fprint(w, gr.Hash().String())
31
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
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
1
+
{
2
2
+
"lexicon": 1,
3
3
+
"id": "sh.tangled.repo.resolveRef",
4
4
+
"defs": {
5
5
+
"main": {
6
6
+
"type": "query",
7
7
+
"description": "Resolve a ref to its corresponding commit hash",
8
8
+
"parameters": {
9
9
+
"type": "params",
10
10
+
"required": ["repo", "ref"],
11
11
+
"properties": {
12
12
+
"repo": {
13
13
+
"type": "string",
14
14
+
"description": "Repository identifier in format 'did:plc:.../repoName'"
15
15
+
},
16
16
+
"ref": {
17
17
+
"type": "string",
18
18
+
"description": "Reference name (branch, tag or other references)"
19
19
+
}
20
20
+
}
21
21
+
},
22
22
+
"output": {
23
23
+
"encoding": "*/*",
24
24
+
"description": "Resolved hash"
25
25
+
},
26
26
+
"errors": [
27
27
+
{
28
28
+
"name": "RepoNotFound",
29
29
+
"description": "Repository not found or access denied"
30
30
+
},
31
31
+
{
32
32
+
"name": "RefNotFound",
33
33
+
"description": "Ref not found"
34
34
+
},
35
35
+
{
36
36
+
"name": "InvalidRequest",
37
37
+
"description": "Invalid request parameters"
38
38
+
}
39
39
+
]
40
40
+
}
41
41
+
}
42
42
+
}