Repository: google/go-github
Pull request #2429 (2022-08-02 02:44:36)

Add LFS control
Add ability to turn LFS on / off for repo

google-cla[bot]: Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this [failed invocation](https://github.com/google/go-github/pull/2429/checks?check_run_id=7623692141) of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.
codecov[bot]: # [Codecov](https://codecov.io/gh/google/go-github/pull/2429?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
> Merging [#2429](https://codecov.io/gh/google/go-github/pull/2429?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (a14e43e) into [master](https://codecov.io/gh/google/go-github/commit/3a432d62bf0318c14b2d1d7415bf94ea1a884342?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (3a432d6) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master    #2429   +/-   ##
=======================================
  Coverage   98.06%   98.07%           
=======================================
  Files         120      121    +1     
  Lines       10558    10582   +24     
=======================================
+ Hits        10354    10378   +24     
  Misses        140      140           
  Partials       64       64           
```


| [Impacted Files](https://codecov.io/gh/google/go-github/pull/2429?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) | Coverage Δ | |
|---|---|---|
| [github/repos\_lfs.go](https://codecov.io/gh/google/go-github/pull/2429/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google#diff-Z2l0aHViL3JlcG9zX2xmcy5nbw==) | `100.00% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).


Patch:
diff --git a/github/actions_workflow_runs.go b/github/actions_workflow_runs.go
index 18fdd57d..9fd01c4a 100644
--- a/github/actions_workflow_runs.go
+++ b/github/actions_workflow_runs.go
@@ -94,6 +94,14 @@ type WorkflowRunAttemptOptions struct {
 	ExcludePullRequests *bool `url:"exclude_pull_requests,omitempty"`
 }
 
+// PendingDeploymentsRequest specifies body parameters to PendingDeployments.
+type PendingDeploymentsRequest struct {
+	EnvironmentIDs []int64 `json:"environment_ids"`
+	// State can be one of: "approved", "rejected".
+	State   string `json:"state"`
+	Comment string `json:"comment"`
+}
+
 func (s *ActionsService) listWorkflowRuns(ctx context.Context, endpoint string, opts *ListWorkflowRunsOptions) (*WorkflowRuns, *Response, error) {
 	u, err := addOptions(endpoint, opts)
 	if err != nil {
@@ -321,3 +329,23 @@ func (s *ActionsService) GetWorkflowRunUsageByID(ctx context.Context, owner, rep
 
 	return workflowRunUsage, resp, nil
 }
+
+// PendingDeployments approve or reject pending deployments that are waiting on approval by a required reviewer.
+//
+// GitHub API docs: https://docs.github.com/en/rest/actions/workflow-runs#review-pending-deployments-for-a-workflow-run
+func (s *ActionsService) PendingDeployments(ctx context.Context, owner, repo string, runID int64, request *PendingDeploymentsRequest) ([]*Deployment, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/pending_deployments", owner, repo, runID)
+
+	req, err := s.client.NewRequest("POST", u, request)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var deployments []*Deployment
+	resp, err := s.client.Do(ctx, req, &deployments)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return deployments, resp, nil
+}
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 334ff395..d84f5697 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -12910,6 +12910,14 @@ func (p *PullRequestReviewRequest) GetNodeID() string {
 	return *p.NodeID
 }
 
+// GetBypassPullRequestAllowances returns the BypassPullRequestAllowances field.
+func (p *PullRequestReviewsEnforcement) GetBypassPullRequestAllowances() *BypassPullRequestAllowances {
+	if p == nil {
+		return nil
+	}
+	return p.BypassPullRequestAllowances
+}
+
 // GetDismissalRestrictions returns the DismissalRestrictions field.
 func (p *PullRequestReviewsEnforcement) GetDismissalRestrictions() *DismissalRestrictions {
 	if p == nil {
@@ -12918,6 +12926,14 @@ func (p *PullRequestReviewsEnforcement) GetDismissalRestrictions() *DismissalRes
 	return p.DismissalRestrictions
 }
 
+// GetBypassPullRequestAllowancesRequest returns the BypassPullRequestAllowancesRequest field.
+func (p *PullRequestReviewsEnforcementRequest) GetBypassPullRequestAllowancesRequest() *BypassPullRequestAllowancesRequest {
+	if p == nil {
+		return nil
+	}
+	return p.BypassPullRequestAllowancesRequest
+}
+
 // GetDismissalRestrictionsRequest returns the DismissalRestrictionsRequest field.
 func (p *PullRequestReviewsEnforcementRequest) GetDismissalRestrictionsRequest() *DismissalRestrictionsRequest {
 	if p == nil {
@@ -12926,6 +12942,14 @@ func (p *PullRequestReviewsEnforcementRequest) GetDismissalRestrictionsRequest()
 	return p.DismissalRestrictionsRequest
 }
 
+// GetBypassPullRequestAllowancesRequest returns the BypassPullRequestAllowancesRequest field.
+func (p *PullRequestReviewsEnforcementUpdate) GetBypassPullRequestAllowancesRequest() *BypassPullRequestAllowancesRequest {
+	if p == nil {
+		return nil
+	}
+	return p.BypassPullRequestAllowancesRequest
+}
+
 // GetDismissalRestrictionsRequest returns the DismissalRestrictionsRequest field.
 func (p *PullRequestReviewsEnforcementUpdate) GetDismissalRestrictionsRequest() *DismissalRestrictionsRequest {
 	if p == nil {
diff --git a/github/repos.go b/github/repos.go
index a5f7fc6c..d5ac5112 100644
--- a/github/repos.go
+++ b/github/repos.go
@@ -919,6 +919,8 @@ type RequiredStatusCheck struct {
 
 // PullRequestReviewsEnforcement represents the pull request reviews enforcement of a protected branch.
 type PullRequestReviewsEnforcement struct {
+	// Allow specific users, teams, or apps to bypass pull request requirements.
+	BypassPullRequestAllowances *BypassPullRequestAllowances `json:"bypass_pull_request_allowances,omitempty"`
 	// Specifies which users and teams can dismiss pull request reviews.
 	DismissalRestrictions *DismissalRestrictions `json:"dismissal_restrictions,omitempty"`
 	// Specifies if approved reviews are dismissed automatically, when a new commit is pushed.
@@ -934,6 +936,8 @@ type PullRequestReviewsEnforcement struct {
 // enforcement of a protected branch. It is separate from PullRequestReviewsEnforcement above
 // because the request structure is different from the response structure.
 type PullRequestReviewsEnforcementRequest struct {
+	// Allow specific users, teams, or apps to bypass pull request requirements.
+	BypassPullRequestAllowancesRequest *BypassPullRequestAllowancesRequest `json:"bypass_pull_request_allowances,omitempty"`
 	// Specifies which users and teams should be allowed to dismiss pull request reviews.
 	// User and team dismissal restrictions are only available for
 	// organization-owned repositories. Must be nil for personal repositories.
@@ -951,6 +955,8 @@ type PullRequestReviewsEnforcementRequest struct {
 // enforcement of a protected branch. It is separate from PullRequestReviewsEnforcementRequest above
 // because the patch request does not require all fields to be initialized.
 type PullRequestReviewsEnforcementUpdate struct {
+	// Allow specific users, teams, or apps to bypass pull request requirements.
+	BypassPullRequestAllowancesRequest *BypassPullRequestAllowancesRequest `json:"bypass_pull_request_allowances,omitempty"`
 	// Specifies which users and teams can dismiss pull request reviews. Can be omitted.
 	DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions,omitempty"`
 	// Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. Can be omitted.
@@ -1012,6 +1018,29 @@ type BranchRestrictionsRequest struct {
 	Apps []string `json:"apps,omitempty"`
 }
 
+// BypassPullRequestAllowances represents the people, teams, or apps who are allowed to bypass required pull requests.
+type BypassPullRequestAllowances struct {
+	// The list of users allowed to bypass pull request requirements.
+	Users []*User `json:"users"`
+	// The list of teams allowed to bypass pull request requirements.
+	Teams []*Team `json:"teams"`
+	// The list of apps allowed to bypass pull request requirements.
+	Apps []*App `json:"apps"`
+}
+
+// BypassPullRequestAllowancesRequest represents the people, teams, or apps who are
+// allowed to bypass required pull requests.
+// It is separate from BypassPullRequestAllowances above because the request structure is
+// different from the response structure.
+type BypassPullRequestAllowancesRequest struct {
+	// The list of user logins allowed to bypass pull request requirements.
+	Users []string `json:"users"`
+	// The list of team slugs allowed to bypass pull request requirements.
+	Teams []string `json:"teams"`
+	// The list of app slugs allowed to bypass pull request requirements.
+	Apps []string `json:"apps"`
+}
+
 // DismissalRestrictions specifies which users and teams can dismiss pull request reviews.
 type DismissalRestrictions struct {
 	// The list of users who can dimiss pull request reviews.
diff --git a/github/repos_contents.go b/github/repos_contents.go
index d6f2dd9d..431d4b12 100644
--- a/github/repos_contents.go
+++ b/github/repos_contents.go
@@ -51,7 +51,7 @@ type RepositoryContentResponse struct {
 // RepositoryContentFileOptions specifies optional parameters for CreateFile, UpdateFile, and DeleteFile.
 type RepositoryContentFileOptions struct {
 	Message   *string       `json:"message,omitempty"`
-	Content   []byte        `json:"content,omitempty"` // unencoded
+	Content   []byte        `json:"content"` // unencoded
 	SHA       *string       `json:"sha,omitempty"`
 	Branch    *string       `json:"branch,omitempty"`
 	Author    *CommitAuthor `json:"author,omitempty"`
diff --git a/github/repos_lfs.go b/github/repos_lfs.go
new file mode 100644
index 00000000..6ac2e5a8
--- /dev/null
+++ b/github/repos_lfs.go
@@ -0,0 +1,49 @@
+// Copyright 2022 The go-github AUTHORS. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import (
+	"context"
+	"fmt"
+)
+
+// EnableLFS turns the LFS (Large File Storage) feature ON for the selected repo.
+//
+// GitHub API docs: https://docs.github.com/en/rest/repos/lfs#enable-git-lfs-for-a-repository
+func (s *RepositoriesService) EnableLFS(ctx context.Context, owner, repo string) (*Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/lfs", owner, repo)
+
+	req, err := s.client.NewRequest("PUT", u, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	resp, err := s.client.Do(ctx, req, nil)
+	if err != nil {
+		return resp, err
+	}
+
+	return resp, nil
+}
+
+// DisableLFS turns the LFS (Large File Storage) feature OFF for the selected repo.
+//
+// GitHub API docs: https://docs.github.com/en/rest/repos/lfs#disable-git-lfs-for-a-repository
+func (s *RepositoriesService) DisableLFS(ctx context.Context, owner, repo string) (*Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/lfs", owner, repo)
+
+	req, err := s.client.NewRequest("DELETE", u, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	resp, err := s.client.Do(ctx, req, nil)
+	if err != nil {
+		return resp, err
+	}
+
+	return resp, nil
+}


Test patch:
diff --git a/github/actions_workflow_runs_test.go b/github/actions_workflow_runs_test.go
index d22b8a52..2705363b 100644
--- a/github/actions_workflow_runs_test.go
+++ b/github/actions_workflow_runs_test.go
@@ -7,6 +7,7 @@ package github
 
 import (
 	"context"
+	"encoding/json"
 	"fmt"
 	"net/http"
 	"net/url"
@@ -1075,3 +1076,47 @@ func TestWorkflowRunUsage_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, u, want)
 }
+
+func TestActionService_PendingDeployments(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	input := &PendingDeploymentsRequest{EnvironmentIDs: []int64{3, 4}, State: "approved", Comment: ""}
+
+	mux.HandleFunc("/repos/o/r/actions/runs/399444496/pending_deployments", func(w http.ResponseWriter, r *http.Request) {
+		v := new(PendingDeploymentsRequest)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, "POST")
+		if !cmp.Equal(v, input) {
+			t.Errorf("Request body = %+v, want %+v", v, input)
+		}
+
+		fmt.Fprint(w, `[{"id":1}, {"id":2}]`)
+	})
+
+	ctx := context.Background()
+	deployments, _, err := client.Actions.PendingDeployments(ctx, "o", "r", 399444496, input)
+	if err != nil {
+		t.Errorf("Actions.PendingDeployments returned error: %v", err)
+	}
+
+	want := []*Deployment{{ID: Int64(1)}, {ID: Int64(2)}}
+	if !cmp.Equal(deployments, want) {
+		t.Errorf("Actions.PendingDeployments returned %+v, want %+v", deployments, want)
+	}
+
+	const methodName = "PendingDeployments"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Actions.PendingDeployments(ctx, "\n", "\n", 399444496, input)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Actions.PendingDeployments(ctx, "o", "r", 399444496, input)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index 4339a879..d8c4024c 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -15019,6 +15019,13 @@ func TestPullRequestReviewRequest_GetNodeID(tt *testing.T) {
 	p.GetNodeID()
 }
 
+func TestPullRequestReviewsEnforcement_GetBypassPullRequestAllowances(tt *testing.T) {
+	p := &PullRequestReviewsEnforcement{}
+	p.GetBypassPullRequestAllowances()
+	p = nil
+	p.GetBypassPullRequestAllowances()
+}
+
 func TestPullRequestReviewsEnforcement_GetDismissalRestrictions(tt *testing.T) {
 	p := &PullRequestReviewsEnforcement{}
 	p.GetDismissalRestrictions()
@@ -15026,6 +15033,13 @@ func TestPullRequestReviewsEnforcement_GetDismissalRestrictions(tt *testing.T) {
 	p.GetDismissalRestrictions()
 }
 
+func TestPullRequestReviewsEnforcementRequest_GetBypassPullRequestAllowancesRequest(tt *testing.T) {
+	p := &PullRequestReviewsEnforcementRequest{}
+	p.GetBypassPullRequestAllowancesRequest()
+	p = nil
+	p.GetBypassPullRequestAllowancesRequest()
+}
+
 func TestPullRequestReviewsEnforcementRequest_GetDismissalRestrictionsRequest(tt *testing.T) {
 	p := &PullRequestReviewsEnforcementRequest{}
 	p.GetDismissalRestrictionsRequest()
@@ -15033,6 +15047,13 @@ func TestPullRequestReviewsEnforcementRequest_GetDismissalRestrictionsRequest(tt
 	p.GetDismissalRestrictionsRequest()
 }
 
+func TestPullRequestReviewsEnforcementUpdate_GetBypassPullRequestAllowancesRequest(tt *testing.T) {
+	p := &PullRequestReviewsEnforcementUpdate{}
+	p.GetBypassPullRequestAllowancesRequest()
+	p = nil
+	p.GetBypassPullRequestAllowancesRequest()
+}
+
 func TestPullRequestReviewsEnforcementUpdate_GetDismissalRestrictionsRequest(tt *testing.T) {
 	p := &PullRequestReviewsEnforcementUpdate{}
 	p.GetDismissalRestrictionsRequest()
diff --git a/github/repos_lfs_test.go b/github/repos_lfs_test.go
new file mode 100644
index 00000000..f171b6e3
--- /dev/null
+++ b/github/repos_lfs_test.go
@@ -0,0 +1,64 @@
+// Copyright 2022 The go-github AUTHORS. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import (
+	"context"
+	"net/http"
+	"testing"
+)
+
+func TestRepositoriesService_EnableLFS(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/lfs", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PUT")
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	ctx := context.Background()
+	if _, err := client.Repositories.EnableLFS(ctx, "o", "r"); err != nil {
+		t.Errorf("Repositories.EnableLFS returned error: %v", err)
+	}
+
+	const methodName = "EnableLFS"
+	testBadOptions(t, methodName, func() (err error) {
+		_, err = client.Repositories.EnableLFS(ctx, "\n", "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Repositories.EnableLFS(ctx, "o", "r")
+	})
+}
+
+func TestRepositoriesService_DisableLFS(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/lfs", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	ctx := context.Background()
+	if _, err := client.Repositories.DisableLFS(ctx, "o", "r"); err != nil {
+		t.Errorf("Repositories.DisableLFS returned error: %v", err)
+	}
+
+	const methodName = "DisableLFS"
+	testBadOptions(t, methodName, func() (err error) {
+		_, err = client.Repositories.DisableLFS(ctx, "\n", "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Repositories.DisableLFS(ctx, "o", "r")
+	})
+}
diff --git a/github/repos_test.go b/github/repos_test.go
index 0bf228eb..6b8cbbd2 100644
--- a/github/repos_test.go
+++ b/github/repos_test.go
@@ -1274,6 +1274,11 @@ func TestRepositoriesService_UpdateBranchProtection_Contexts(t *testing.T) {
 				Users: &[]string{"uu"},
 				Teams: &[]string{"tt"},
 			},
+			BypassPullRequestAllowancesRequest: &BypassPullRequestAllowancesRequest{
+				Users: []string{"uuu"},
+				Teams: []string{"ttt"},
+				Apps:  []string{"aaa"},
+			},
 		},
 		Restrictions: &BranchRestrictionsRequest{
 			Users: []string{"u"},
@@ -1316,7 +1321,12 @@ func TestRepositoriesService_UpdateBranchProtection_Contexts(t *testing.T) {
 					}]
 				},
 				"dismiss_stale_reviews":true,
-				"require_code_owner_reviews":true
+				"require_code_owner_reviews":true,
+				"bypass_pull_request_allowances": {
+					"users":[{"id":10,"login":"uuu"}],
+					"teams":[{"id":20,"slug":"ttt"}],
+					"apps":[{"id":30,"slug":"aaa"}]
+				}
 			},
 			"restrictions":{
 				"users":[{"id":1,"login":"u"}],
@@ -1353,6 +1363,17 @@ func TestRepositoriesService_UpdateBranchProtection_Contexts(t *testing.T) {
 				},
 			},
 			RequireCodeOwnerReviews: true,
+			BypassPullRequestAllowances: &BypassPullRequestAllowances{
+				Users: []*User{
+					{Login: String("uuu"), ID: Int64(10)},
+				},
+				Teams: []*Team{
+					{Slug: String("ttt"), ID: Int64(20)},
+				},
+				Apps: []*App{
+					{Slug: String("aaa"), ID: Int64(30)},
+				},
+			},
 		},
 		Restrictions: &BranchRestrictions{
 			Users: []*User{
@@ -1404,6 +1425,11 @@ func TestRepositoriesService_UpdateBranchProtection_Checks(t *testing.T) {
 				Users: &[]string{"uu"},
 				Teams: &[]string{"tt"},
 			},
+			BypassPullRequestAllowancesRequest: &BypassPullRequestAllowancesRequest{
+				Users: []string{"uuu"},
+				Teams: []string{"ttt"},
+				Apps:  []string{"aaa"},
+			},
 		},
 		Restrictions: &BranchRestrictionsRequest{
 			Users: []string{"u"},
@@ -1446,7 +1472,12 @@ func TestRepositoriesService_UpdateBranchProtection_Checks(t *testing.T) {
 					}]
 				},
 				"dismiss_stale_reviews":true,
-				"require_code_owner_reviews":true
+				"require_code_owner_reviews":true,
+				"bypass_pull_request_allowances": {
+					"users":[{"id":10,"login":"uuu"}],
+					"teams":[{"id":20,"slug":"ttt"}],
+					"apps":[{"id":30,"slug":"aaa"}]
+				}
 			},
 			"restrictions":{
 				"users":[{"id":1,"login":"u"}],
@@ -1483,6 +1514,17 @@ func TestRepositoriesService_UpdateBranchProtection_Checks(t *testing.T) {
 				},
 			},
 			RequireCodeOwnerReviews: true,
+			BypassPullRequestAllowances: &BypassPullRequestAllowances{
+				Users: []*User{
+					{Login: String("uuu"), ID: Int64(10)},
+				},
+				Teams: []*Team{
+					{Slug: String("ttt"), ID: Int64(20)},
+				},
+				Apps: []*App{
+					{Slug: String("aaa"), ID: Int64(30)},
+				},
+			},
 		},
 		Restrictions: &BranchRestrictions{
 			Users: []*User{