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  }