...

Source file src/github.com/google/go-github/v33/github/pulls.go

Documentation: github.com/google/go-github/v33/github

     1  // Copyright 2013 The go-github AUTHORS. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package github
     7  
     8  import (
     9  	"bytes"
    10  	"context"
    11  	"fmt"
    12  	"time"
    13  )
    14  
    15  // PullRequestsService handles communication with the pull request related
    16  // methods of the GitHub API.
    17  //
    18  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/
    19  type PullRequestsService service
    20  
    21  // PullRequest represents a GitHub pull request on a repository.
    22  type PullRequest struct {
    23  	ID                  *int64     `json:"id,omitempty"`
    24  	Number              *int       `json:"number,omitempty"`
    25  	State               *string    `json:"state,omitempty"`
    26  	Locked              *bool      `json:"locked,omitempty"`
    27  	Title               *string    `json:"title,omitempty"`
    28  	Body                *string    `json:"body,omitempty"`
    29  	CreatedAt           *time.Time `json:"created_at,omitempty"`
    30  	UpdatedAt           *time.Time `json:"updated_at,omitempty"`
    31  	ClosedAt            *time.Time `json:"closed_at,omitempty"`
    32  	MergedAt            *time.Time `json:"merged_at,omitempty"`
    33  	Labels              []*Label   `json:"labels,omitempty"`
    34  	User                *User      `json:"user,omitempty"`
    35  	Draft               *bool      `json:"draft,omitempty"`
    36  	Merged              *bool      `json:"merged,omitempty"`
    37  	Mergeable           *bool      `json:"mergeable,omitempty"`
    38  	MergeableState      *string    `json:"mergeable_state,omitempty"`
    39  	MergedBy            *User      `json:"merged_by,omitempty"`
    40  	MergeCommitSHA      *string    `json:"merge_commit_sha,omitempty"`
    41  	Rebaseable          *bool      `json:"rebaseable,omitempty"`
    42  	Comments            *int       `json:"comments,omitempty"`
    43  	Commits             *int       `json:"commits,omitempty"`
    44  	Additions           *int       `json:"additions,omitempty"`
    45  	Deletions           *int       `json:"deletions,omitempty"`
    46  	ChangedFiles        *int       `json:"changed_files,omitempty"`
    47  	URL                 *string    `json:"url,omitempty"`
    48  	HTMLURL             *string    `json:"html_url,omitempty"`
    49  	IssueURL            *string    `json:"issue_url,omitempty"`
    50  	StatusesURL         *string    `json:"statuses_url,omitempty"`
    51  	DiffURL             *string    `json:"diff_url,omitempty"`
    52  	PatchURL            *string    `json:"patch_url,omitempty"`
    53  	CommitsURL          *string    `json:"commits_url,omitempty"`
    54  	CommentsURL         *string    `json:"comments_url,omitempty"`
    55  	ReviewCommentsURL   *string    `json:"review_comments_url,omitempty"`
    56  	ReviewCommentURL    *string    `json:"review_comment_url,omitempty"`
    57  	ReviewComments      *int       `json:"review_comments,omitempty"`
    58  	Assignee            *User      `json:"assignee,omitempty"`
    59  	Assignees           []*User    `json:"assignees,omitempty"`
    60  	Milestone           *Milestone `json:"milestone,omitempty"`
    61  	MaintainerCanModify *bool      `json:"maintainer_can_modify,omitempty"`
    62  	AuthorAssociation   *string    `json:"author_association,omitempty"`
    63  	NodeID              *string    `json:"node_id,omitempty"`
    64  	RequestedReviewers  []*User    `json:"requested_reviewers,omitempty"`
    65  
    66  	// RequestedTeams is populated as part of the PullRequestEvent.
    67  	// See, https://docs.github.com/en/free-pro-team@latest/rest/reference/activity/events/types/#pullrequestevent for an example.
    68  	RequestedTeams []*Team `json:"requested_teams,omitempty"`
    69  
    70  	Links *PRLinks           `json:"_links,omitempty"`
    71  	Head  *PullRequestBranch `json:"head,omitempty"`
    72  	Base  *PullRequestBranch `json:"base,omitempty"`
    73  
    74  	// ActiveLockReason is populated only when LockReason is provided while locking the pull request.
    75  	// Possible values are: "off-topic", "too heated", "resolved", and "spam".
    76  	ActiveLockReason *string `json:"active_lock_reason,omitempty"`
    77  }
    78  
    79  func (p PullRequest) String() string {
    80  	return Stringify(p)
    81  }
    82  
    83  // PRLink represents a single link object from GitHub pull request _links.
    84  type PRLink struct {
    85  	HRef *string `json:"href,omitempty"`
    86  }
    87  
    88  // PRLinks represents the "_links" object in a GitHub pull request.
    89  type PRLinks struct {
    90  	Self           *PRLink `json:"self,omitempty"`
    91  	HTML           *PRLink `json:"html,omitempty"`
    92  	Issue          *PRLink `json:"issue,omitempty"`
    93  	Comments       *PRLink `json:"comments,omitempty"`
    94  	ReviewComments *PRLink `json:"review_comments,omitempty"`
    95  	ReviewComment  *PRLink `json:"review_comment,omitempty"`
    96  	Commits        *PRLink `json:"commits,omitempty"`
    97  	Statuses       *PRLink `json:"statuses,omitempty"`
    98  }
    99  
   100  // PullRequestBranch represents a base or head branch in a GitHub pull request.
   101  type PullRequestBranch struct {
   102  	Label *string     `json:"label,omitempty"`
   103  	Ref   *string     `json:"ref,omitempty"`
   104  	SHA   *string     `json:"sha,omitempty"`
   105  	Repo  *Repository `json:"repo,omitempty"`
   106  	User  *User       `json:"user,omitempty"`
   107  }
   108  
   109  // PullRequestListOptions specifies the optional parameters to the
   110  // PullRequestsService.List method.
   111  type PullRequestListOptions struct {
   112  	// State filters pull requests based on their state. Possible values are:
   113  	// open, closed, all. Default is "open".
   114  	State string `url:"state,omitempty"`
   115  
   116  	// Head filters pull requests by head user and branch name in the format of:
   117  	// "user:ref-name".
   118  	Head string `url:"head,omitempty"`
   119  
   120  	// Base filters pull requests by base branch name.
   121  	Base string `url:"base,omitempty"`
   122  
   123  	// Sort specifies how to sort pull requests. Possible values are: created,
   124  	// updated, popularity, long-running. Default is "created".
   125  	Sort string `url:"sort,omitempty"`
   126  
   127  	// Direction in which to sort pull requests. Possible values are: asc, desc.
   128  	// If Sort is "created" or not specified, Default is "desc", otherwise Default
   129  	// is "asc"
   130  	Direction string `url:"direction,omitempty"`
   131  
   132  	ListOptions
   133  }
   134  
   135  // List the pull requests for the specified repository.
   136  //
   137  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#list-pull-requests
   138  func (s *PullRequestsService) List(ctx context.Context, owner string, repo string, opts *PullRequestListOptions) ([]*PullRequest, *Response, error) {
   139  	u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo)
   140  	u, err := addOptions(u, opts)
   141  	if err != nil {
   142  		return nil, nil, err
   143  	}
   144  
   145  	req, err := s.client.NewRequest("GET", u, nil)
   146  	if err != nil {
   147  		return nil, nil, err
   148  	}
   149  
   150  	var pulls []*PullRequest
   151  	resp, err := s.client.Do(ctx, req, &pulls)
   152  	if err != nil {
   153  		return nil, resp, err
   154  	}
   155  
   156  	return pulls, resp, nil
   157  }
   158  
   159  // ListPullRequestsWithCommit returns pull requests associated with a commit SHA.
   160  //
   161  // The results will include open and closed pull requests.
   162  //
   163  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/repos/#list-pull-requests-associated-with-a-commit
   164  func (s *PullRequestsService) ListPullRequestsWithCommit(ctx context.Context, owner, repo, sha string, opts *PullRequestListOptions) ([]*PullRequest, *Response, error) {
   165  	u := fmt.Sprintf("repos/%v/%v/commits/%v/pulls", owner, repo, sha)
   166  	u, err := addOptions(u, opts)
   167  	if err != nil {
   168  		return nil, nil, err
   169  	}
   170  
   171  	req, err := s.client.NewRequest("GET", u, nil)
   172  	if err != nil {
   173  		return nil, nil, err
   174  	}
   175  
   176  	// TODO: remove custom Accept header when this API fully launches.
   177  	req.Header.Set("Accept", mediaTypeListPullsOrBranchesForCommitPreview)
   178  	var pulls []*PullRequest
   179  	resp, err := s.client.Do(ctx, req, &pulls)
   180  	if err != nil {
   181  		return nil, resp, err
   182  	}
   183  
   184  	return pulls, resp, nil
   185  }
   186  
   187  // Get a single pull request.
   188  //
   189  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#get-a-pull-request
   190  func (s *PullRequestsService) Get(ctx context.Context, owner string, repo string, number int) (*PullRequest, *Response, error) {
   191  	u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number)
   192  	req, err := s.client.NewRequest("GET", u, nil)
   193  	if err != nil {
   194  		return nil, nil, err
   195  	}
   196  
   197  	pull := new(PullRequest)
   198  	resp, err := s.client.Do(ctx, req, pull)
   199  	if err != nil {
   200  		return nil, resp, err
   201  	}
   202  
   203  	return pull, resp, nil
   204  }
   205  
   206  // GetRaw gets a single pull request in raw (diff or patch) format.
   207  //
   208  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#get-a-pull-request
   209  func (s *PullRequestsService) GetRaw(ctx context.Context, owner string, repo string, number int, opts RawOptions) (string, *Response, error) {
   210  	u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number)
   211  	req, err := s.client.NewRequest("GET", u, nil)
   212  	if err != nil {
   213  		return "", nil, err
   214  	}
   215  
   216  	switch opts.Type {
   217  	case Diff:
   218  		req.Header.Set("Accept", mediaTypeV3Diff)
   219  	case Patch:
   220  		req.Header.Set("Accept", mediaTypeV3Patch)
   221  	default:
   222  		return "", nil, fmt.Errorf("unsupported raw type %d", opts.Type)
   223  	}
   224  
   225  	var buf bytes.Buffer
   226  	resp, err := s.client.Do(ctx, req, &buf)
   227  	if err != nil {
   228  		return "", resp, err
   229  	}
   230  
   231  	return buf.String(), resp, nil
   232  }
   233  
   234  // NewPullRequest represents a new pull request to be created.
   235  type NewPullRequest struct {
   236  	Title               *string `json:"title,omitempty"`
   237  	Head                *string `json:"head,omitempty"`
   238  	Base                *string `json:"base,omitempty"`
   239  	Body                *string `json:"body,omitempty"`
   240  	Issue               *int    `json:"issue,omitempty"`
   241  	MaintainerCanModify *bool   `json:"maintainer_can_modify,omitempty"`
   242  	Draft               *bool   `json:"draft,omitempty"`
   243  }
   244  
   245  // Create a new pull request on the specified repository.
   246  //
   247  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#create-a-pull-request
   248  func (s *PullRequestsService) Create(ctx context.Context, owner string, repo string, pull *NewPullRequest) (*PullRequest, *Response, error) {
   249  	u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo)
   250  	req, err := s.client.NewRequest("POST", u, pull)
   251  	if err != nil {
   252  		return nil, nil, err
   253  	}
   254  
   255  	p := new(PullRequest)
   256  	resp, err := s.client.Do(ctx, req, p)
   257  	if err != nil {
   258  		return nil, resp, err
   259  	}
   260  
   261  	return p, resp, nil
   262  }
   263  
   264  // PullRequestBranchUpdateOptions specifies the optional parameters to the
   265  // PullRequestsService.UpdateBranch method.
   266  type PullRequestBranchUpdateOptions struct {
   267  	// ExpectedHeadSHA specifies the most recent commit on the pull request's branch.
   268  	// Default value is the SHA of the pull request's current HEAD ref.
   269  	ExpectedHeadSHA *string `json:"expected_head_sha,omitempty"`
   270  }
   271  
   272  // PullRequestBranchUpdateResponse specifies the response of pull request branch update.
   273  type PullRequestBranchUpdateResponse struct {
   274  	Message *string `json:"message,omitempty"`
   275  	URL     *string `json:"url,omitempty"`
   276  }
   277  
   278  // UpdateBranch updates the pull request branch with latest upstream changes.
   279  //
   280  // This method might return an AcceptedError and a status code of
   281  // 202. This is because this is the status that GitHub returns to signify that
   282  // it has now scheduled the update of the pull request branch in a background task.
   283  // A follow up request, after a delay of a second or so, should result
   284  // in a successful request.
   285  //
   286  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#update-a-pull-request-branch
   287  func (s *PullRequestsService) UpdateBranch(ctx context.Context, owner, repo string, number int, opts *PullRequestBranchUpdateOptions) (*PullRequestBranchUpdateResponse, *Response, error) {
   288  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/update-branch", owner, repo, number)
   289  
   290  	req, err := s.client.NewRequest("PUT", u, opts)
   291  	if err != nil {
   292  		return nil, nil, err
   293  	}
   294  
   295  	// TODO: remove custom Accept header when this API fully launches.
   296  	req.Header.Set("Accept", mediaTypeUpdatePullRequestBranchPreview)
   297  
   298  	p := new(PullRequestBranchUpdateResponse)
   299  	resp, err := s.client.Do(ctx, req, p)
   300  	if err != nil {
   301  		return nil, resp, err
   302  	}
   303  
   304  	return p, resp, nil
   305  }
   306  
   307  type pullRequestUpdate struct {
   308  	Title               *string `json:"title,omitempty"`
   309  	Body                *string `json:"body,omitempty"`
   310  	State               *string `json:"state,omitempty"`
   311  	Base                *string `json:"base,omitempty"`
   312  	MaintainerCanModify *bool   `json:"maintainer_can_modify,omitempty"`
   313  }
   314  
   315  // Edit a pull request.
   316  // pull must not be nil.
   317  //
   318  // The following fields are editable: Title, Body, State, Base.Ref and MaintainerCanModify.
   319  // Base.Ref updates the base branch of the pull request.
   320  //
   321  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#update-a-pull-request
   322  func (s *PullRequestsService) Edit(ctx context.Context, owner string, repo string, number int, pull *PullRequest) (*PullRequest, *Response, error) {
   323  	if pull == nil {
   324  		return nil, nil, fmt.Errorf("pull must be provided")
   325  	}
   326  
   327  	u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number)
   328  
   329  	update := &pullRequestUpdate{
   330  		Title:               pull.Title,
   331  		Body:                pull.Body,
   332  		State:               pull.State,
   333  		MaintainerCanModify: pull.MaintainerCanModify,
   334  	}
   335  	// avoid updating the base branch when closing the Pull Request
   336  	// - otherwise the GitHub API server returns a "Validation Failed" error:
   337  	// "Cannot change base branch of closed pull request".
   338  	if pull.Base != nil && pull.GetState() != "closed" {
   339  		update.Base = pull.Base.Ref
   340  	}
   341  
   342  	req, err := s.client.NewRequest("PATCH", u, update)
   343  	if err != nil {
   344  		return nil, nil, err
   345  	}
   346  
   347  	p := new(PullRequest)
   348  	resp, err := s.client.Do(ctx, req, p)
   349  	if err != nil {
   350  		return nil, resp, err
   351  	}
   352  
   353  	return p, resp, nil
   354  }
   355  
   356  // ListCommits lists the commits in a pull request.
   357  //
   358  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#list-commits-on-a-pull-request
   359  func (s *PullRequestsService) ListCommits(ctx context.Context, owner string, repo string, number int, opts *ListOptions) ([]*RepositoryCommit, *Response, error) {
   360  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/commits", owner, repo, number)
   361  	u, err := addOptions(u, opts)
   362  	if err != nil {
   363  		return nil, nil, err
   364  	}
   365  
   366  	req, err := s.client.NewRequest("GET", u, nil)
   367  	if err != nil {
   368  		return nil, nil, err
   369  	}
   370  
   371  	var commits []*RepositoryCommit
   372  	resp, err := s.client.Do(ctx, req, &commits)
   373  	if err != nil {
   374  		return nil, resp, err
   375  	}
   376  
   377  	return commits, resp, nil
   378  }
   379  
   380  // ListFiles lists the files in a pull request.
   381  //
   382  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#list-pull-requests-files
   383  func (s *PullRequestsService) ListFiles(ctx context.Context, owner string, repo string, number int, opts *ListOptions) ([]*CommitFile, *Response, error) {
   384  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/files", owner, repo, number)
   385  	u, err := addOptions(u, opts)
   386  	if err != nil {
   387  		return nil, nil, err
   388  	}
   389  
   390  	req, err := s.client.NewRequest("GET", u, nil)
   391  	if err != nil {
   392  		return nil, nil, err
   393  	}
   394  
   395  	var commitFiles []*CommitFile
   396  	resp, err := s.client.Do(ctx, req, &commitFiles)
   397  	if err != nil {
   398  		return nil, resp, err
   399  	}
   400  
   401  	return commitFiles, resp, nil
   402  }
   403  
   404  // IsMerged checks if a pull request has been merged.
   405  //
   406  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#check-if-a-pull-request-has-been-merged
   407  func (s *PullRequestsService) IsMerged(ctx context.Context, owner string, repo string, number int) (bool, *Response, error) {
   408  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number)
   409  	req, err := s.client.NewRequest("GET", u, nil)
   410  	if err != nil {
   411  		return false, nil, err
   412  	}
   413  
   414  	resp, err := s.client.Do(ctx, req, nil)
   415  	merged, err := parseBoolResponse(err)
   416  	return merged, resp, err
   417  }
   418  
   419  // PullRequestMergeResult represents the result of merging a pull request.
   420  type PullRequestMergeResult struct {
   421  	SHA     *string `json:"sha,omitempty"`
   422  	Merged  *bool   `json:"merged,omitempty"`
   423  	Message *string `json:"message,omitempty"`
   424  }
   425  
   426  // PullRequestOptions lets you define how a pull request will be merged.
   427  type PullRequestOptions struct {
   428  	CommitTitle string // Title for the automatic commit message. (Optional.)
   429  	SHA         string // SHA that pull request head must match to allow merge. (Optional.)
   430  
   431  	// The merge method to use. Possible values include: "merge", "squash", and "rebase" with the default being merge. (Optional.)
   432  	MergeMethod string
   433  }
   434  
   435  type pullRequestMergeRequest struct {
   436  	CommitMessage string `json:"commit_message,omitempty"`
   437  	CommitTitle   string `json:"commit_title,omitempty"`
   438  	MergeMethod   string `json:"merge_method,omitempty"`
   439  	SHA           string `json:"sha,omitempty"`
   440  }
   441  
   442  // Merge a pull request.
   443  // commitMessage is an extra detail to append to automatic commit message.
   444  //
   445  // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#merge-a-pull-request
   446  func (s *PullRequestsService) Merge(ctx context.Context, owner string, repo string, number int, commitMessage string, options *PullRequestOptions) (*PullRequestMergeResult, *Response, error) {
   447  	u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number)
   448  
   449  	pullRequestBody := &pullRequestMergeRequest{CommitMessage: commitMessage}
   450  	if options != nil {
   451  		pullRequestBody.CommitTitle = options.CommitTitle
   452  		pullRequestBody.MergeMethod = options.MergeMethod
   453  		pullRequestBody.SHA = options.SHA
   454  	}
   455  	req, err := s.client.NewRequest("PUT", u, pullRequestBody)
   456  	if err != nil {
   457  		return nil, nil, err
   458  	}
   459  
   460  	mergeResult := new(PullRequestMergeResult)
   461  	resp, err := s.client.Do(ctx, req, mergeResult)
   462  	if err != nil {
   463  		return nil, resp, err
   464  	}
   465  
   466  	return mergeResult, resp, nil
   467  }
   468  

View as plain text