···
···
15
+
"go.opentelemetry.io/otel/attribute"
"tangled.sh/tangled.sh/core/api/tangled"
"tangled.sh/tangled.sh/core/appview"
"tangled.sh/tangled.sh/core/appview/auth"
"tangled.sh/tangled.sh/core/appview/db"
"tangled.sh/tangled.sh/core/appview/pages"
"tangled.sh/tangled.sh/core/patchutil"
22
+
"tangled.sh/tangled.sh/core/telemetry"
"tangled.sh/tangled.sh/core/types"
comatproto "github.com/bluesky-social/indigo/api/atproto"
···
func (s *State) PullActions(w http.ResponseWriter, r *http.Request) {
33
+
ctx, span := s.t.TraceStart(r.Context(), "PullActions")
user := s.auth.GetUser(r)
33
-
f, err := s.fullyResolvedRepo(r)
39
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
39
-
pull, ok := r.Context().Value("pull").(*db.Pull)
45
+
pull, ok := ctx.Value("pull").(*db.Pull)
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
57
-
mergeCheckResponse := s.mergeCheck(f, pull)
63
+
_, mergeSpan := s.t.TraceStart(ctx, "mergeCheck")
64
+
mergeCheckResponse := s.mergeCheck(ctx, f, pull)
resubmitResult := pages.Unknown
if user.Did == pull.OwnerDid {
60
-
resubmitResult = s.resubmitCheck(f, pull)
69
+
_, resubmitSpan := s.t.TraceStart(ctx, "resubmitCheck")
70
+
resubmitResult = s.resubmitCheck(ctx, f, pull)
74
+
_, renderSpan := s.t.TraceStart(ctx, "renderPullActions")
s.pages.PullActionsFragment(w, pages.PullActionsParams{
65
-
RepoInfo: f.RepoInfo(s, user),
77
+
RepoInfo: f.RepoInfo(ctx, s, user),
RoundNumber: roundNumber,
MergeCheck: mergeCheckResponse,
ResubmitCheck: resubmitResult,
func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) {
89
+
ctx, span := s.t.TraceStart(r.Context(), "RepoSinglePull")
user := s.auth.GetUser(r)
77
-
f, err := s.fullyResolvedRepo(r)
93
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
96
+
span.RecordError(err)
83
-
pull, ok := r.Context().Value("pull").(*db.Pull)
100
+
pull, ok := ctx.Value("pull").(*db.Pull)
85
-
log.Println("failed to get pull")
102
+
err := errors.New("failed to get pull from context")
104
+
span.RecordError(err)
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
109
+
attrs := telemetry.MapAttrs[string](map[string]string{
110
+
"pull.id": fmt.Sprintf("%d", pull.PullId),
111
+
"pull.owner": pull.OwnerDid,
114
+
span.SetAttributes(attrs...)
for _, submission := range pull.Submissions {
totalIdents += len(submission.Comments)
···
107
-
resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve)
133
+
resolvedIds := s.resolver.ResolveIdents(ctx, identsToResolve)
didHandleMap := make(map[string]string)
for _, identity := range resolvedIds {
if !identity.Handle.IsInvalidHandle() {
···
didHandleMap[identity.DID.String()] = identity.DID.String()
142
+
span.SetAttributes(attribute.Int("identities.resolved", len(resolvedIds)))
117
-
mergeCheckResponse := s.mergeCheck(f, pull)
144
+
mergeCheckResponse := s.mergeCheck(ctx, f, pull)
resubmitResult := pages.Unknown
if user != nil && user.Did == pull.OwnerDid {
120
-
resubmitResult = s.resubmitCheck(f, pull)
148
+
resubmitResult = s.resubmitCheck(ctx, f, pull)
s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{
125
-
RepoInfo: f.RepoInfo(s, user),
153
+
RepoInfo: f.RepoInfo(ctx, s, user),
DidHandleMap: didHandleMap,
MergeCheck: mergeCheckResponse,
···
133
-
func (s *State) mergeCheck(f *FullyResolvedRepo, pull *db.Pull) types.MergeCheckResponse {
161
+
func (s *State) mergeCheck(ctx context.Context, f *FullyResolvedRepo, pull *db.Pull) types.MergeCheckResponse {
if pull.State == db.PullMerged {
return types.MergeCheckResponse{}
···
return mergeCheckResponse
193
-
func (s *State) resubmitCheck(f *FullyResolvedRepo, pull *db.Pull) pages.ResubmitResult {
221
+
func (s *State) resubmitCheck(ctx context.Context, f *FullyResolvedRepo, pull *db.Pull) pages.ResubmitResult {
222
+
ctx, span := s.t.TraceStart(ctx, "resubmitCheck")
225
+
span.SetAttributes(attribute.Int("pull.id", pull.PullId))
if pull.State == db.PullMerged || pull.PullSource == nil {
228
+
span.SetAttributes(attribute.String("result", "Unknown"))
···
if pull.PullSource.RepoAt != nil {
202
-
sourceRepo, err := db.GetRepoByAtUri(s.db, pull.PullSource.RepoAt.String())
236
+
span.SetAttributes(attribute.Bool("isForkBased", true))
237
+
sourceRepo, err := db.GetRepoByAtUri(ctx, s.db, pull.PullSource.RepoAt.String())
log.Println("failed to get source repo", err)
240
+
span.RecordError(err)
241
+
span.SetAttributes(attribute.String("error", "failed_to_get_source_repo"))
242
+
span.SetAttributes(attribute.String("result", "Unknown"))
···
repoName = sourceRepo.Name
// pulls within the same repo
251
+
span.SetAttributes(attribute.Bool("isBranchBased", true))
257
+
span.SetAttributes(
258
+
attribute.String("knot", knot),
259
+
attribute.String("ownerDid", ownerDid),
260
+
attribute.String("repoName", repoName),
261
+
attribute.String("sourceBranch", pull.PullSource.Branch),
us, err := NewUnsignedClient(knot, s.config.Dev)
log.Printf("failed to setup client for %s; ignoring: %v", knot, err)
267
+
span.RecordError(err)
268
+
span.SetAttributes(attribute.String("error", "failed_to_setup_client"))
269
+
span.SetAttributes(attribute.String("result", "Unknown"))
resp, err := us.Branch(ownerDid, repoName, pull.PullSource.Branch)
log.Println("failed to reach knotserver", err)
276
+
span.RecordError(err)
277
+
span.SetAttributes(attribute.String("error", "failed_to_reach_knotserver"))
278
+
span.SetAttributes(attribute.String("result", "Unknown"))
body, err := io.ReadAll(resp.Body)
log.Printf("error reading response body: %v", err)
285
+
span.RecordError(err)
286
+
span.SetAttributes(attribute.String("error", "failed_to_read_response"))
287
+
span.SetAttributes(attribute.String("result", "Unknown"))
···
var result types.RepoBranchResponse
if err := json.Unmarshal(body, &result); err != nil {
log.Println("failed to parse response:", err)
295
+
span.RecordError(err)
296
+
span.SetAttributes(attribute.String("error", "failed_to_parse_response"))
297
+
span.SetAttributes(attribute.String("result", "Unknown"))
latestSubmission := pull.Submissions[pull.LastRoundNumber()]
303
+
span.SetAttributes(
304
+
attribute.String("latestSubmission.SourceRev", latestSubmission.SourceRev),
305
+
attribute.String("branch.Hash", result.Branch.Hash),
if latestSubmission.SourceRev != result.Branch.Hash {
fmt.Println(latestSubmission.SourceRev, result.Branch.Hash)
310
+
span.SetAttributes(attribute.String("result", "ShouldResubmit"))
return pages.ShouldResubmit
314
+
span.SetAttributes(attribute.String("result", "ShouldNotResubmit"))
return pages.ShouldNotResubmit
func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) {
253
-
user := s.auth.GetUser(r)
254
-
f, err := s.fullyResolvedRepo(r)
319
+
ctx, span := s.t.TraceStart(r.Context(), "RepoPullPatch")
322
+
user := s.auth.GetUser(r.WithContext(ctx))
323
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
326
+
span.RecordError(err)
260
-
pull, ok := r.Context().Value("pull").(*db.Pull)
330
+
pull, ok := ctx.Value("pull").(*db.Pull)
262
-
log.Println("failed to get pull")
332
+
err := errors.New("failed to get pull from context")
334
+
span.RecordError(err)
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
···
if err != nil || roundIdInt >= len(pull.Submissions) {
http.Error(w, "bad round id", http.StatusBadRequest)
log.Println("failed to parse round id", err)
344
+
span.RecordError(err)
345
+
span.SetAttributes(attribute.String("error", "bad_round_id"))
349
+
span.SetAttributes(
350
+
attribute.Int("pull.id", pull.PullId),
351
+
attribute.Int("round", roundIdInt),
352
+
attribute.String("pull.owner", pull.OwnerDid),
identsToResolve := []string{pull.OwnerDid}
276
-
resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve)
356
+
resolvedIds := s.resolver.ResolveIdents(ctx, identsToResolve)
didHandleMap := make(map[string]string)
for _, identity := range resolvedIds {
if !identity.Handle.IsInvalidHandle() {
···
didHandleMap[identity.DID.String()] = identity.DID.String()
365
+
span.SetAttributes(attribute.Int("identities.resolved", len(resolvedIds)))
diff := pull.Submissions[roundIdInt].AsNiceDiff(pull.TargetBranch)
s.pages.RepoPullPatchPage(w, pages.RepoPullPatchParams{
DidHandleMap: didHandleMap,
291
-
RepoInfo: f.RepoInfo(s, user),
372
+
RepoInfo: f.RepoInfo(ctx, s, user),
Submission: pull.Submissions[roundIdInt],
func (s *State) RepoPullInterdiff(w http.ResponseWriter, r *http.Request) {
381
+
ctx, span := s.t.TraceStart(r.Context(), "RepoPullInterdiff")
user := s.auth.GetUser(r)
303
-
f, err := s.fullyResolvedRepo(r)
386
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
309
-
pull, ok := r.Context().Value("pull").(*db.Pull)
392
+
pull, ok := ctx.Value("pull").(*db.Pull)
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to get pull.")
399
+
_, roundSpan := s.t.TraceStart(ctx, "parseRound")
roundId := chi.URLParam(r, "round")
roundIdInt, err := strconv.Atoi(roundId)
if err != nil || roundIdInt >= len(pull.Submissions) {
http.Error(w, "bad round id", http.StatusBadRequest)
log.Println("failed to parse round id", err)
http.Error(w, "bad round id", http.StatusBadRequest)
log.Println("cannot interdiff initial submission")
417
+
_, identSpan := s.t.TraceStart(ctx, "resolveIdentities")
identsToResolve := []string{pull.OwnerDid}
331
-
resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve)
419
+
resolvedIds := s.resolver.ResolveIdents(ctx, identsToResolve)
didHandleMap := make(map[string]string)
for _, identity := range resolvedIds {
if !identity.Handle.IsInvalidHandle() {
···
didHandleMap[identity.DID.String()] = identity.DID.String()
430
+
_, diffSpan := s.t.TraceStart(ctx, "calculateInterdiff")
currentPatch, err := pull.Submissions[roundIdInt].AsDiff(pull.TargetBranch)
log.Println("failed to interdiff; current patch malformed")
s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; current patch is invalid.")
···
log.Println("failed to interdiff; previous patch malformed")
s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; previous patch is invalid.")
interdiff := patchutil.Interdiff(previousPatch, currentPatch)
450
+
_, renderSpan := s.t.TraceStart(ctx, "renderInterdiffPage")
s.pages.RepoPullInterdiffPage(w, pages.RepoPullInterdiffParams{
358
-
LoggedInUser: s.auth.GetUser(r),
359
-
RepoInfo: f.RepoInfo(s, user),
452
+
LoggedInUser: s.auth.GetUser(r.WithContext(ctx)),
453
+
RepoInfo: f.RepoInfo(ctx, s, user),
DidHandleMap: didHandleMap,
func (s *State) RepoPullPatchRaw(w http.ResponseWriter, r *http.Request) {
369
-
pull, ok := r.Context().Value("pull").(*db.Pull)
464
+
ctx, span := s.t.TraceStart(r.Context(), "RepoPullPatchRaw")
467
+
pull, ok := ctx.Value("pull").(*db.Pull)
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
474
+
_, roundSpan := s.t.TraceStart(ctx, "parseRound")
roundId := chi.URLParam(r, "round")
roundIdInt, err := strconv.Atoi(roundId)
if err != nil || roundIdInt >= len(pull.Submissions) {
http.Error(w, "bad round id", http.StatusBadRequest)
log.Println("failed to parse round id", err)
485
+
_, identSpan := s.t.TraceStart(ctx, "resolveIdentities")
identsToResolve := []string{pull.OwnerDid}
385
-
resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve)
487
+
resolvedIds := s.resolver.ResolveIdents(ctx, identsToResolve)
didHandleMap := make(map[string]string)
for _, identity := range resolvedIds {
if !identity.Handle.IsInvalidHandle() {
···
didHandleMap[identity.DID.String()] = identity.DID.String()
498
+
_, writeSpan := s.t.TraceStart(ctx, "writePatch")
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(pull.Submissions[roundIdInt].Patch))
func (s *State) RepoPulls(w http.ResponseWriter, r *http.Request) {
505
+
ctx, span := s.t.TraceStart(r.Context(), "RepoPulls")
user := s.auth.GetUser(r)
511
+
_, stateSpan := s.t.TraceStart(ctx, "determinePullState")
switch params.Get("state") {
···
411
-
f, err := s.fullyResolvedRepo(r)
521
+
_, repoSpan := s.t.TraceStart(ctx, "resolveRepo")
522
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
417
-
pulls, err := db.GetPulls(s.db, f.RepoAt, state)
530
+
_, pullsSpan := s.t.TraceStart(ctx, "getPulls")
531
+
pulls, err := db.GetPulls(ctx, s.db, f.RepoAt, state)
log.Println("failed to get pulls", err)
s.pages.Notice(w, "pulls", "Failed to load pulls. Try again later.")
540
+
_, sourceRepoSpan := s.t.TraceStart(ctx, "resolvePullSources")
for _, p := range pulls {
var pullSourceRepo *db.Repo
if p.PullSource.RepoAt != nil {
428
-
pullSourceRepo, err = db.GetRepoByAtUri(s.db, p.PullSource.RepoAt.String())
545
+
pullSourceRepo, err = db.GetRepoByAtUri(ctx, s.db, p.PullSource.RepoAt.String())
log.Printf("failed to get repo by at uri: %v", err)
···
555
+
sourceRepoSpan.End()
557
+
_, identSpan := s.t.TraceStart(ctx, "resolveIdentities")
identsToResolve := make([]string, len(pulls))
for i, pull := range pulls {
identsToResolve[i] = pull.OwnerDid
443
-
resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve)
562
+
resolvedIds := s.resolver.ResolveIdents(ctx, identsToResolve)
didHandleMap := make(map[string]string)
for _, identity := range resolvedIds {
if !identity.Handle.IsInvalidHandle() {
···
didHandleMap[identity.DID.String()] = identity.DID.String()
573
+
_, renderSpan := s.t.TraceStart(ctx, "renderPullsPage")
s.pages.RepoPulls(w, pages.RepoPullsParams{
454
-
LoggedInUser: s.auth.GetUser(r),
455
-
RepoInfo: f.RepoInfo(s, user),
575
+
LoggedInUser: s.auth.GetUser(r.WithContext(ctx)),
576
+
RepoInfo: f.RepoInfo(ctx, s, user),
DidHandleMap: didHandleMap,
func (s *State) PullComment(w http.ResponseWriter, r *http.Request) {
464
-
user := s.auth.GetUser(r)
465
-
f, err := s.fullyResolvedRepo(r)
586
+
ctx, span := s.t.TraceStart(r.Context(), "PullComment")
589
+
user := s.auth.GetUser(r.WithContext(ctx))
590
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
471
-
pull, ok := r.Context().Value("pull").(*db.Pull)
596
+
pull, ok := ctx.Value("pull").(*db.Pull)
log.Println("failed to get pull")
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
603
+
_, roundSpan := s.t.TraceStart(ctx, "parseRoundNumber")
roundNumberStr := chi.URLParam(r, "round")
roundNumber, err := strconv.Atoi(roundNumberStr)
if err != nil || roundNumber >= len(pull.Submissions) {
http.Error(w, "bad round id", http.StatusBadRequest)
log.Println("failed to parse round id", err)
616
+
_, renderSpan := s.t.TraceStart(ctx, "renderCommentFragment")
s.pages.PullNewCommentFragment(w, pages.PullNewCommentParams{
490
-
RepoInfo: f.RepoInfo(s, user),
619
+
RepoInfo: f.RepoInfo(ctx, s, user),
RoundNumber: roundNumber,
626
+
postCtx, postSpan := s.t.TraceStart(ctx, "CreateComment")
627
+
defer postSpan.End()
629
+
_, validateSpan := s.t.TraceStart(postCtx, "validateComment")
body := r.FormValue("body")
s.pages.Notice(w, "pull", "Comment body is required")
503
-
tx, err := s.db.BeginTx(r.Context(), nil)
639
+
_, txSpan := s.t.TraceStart(postCtx, "startTransaction")
640
+
tx, err := s.db.BeginTx(postCtx, nil)
log.Println("failed to start transaction", err)
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
createdAt := time.Now().Format(time.RFC3339)
514
-
pullAt, err := db.GetPullAt(s.db, f.RepoAt, pull.PullId)
653
+
_, pullAtSpan := s.t.TraceStart(postCtx, "getPullAt")
654
+
pullAt, err := db.GetPullAt(postCtx, s.db, f.RepoAt, pull.PullId)
log.Println("failed to get pull at", err)
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
663
+
_, atProtoSpan := s.t.TraceStart(postCtx, "createAtProtoRecord")
atUri := f.RepoAt.String()
522
-
client, _ := s.auth.AuthorizedClient(r)
523
-
atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
665
+
client, _ := s.auth.AuthorizedClient(r.WithContext(postCtx))
666
+
atResp, err := comatproto.RepoPutRecord(postCtx, client, &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoPullCommentNSID,
···
log.Println("failed to create pull comment", err)
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
// Create the pull comment in the database with the commentAt field
544
-
commentId, err := db.NewPullComment(tx, &db.PullComment{
689
+
_, dbSpan := s.t.TraceStart(postCtx, "createDbComment")
690
+
commentId, err := db.NewPullComment(postCtx, tx, &db.PullComment{
RepoAt: f.RepoAt.String(),
···
log.Println("failed to create pull comment", err)
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
558
-
// Commit the transaction
if err = tx.Commit(); err != nil {
log.Println("failed to commit transaction", err)
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
···
func (s *State) NewPull(w http.ResponseWriter, r *http.Request) {
571
-
user := s.auth.GetUser(r)
572
-
f, err := s.fullyResolvedRepo(r)
718
+
ctx, span := s.t.TraceStart(r.Context(), "NewPull")
721
+
user := s.auth.GetUser(r.WithContext(ctx))
722
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
725
+
span.RecordError(err)
731
+
span.SetAttributes(attribute.String("method", "GET"))
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
log.Printf("failed to create unsigned client for %s", f.Knot)
736
+
span.RecordError(err)
···
resp, err := us.Branches(f.OwnerDid(), f.RepoName)
log.Println("failed to reach knotserver", err)
744
+
span.RecordError(err)
body, err := io.ReadAll(resp.Body)
log.Printf("Error reading response body: %v", err)
751
+
span.RecordError(err)
···
err = json.Unmarshal(body, &result)
log.Println("failed to parse response:", err)
759
+
span.RecordError(err)
s.pages.RepoNewPull(w, pages.RepoNewPullParams{
608
-
RepoInfo: f.RepoInfo(s, user),
765
+
RepoInfo: f.RepoInfo(ctx, s, user),
Branches: result.Branches,
769
+
span.SetAttributes(attribute.String("method", "POST"))
title := r.FormValue("title")
body := r.FormValue("body")
targetBranch := r.FormValue("targetBranch")
fromFork := r.FormValue("fork")
sourceBranch := r.FormValue("sourceBranch")
patch := r.FormValue("patch")
778
+
span.SetAttributes(
779
+
attribute.String("targetBranch", targetBranch),
780
+
attribute.String("sourceBranch", sourceBranch),
781
+
attribute.Bool("hasFork", fromFork != ""),
782
+
attribute.Bool("hasPatch", patch != ""),
s.pages.Notice(w, "pull", "Target branch is required.")
787
+
span.SetAttributes(attribute.String("error", "missing_target_branch"))
// Determine PR type based on input parameters
625
-
isPushAllowed := f.RepoInfo(s, user).Roles.IsPushAllowed()
792
+
isPushAllowed := f.RepoInfo(ctx, s, user).Roles.IsPushAllowed()
isBranchBased := isPushAllowed && sourceBranch != "" && fromFork == ""
isForkBased := fromFork != "" && sourceBranch != ""
isPatchBased := patch != "" && !isBranchBased && !isForkBased
797
+
span.SetAttributes(
798
+
attribute.Bool("isPushAllowed", isPushAllowed),
799
+
attribute.Bool("isBranchBased", isBranchBased),
800
+
attribute.Bool("isForkBased", isForkBased),
801
+
attribute.Bool("isPatchBased", isPatchBased),
if isPatchBased && !patchutil.IsFormatPatch(patch) {
s.pages.Notice(w, "pull", "Title is required for git-diff patches.")
807
+
span.SetAttributes(attribute.String("error", "missing_title_for_git_diff"))
···
// Validate we have at least one valid PR creation method
if !isBranchBased && !isPatchBased && !isForkBased {
s.pages.Notice(w, "pull", "Neither source branch nor patch supplied.")
815
+
span.SetAttributes(attribute.String("error", "no_valid_pr_method"))
// Can't mix branch-based and patch-based approaches
if isBranchBased && patch != "" {
s.pages.Notice(w, "pull", "Cannot select both patch and source branch.")
822
+
span.SetAttributes(attribute.String("error", "mixed_pr_methods"))
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
log.Printf("failed to create unsigned client to %s: %v", f.Knot, err)
829
+
span.RecordError(err)
s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.")
···
caps, err := us.Capabilities()
log.Println("error fetching knot caps", f.Knot, err)
837
+
span.RecordError(err)
s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.")
842
+
span.SetAttributes(
843
+
attribute.Bool("caps.pullRequests.formatPatch", caps.PullRequests.FormatPatch),
844
+
attribute.Bool("caps.pullRequests.branchSubmissions", caps.PullRequests.BranchSubmissions),
845
+
attribute.Bool("caps.pullRequests.forkSubmissions", caps.PullRequests.ForkSubmissions),
846
+
attribute.Bool("caps.pullRequests.patchSubmissions", caps.PullRequests.PatchSubmissions),
if !caps.PullRequests.FormatPatch {
s.pages.Notice(w, "pull", "This knot doesn't support format-patch. Unfortunately, there is no fallback for now.")
851
+
span.SetAttributes(attribute.String("error", "formatpatch_not_supported"))
···
if !caps.PullRequests.BranchSubmissions {
s.pages.Notice(w, "pull", "This knot doesn't support branch-based pull requests. Try another way?")
859
+
span.SetAttributes(attribute.String("error", "branch_submissions_not_supported"))
674
-
s.handleBranchBasedPull(w, r, f, user, title, body, targetBranch, sourceBranch)
862
+
s.handleBranchBasedPull(w, r.WithContext(ctx), f, user, title, body, targetBranch, sourceBranch)
if !caps.PullRequests.ForkSubmissions {
s.pages.Notice(w, "pull", "This knot doesn't support fork-based pull requests. Try another way?")
866
+
span.SetAttributes(attribute.String("error", "fork_submissions_not_supported"))
680
-
s.handleForkBasedPull(w, r, f, user, fromFork, title, body, targetBranch, sourceBranch)
869
+
s.handleForkBasedPull(w, r.WithContext(ctx), f, user, fromFork, title, body, targetBranch, sourceBranch)
if !caps.PullRequests.PatchSubmissions {
s.pages.Notice(w, "pull", "This knot doesn't support patch-based pull requests. Send your patch over email.")
873
+
span.SetAttributes(attribute.String("error", "patch_submissions_not_supported"))
686
-
s.handlePatchBasedPull(w, r, f, user, title, body, targetBranch, patch)
876
+
s.handlePatchBasedPull(w, r.WithContext(ctx), f, user, title, body, targetBranch, patch)
func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, sourceBranch string) {
883
+
ctx, span := s.t.TraceStart(r.Context(), "handleBranchBasedPull")
886
+
span.SetAttributes(
887
+
attribute.String("targetBranch", targetBranch),
888
+
attribute.String("sourceBranch", sourceBranch),
pullSource := &db.PullSource{
···
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
902
+
span.RecordError(err)
903
+
span.SetAttributes(attribute.String("error", "client_creation_failed"))
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
···
comparison, err := ksClient.Compare(f.OwnerDid(), f.RepoName, targetBranch, sourceBranch)
log.Println("failed to compare", err)
911
+
span.RecordError(err)
912
+
span.SetAttributes(attribute.String("error", "comparison_failed"))
s.pages.Notice(w, "pull", err.Error())
···
sourceRev := comparison.Rev2
patch := comparison.Patch
920
+
span.SetAttributes(attribute.String("sourceRev", sourceRev))
if !patchutil.IsPatchValid(patch) {
923
+
span.SetAttributes(attribute.String("error", "invalid_patch_format"))
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
723
-
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource)
928
+
s.createPullRequest(w, r.WithContext(ctx), f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource)
func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch string) {
932
+
ctx, span := s.t.TraceStart(r.Context(), "handlePatchBasedPull")
935
+
span.SetAttributes(attribute.String("targetBranch", targetBranch))
if !patchutil.IsPatchValid(patch) {
938
+
span.SetAttributes(attribute.String("error", "invalid_patch_format"))
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
732
-
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil)
943
+
s.createPullRequest(w, r.WithContext(ctx), f, user, title, body, targetBranch, patch, "", nil, nil)
func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, forkRepo string, title, body, targetBranch, sourceBranch string) {
736
-
fork, err := db.GetForkByDid(s.db, user.Did, forkRepo)
947
+
ctx, span := s.t.TraceStart(r.Context(), "handleForkBasedPull")
950
+
span.SetAttributes(
951
+
attribute.String("forkRepo", forkRepo),
952
+
attribute.String("targetBranch", targetBranch),
953
+
attribute.String("sourceBranch", sourceBranch),
956
+
fork, err := db.GetForkByDid(ctx, s.db, user.Did, forkRepo)
if errors.Is(err, sql.ErrNoRows) {
958
+
span.SetAttributes(attribute.String("error", "fork_not_found"))
s.pages.Notice(w, "pull", "No such fork.")
log.Println("failed to fetch fork:", err)
963
+
span.RecordError(err)
964
+
span.SetAttributes(attribute.String("error", "fork_fetch_failed"))
s.pages.Notice(w, "pull", "Failed to fetch fork.")
···
secret, err := db.GetRegistrationKey(s.db, fork.Knot)
log.Println("failed to fetch registration key:", err)
972
+
span.RecordError(err)
973
+
span.SetAttributes(attribute.String("error", "registration_key_fetch_failed"))
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
···
sc, err := NewSignedClient(fork.Knot, secret, s.config.Dev)
log.Println("failed to create signed client:", err)
981
+
span.RecordError(err)
982
+
span.SetAttributes(attribute.String("error", "signed_client_creation_failed"))
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
···
us, err := NewUnsignedClient(fork.Knot, s.config.Dev)
log.Println("failed to create unsigned client:", err)
990
+
span.RecordError(err)
991
+
span.SetAttributes(attribute.String("error", "unsigned_client_creation_failed"))
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
···
resp, err := sc.NewHiddenRef(user.Did, fork.Name, sourceBranch, targetBranch)
log.Println("failed to create hidden ref:", err, resp.StatusCode)
999
+
span.RecordError(err)
1000
+
span.SetAttributes(attribute.String("error", "hidden_ref_creation_failed"))
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
1007
+
span.SetAttributes(attribute.String("error", "not_found_status"))
1009
+
span.SetAttributes(attribute.String("error", "bad_request_status"))
s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.")
hiddenRef := fmt.Sprintf("hidden/%s/%s", sourceBranch, targetBranch)
1015
+
span.SetAttributes(attribute.String("hiddenRef", hiddenRef))
// We're now comparing the sourceBranch (on the fork) against the hiddenRef which is tracking
// the targetBranch on the target repository. This code is a bit confusing, but here's an example:
// hiddenRef: hidden/feature-1/main (on repo-fork)
···
comparison, err := us.Compare(user.Did, fork.Name, hiddenRef, sourceBranch)
log.Println("failed to compare across branches", err)
1025
+
span.RecordError(err)
1026
+
span.SetAttributes(attribute.String("error", "branch_comparison_failed"))
s.pages.Notice(w, "pull", err.Error())
sourceRev := comparison.Rev2
patch := comparison.Patch
1033
+
span.SetAttributes(attribute.String("sourceRev", sourceRev))
if !patchutil.IsPatchValid(patch) {
1036
+
span.SetAttributes(attribute.String("error", "invalid_patch_format"))
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
···
forkAtUri, err := syntax.ParseATURI(fork.AtUri)
log.Println("failed to parse fork AT URI", err)
1044
+
span.RecordError(err)
1045
+
span.SetAttributes(attribute.String("error", "fork_aturi_parse_failed"))
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
809
-
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, &db.PullSource{
1050
+
s.createPullRequest(w, r.WithContext(ctx), f, user, title, body, targetBranch, patch, sourceRev, &db.PullSource{
}, &tangled.RepoPull_Source{Branch: sourceBranch, Repo: &fork.AtUri})
···
pullSource *db.PullSource,
recordPullSource *tangled.RepoPull_Source,
826
-
tx, err := s.db.BeginTx(r.Context(), nil)
1067
+
ctx, span := s.t.TraceStart(r.Context(), "createPullRequest")
1070
+
span.SetAttributes(
1071
+
attribute.String("targetBranch", targetBranch),
1072
+
attribute.String("sourceRev", sourceRev),
1073
+
attribute.Bool("hasPullSource", pullSource != nil),
1076
+
tx, err := s.db.BeginTx(ctx, nil)
log.Println("failed to start tx")
1079
+
span.RecordError(err)
1080
+
span.SetAttributes(attribute.String("error", "transaction_start_failed"))
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
···
formatPatches, err := patchutil.ExtractPatches(patch)
1091
+
span.RecordError(err)
1092
+
span.SetAttributes(attribute.String("error", "extract_patches_failed"))
s.pages.Notice(w, "pull", fmt.Sprintf("Failed to extract patches: %v", err))
if len(formatPatches) == 0 {
1097
+
span.SetAttributes(attribute.String("error", "no_patches_found"))
s.pages.Notice(w, "pull", "No patches found in the supplied format-patch.")
title = formatPatches[0].Title
body = formatPatches[0].Body
1104
+
span.SetAttributes(
1105
+
attribute.Bool("title_extracted", true),
1106
+
attribute.Bool("body_extracted", formatPatches[0].Body != ""),
···
856
-
err = db.NewPull(tx, &db.Pull{
1115
+
err = db.NewPull(ctx, tx, &db.Pull{
TargetBranch: targetBranch,
···
log.Println("failed to create pull request", err)
1129
+
span.RecordError(err)
1130
+
span.SetAttributes(attribute.String("error", "db_create_pull_failed"))
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
873
-
client, _ := s.auth.AuthorizedClient(r)
1135
+
client, _ := s.auth.AuthorizedClient(r.WithContext(ctx))
pullId, err := db.NextPullId(s.db, f.RepoAt)
log.Println("failed to get pull id", err)
1139
+
span.RecordError(err)
1140
+
span.SetAttributes(attribute.String("error", "get_pull_id_failed"))
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
1144
+
span.SetAttributes(attribute.Int("pullId", pullId))
881
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1146
+
_, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoPullNSID,
···
log.Println("failed to create pull request", err)
1164
+
span.RecordError(err)
1165
+
span.SetAttributes(attribute.String("error", "atproto_create_record_failed"))
1166
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
1170
+
if err = tx.Commit(); err != nil {
1171
+
log.Println("failed to commit transaction", err)
1172
+
span.RecordError(err)
1173
+
span.SetAttributes(attribute.String("error", "transaction_commit_failed"))
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
···
func (s *State) ValidatePatch(w http.ResponseWriter, r *http.Request) {
907
-
_, err := s.fullyResolvedRepo(r)
1182
+
ctx, span := s.t.TraceStart(r.Context(), "ValidatePatch")
1185
+
_, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
1188
+
span.RecordError(err)
1189
+
span.SetAttributes(attribute.String("error", "resolve_repo_failed"))
patch := r.FormValue("patch")
1194
+
span.SetAttributes(attribute.Bool("hasPatch", patch != ""))
1197
+
span.SetAttributes(attribute.String("error", "empty_patch"))
s.pages.Notice(w, "patch-error", "Patch is required.")
919
-
if patch == "" || !patchutil.IsPatchValid(patch) {
1202
+
if !patchutil.IsPatchValid(patch) {
1203
+
span.SetAttributes(attribute.String("error", "invalid_patch_format"))
s.pages.Notice(w, "patch-error", "Invalid patch format. Please provide a valid git diff or format-patch.")
924
-
if patchutil.IsFormatPatch(patch) {
1208
+
isFormatPatch := patchutil.IsFormatPatch(patch)
1209
+
span.SetAttributes(attribute.Bool("isFormatPatch", isFormatPatch))
1211
+
if isFormatPatch {
s.pages.Notice(w, "patch-preview", "git-format-patch detected. Title and description are optional; if left out, they will be extracted from the first commit.")
s.pages.Notice(w, "patch-preview", "Regular git-diff detected. Please provide a title and description.")
···
func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) {
932
-
user := s.auth.GetUser(r)
933
-
f, err := s.fullyResolvedRepo(r)
1219
+
ctx, span := s.t.TraceStart(r.Context(), "PatchUploadFragment")
1222
+
user := s.auth.GetUser(r.WithContext(ctx))
1223
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
1226
+
span.RecordError(err)
1227
+
span.SetAttributes(attribute.String("error", "resolve_repo_failed"))
s.pages.PullPatchUploadFragment(w, pages.PullPatchUploadParams{
940
-
RepoInfo: f.RepoInfo(s, user),
1232
+
RepoInfo: f.RepoInfo(ctx, s, user),
func (s *State) CompareBranchesFragment(w http.ResponseWriter, r *http.Request) {
945
-
user := s.auth.GetUser(r)
946
-
f, err := s.fullyResolvedRepo(r)
1237
+
ctx, span := s.t.TraceStart(r.Context(), "CompareBranchesFragment")
1240
+
user := s.auth.GetUser(r.WithContext(ctx))
1241
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
1244
+
span.RecordError(err)
1245
+
span.SetAttributes(attribute.String("error", "resolve_repo_failed"))
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
log.Printf("failed to create unsigned client for %s", f.Knot)
1252
+
span.RecordError(err)
1253
+
span.SetAttributes(attribute.String("error", "client_creation_failed"))
···
resp, err := us.Branches(f.OwnerDid(), f.RepoName)
log.Println("failed to reach knotserver", err)
1261
+
span.RecordError(err)
1262
+
span.SetAttributes(attribute.String("error", "knotserver_connection_failed"))
body, err := io.ReadAll(resp.Body)
log.Printf("Error reading response body: %v", err)
1269
+
span.RecordError(err)
1270
+
span.SetAttributes(attribute.String("error", "response_read_failed"))
1273
+
defer resp.Body.Close()
var result types.RepoBranchesResponse
err = json.Unmarshal(body, &result)
log.Println("failed to parse response:", err)
1279
+
span.RecordError(err)
1280
+
span.SetAttributes(attribute.String("error", "response_parse_failed"))
1283
+
span.SetAttributes(attribute.Int("branches.count", len(result.Branches)))
s.pages.PullCompareBranchesFragment(w, pages.PullCompareBranchesParams{
979
-
RepoInfo: f.RepoInfo(s, user),
1286
+
RepoInfo: f.RepoInfo(ctx, s, user),
Branches: result.Branches,
func (s *State) CompareForksFragment(w http.ResponseWriter, r *http.Request) {
985
-
user := s.auth.GetUser(r)
986
-
f, err := s.fullyResolvedRepo(r)
1292
+
ctx, span := s.t.TraceStart(r.Context(), "CompareForksFragment")
1295
+
user := s.auth.GetUser(r.WithContext(ctx))
1296
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
1299
+
span.RecordError(err)
992
-
forks, err := db.GetForksByDid(s.db, user.Did)
1303
+
forks, err := db.GetForksByDid(ctx, s.db, user.Did)
log.Println("failed to get forks", err)
1306
+
span.RecordError(err)
s.pages.PullCompareForkFragment(w, pages.PullCompareForkParams{
999
-
RepoInfo: f.RepoInfo(s, user),
1311
+
RepoInfo: f.RepoInfo(ctx, s, user),
func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Request) {
1005
-
user := s.auth.GetUser(r)
1317
+
ctx, span := s.t.TraceStart(r.Context(), "CompareForksBranchesFragment")
1320
+
user := s.auth.GetUser(r.WithContext(ctx))
1007
-
f, err := s.fullyResolvedRepo(r)
1322
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
1325
+
span.RecordError(err)
forkVal := r.URL.Query().Get("fork")
1330
+
span.SetAttributes(attribute.String("fork", forkVal))
1016
-
repo, err := db.GetRepo(s.db, user.Did, forkVal)
1333
+
repo, err := db.GetRepo(ctx, s.db, user.Did, forkVal)
log.Println("failed to get repo", user.Did, forkVal)
1336
+
span.RecordError(err)
sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Dev)
log.Printf("failed to create unsigned client for %s", repo.Knot)
1343
+
span.RecordError(err)
···
sourceResp, err := sourceBranchesClient.Branches(user.Did, repo.Name)
log.Println("failed to reach knotserver for source branches", err)
1351
+
span.RecordError(err)
sourceBody, err := io.ReadAll(sourceResp.Body)
log.Println("failed to read source response body", err)
1358
+
span.RecordError(err)
defer sourceResp.Body.Close()
···
err = json.Unmarshal(sourceBody, &sourceResult)
log.Println("failed to parse source branches response:", err)
1367
+
span.RecordError(err)
targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
log.Printf("failed to create unsigned client for target knot %s", f.Knot)
1374
+
span.RecordError(err)
···
targetResp, err := targetBranchesClient.Branches(f.OwnerDid(), f.RepoName)
log.Println("failed to reach knotserver for target branches", err)
1382
+
span.RecordError(err)
targetBody, err := io.ReadAll(targetResp.Body)
log.Println("failed to read target response body", err)
1389
+
span.RecordError(err)
defer targetResp.Body.Close()
···
err = json.Unmarshal(targetBody, &targetResult)
log.Println("failed to parse target branches response:", err)
1398
+
span.RecordError(err)
s.pages.PullCompareForkBranchesFragment(w, pages.PullCompareForkBranchesParams{
1077
-
RepoInfo: f.RepoInfo(s, user),
1403
+
RepoInfo: f.RepoInfo(ctx, s, user),
SourceBranches: sourceResult.Branches,
TargetBranches: targetResult.Branches,
func (s *State) ResubmitPull(w http.ResponseWriter, r *http.Request) {
1084
-
user := s.auth.GetUser(r)
1085
-
f, err := s.fullyResolvedRepo(r)
1410
+
ctx, span := s.t.TraceStart(r.Context(), "ResubmitPull")
1413
+
user := s.auth.GetUser(r.WithContext(ctx))
1414
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
1417
+
span.RecordError(err)
1091
-
pull, ok := r.Context().Value("pull").(*db.Pull)
1421
+
pull, ok := ctx.Value("pull").(*db.Pull)
log.Println("failed to get pull")
1424
+
span.RecordError(errors.New("failed to get pull from context"))
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1429
+
span.SetAttributes(
1430
+
attribute.Int("pull.id", pull.PullId),
1431
+
attribute.String("pull.owner", pull.OwnerDid),
1432
+
attribute.String("method", r.Method),
s.pages.PullResubmitFragment(w, pages.PullResubmitParams{
1101
-
RepoInfo: f.RepoInfo(s, user),
1438
+
RepoInfo: f.RepoInfo(ctx, s, user),
1107
-
s.resubmitPatch(w, r)
1444
+
span.SetAttributes(attribute.String("pull.type", "patch_based"))
1445
+
s.resubmitPatch(w, r.WithContext(ctx))
} else if pull.IsBranchBased() {
1110
-
s.resubmitBranch(w, r)
1448
+
span.SetAttributes(attribute.String("pull.type", "branch_based"))
1449
+
s.resubmitBranch(w, r.WithContext(ctx))
} else if pull.IsForkBased() {
1113
-
s.resubmitFork(w, r)
1452
+
span.SetAttributes(attribute.String("pull.type", "fork_based"))
1453
+
s.resubmitFork(w, r.WithContext(ctx))
1456
+
span.SetAttributes(attribute.String("pull.type", "unknown"))
func (s *State) resubmitPatch(w http.ResponseWriter, r *http.Request) {
1120
-
user := s.auth.GetUser(r)
1461
+
ctx, span := s.t.TraceStart(r.Context(), "resubmitPatch")
1122
-
pull, ok := r.Context().Value("pull").(*db.Pull)
1464
+
user := s.auth.GetUser(r.WithContext(ctx))
1466
+
pull, ok := ctx.Value("pull").(*db.Pull)
log.Println("failed to get pull")
1469
+
span.RecordError(errors.New("failed to get pull from context"))
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1129
-
f, err := s.fullyResolvedRepo(r)
1474
+
span.SetAttributes(
1475
+
attribute.Int("pull.id", pull.PullId),
1476
+
attribute.String("pull.owner", pull.OwnerDid),
1479
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
1482
+
span.RecordError(err)
if user.Did != pull.OwnerDid {
log.Println("unauthorized user")
1488
+
span.SetAttributes(attribute.String("error", "unauthorized_user"))
w.WriteHeader(http.StatusUnauthorized)
patch := r.FormValue("patch")
1494
+
span.SetAttributes(attribute.Bool("has_patch", patch != ""))
if err = validateResubmittedPatch(pull, patch); err != nil {
1497
+
span.SetAttributes(attribute.String("error", "invalid_patch"))
s.pages.Notice(w, "resubmit-error", err.Error())
1148
-
tx, err := s.db.BeginTx(r.Context(), nil)
1502
+
tx, err := s.db.BeginTx(ctx, nil)
log.Println("failed to start tx")
1505
+
span.RecordError(err)
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
err = db.ResubmitPull(tx, pull, patch, "")
log.Println("failed to resubmit pull request", err)
1514
+
span.RecordError(err)
s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull request. Try again later.")
1162
-
client, _ := s.auth.AuthorizedClient(r)
1518
+
client, _ := s.auth.AuthorizedClient(r.WithContext(ctx))
1164
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1520
+
ex, err := comatproto.RepoGetRecord(ctx, client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1523
+
span.RecordError(err)
1524
+
span.SetAttributes(attribute.String("error", "record_not_found"))
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
1171
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1529
+
_, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoPullNSID,
···
log.Println("failed to update record", err)
1546
+
span.RecordError(err)
s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.")
if err = tx.Commit(); err != nil {
log.Println("failed to commit transaction", err)
1553
+
span.RecordError(err)
s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull.")
···
func (s *State) resubmitBranch(w http.ResponseWriter, r *http.Request) {
1203
-
user := s.auth.GetUser(r)
1563
+
ctx, span := s.t.TraceStart(r.Context(), "resubmitBranch")
1566
+
user := s.auth.GetUser(r.WithContext(ctx))
1205
-
pull, ok := r.Context().Value("pull").(*db.Pull)
1568
+
pull, ok := ctx.Value("pull").(*db.Pull)
log.Println("failed to get pull")
1208
-
s.pages.Notice(w, "resubmit-error", "Failed to edit patch. Try again later.")
1571
+
span.RecordError(errors.New("failed to get pull from context"))
1572
+
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1212
-
f, err := s.fullyResolvedRepo(r)
1576
+
span.SetAttributes(
1577
+
attribute.Int("pull.id", pull.PullId),
1578
+
attribute.String("pull.owner", pull.OwnerDid),
1579
+
attribute.String("pull.source_branch", pull.PullSource.Branch),
1580
+
attribute.String("pull.target_branch", pull.TargetBranch),
1583
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
1586
+
span.RecordError(err)
if user.Did != pull.OwnerDid {
log.Println("unauthorized user")
1592
+
span.SetAttributes(attribute.String("error", "unauthorized_user"))
w.WriteHeader(http.StatusUnauthorized)
1224
-
if !f.RepoInfo(s, user).Roles.IsPushAllowed() {
1597
+
if !f.RepoInfo(ctx, s, user).Roles.IsPushAllowed() {
log.Println("unauthorized user")
1599
+
span.SetAttributes(attribute.String("error", "push_not_allowed"))
w.WriteHeader(http.StatusUnauthorized)
···
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
log.Printf("failed to create client for %s: %s", f.Knot, err)
1607
+
span.RecordError(err)
1608
+
span.SetAttributes(attribute.String("error", "client_creation_failed"))
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
comparison, err := ksClient.Compare(f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.PullSource.Branch)
log.Printf("compare request failed: %s", err)
1616
+
span.RecordError(err)
1617
+
span.SetAttributes(attribute.String("error", "compare_failed"))
s.pages.Notice(w, "resubmit-error", err.Error())
sourceRev := comparison.Rev2
patch := comparison.Patch
1624
+
span.SetAttributes(attribute.String("source_rev", sourceRev))
if err = validateResubmittedPatch(pull, patch); err != nil {
1627
+
span.SetAttributes(attribute.String("error", "invalid_patch"))
s.pages.Notice(w, "resubmit-error", err.Error())
if sourceRev == pull.Submissions[pull.LastRoundNumber()].SourceRev {
1633
+
span.SetAttributes(attribute.String("error", "no_changes"))
s.pages.Notice(w, "resubmit-error", "This branch has not changed since the last submission.")
1257
-
tx, err := s.db.BeginTx(r.Context(), nil)
1638
+
tx, err := s.db.BeginTx(ctx, nil)
log.Println("failed to start tx")
1641
+
span.RecordError(err)
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
err = db.ResubmitPull(tx, pull, patch, sourceRev)
log.Println("failed to create pull request", err)
1650
+
span.RecordError(err)
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1271
-
client, _ := s.auth.AuthorizedClient(r)
1654
+
client, _ := s.auth.AuthorizedClient(r.WithContext(ctx))
1273
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1656
+
ex, err := comatproto.RepoGetRecord(ctx, client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1659
+
span.RecordError(err)
1660
+
span.SetAttributes(attribute.String("error", "record_not_found"))
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
···
recordPullSource := &tangled.RepoPull_Source{
Branch: pull.PullSource.Branch,
1283
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1668
+
_, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoPullNSID,
···
log.Println("failed to update record", err)
1686
+
span.RecordError(err)
s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.")
if err = tx.Commit(); err != nil {
log.Println("failed to commit transaction", err)
1693
+
span.RecordError(err)
s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull.")
···
func (s *State) resubmitFork(w http.ResponseWriter, r *http.Request) {
1316
-
user := s.auth.GetUser(r)
1703
+
ctx, span := s.t.TraceStart(r.Context(), "resubmitFork")
1318
-
pull, ok := r.Context().Value("pull").(*db.Pull)
1706
+
user := s.auth.GetUser(r.WithContext(ctx))
1708
+
pull, ok := ctx.Value("pull").(*db.Pull)
log.Println("failed to get pull")
1321
-
s.pages.Notice(w, "resubmit-error", "Failed to edit patch. Try again later.")
1711
+
span.RecordError(errors.New("failed to get pull from context"))
1712
+
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1325
-
f, err := s.fullyResolvedRepo(r)
1716
+
span.SetAttributes(
1717
+
attribute.Int("pull.id", pull.PullId),
1718
+
attribute.String("pull.owner", pull.OwnerDid),
1719
+
attribute.String("pull.source_branch", pull.PullSource.Branch),
1720
+
attribute.String("pull.target_branch", pull.TargetBranch),
1723
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to get repo and knot", err)
1726
+
span.RecordError(err)
if user.Did != pull.OwnerDid {
log.Println("unauthorized user")
1732
+
span.SetAttributes(attribute.String("error", "unauthorized_user"))
w.WriteHeader(http.StatusUnauthorized)
1337
-
forkRepo, err := db.GetRepoByAtUri(s.db, pull.PullSource.RepoAt.String())
1737
+
forkRepo, err := db.GetRepoByAtUri(ctx, s.db, pull.PullSource.RepoAt.String())
log.Println("failed to get source repo", err)
1740
+
span.RecordError(err)
1741
+
span.SetAttributes(attribute.String("error", "source_repo_not_found"))
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1746
+
span.SetAttributes(
1747
+
attribute.String("fork.knot", forkRepo.Knot),
1748
+
attribute.String("fork.did", forkRepo.Did),
1749
+
attribute.String("fork.name", forkRepo.Name),
// extract patch by performing compare
ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Dev)
log.Printf("failed to create client for %s: %s", forkRepo.Knot, err)
1756
+
span.RecordError(err)
1757
+
span.SetAttributes(attribute.String("error", "client_creation_failed"))
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
secret, err := db.GetRegistrationKey(s.db, forkRepo.Knot)
log.Printf("failed to get registration key for %s: %s", forkRepo.Knot, err)
1765
+
span.RecordError(err)
1766
+
span.SetAttributes(attribute.String("error", "reg_key_not_found"))
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Dev)
log.Printf("failed to create signed client for %s: %s", forkRepo.Knot, err)
1775
+
span.RecordError(err)
1776
+
span.SetAttributes(attribute.String("error", "signed_client_creation_failed"))
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
resp, err := signedClient.NewHiddenRef(forkRepo.Did, forkRepo.Name, pull.PullSource.Branch, pull.TargetBranch)
if err != nil || resp.StatusCode != http.StatusNoContent {
log.Printf("failed to update tracking branch: %s", err)
1784
+
span.RecordError(err)
1785
+
span.SetAttributes(attribute.String("error", "hidden_ref_update_failed"))
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
hiddenRef := fmt.Sprintf("hidden/%s/%s", pull.PullSource.Branch, pull.TargetBranch)
1791
+
span.SetAttributes(attribute.String("hidden_ref", hiddenRef))
comparison, err := ksClient.Compare(forkRepo.Did, forkRepo.Name, hiddenRef, pull.PullSource.Branch)
log.Printf("failed to compare branches: %s", err)
1796
+
span.RecordError(err)
1797
+
span.SetAttributes(attribute.String("error", "compare_failed"))
s.pages.Notice(w, "resubmit-error", err.Error())
sourceRev := comparison.Rev2
patch := comparison.Patch
1804
+
span.SetAttributes(attribute.String("source_rev", sourceRev))
if err = validateResubmittedPatch(pull, patch); err != nil {
1807
+
span.SetAttributes(attribute.String("error", "invalid_patch"))
s.pages.Notice(w, "resubmit-error", err.Error())
if sourceRev == pull.Submissions[pull.LastRoundNumber()].SourceRev {
1813
+
span.SetAttributes(attribute.String("error", "no_changes"))
s.pages.Notice(w, "resubmit-error", "This branch has not changed since the last submission.")
1395
-
tx, err := s.db.BeginTx(r.Context(), nil)
1818
+
tx, err := s.db.BeginTx(ctx, nil)
log.Println("failed to start tx")
1821
+
span.RecordError(err)
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
···
err = db.ResubmitPull(tx, pull, patch, sourceRev)
log.Println("failed to create pull request", err)
1830
+
span.RecordError(err)
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
1409
-
client, _ := s.auth.AuthorizedClient(r)
1834
+
client, _ := s.auth.AuthorizedClient(r.WithContext(ctx))
1411
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1836
+
ex, err := comatproto.RepoGetRecord(ctx, client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)
1839
+
span.RecordError(err)
1840
+
span.SetAttributes(attribute.String("error", "record_not_found"))
s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.")
···
Branch: pull.PullSource.Branch,
1423
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1850
+
_, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{
Collection: tangled.RepoPullNSID,
···
log.Println("failed to update record", err)
1868
+
span.RecordError(err)
s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.")
if err = tx.Commit(); err != nil {
log.Println("failed to commit transaction", err)
1875
+
span.RecordError(err)
s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull.")
···
func (s *State) MergePull(w http.ResponseWriter, r *http.Request) {
1473
-
f, err := s.fullyResolvedRepo(r)
1902
+
ctx, span := s.t.TraceStart(r.Context(), "MergePull")
1905
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to resolve repo:", err)
1908
+
span.RecordError(err)
1909
+
span.SetAttributes(attribute.String("error", "resolve_repo_failed"))
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1480
-
pull, ok := r.Context().Value("pull").(*db.Pull)
1914
+
pull, ok := ctx.Value("pull").(*db.Pull)
log.Println("failed to get pull")
1917
+
span.SetAttributes(attribute.String("error", "pull_not_in_context"))
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1922
+
span.SetAttributes(
1923
+
attribute.Int("pull.id", pull.PullId),
1924
+
attribute.String("pull.owner", pull.OwnerDid),
1925
+
attribute.String("target_branch", pull.TargetBranch),
secret, err := db.GetRegistrationKey(s.db, f.Knot)
log.Printf("no registration key found for domain %s: %s\n", f.Knot, err)
1931
+
span.RecordError(err)
1932
+
span.SetAttributes(attribute.String("error", "reg_key_not_found"))
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1494
-
ident, err := s.resolver.ResolveIdent(r.Context(), pull.OwnerDid)
1937
+
ident, err := s.resolver.ResolveIdent(ctx, pull.OwnerDid)
log.Printf("resolving identity: %s", err)
1940
+
span.RecordError(err)
1941
+
span.SetAttributes(attribute.String("error", "resolve_identity_failed"))
w.WriteHeader(http.StatusNotFound)
···
email, err := db.GetPrimaryEmail(s.db, pull.OwnerDid)
log.Printf("failed to get primary email: %s", err)
1949
+
span.RecordError(err)
1950
+
span.SetAttributes(attribute.String("error", "get_email_failed"))
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
1956
+
span.RecordError(err)
1957
+
span.SetAttributes(attribute.String("error", "client_creation_failed"))
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
···
resp, err := ksClient.Merge([]byte(pull.LatestPatch()), f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.Title, pull.Body, ident.Handle.String(), email.Address)
log.Printf("failed to merge pull request: %s", err)
1966
+
span.RecordError(err)
1967
+
span.SetAttributes(attribute.String("error", "merge_failed"))
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1972
+
span.SetAttributes(attribute.Int("response.status", resp.StatusCode))
if resp.StatusCode == http.StatusOK {
err := db.MergePull(s.db, f.RepoAt, pull.PullId)
log.Printf("failed to update pull request status in database: %s", err)
1978
+
span.RecordError(err)
1979
+
span.SetAttributes(attribute.String("error", "db_update_failed"))
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s/pulls/%d", f.OwnerHandle(), f.RepoName, pull.PullId))
log.Printf("knotserver returned non-OK status code for merge: %d", resp.StatusCode)
1986
+
span.SetAttributes(attribute.String("error", "non_ok_response"))
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
func (s *State) ClosePull(w http.ResponseWriter, r *http.Request) {
1536
-
user := s.auth.GetUser(r)
1992
+
ctx, span := s.t.TraceStart(r.Context(), "ClosePull")
1538
-
f, err := s.fullyResolvedRepo(r)
1995
+
user := s.auth.GetUser(r.WithContext(ctx))
1997
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("malformed middleware")
2000
+
span.RecordError(err)
2001
+
span.SetAttributes(attribute.String("error", "resolve_repo_failed"))
1544
-
pull, ok := r.Context().Value("pull").(*db.Pull)
2005
+
pull, ok := ctx.Value("pull").(*db.Pull)
log.Println("failed to get pull")
2008
+
span.SetAttributes(attribute.String("error", "pull_not_in_context"))
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
2013
+
span.SetAttributes(
2014
+
attribute.Int("pull.id", pull.PullId),
2015
+
attribute.String("pull.owner", pull.OwnerDid),
2016
+
attribute.String("user.did", user.Did),
// auth filter: only owner or collaborators can close
roles := RolesInRepo(s, user, f)
isCollaborator := roles.IsCollaborator()
isPullAuthor := user.Did == pull.OwnerDid
isCloseAllowed := isCollaborator || isPullAuthor
2025
+
span.SetAttributes(
2026
+
attribute.Bool("is_collaborator", isCollaborator),
2027
+
attribute.Bool("is_pull_author", isPullAuthor),
2028
+
attribute.Bool("is_close_allowed", isCloseAllowed),
log.Println("failed to close pull")
2033
+
span.SetAttributes(attribute.String("error", "unauthorized"))
s.pages.Notice(w, "pull-close", "You are unauthorized to close this pull.")
1563
-
tx, err := s.db.BeginTx(r.Context(), nil)
2039
+
tx, err := s.db.BeginTx(ctx, nil)
log.Println("failed to start transaction", err)
2042
+
span.RecordError(err)
2043
+
span.SetAttributes(attribute.String("error", "transaction_start_failed"))
s.pages.Notice(w, "pull-close", "Failed to close pull.")
···
err = db.ClosePull(tx, f.RepoAt, pull.PullId)
log.Println("failed to close pull", err)
2052
+
span.RecordError(err)
2053
+
span.SetAttributes(attribute.String("error", "db_close_failed"))
s.pages.Notice(w, "pull-close", "Failed to close pull.")
···
// Commit the transaction
if err = tx.Commit(); err != nil {
log.Println("failed to commit transaction", err)
2061
+
span.RecordError(err)
2062
+
span.SetAttributes(attribute.String("error", "transaction_commit_failed"))
s.pages.Notice(w, "pull-close", "Failed to close pull.")
···
func (s *State) ReopenPull(w http.ResponseWriter, r *http.Request) {
1590
-
user := s.auth.GetUser(r)
2072
+
ctx, span := s.t.TraceStart(r.Context(), "ReopenPull")
1592
-
f, err := s.fullyResolvedRepo(r)
2075
+
user := s.auth.GetUser(r.WithContext(ctx))
2077
+
f, err := s.fullyResolvedRepo(r.WithContext(ctx))
log.Println("failed to resolve repo", err)
2080
+
span.RecordError(err)
2081
+
span.SetAttributes(attribute.String("error", "resolve_repo_failed"))
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
1599
-
pull, ok := r.Context().Value("pull").(*db.Pull)
2086
+
pull, ok := ctx.Value("pull").(*db.Pull)
log.Println("failed to get pull")
2089
+
span.SetAttributes(attribute.String("error", "pull_not_in_context"))
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1606
-
// auth filter: only owner or collaborators can close
2094
+
span.SetAttributes(
2095
+
attribute.Int("pull.id", pull.PullId),
2096
+
attribute.String("pull.owner", pull.OwnerDid),
2097
+
attribute.String("user.did", user.Did),
2100
+
// auth filter: only owner or collaborators can reopen
roles := RolesInRepo(s, user, f)
isCollaborator := roles.IsCollaborator()
isPullAuthor := user.Did == pull.OwnerDid
1610
-
isCloseAllowed := isCollaborator || isPullAuthor
1611
-
if !isCloseAllowed {
1612
-
log.Println("failed to close pull")
1613
-
s.pages.Notice(w, "pull-close", "You are unauthorized to close this pull.")
2104
+
isReopenAllowed := isCollaborator || isPullAuthor
2106
+
span.SetAttributes(
2107
+
attribute.Bool("is_collaborator", isCollaborator),
2108
+
attribute.Bool("is_pull_author", isPullAuthor),
2109
+
attribute.Bool("is_reopen_allowed", isReopenAllowed),
2112
+
if !isReopenAllowed {
2113
+
log.Println("failed to reopen pull")
2114
+
span.SetAttributes(attribute.String("error", "unauthorized"))
2115
+
s.pages.Notice(w, "pull-close", "You are unauthorized to reopen this pull.")
1618
-
tx, err := s.db.BeginTx(r.Context(), nil)
2120
+
tx, err := s.db.BeginTx(ctx, nil)
log.Println("failed to start transaction", err)
2123
+
span.RecordError(err)
2124
+
span.SetAttributes(attribute.String("error", "transaction_start_failed"))
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
···
err = db.ReopenPull(tx, f.RepoAt, pull.PullId)
log.Println("failed to reopen pull", err)
2133
+
span.RecordError(err)
2134
+
span.SetAttributes(attribute.String("error", "db_reopen_failed"))
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
···
// Commit the transaction
if err = tx.Commit(); err != nil {
log.Println("failed to commit transaction", err)
2142
+
span.RecordError(err)
2143
+
span.SetAttributes(attribute.String("error", "transaction_commit_failed"))
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")