Signed-off-by: Lewis lewis@tangled.org
+245
-157
Diff
round #3
+30
-24
knotserver/git.go
+30
-24
knotserver/git.go
···
5
5
"fmt"
6
6
"io"
7
7
"net/http"
8
-
"path/filepath"
9
8
"strings"
10
9
11
-
securejoin "github.com/cyphar/filepath-securejoin"
12
10
"github.com/go-chi/chi/v5"
13
11
"tangled.org/core/knotserver/git/service"
14
12
)
15
13
16
-
func (h *Knot) InfoRefs(w http.ResponseWriter, r *http.Request) {
14
+
func (h *Knot) resolveRepoPath(r *http.Request) (string, string, error) {
17
15
did := chi.URLParam(r, "did")
18
16
name := chi.URLParam(r, "name")
19
-
repoName, err := securejoin.SecureJoin(did, name)
17
+
18
+
if name == "" && strings.HasPrefix(did, "did:") {
19
+
repoPath, _, repoName, err := h.db.ResolveRepoDIDOnDisk(h.c.Repo.ScanPath, did)
20
+
if err != nil {
21
+
return "", "", fmt.Errorf("unknown repo DID: %w", err)
22
+
}
23
+
return repoPath, repoName, nil
24
+
}
25
+
26
+
repoDid, err := h.db.GetRepoDid(did, name)
20
27
if err != nil {
21
-
gitError(w, "repository not found", http.StatusNotFound)
22
-
h.l.Error("git: failed to secure join repo path", "handler", "InfoRefs", "error", err)
23
-
return
28
+
return "", "", fmt.Errorf("repo not found: %w", err)
24
29
}
30
+
repoPath, _, _, err := h.db.ResolveRepoDIDOnDisk(h.c.Repo.ScanPath, repoDid)
31
+
if err != nil {
32
+
return "", "", fmt.Errorf("repo not found: %w", err)
33
+
}
34
+
return repoPath, name, nil
35
+
}
25
36
26
-
repoPath, err := securejoin.SecureJoin(h.c.Repo.ScanPath, repoName)
37
+
func (h *Knot) InfoRefs(w http.ResponseWriter, r *http.Request) {
38
+
repoPath, name, err := h.resolveRepoPath(r)
27
39
if err != nil {
28
40
gitError(w, "repository not found", http.StatusNotFound)
29
-
h.l.Error("git: failed to secure join repo path", "handler", "InfoRefs", "error", err)
41
+
h.l.Error("git: failed to resolve repo path", "handler", "InfoRefs", "error", err)
30
42
return
31
43
}
32
44
···
57
69
}
58
70
59
71
func (h *Knot) UploadArchive(w http.ResponseWriter, r *http.Request) {
60
-
did := chi.URLParam(r, "did")
61
-
name := chi.URLParam(r, "name")
62
-
repo, err := securejoin.SecureJoin(h.c.Repo.ScanPath, filepath.Join(did, name))
72
+
repo, _, err := h.resolveRepoPath(r)
63
73
if err != nil {
64
-
gitError(w, err.Error(), http.StatusInternalServerError)
65
-
h.l.Error("git: failed to secure join repo path", "handler", "UploadPack", "error", err)
74
+
gitError(w, "repository not found", http.StatusNotFound)
75
+
h.l.Error("git: failed to resolve repo path", "handler", "UploadArchive", "error", err)
66
76
return
67
77
}
68
78
···
104
114
}
105
115
106
116
func (h *Knot) UploadPack(w http.ResponseWriter, r *http.Request) {
107
-
did := chi.URLParam(r, "did")
108
-
name := chi.URLParam(r, "name")
109
-
repo, err := securejoin.SecureJoin(h.c.Repo.ScanPath, filepath.Join(did, name))
117
+
repo, _, err := h.resolveRepoPath(r)
110
118
if err != nil {
111
-
gitError(w, err.Error(), http.StatusInternalServerError)
112
-
h.l.Error("git: failed to secure join repo path", "handler", "UploadPack", "error", err)
119
+
gitError(w, "repository not found", http.StatusNotFound)
120
+
h.l.Error("git: failed to resolve repo path", "handler", "UploadPack", "error", err)
113
121
return
114
122
}
115
123
···
153
161
}
154
162
155
163
func (h *Knot) ReceivePack(w http.ResponseWriter, r *http.Request) {
156
-
did := chi.URLParam(r, "did")
157
-
name := chi.URLParam(r, "name")
158
-
_, err := securejoin.SecureJoin(h.c.Repo.ScanPath, filepath.Join(did, name))
164
+
_, name, err := h.resolveRepoPath(r)
159
165
if err != nil {
160
-
gitError(w, err.Error(), http.StatusForbidden)
161
-
h.l.Error("git: failed to secure join repo path", "handler", "ReceivePack", "error", err)
166
+
gitError(w, "repository not found", http.StatusNotFound)
167
+
h.l.Error("git: failed to resolve repo path", "handler", "ReceivePack", "error", err)
162
168
return
163
169
}
164
170
+95
-57
knotserver/ingester.go
+95
-57
knotserver/ingester.go
···
14
14
"github.com/bluesky-social/indigo/atproto/syntax"
15
15
"github.com/bluesky-social/indigo/xrpc"
16
16
"github.com/bluesky-social/jetstream/pkg/models"
17
-
securejoin "github.com/cyphar/filepath-securejoin"
18
17
"tangled.org/core/api/tangled"
19
18
"tangled.org/core/knotserver/db"
20
19
"tangled.org/core/knotserver/git"
···
102
101
return fmt.Errorf("ignoring pull record: target repo is nil")
103
102
}
104
103
105
-
l = l.With("target_repo", record.Target.Repo)
104
+
l = l.With("target_repo", record.Target.Repo, "target_repo_did", record.Target.RepoDid)
106
105
l = l.With("target_branch", record.Target.Branch)
107
106
108
107
if record.Source == nil {
109
108
return fmt.Errorf("ignoring pull record: not a branch-based pull request")
110
109
}
111
110
112
-
if record.Source.Repo != nil {
111
+
if record.Source.Repo != nil || record.Source.RepoDid != nil {
113
112
return fmt.Errorf("ignoring pull record: fork based pull")
114
113
}
115
114
116
-
repoAt, err := syntax.ParseATURI(record.Target.Repo)
117
-
if err != nil {
118
-
return fmt.Errorf("failed to parse ATURI: %w", err)
119
-
}
115
+
var repoPath, ownerDid, repoName, repoDid string
116
+
switch {
117
+
case record.Target.RepoDid != nil && *record.Target.RepoDid != "":
118
+
repoDid = *record.Target.RepoDid
119
+
var lookupErr error
120
+
repoPath, ownerDid, repoName, lookupErr = h.db.ResolveRepoDIDOnDisk(h.c.Repo.ScanPath, repoDid)
121
+
if lookupErr != nil {
122
+
return fmt.Errorf("unknown target repo DID %s: %w", repoDid, lookupErr)
123
+
}
120
124
121
-
// resolve this aturi to extract the repo record
122
-
ident, err := h.resolver.ResolveIdent(ctx, repoAt.Authority().String())
123
-
if err != nil || ident.Handle.IsInvalidHandle() {
124
-
return fmt.Errorf("failed to resolve handle: %w", err)
125
-
}
125
+
case record.Target.Repo != nil:
126
+
// TODO: get rid of this PDS fetch once all repos have DIDs
127
+
repoAt, parseErr := syntax.ParseATURI(*record.Target.Repo)
128
+
if parseErr != nil {
129
+
return fmt.Errorf("failed to parse ATURI: %w", parseErr)
130
+
}
126
131
127
-
xrpcc := xrpc.Client{
128
-
Host: ident.PDSEndpoint(),
129
-
}
132
+
ident, resolveErr := h.resolver.ResolveIdent(ctx, repoAt.Authority().String())
133
+
if resolveErr != nil || ident.Handle.IsInvalidHandle() {
134
+
return fmt.Errorf("failed to resolve handle: %w", resolveErr)
135
+
}
130
136
131
-
resp, err := comatproto.RepoGetRecord(ctx, &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String())
132
-
if err != nil {
133
-
return fmt.Errorf("failed to resolver repo: %w", err)
134
-
}
137
+
xrpcc := xrpc.Client{
138
+
Host: ident.PDSEndpoint(),
139
+
}
135
140
136
-
repo := resp.Value.Val.(*tangled.Repo)
141
+
resp, getErr := comatproto.RepoGetRecord(ctx, &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String())
142
+
if getErr != nil {
143
+
return fmt.Errorf("failed to resolve repo: %w", getErr)
144
+
}
137
145
138
-
if repo.Knot != h.c.Server.Hostname {
139
-
return fmt.Errorf("rejected pull record: not this knot, %s != %s", repo.Knot, h.c.Server.Hostname)
140
-
}
146
+
repo := resp.Value.Val.(*tangled.Repo)
141
147
142
-
didSlashRepo, err := securejoin.SecureJoin(ident.DID.String(), repo.Name)
143
-
if err != nil {
144
-
return fmt.Errorf("failed to construct relative repo path: %w", err)
145
-
}
148
+
if repo.Knot != h.c.Server.Hostname {
149
+
return fmt.Errorf("rejected pull record: not this knot, %s != %s", repo.Knot, h.c.Server.Hostname)
150
+
}
146
151
147
-
repoPath, err := securejoin.SecureJoin(h.c.Repo.ScanPath, didSlashRepo)
148
-
if err != nil {
149
-
return fmt.Errorf("failed to construct absolute repo path: %w", err)
152
+
ownerDid = ident.DID.String()
153
+
repoName = repo.Name
154
+
155
+
repoDid, didErr := h.db.GetRepoDid(ownerDid, repoName)
156
+
if didErr != nil {
157
+
return fmt.Errorf("failed to resolve repo DID for %s/%s: %w", ownerDid, repoName, didErr)
158
+
}
159
+
160
+
var lookupErr error
161
+
repoPath, _, _, lookupErr = h.db.ResolveRepoDIDOnDisk(h.c.Repo.ScanPath, repoDid)
162
+
if lookupErr != nil {
163
+
return fmt.Errorf("failed to resolve repo on disk: %w", lookupErr)
164
+
}
165
+
166
+
default:
167
+
return fmt.Errorf("ignoring pull record: target has neither repo nor repoDid")
150
168
}
151
169
152
170
gr, err := git.Open(repoPath, record.Source.Sha)
···
189
207
Kind: string(workflow.TriggerKindPullRequest),
190
208
PullRequest: &trigger,
191
209
Repo: &tangled.Pipeline_TriggerRepo{
192
-
Did: ident.DID.String(),
193
-
Knot: repo.Knot,
194
-
Repo: repo.Name,
210
+
Did: ownerDid,
211
+
Knot: h.c.Server.Hostname,
212
+
Repo: &repoName,
213
+
RepoDid: &repoDid,
195
214
},
196
215
},
197
216
}
···
226
245
return fmt.Errorf("failed to unmarshal record: %w", err)
227
246
}
228
247
229
-
repoAt, err := syntax.ParseATURI(record.Repo)
230
-
if err != nil {
231
-
return err
232
-
}
233
-
234
248
subjectId, err := h.resolver.ResolveIdent(ctx, record.Subject)
235
249
if err != nil || subjectId.Handle.IsInvalidHandle() {
236
250
return err
237
251
}
238
252
239
-
// TODO: fix this for good, we need to fetch the record here unfortunately
240
-
// resolve this aturi to extract the repo record
241
-
owner, err := h.resolver.ResolveIdent(ctx, repoAt.Authority().String())
242
-
if err != nil || owner.Handle.IsInvalidHandle() {
243
-
return fmt.Errorf("failed to resolve handle: %w", err)
244
-
}
253
+
var rbacResource string
254
+
switch {
255
+
case record.RepoDid != nil && *record.RepoDid != "":
256
+
ownerDid, _, lookupErr := h.db.GetRepoKeyOwner(*record.RepoDid)
257
+
if lookupErr != nil {
258
+
return fmt.Errorf("unknown repo DID %s: %w", *record.RepoDid, lookupErr)
259
+
}
260
+
if ownerDid != did {
261
+
return fmt.Errorf("collaborator record author %s does not own repo %s", did, *record.RepoDid)
262
+
}
263
+
rbacResource = *record.RepoDid
245
264
246
-
xrpcc := xrpc.Client{
247
-
Host: owner.PDSEndpoint(),
248
-
}
265
+
case record.Repo != nil:
266
+
// TODO: get rid of this PDS fetch once all repos have DIDs
267
+
repoAt, parseErr := syntax.ParseATURI(*record.Repo)
268
+
if parseErr != nil {
269
+
return parseErr
270
+
}
249
271
250
-
resp, err := comatproto.RepoGetRecord(ctx, &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String())
251
-
if err != nil {
252
-
return err
253
-
}
272
+
owner, resolveErr := h.resolver.ResolveIdent(ctx, repoAt.Authority().String())
273
+
if resolveErr != nil || owner.Handle.IsInvalidHandle() {
274
+
return fmt.Errorf("failed to resolve handle: %w", resolveErr)
275
+
}
276
+
277
+
xrpcc := xrpc.Client{
278
+
Host: owner.PDSEndpoint(),
279
+
}
254
280
255
-
repo := resp.Value.Val.(*tangled.Repo)
256
-
didSlashRepo, _ := securejoin.SecureJoin(owner.DID.String(), repo.Name)
281
+
resp, getErr := comatproto.RepoGetRecord(ctx, &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String())
282
+
if getErr != nil {
283
+
return getErr
284
+
}
285
+
286
+
repo := resp.Value.Val.(*tangled.Repo)
287
+
repoDid, didErr := h.db.GetRepoDid(owner.DID.String(), repo.Name)
288
+
if didErr != nil {
289
+
return fmt.Errorf("failed to resolve repo DID for %s/%s: %w", owner.DID.String(), repo.Name, didErr)
290
+
}
291
+
rbacResource = repoDid
292
+
293
+
default:
294
+
return fmt.Errorf("collaborator record has neither repo nor repoDid")
295
+
}
257
296
258
-
// check perms for this user
259
-
ok, err := h.e.IsCollaboratorInviteAllowed(did, rbac.ThisServer, didSlashRepo)
297
+
ok, err := h.e.IsCollaboratorInviteAllowed(did, rbac.ThisServer, rbacResource)
260
298
if err != nil {
261
299
return fmt.Errorf("failed to check permissions: %w", err)
262
300
}
263
301
if !ok {
264
-
return fmt.Errorf("insufficient permissions: %s, %s, %s", did, "IsCollaboratorInviteAllowed", didSlashRepo)
302
+
return fmt.Errorf("insufficient permissions: %s, %s, %s", did, "IsCollaboratorInviteAllowed", rbacResource)
265
303
}
266
304
267
305
if err := h.db.AddDid(subjectId.DID.String()); err != nil {
···
269
307
}
270
308
h.jc.AddDid(subjectId.DID.String())
271
309
272
-
if err := h.e.AddCollaborator(subjectId.DID.String(), rbac.ThisServer, didSlashRepo); err != nil {
310
+
if err := h.e.AddCollaborator(subjectId.DID.String(), rbac.ThisServer, rbacResource); err != nil {
273
311
return err
274
312
}
275
313
+110
-74
knotserver/internal.go
+110
-74
knotserver/internal.go
···
3
3
import (
4
4
"context"
5
5
"encoding/json"
6
-
"errors"
7
6
"fmt"
8
7
"log/slog"
9
8
"net/http"
10
9
"path/filepath"
11
10
"strings"
12
11
13
-
securejoin "github.com/cyphar/filepath-securejoin"
14
12
"github.com/go-chi/chi/v5"
15
13
"github.com/go-chi/chi/v5/middleware"
16
14
"github.com/go-git/go-git/v5/plumbing"
···
72
70
// the body will be qualified repository path on success/push-denied
73
71
// or an error message when process failed
74
72
func (h *InternalHandle) Guard(w http.ResponseWriter, r *http.Request) {
75
-
l := h.l.With("handler", "PostReceiveHook")
73
+
l := h.l.With("handler", "Guard")
76
74
77
75
var (
78
76
incomingUser = r.URL.Query().Get("user")
···
87
85
return
88
86
}
89
87
90
-
// did:foo/repo-name or
91
-
// handle/repo-name or
92
-
// any of the above with a leading slash (/)
93
88
components := strings.Split(strings.TrimPrefix(strings.Trim(repo, "'"), "/"), "/")
94
89
l.Info("command components", "components", components)
95
90
96
-
if len(components) != 2 {
91
+
var rbacResource string
92
+
var diskRelative string
93
+
94
+
switch {
95
+
case len(components) == 1 && strings.HasPrefix(components[0], "did:"):
96
+
repoDid := components[0]
97
+
repoPath, _, _, lookupErr := h.db.ResolveRepoDIDOnDisk(h.c.Repo.ScanPath, repoDid)
98
+
if lookupErr != nil {
99
+
w.WriteHeader(http.StatusNotFound)
100
+
l.Error("repo DID not found", "repoDid", repoDid, "err", lookupErr)
101
+
fmt.Fprintln(w, "repo not found")
102
+
return
103
+
}
104
+
rbacResource = repoDid
105
+
rel, relErr := filepath.Rel(h.c.Repo.ScanPath, repoPath)
106
+
if relErr != nil {
107
+
w.WriteHeader(http.StatusInternalServerError)
108
+
l.Error("failed to compute relative path", "repoPath", repoPath, "err", relErr)
109
+
fmt.Fprintln(w, "internal error")
110
+
return
111
+
}
112
+
diskRelative = rel
113
+
114
+
case len(components) == 2:
115
+
repoOwner := components[0]
116
+
resolver := idresolver.DefaultResolver(h.c.Server.PlcUrl)
117
+
repoOwnerIdent, resolveErr := resolver.ResolveIdent(r.Context(), repoOwner)
118
+
if resolveErr != nil || repoOwnerIdent.Handle.IsInvalidHandle() {
119
+
l.Error("Error resolving handle", "handle", repoOwner, "err", resolveErr)
120
+
w.WriteHeader(http.StatusInternalServerError)
121
+
fmt.Fprintf(w, "error resolving handle: invalid handle\n")
122
+
return
123
+
}
124
+
ownerDid := repoOwnerIdent.DID.String()
125
+
repoName := components[1]
126
+
repoDid, didErr := h.db.GetRepoDid(ownerDid, repoName)
127
+
if didErr != nil {
128
+
w.WriteHeader(http.StatusNotFound)
129
+
l.Error("repo DID not found", "owner", ownerDid, "name", repoName, "err", didErr)
130
+
fmt.Fprintln(w, "repo not found")
131
+
return
132
+
}
133
+
repoPath, _, _, lookupErr := h.db.ResolveRepoDIDOnDisk(h.c.Repo.ScanPath, repoDid)
134
+
if lookupErr != nil {
135
+
w.WriteHeader(http.StatusNotFound)
136
+
l.Error("repo not found on disk", "repoDid", repoDid, "err", lookupErr)
137
+
fmt.Fprintln(w, "repo not found")
138
+
return
139
+
}
140
+
rbacResource = repoDid
141
+
rel, relErr := filepath.Rel(h.c.Repo.ScanPath, repoPath)
142
+
if relErr != nil {
143
+
w.WriteHeader(http.StatusInternalServerError)
144
+
l.Error("failed to compute relative path", "repoPath", repoPath, "err", relErr)
145
+
fmt.Fprintln(w, "internal error")
146
+
return
147
+
}
148
+
diskRelative = rel
149
+
150
+
default:
97
151
w.WriteHeader(http.StatusBadRequest)
98
152
l.Error("invalid repo format", "components", components)
99
-
fmt.Fprintln(w, "invalid repo format, needs <user>/<repo> or /<user>/<repo>")
153
+
fmt.Fprintln(w, "invalid repo format, needs <user>/<repo>, /<user>/<repo>, or <repo-did>")
100
154
return
101
155
}
102
-
repoOwner := components[0]
103
-
repoName := components[1]
104
-
105
-
resolver := idresolver.DefaultResolver(h.c.Server.PlcUrl)
106
-
107
-
repoOwnerIdent, err := resolver.ResolveIdent(r.Context(), repoOwner)
108
-
if err != nil || repoOwnerIdent.Handle.IsInvalidHandle() {
109
-
l.Error("Error resolving handle", "handle", repoOwner, "err", err)
110
-
w.WriteHeader(http.StatusInternalServerError)
111
-
fmt.Fprintf(w, "error resolving handle: invalid handle\n")
112
-
return
113
-
}
114
-
repoOwnerDid := repoOwnerIdent.DID.String()
115
-
116
-
qualifiedRepo, _ := securejoin.SecureJoin(repoOwnerDid, repoName)
117
156
118
157
if gitCommand == "git-receive-pack" {
119
-
ok, err := h.e.IsPushAllowed(incomingUser, rbac.ThisServer, qualifiedRepo)
158
+
ok, err := h.e.IsPushAllowed(incomingUser, rbac.ThisServer, rbacResource)
120
159
if err != nil || !ok {
121
160
w.WriteHeader(http.StatusForbidden)
122
161
fmt.Fprint(w, repo)
···
125
164
}
126
165
127
166
w.WriteHeader(http.StatusOK)
128
-
fmt.Fprint(w, qualifiedRepo)
167
+
fmt.Fprint(w, diskRelative)
129
168
}
130
169
131
170
type PushOptions struct {
···
140
179
gitRelativeDir, err := filepath.Rel(h.c.Repo.ScanPath, gitAbsoluteDir)
141
180
if err != nil {
142
181
l.Error("failed to calculate relative git dir", "scanPath", h.c.Repo.ScanPath, "gitAbsoluteDir", gitAbsoluteDir)
182
+
w.WriteHeader(http.StatusInternalServerError)
143
183
return
144
184
}
145
185
146
-
parts := strings.SplitN(gitRelativeDir, "/", 2)
147
-
if len(parts) != 2 {
148
-
l.Error("invalid git dir", "gitRelativeDir", gitRelativeDir)
186
+
repoDid := gitRelativeDir
187
+
if !strings.HasPrefix(repoDid, "did:") {
188
+
l.Error("invalid git dir, expected repo DID", "gitRelativeDir", gitRelativeDir)
189
+
w.WriteHeader(http.StatusBadRequest)
190
+
return
191
+
}
192
+
193
+
ownerDid, repoName, err := h.db.GetRepoKeyOwner(repoDid)
194
+
if err != nil {
195
+
l.Error("failed to resolve repo DID from git dir", "repoDid", repoDid, "err", err)
196
+
w.WriteHeader(http.StatusBadRequest)
149
197
return
150
198
}
151
-
repoDid := parts[0]
152
-
repoName := parts[1]
153
199
154
200
gitUserDid := r.Header.Get("X-Git-User-Did")
155
201
···
176
222
}
177
223
178
224
for _, line := range lines {
179
-
err := h.insertRefUpdate(line, gitUserDid, repoDid, repoName)
225
+
err := h.insertRefUpdate(line, gitUserDid, ownerDid, repoName, repoDid)
180
226
if err != nil {
181
227
l.Error("failed to insert op", "err", err, "line", line, "did", gitUserDid, "repo", gitRelativeDir)
182
-
// non-fatal
183
228
}
184
229
185
-
err = h.emitCompareLink(&resp.Messages, line, repoDid, repoName)
230
+
err = h.emitCompareLink(&resp.Messages, line, ownerDid, repoName, repoDid)
186
231
if err != nil {
187
232
l.Error("failed to reply with compare link", "err", err, "line", line, "did", gitUserDid, "repo", gitRelativeDir)
188
-
// non-fatal
189
233
}
190
234
191
-
err = h.triggerPipeline(&resp.Messages, line, gitUserDid, repoDid, repoName, pushOptions)
235
+
err = h.triggerPipeline(&resp.Messages, line, gitUserDid, ownerDid, repoName, repoDid, pushOptions)
192
236
if err != nil {
193
237
l.Error("failed to trigger pipeline", "err", err, "line", line, "did", gitUserDid, "repo", gitRelativeDir)
194
-
// non-fatal
195
238
}
196
239
}
197
240
198
241
writeJSON(w, resp)
199
242
}
200
243
201
-
func (h *InternalHandle) insertRefUpdate(line git.PostReceiveLine, gitUserDid, repoDid, repoName string) error {
202
-
didSlashRepo, err := securejoin.SecureJoin(repoDid, repoName)
203
-
if err != nil {
204
-
return err
205
-
}
206
-
207
-
repoPath, err := securejoin.SecureJoin(h.c.Repo.ScanPath, didSlashRepo)
208
-
if err != nil {
209
-
return err
244
+
func (h *InternalHandle) insertRefUpdate(line git.PostReceiveLine, gitUserDid, ownerDid, repoName, repoDid string) error {
245
+
repoPath, _, _, resolveErr := h.db.ResolveRepoDIDOnDisk(h.c.Repo.ScanPath, repoDid)
246
+
if resolveErr != nil {
247
+
return fmt.Errorf("failed to resolve repo on disk: %w", resolveErr)
210
248
}
211
249
212
250
gr, err := git.Open(repoPath, line.Ref)
···
214
252
return fmt.Errorf("failed to open git repo at ref %s: %w", line.Ref, err)
215
253
}
216
254
217
-
var errs error
218
255
meta, err := gr.RefUpdateMeta(line)
219
-
errors.Join(errs, err)
256
+
if err != nil {
257
+
return fmt.Errorf("failed to get ref update metadata: %w", err)
258
+
}
220
259
221
260
metaRecord := meta.AsRecord()
222
261
···
225
264
NewSha: line.NewSha.String(),
226
265
Ref: line.Ref,
227
266
CommitterDid: gitUserDid,
228
-
RepoDid: repoDid,
267
+
OwnerDid: &ownerDid,
229
268
RepoName: repoName,
269
+
RepoDid: &repoDid,
230
270
Meta: &metaRecord,
231
271
}
272
+
232
273
eventJson, err := json.Marshal(refUpdate)
233
274
if err != nil {
234
275
return err
···
240
281
EventJson: string(eventJson),
241
282
}
242
283
243
-
return errors.Join(errs, h.db.InsertEvent(event, h.n))
284
+
return h.db.InsertEvent(event, h.n)
244
285
}
245
286
246
287
func (h *InternalHandle) triggerPipeline(
247
288
clientMsgs *[]string,
248
289
line git.PostReceiveLine,
249
290
gitUserDid string,
250
-
repoDid string,
291
+
ownerDid string,
251
292
repoName string,
293
+
repoDid string,
252
294
pushOptions PushOptions,
253
295
) error {
254
296
if pushOptions.skipCi {
255
297
return nil
256
298
}
257
299
258
-
didSlashRepo, err := securejoin.SecureJoin(repoDid, repoName)
259
-
if err != nil {
260
-
return err
261
-
}
262
-
263
-
repoPath, err := securejoin.SecureJoin(h.c.Repo.ScanPath, didSlashRepo)
264
-
if err != nil {
265
-
return err
300
+
repoPath, _, _, resolveErr := h.db.ResolveRepoDIDOnDisk(h.c.Repo.ScanPath, repoDid)
301
+
if resolveErr != nil {
302
+
return fmt.Errorf("failed to resolve repo on disk: %w", resolveErr)
266
303
}
267
304
268
305
gr, err := git.Open(repoPath, line.Ref)
···
299
336
NewSha: line.NewSha.String(),
300
337
}
301
338
339
+
triggerRepo := &tangled.Pipeline_TriggerRepo{
340
+
Did: ownerDid,
341
+
Knot: h.c.Server.Hostname,
342
+
Repo: &repoName,
343
+
RepoDid: &repoDid,
344
+
}
345
+
302
346
compiler := workflow.Compiler{
303
347
Trigger: tangled.Pipeline_TriggerMetadata{
304
348
Kind: string(workflow.TriggerKindPush),
305
349
Push: &trigger,
306
-
Repo: &tangled.Pipeline_TriggerRepo{
307
-
Did: repoDid,
308
-
Knot: h.c.Server.Hostname,
309
-
Repo: repoName,
310
-
},
350
+
Repo: triggerRepo,
311
351
},
312
352
}
313
353
···
348
388
func (h *InternalHandle) emitCompareLink(
349
389
clientMsgs *[]string,
350
390
line git.PostReceiveLine,
351
-
repoDid string,
391
+
ownerDid string,
352
392
repoName string,
393
+
repoDid string,
353
394
) error {
354
395
// this is a second push to a branch, don't reply with the link again
355
396
if !line.OldSha.IsZero() {
···
365
406
366
407
pushedRef := plumbing.ReferenceName(line.Ref)
367
408
368
-
userIdent, err := h.res.ResolveIdent(context.Background(), repoDid)
369
-
user := repoDid
409
+
userIdent, err := h.res.ResolveIdent(context.Background(), ownerDid)
410
+
user := ownerDid
370
411
if err == nil {
371
412
user = userIdent.Handle.String()
372
413
}
373
414
374
-
didSlashRepo, err := securejoin.SecureJoin(repoDid, repoName)
375
-
if err != nil {
376
-
return err
377
-
}
378
-
379
-
repoPath, err := securejoin.SecureJoin(h.c.Repo.ScanPath, didSlashRepo)
380
-
if err != nil {
381
-
return err
415
+
repoPath, _, _, resolveErr := h.db.ResolveRepoDIDOnDisk(h.c.Repo.ScanPath, repoDid)
416
+
if resolveErr != nil {
417
+
return fmt.Errorf("failed to resolve repo on disk: %w", resolveErr)
382
418
}
383
419
384
420
gr, err := git.PlainOpen(repoPath)
+10
-2
knotserver/router.go
+10
-2
knotserver/router.go
···
81
81
82
82
r.Route("/{did}", func(r chi.Router) {
83
83
r.Use(h.resolveDidRedirect)
84
+
85
+
r.Get("/info/refs", h.InfoRefs)
86
+
r.Post("/git-upload-archive", h.UploadArchive)
87
+
r.Post("/git-upload-pack", h.UploadPack)
88
+
r.Post("/git-receive-pack", h.ReceivePack)
89
+
84
90
r.Route("/{name}", func(r chi.Router) {
85
-
// routes for git operations
86
91
r.Get("/info/refs", h.InfoRefs)
87
92
r.Post("/git-upload-archive", h.UploadArchive)
88
93
r.Post("/git-upload-pack", h.UploadPack)
···
136
141
}
137
142
138
143
suffix := strings.TrimPrefix(r.URL.Path, "/"+didOrHandle)
139
-
newPath := fmt.Sprintf("/%s/%s?%s", id.DID.String(), suffix, r.URL.RawQuery)
144
+
newPath := "/" + id.DID.String() + suffix
145
+
if r.URL.RawQuery != "" {
146
+
newPath += "?" + r.URL.RawQuery
147
+
}
140
148
http.Redirect(w, r, newPath, http.StatusTemporaryRedirect)
141
149
})
142
150
}
History
12 rounds
0 comments
oyster.cafe
submitted
#11
1 commit
expand
collapse
knotserver: DID-based repo resolution in internal, ingester, git, and router
Signed-off-by: Lewis <lewis@tangled.org>
merge conflicts detected
expand
collapse
expand
collapse
- go.mod:34
- go.sum:339
expand 0 comments
oyster.cafe
submitted
#10
1 commit
expand
collapse
knotserver: DID-based repo resolution in internal, ingester, git, and router
Signed-off-by: Lewis <lewis@tangled.org>
expand 0 comments
oyster.cafe
submitted
#9
1 commit
expand
collapse
knotserver: DID-based repo resolution in internal, ingester, git, and router
Signed-off-by: Lewis <lewis@tangled.org>
expand 0 comments
oyster.cafe
submitted
#8
1 commit
expand
collapse
knotserver: DID-based repo resolution in internal, ingester, git, and router
Signed-off-by: Lewis <lewis@tangled.org>
expand 0 comments
oyster.cafe
submitted
#7
1 commit
expand
collapse
knotserver: DID-based repo resolution in internal, ingester, git, and router
Signed-off-by: Lewis <lewis@tangled.org>
expand 0 comments
oyster.cafe
submitted
#6
1 commit
expand
collapse
knotserver: DID-based repo resolution in internal, ingester, git, and router
Signed-off-by: Lewis <lewis@tangled.org>
expand 0 comments
oyster.cafe
submitted
#5
1 commit
expand
collapse
knotserver: DID-based repo resolution in internal, ingester, git, and router
Signed-off-by: Lewis <lewis@tangled.org>
expand 0 comments
oyster.cafe
submitted
#4
1 commit
expand
collapse
knotserver: DID-based repo resolution in internal, ingester, git, and router
Signed-off-by: Lewis <lewis@tangled.org>
expand 0 comments
oyster.cafe
submitted
#3
1 commit
expand
collapse
knotserver: DID-based repo resolution in internal, ingester, git, and router
Signed-off-by: Lewis <lewis@tangled.org>
expand 0 comments
oyster.cafe
submitted
#2
1 commit
expand
collapse
knotserver: DID-based repo resolution in internal, ingester, git, and router
Signed-off-by: Lewis <lewis@tangled.org>
expand 0 comments
oyster.cafe
submitted
#1
1 commit
expand
collapse
knotserver: DID-based repo resolution in internal, ingester, git, and router
Signed-off-by: Lewis <lewis@tangled.org>
expand 0 comments
oyster.cafe
submitted
#0
1 commit
expand
collapse
knotserver: DID-based repo resolution in internal, ingester, git, and router
Signed-off-by: Lewis <lewis@tangled.org>