code.gitea.io/gitea@v1.22.3/services/convert/pull.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package convert 5 6 import ( 7 "context" 8 "fmt" 9 10 issues_model "code.gitea.io/gitea/models/issues" 11 "code.gitea.io/gitea/models/perm" 12 access_model "code.gitea.io/gitea/models/perm/access" 13 user_model "code.gitea.io/gitea/models/user" 14 "code.gitea.io/gitea/modules/git" 15 "code.gitea.io/gitea/modules/gitrepo" 16 "code.gitea.io/gitea/modules/log" 17 api "code.gitea.io/gitea/modules/structs" 18 ) 19 20 // ToAPIPullRequest assumes following fields have been assigned with valid values: 21 // Required - Issue 22 // Optional - Merger 23 func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User) *api.PullRequest { 24 var ( 25 baseBranch *git.Branch 26 headBranch *git.Branch 27 baseCommit *git.Commit 28 err error 29 ) 30 31 if err = pr.Issue.LoadRepo(ctx); err != nil { 32 log.Error("pr.Issue.LoadRepo[%d]: %v", pr.ID, err) 33 return nil 34 } 35 36 apiIssue := ToAPIIssue(ctx, doer, pr.Issue) 37 if err := pr.LoadBaseRepo(ctx); err != nil { 38 log.Error("GetRepositoryById[%d]: %v", pr.ID, err) 39 return nil 40 } 41 42 if err := pr.LoadHeadRepo(ctx); err != nil { 43 log.Error("GetRepositoryById[%d]: %v", pr.ID, err) 44 return nil 45 } 46 47 p, err := access_model.GetUserRepoPermission(ctx, pr.BaseRepo, doer) 48 if err != nil { 49 log.Error("GetUserRepoPermission[%d]: %v", pr.BaseRepoID, err) 50 p.AccessMode = perm.AccessModeNone 51 } 52 53 apiPullRequest := &api.PullRequest{ 54 ID: pr.ID, 55 URL: pr.Issue.HTMLURL(), 56 Index: pr.Index, 57 Poster: apiIssue.Poster, 58 Title: apiIssue.Title, 59 Body: apiIssue.Body, 60 Labels: apiIssue.Labels, 61 Milestone: apiIssue.Milestone, 62 Assignee: apiIssue.Assignee, 63 Assignees: apiIssue.Assignees, 64 State: apiIssue.State, 65 Draft: pr.IsWorkInProgress(ctx), 66 IsLocked: apiIssue.IsLocked, 67 Comments: apiIssue.Comments, 68 ReviewComments: pr.GetReviewCommentsCount(ctx), 69 HTMLURL: pr.Issue.HTMLURL(), 70 DiffURL: pr.Issue.DiffURL(), 71 PatchURL: pr.Issue.PatchURL(), 72 HasMerged: pr.HasMerged, 73 MergeBase: pr.MergeBase, 74 Mergeable: pr.Mergeable(ctx), 75 Deadline: apiIssue.Deadline, 76 Created: pr.Issue.CreatedUnix.AsTimePtr(), 77 Updated: pr.Issue.UpdatedUnix.AsTimePtr(), 78 PinOrder: apiIssue.PinOrder, 79 80 AllowMaintainerEdit: pr.AllowMaintainerEdit, 81 82 Base: &api.PRBranchInfo{ 83 Name: pr.BaseBranch, 84 Ref: pr.BaseBranch, 85 RepoID: pr.BaseRepoID, 86 Repository: ToRepo(ctx, pr.BaseRepo, p), 87 }, 88 Head: &api.PRBranchInfo{ 89 Name: pr.HeadBranch, 90 Ref: fmt.Sprintf("%s%d/head", git.PullPrefix, pr.Index), 91 RepoID: -1, 92 }, 93 } 94 95 if err = pr.LoadRequestedReviewers(ctx); err != nil { 96 log.Error("LoadRequestedReviewers[%d]: %v", pr.ID, err) 97 return nil 98 } 99 for _, reviewer := range pr.RequestedReviewers { 100 apiPullRequest.RequestedReviewers = append(apiPullRequest.RequestedReviewers, ToUser(ctx, reviewer, nil)) 101 } 102 103 if pr.Issue.ClosedUnix != 0 { 104 apiPullRequest.Closed = pr.Issue.ClosedUnix.AsTimePtr() 105 } 106 107 gitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo) 108 if err != nil { 109 log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RepoPath(), err) 110 return nil 111 } 112 defer gitRepo.Close() 113 114 baseBranch, err = gitRepo.GetBranch(pr.BaseBranch) 115 if err != nil && !git.IsErrBranchNotExist(err) { 116 log.Error("GetBranch[%s]: %v", pr.BaseBranch, err) 117 return nil 118 } 119 120 if err == nil { 121 baseCommit, err = baseBranch.GetCommit() 122 if err != nil && !git.IsErrNotExist(err) { 123 log.Error("GetCommit[%s]: %v", baseBranch.Name, err) 124 return nil 125 } 126 127 if err == nil { 128 apiPullRequest.Base.Sha = baseCommit.ID.String() 129 } 130 } 131 132 if pr.Flow == issues_model.PullRequestFlowAGit { 133 gitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo) 134 if err != nil { 135 log.Error("OpenRepository[%s]: %v", pr.GetGitRefName(), err) 136 return nil 137 } 138 defer gitRepo.Close() 139 140 apiPullRequest.Head.Sha, err = gitRepo.GetRefCommitID(pr.GetGitRefName()) 141 if err != nil { 142 log.Error("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err) 143 return nil 144 } 145 apiPullRequest.Head.RepoID = pr.BaseRepoID 146 apiPullRequest.Head.Repository = apiPullRequest.Base.Repository 147 apiPullRequest.Head.Name = "" 148 } 149 150 if pr.HeadRepo != nil && pr.Flow == issues_model.PullRequestFlowGithub { 151 p, err := access_model.GetUserRepoPermission(ctx, pr.HeadRepo, doer) 152 if err != nil { 153 log.Error("GetUserRepoPermission[%d]: %v", pr.HeadRepoID, err) 154 p.AccessMode = perm.AccessModeNone 155 } 156 157 apiPullRequest.Head.RepoID = pr.HeadRepo.ID 158 apiPullRequest.Head.Repository = ToRepo(ctx, pr.HeadRepo, p) 159 160 headGitRepo, err := gitrepo.OpenRepository(ctx, pr.HeadRepo) 161 if err != nil { 162 log.Error("OpenRepository[%s]: %v", pr.HeadRepo.RepoPath(), err) 163 return nil 164 } 165 defer headGitRepo.Close() 166 167 headBranch, err = headGitRepo.GetBranch(pr.HeadBranch) 168 if err != nil && !git.IsErrBranchNotExist(err) { 169 log.Error("GetBranch[%s]: %v", pr.HeadBranch, err) 170 return nil 171 } 172 173 // Outer scope variables to be used in diff calculation 174 var ( 175 startCommitID string 176 endCommitID string 177 ) 178 179 if git.IsErrBranchNotExist(err) { 180 headCommitID, err := headGitRepo.GetRefCommitID(apiPullRequest.Head.Ref) 181 if err != nil && !git.IsErrNotExist(err) { 182 log.Error("GetCommit[%s]: %v", pr.HeadBranch, err) 183 return nil 184 } 185 if err == nil { 186 apiPullRequest.Head.Sha = headCommitID 187 endCommitID = headCommitID 188 } 189 } else { 190 commit, err := headBranch.GetCommit() 191 if err != nil && !git.IsErrNotExist(err) { 192 log.Error("GetCommit[%s]: %v", headBranch.Name, err) 193 return nil 194 } 195 if err == nil { 196 apiPullRequest.Head.Ref = pr.HeadBranch 197 apiPullRequest.Head.Sha = commit.ID.String() 198 endCommitID = commit.ID.String() 199 } 200 } 201 202 // Calculate diff 203 startCommitID = pr.MergeBase 204 205 apiPullRequest.ChangedFiles, apiPullRequest.Additions, apiPullRequest.Deletions, err = gitRepo.GetDiffShortStat(startCommitID, endCommitID) 206 if err != nil { 207 log.Error("GetDiffShortStat: %v", err) 208 } 209 } 210 211 if len(apiPullRequest.Head.Sha) == 0 && len(apiPullRequest.Head.Ref) != 0 { 212 baseGitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo) 213 if err != nil { 214 log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RepoPath(), err) 215 return nil 216 } 217 defer baseGitRepo.Close() 218 refs, err := baseGitRepo.GetRefsFiltered(apiPullRequest.Head.Ref) 219 if err != nil { 220 log.Error("GetRefsFiltered[%s]: %v", apiPullRequest.Head.Ref, err) 221 return nil 222 } else if len(refs) == 0 { 223 log.Error("unable to resolve PR head ref") 224 } else { 225 apiPullRequest.Head.Sha = refs[0].Object.String() 226 } 227 } 228 229 if pr.HasMerged { 230 apiPullRequest.Merged = pr.MergedUnix.AsTimePtr() 231 apiPullRequest.MergedCommitID = &pr.MergedCommitID 232 apiPullRequest.MergedBy = ToUser(ctx, pr.Merger, nil) 233 } 234 235 return apiPullRequest 236 }