package pulls import ( "context" "net/http" "strconv" "tangled.org/core/api/tangled" "tangled.org/core/appview/db" "tangled.org/core/appview/models" "tangled.org/core/appview/pages" "tangled.org/core/xrpc/xrpcclient" "github.com/bluesky-social/indigo/atproto/syntax" indigoxrpc "github.com/bluesky-social/indigo/xrpc" "github.com/go-chi/chi/v5" ) // htmx fragment func (s *Pulls) PullActions(w http.ResponseWriter, r *http.Request) { l := s.logger.With("handler", "PullActions") switch r.Method { case http.MethodGet: user := s.oauth.GetMultiAccountUser(r) if user != nil { l = l.With("user", user.Did) } f, err := s.repoResolver.Resolve(r) if err != nil { l.Error("failed to get repo and knot", "err", err) return } pull, ok := r.Context().Value("pull").(*models.Pull) if !ok { l.Error("failed to get pull") s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") return } l = l.With("pull_id", pull.PullId, "pull_owner", pull.OwnerDid) versionNumber, err := strconv.Atoi(chi.URLParam(r, "version")) if err != nil { versionNumber = pull.LatestVersionNumber() } if versionNumber >= len(pull.Versions) { http.Error(w, "bad round id", http.StatusBadRequest) l.Error("failed to parse round id", "err", err, "round_number", versionNumber) return } // only the last round's buttons and banners use merge/resubmit checks var mergeCheckParams pages.MergeCheckParams var resubmitResult = pages.Unknown if versionNumber == pull.LatestVersionNumber() { mergeCheckParams = s.composeMergeCheck(r.Context(), f, pull.TargetBranch, pull.SourceRepo, pull.LatestVersion().Head) if user != nil && syntax.DID(user.Did) == pull.OwnerDid { resubmitResult = s.resubmitCheck(r.Context(), pull) } } s.pages.PullActionsFragment(w, pages.PullActionsParams{ BaseParams: pages.BaseParamsFromContext(r.Context()), RepoInfo: s.repoResolver.GetRepoInfo(r, user), Pull: pull, RoundNumber: versionNumber, MergeCheck: mergeCheckParams, ResubmitCheck: resubmitResult, BranchDeleteStatus: s.branchDeleteStatus(r, f, pull), }) return } } func (s *Pulls) branchDeleteStatus(r *http.Request, repo *models.Repo, pull *models.Pull) *pages.BranchDeleteStatus { if pull.State != models.PullMerged || pull.SourceBranch == nil { return nil } user := s.oauth.GetMultiAccountUser(r) if user == nil { return nil } branch := *pull.SourceBranch if syntax.DID(repo.RepoDid) != pull.SourceRepo { var err error repo, err = db.GetRepoByDid(s.db, pull.SourceRepo.String()) if err != nil { return nil } } // user can only delete branch if they are a collaborator in the repo that the branch belongs to if !s.acl.HasRepoPermission(r.Context(), repo, user.Did, "repo:push") { return nil } xrpcc := &indigoxrpc.Client{Host: s.config.KnotMirror.Url} resp, err := tangled.GitTempGetBranch(r.Context(), xrpcc, branch, repo.RepoDid) if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { s.logger.Error("failed to get branch", "xrpcerr", xrpcerr, "err", err) return nil } return &pages.BranchDeleteStatus{ Repo: repo, Branch: resp.Name, } } func (s *Pulls) resubmitCheck(ctx context.Context, pull *models.Pull) pages.ResubmitResult { if pull.State == models.PullMerged || pull.State == models.PullAbandoned || pull.SourceBranch == nil { return pages.Unknown } xrpcc := &indigoxrpc.Client{Host: s.config.KnotMirror.Url} branchResp, err := tangled.GitTempGetBranch(ctx, xrpcc, *pull.SourceBranch, pull.SourceRepo.String()) if err != nil { if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { s.logger.Error("failed to call XRPC repo.branches", "xrpcerr", xrpcerr, "err", err, "pull_id", pull.PullId, "branch", *pull.SourceBranch) return pages.Unknown } s.logger.Error("failed to reach knotserver", "err", err, "pull_id", pull.PullId) return pages.Unknown } targetBranch := branchResp if pull.LatestVersion().Head != targetBranch.Hash { return pages.ShouldResubmit } return pages.ShouldNotResubmit }