Repository: google/go-github
Pull request #2434 (2022-08-12 04:45:02)

chore: Update README with additional auth use case
This adds a use case for endpoints that require JWT authentication


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ef94c683..25d455b8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -127,3 +127,5 @@ this][modified-comment].
 [git-aliases]: https://github.com/willnorris/dotfiles/blob/d640d010c23b1116bdb3d4dc12088ed26120d87d/git/.gitconfig#L13-L15
 [rebase-comment]: https://github.com/google/go-github/pull/277#issuecomment-183035491
 [modified-comment]: https://github.com/google/go-github/pull/280#issuecomment-184859046
+
+**When creating a release, don't forget to update the `Version` constant in `github.go`.** This is used to send the version in the `User-Agent` header to identify clients to the GitHub API.
diff --git a/README.md b/README.md
index 2b70743a..d6a98dfe 100644
--- a/README.md
+++ b/README.md
@@ -117,11 +117,23 @@ See the [oauth2 docs][] for complete instructions on using that library.
 For API methods that require HTTP Basic Authentication, use the
 [`BasicAuthTransport`](https://godoc.org/github.com/google/go-github/github#BasicAuthTransport).
 
+#### As a GitHub App ####
+
 GitHub Apps authentication can be provided by the [ghinstallation](https://github.com/bradleyfalzon/ghinstallation)
 package.
 
+> **Note**: Most endpoints (ex. [`GET /rate_limit`]) require access token authentication
+> while a few others (ex. [`GET /app/hook/deliverires`]) require [JWT] authentication.
+
+[`GET /rate_limit`]: https://docs.github.com/en/rest/rate-limit#get-rate-limit-status-for-the-authenticated-user
+[`GET /app/hook/deliverires`]: https://docs.github.com/en/rest/apps/webhooks#list-deliveries-for-an-app-webhook
+[JWT]: https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps#authenticating-as-a-github-app
+
+
 ```go
 import (
+	"net/http"
+
 	"github.com/bradleyfalzon/ghinstallation/v2"
 	"github.com/google/go-github/v45/github"
 )
@@ -129,6 +141,10 @@ import (
 func main() {
 	// Wrap the shared transport for use with the integration ID 1 authenticating with installation ID 99.
 	itr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, 1, 99, "2016-10-19.private-key.pem")
+
+	// Or for endpoints that require JWT authentication
+	// itr, err := ghinstallation.NewAppsTransportKeyFromFile(http.DefaultTransport, 1, "2016-10-19.private-key.pem")
+
 	if err != nil {
 		// Handle error.
 	}
diff --git a/example/tagprotection/main.go b/example/tagprotection/main.go
new file mode 100644
index 00000000..6f347b97
--- /dev/null
+++ b/example/tagprotection/main.go
@@ -0,0 +1,76 @@
+// 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.
+
+// The tagprotection command demonstrates the functionality that
+// prompts the user for GitHub owner, repo, tag protection pattern and token,
+// then creates a new tag protection if the user entered a pattern at the prompt.
+// Otherwise, it will just list all existing tag protections.
+package main
+
+import (
+	"bufio"
+	"context"
+	"encoding/json"
+	"fmt"
+	"log"
+	"os"
+	"strings"
+	"syscall"
+
+	"github.com/google/go-github/v45/github"
+	"golang.org/x/crypto/ssh/terminal"
+	"golang.org/x/oauth2"
+)
+
+func main() {
+	// read github owner, repo, token from standard input
+	r := bufio.NewReader(os.Stdin)
+	fmt.Print("GitHub Org/User name: ")
+	owner, _ := r.ReadString('\n')
+	owner = strings.TrimSpace(owner)
+
+	fmt.Print("GitHub repo name: ")
+	repo, _ := r.ReadString('\n')
+	repo = strings.TrimSpace(repo)
+
+	fmt.Print("Tag pattern(leave blank to not create new tag protection): ")
+	pattern, _ := r.ReadString('\n')
+	pattern = strings.TrimSpace(pattern)
+
+	fmt.Print("GitHub Token: ")
+	byteToken, _ := terminal.ReadPassword(int(syscall.Stdin))
+	println()
+	token := string(byteToken)
+
+	ctx := context.Background()
+	ts := oauth2.StaticTokenSource(
+		&oauth2.Token{AccessToken: token},
+	)
+	tc := oauth2.NewClient(ctx, ts)
+
+	client := github.NewClient(tc)
+
+	// create new tag protection
+	if pattern != "" {
+		tagProtection, _, err := client.Repositories.CreateTagProtection(ctx, owner, repo, pattern)
+		if err != nil {
+			log.Fatalf("Error: %v\n", err)
+		}
+		println()
+		fmt.Printf("New tag protection created in github.com/%v/%v\n", owner, repo)
+		tp, _ := json.Marshal(tagProtection)
+		fmt.Println(string(tp))
+	}
+
+	// list all tag protection
+	println()
+	fmt.Printf("List all tag protection in github.com/%v/%v\n", owner, repo)
+	tagProtections, _, err := client.Repositories.ListTagProtection(ctx, owner, repo)
+	if err != nil {
+		log.Fatalf("Error: %v\n", err)
+	}
+	results, _ := json.Marshal(tagProtections)
+	fmt.Println(string(results))
+}
diff --git a/github/actions_secrets.go b/github/actions_secrets.go
index dc057edb..316badb7 100644
--- a/github/actions_secrets.go
+++ b/github/actions_secrets.go
@@ -186,7 +186,7 @@ func (s *ActionsService) GetEnvSecret(ctx context.Context, repoID int, env, secr
 	return s.getSecret(ctx, url)
 }
 
-// SelectedRepoIDs are the repository IDs that have access to the secret.
+// SelectedRepoIDs are the repository IDs that have access to the actions secrets.
 type SelectedRepoIDs []int64
 
 // EncryptedSecret represents a secret that is encrypted using a public key.
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/dependabot_secrets.go b/github/dependabot_secrets.go
index f51f3396..8318cd81 100644
--- a/github/dependabot_secrets.go
+++ b/github/dependabot_secrets.go
@@ -110,7 +110,20 @@ func (s *DependabotService) GetOrgSecret(ctx context.Context, org, name string)
 	return s.getSecret(ctx, url)
 }
 
-func (s *DependabotService) putSecret(ctx context.Context, url string, eSecret *EncryptedSecret) (*Response, error) {
+// DependabotEncryptedSecret represents a secret that is encrypted using a public key for Dependabot.
+//
+// The value of EncryptedValue must be your secret, encrypted with
+// LibSodium (see documentation here: https://libsodium.gitbook.io/doc/bindings_for_other_languages)
+// using the public key retrieved using the GetPublicKey method.
+type DependabotEncryptedSecret struct {
+	Name                  string                           `json:"-"`
+	KeyID                 string                           `json:"key_id"`
+	EncryptedValue        string                           `json:"encrypted_value"`
+	Visibility            string                           `json:"visibility,omitempty"`
+	SelectedRepositoryIDs DependabotSecretsSelectedRepoIDs `json:"selected_repository_ids,omitempty"`
+}
+
+func (s *DependabotService) putSecret(ctx context.Context, url string, eSecret *DependabotEncryptedSecret) (*Response, error) {
 	req, err := s.client.NewRequest("PUT", url, eSecret)
 	if err != nil {
 		return nil, err
@@ -122,7 +135,7 @@ func (s *DependabotService) putSecret(ctx context.Context, url string, eSecret *
 // CreateOrUpdateRepoSecret creates or updates a repository Dependabot secret with an encrypted value.
 //
 // GitHub API docs: https://docs.github.com/en/rest/dependabot/secrets#create-or-update-a-repository-secret
-func (s *DependabotService) CreateOrUpdateRepoSecret(ctx context.Context, owner, repo string, eSecret *EncryptedSecret) (*Response, error) {
+func (s *DependabotService) CreateOrUpdateRepoSecret(ctx context.Context, owner, repo string, eSecret *DependabotEncryptedSecret) (*Response, error) {
 	url := fmt.Sprintf("repos/%v/%v/dependabot/secrets/%v", owner, repo, eSecret.Name)
 	return s.putSecret(ctx, url, eSecret)
 }
@@ -130,7 +143,7 @@ func (s *DependabotService) CreateOrUpdateRepoSecret(ctx context.Context, owner,
 // CreateOrUpdateOrgSecret creates or updates an organization Dependabot secret with an encrypted value.
 //
 // GitHub API docs: https://docs.github.com/en/rest/dependabot/secrets#create-or-update-an-organization-secret
-func (s *DependabotService) CreateOrUpdateOrgSecret(ctx context.Context, org string, eSecret *EncryptedSecret) (*Response, error) {
+func (s *DependabotService) CreateOrUpdateOrgSecret(ctx context.Context, org string, eSecret *DependabotEncryptedSecret) (*Response, error) {
 	url := fmt.Sprintf("orgs/%v/dependabot/secrets/%v", org, eSecret.Name)
 	return s.putSecret(ctx, url, eSecret)
 }
@@ -184,13 +197,16 @@ func (s *DependabotService) ListSelectedReposForOrgSecret(ctx context.Context, o
 	return result, resp, nil
 }
 
+// DependabotSecretsSelectedRepoIDs are the repository IDs that have access to the dependabot secrets.
+type DependabotSecretsSelectedRepoIDs []string
+
 // SetSelectedReposForOrgSecret sets the repositories that have access to a Dependabot secret.
 //
 // GitHub API docs: https://docs.github.com/en/rest/dependabot/secrets#set-selected-repositories-for-an-organization-secret
-func (s *DependabotService) SetSelectedReposForOrgSecret(ctx context.Context, org, name string, ids SelectedRepoIDs) (*Response, error) {
+func (s *DependabotService) SetSelectedReposForOrgSecret(ctx context.Context, org, name string, ids DependabotSecretsSelectedRepoIDs) (*Response, error) {
 	url := fmt.Sprintf("orgs/%v/dependabot/secrets/%v/repositories", org, name)
 	type repoIDs struct {
-		SelectedIDs SelectedRepoIDs `json:"selected_repository_ids"`
+		SelectedIDs DependabotSecretsSelectedRepoIDs `json:"selected_repository_ids"`
 	}
 
 	req, err := s.client.NewRequest("PUT", url, repoIDs{SelectedIDs: ids})
diff --git a/github/event.go b/github/event.go
index 5a052de0..4cacf0a8 100644
--- a/github/event.go
+++ b/github/event.go
@@ -36,6 +36,8 @@ func (e *Event) ParsePayload() (payload interface{}, err error) {
 		payload = &CheckRunEvent{}
 	case "CheckSuiteEvent":
 		payload = &CheckSuiteEvent{}
+	case "CodeScanningAlertEvent":
+		payload = &CodeScanningAlertEvent{}
 	case "CommitCommentEvent":
 		payload = &CommitCommentEvent{}
 	case "ContentReferenceEvent":
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 334ff395..013780bf 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 {
@@ -17302,6 +17326,22 @@ func (t *Tag) GetVerification() *SignatureVerification {
 	return t.Verification
 }
 
+// GetID returns the ID field if it's non-nil, zero value otherwise.
+func (t *TagProtection) GetID() int64 {
+	if t == nil || t.ID == nil {
+		return 0
+	}
+	return *t.ID
+}
+
+// GetPattern returns the Pattern field if it's non-nil, zero value otherwise.
+func (t *TagProtection) GetPattern() string {
+	if t == nil || t.Pattern == nil {
+		return ""
+	}
+	return *t.Pattern
+}
+
 // GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise.
 func (t *TaskStep) GetCompletedAt() Timestamp {
 	if t == nil || t.CompletedAt == nil {
diff --git a/github/github.go b/github/github.go
index 08b7db8e..fe346f5a 100644
--- a/github/github.go
+++ b/github/github.go
@@ -28,9 +28,11 @@ import (
 )
 
 const (
-	defaultBaseURL = "https://api.github.com/"
-	uploadBaseURL  = "https://uploads.github.com/"
-	userAgent      = "go-github"
+	Version = "45.2.0"
+
+	defaultBaseURL   = "https://api.github.com/"
+	defaultUserAgent = "go-github" + "/" + Version
+	uploadBaseURL    = "https://uploads.github.com/"
 
 	headerRateLimit     = "X-RateLimit-Limit"
 	headerRateRemaining = "X-RateLimit-Remaining"
@@ -301,7 +303,7 @@ func NewClient(httpClient *http.Client) *Client {
 	baseURL, _ := url.Parse(defaultBaseURL)
 	uploadURL, _ := url.Parse(uploadBaseURL)
 
-	c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent, UploadURL: uploadURL}
+	c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: defaultUserAgent, UploadURL: uploadURL}
 	c.common.client = c
 	c.Actions = (*ActionsService)(&c.common)
 	c.Activity = (*ActivityService)(&c.common)
diff --git a/github/messages.go b/github/messages.go
index 44477ddb..05cffb4b 100644
--- a/github/messages.go
+++ b/github/messages.go
@@ -48,6 +48,7 @@ var (
 		"branch_protection_rule":         "BranchProtectionRuleEvent",
 		"check_run":                      "CheckRunEvent",
 		"check_suite":                    "CheckSuiteEvent",
+		"code_scanning_alert":            "CodeScanningAlertEvent",
 		"commit_comment":                 "CommitCommentEvent",
 		"content_reference":              "ContentReferenceEvent",
 		"create":                         "CreateEvent",
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_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
+}
diff --git a/github/repos_tags.go b/github/repos_tags.go
new file mode 100644
index 00000000..ff46d90c
--- /dev/null
+++ b/github/repos_tags.go
@@ -0,0 +1,76 @@
+// 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"
+)
+
+// TagProtection represents a repository tag protection.
+type TagProtection struct {
+	ID      *int64  `json:"id"`
+	Pattern *string `json:"pattern"`
+}
+
+// tagProtectionRequest represents a request to create tag protection.
+type tagProtectionRequest struct {
+	// An optional glob pattern to match against when enforcing tag protection.
+	Pattern string `json:"pattern"`
+}
+
+// ListTagProtection lists tag protection of the specified repository.
+//
+// GitHub API docs: https://docs.github.com/en/rest/repos/tags#list-tag-protection-states-for-a-repository
+func (s *RepositoriesService) ListTagProtection(ctx context.Context, owner, repo string) ([]*TagProtection, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/tags/protection", owner, repo)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var tagProtections []*TagProtection
+	resp, err := s.client.Do(ctx, req, &tagProtections)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return tagProtections, resp, nil
+}
+
+// CreateTagProtection creates the tag protection of the specified repository.
+//
+// GitHub API docs: https://docs.github.com/en/rest/repos/tags#create-a-tag-protection-state-for-a-repository
+func (s *RepositoriesService) CreateTagProtection(ctx context.Context, owner, repo, pattern string) (*TagProtection, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/tags/protection", owner, repo)
+	r := &tagProtectionRequest{Pattern: pattern}
+	req, err := s.client.NewRequest("POST", u, r)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	tagProtection := new(TagProtection)
+	resp, err := s.client.Do(ctx, req, tagProtection)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return tagProtection, resp, nil
+}
+
+// DeleteTagProtection deletes a tag protection from the specified repository.
+//
+// GitHub API docs: https://docs.github.com/en/rest/repos/tags#delete-a-tag-protection-state-for-a-repository
+func (s *RepositoriesService) DeleteTagProtection(ctx context.Context, owner, repo string, tagProtectionID int64) (*Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/tags/protection/%v", owner, repo, tagProtectionID)
+	req, err := s.client.NewRequest("DELETE", u, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(ctx, req, nil)
+}
diff --git a/github/search.go b/github/search.go
index 344f1bb9..4d166640 100644
--- a/github/search.go
+++ b/github/search.go
@@ -9,6 +9,7 @@ import (
 	"context"
 	"fmt"
 	"strconv"
+	"strings"
 
 	qs "github.com/google/go-querystring/query"
 )
@@ -299,29 +300,32 @@ func (s *SearchService) search(ctx context.Context, searchType string, parameter
 	if err != nil {
 		return nil, err
 	}
-
+	var acceptHeaders []string
 	switch {
 	case searchType == "commits":
 		// Accept header for search commits preview endpoint
 		// TODO: remove custom Accept header when this API fully launches.
-		req.Header.Set("Accept", mediaTypeCommitSearchPreview)
+		acceptHeaders = append(acceptHeaders, mediaTypeCommitSearchPreview)
 	case searchType == "topics":
 		// Accept header for search repositories based on topics preview endpoint
 		// TODO: remove custom Accept header when this API fully launches.
-		req.Header.Set("Accept", mediaTypeTopicsPreview)
+		acceptHeaders = append(acceptHeaders, mediaTypeTopicsPreview)
 	case searchType == "repositories":
 		// Accept header for search repositories based on topics preview endpoint
 		// TODO: remove custom Accept header when this API fully launches.
-		req.Header.Set("Accept", mediaTypeTopicsPreview)
+		acceptHeaders = append(acceptHeaders, mediaTypeTopicsPreview)
 	case searchType == "issues":
 		// Accept header for search issues based on reactions preview endpoint
 		// TODO: remove custom Accept header when this API fully launches.
-		req.Header.Set("Accept", mediaTypeReactionsPreview)
-	case opts != nil && opts.TextMatch:
-		// Accept header defaults to "application/vnd.github.v3+json"
-		// We change it here to fetch back text-match metadata
-		req.Header.Set("Accept", "application/vnd.github.v3.text-match+json")
+		acceptHeaders = append(acceptHeaders, mediaTypeReactionsPreview)
+	}
+	// https://docs.github.com/en/rest/search#search-repositories
+	// Accept header defaults to "application/vnd.github.v3+json"
+	// We change it here to fetch back text-match metadata
+	if opts != nil && opts.TextMatch {
+		acceptHeaders = append(acceptHeaders, "application/vnd.github.v3.text-match+json")
 	}
+	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
 
 	return s.client.Do(ctx, req, result)
 }


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/dependabot_secrets_test.go b/github/dependabot_secrets_test.go
index 49b93e76..49ddaab6 100644
--- a/github/dependabot_secrets_test.go
+++ b/github/dependabot_secrets_test.go
@@ -178,7 +178,7 @@ func TestDependabotService_CreateOrUpdateRepoSecret(t *testing.T) {
 		w.WriteHeader(http.StatusCreated)
 	})
 
-	input := &EncryptedSecret{
+	input := &DependabotEncryptedSecret{
 		Name:           "NAME",
 		EncryptedValue: "QIv=",
 		KeyID:          "1234",
@@ -352,16 +352,16 @@ func TestDependabotService_CreateOrUpdateOrgSecret(t *testing.T) {
 	mux.HandleFunc("/orgs/o/dependabot/secrets/NAME", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "PUT")
 		testHeader(t, r, "Content-Type", "application/json")
-		testBody(t, r, `{"key_id":"1234","encrypted_value":"QIv=","visibility":"selected","selected_repository_ids":[1296269,1269280]}`+"\n")
+		testBody(t, r, `{"key_id":"1234","encrypted_value":"QIv=","visibility":"selected","selected_repository_ids":["1296269","1269280"]}`+"\n")
 		w.WriteHeader(http.StatusCreated)
 	})
 
-	input := &EncryptedSecret{
+	input := &DependabotEncryptedSecret{
 		Name:                  "NAME",
 		EncryptedValue:        "QIv=",
 		KeyID:                 "1234",
 		Visibility:            "selected",
-		SelectedRepositoryIDs: SelectedRepoIDs{1296269, 1269280},
+		SelectedRepositoryIDs: DependabotSecretsSelectedRepoIDs{"1296269", "1269280"},
 	}
 	ctx := context.Background()
 	_, err := client.Dependabot.CreateOrUpdateOrgSecret(ctx, "o", input)
@@ -428,23 +428,23 @@ func TestDependabotService_SetSelectedReposForOrgSecret(t *testing.T) {
 	mux.HandleFunc("/orgs/o/dependabot/secrets/NAME/repositories", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "PUT")
 		testHeader(t, r, "Content-Type", "application/json")
-		testBody(t, r, `{"selected_repository_ids":[64780797]}`+"\n")
+		testBody(t, r, `{"selected_repository_ids":["64780797"]}`+"\n")
 	})
 
 	ctx := context.Background()
-	_, err := client.Dependabot.SetSelectedReposForOrgSecret(ctx, "o", "NAME", SelectedRepoIDs{64780797})
+	_, err := client.Dependabot.SetSelectedReposForOrgSecret(ctx, "o", "NAME", DependabotSecretsSelectedRepoIDs{"64780797"})
 	if err != nil {
 		t.Errorf("Dependabot.SetSelectedReposForOrgSecret returned error: %v", err)
 	}
 
 	const methodName = "SetSelectedReposForOrgSecret"
 	testBadOptions(t, methodName, func() (err error) {
-		_, err = client.Dependabot.SetSelectedReposForOrgSecret(ctx, "\n", "\n", SelectedRepoIDs{64780797})
+		_, err = client.Dependabot.SetSelectedReposForOrgSecret(ctx, "\n", "\n", DependabotSecretsSelectedRepoIDs{"64780797"})
 		return err
 	})
 
 	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
-		return client.Dependabot.SetSelectedReposForOrgSecret(ctx, "o", "NAME", SelectedRepoIDs{64780797})
+		return client.Dependabot.SetSelectedReposForOrgSecret(ctx, "o", "NAME", DependabotSecretsSelectedRepoIDs{"64780797"})
 	})
 }
 
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index 4339a879..984a69b7 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()
@@ -20182,6 +20203,26 @@ func TestTag_GetVerification(tt *testing.T) {
 	t.GetVerification()
 }
 
+func TestTagProtection_GetID(tt *testing.T) {
+	var zeroValue int64
+	t := &TagProtection{ID: &zeroValue}
+	t.GetID()
+	t = &TagProtection{}
+	t.GetID()
+	t = nil
+	t.GetID()
+}
+
+func TestTagProtection_GetPattern(tt *testing.T) {
+	var zeroValue string
+	t := &TagProtection{Pattern: &zeroValue}
+	t.GetPattern()
+	t = &TagProtection{}
+	t.GetPattern()
+	t = nil
+	t.GetPattern()
+}
+
 func TestTaskStep_GetCompletedAt(tt *testing.T) {
 	var zeroValue Timestamp
 	t := &TaskStep{CompletedAt: &zeroValue}
diff --git a/github/github_test.go b/github/github_test.go
index 0a28ced9..3fa5b441 100644
--- a/github/github_test.go
+++ b/github/github_test.go
@@ -248,7 +248,7 @@ func TestNewClient(t *testing.T) {
 	if got, want := c.BaseURL.String(), defaultBaseURL; got != want {
 		t.Errorf("NewClient BaseURL is %v, want %v", got, want)
 	}
-	if got, want := c.UserAgent, userAgent; got != want {
+	if got, want := c.UserAgent, defaultUserAgent; got != want {
 		t.Errorf("NewClient UserAgent is %v, want %v", got, want)
 	}
 
@@ -507,10 +507,16 @@ func TestNewRequest(t *testing.T) {
 		t.Errorf("NewRequest(%q) Body is %v, want %v", inBody, got, want)
 	}
 
+	userAgent := req.Header.Get("User-Agent")
+
 	// test that default user-agent is attached to the request
-	if got, want := req.Header.Get("User-Agent"), c.UserAgent; got != want {
+	if got, want := userAgent, c.UserAgent; got != want {
 		t.Errorf("NewRequest() User-Agent is %v, want %v", got, want)
 	}
+
+	if !strings.Contains(userAgent, Version) {
+		t.Errorf("NewRequest() User-Agent should contain %v, found %v", Version, userAgent)
+	}
 }
 
 func TestNewRequest_invalidJSON(t *testing.T) {
diff --git a/github/messages_test.go b/github/messages_test.go
index 1d999c96..083cec9a 100644
--- a/github/messages_test.go
+++ b/github/messages_test.go
@@ -273,6 +273,10 @@ func TestParseWebHook(t *testing.T) {
 			payload:     &CheckSuiteEvent{},
 			messageType: "check_suite",
 		},
+		{
+			payload:     &CodeScanningAlertEvent{},
+			messageType: "code_scanning_alert",
+		},
 		{
 			payload:     &CommitCommentEvent{},
 			messageType: "commit_comment",
diff --git a/github/repos_hooks_deliveries_test.go b/github/repos_hooks_deliveries_test.go
index f136639b..44b0f89a 100644
--- a/github/repos_hooks_deliveries_test.go
+++ b/github/repos_hooks_deliveries_test.go
@@ -145,6 +145,7 @@ func TestRepositoriesService_RedeliverHookDelivery(t *testing.T) {
 var hookDeliveryPayloadTypeToStruct = map[string]interface{}{
 	"check_run":                      &CheckRunEvent{},
 	"check_suite":                    &CheckSuiteEvent{},
+	"code_scanning_alert":            &CodeScanningAlertEvent{},
 	"commit_comment":                 &CommitCommentEvent{},
 	"content_reference":              &ContentReferenceEvent{},
 	"create":                         &CreateEvent{},
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_tags_test.go b/github/repos_tags_test.go
new file mode 100644
index 00000000..e5e15a69
--- /dev/null
+++ b/github/repos_tags_test.go
@@ -0,0 +1,132 @@
+// 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"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestRepositoriesService_ListTagProtection(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/tags/protection", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+
+		fmt.Fprint(w, `[{"id":1, "pattern":"tag1"},{"id":2, "pattern":"tag2"}]`)
+	})
+
+	ctx := context.Background()
+	tagProtections, _, err := client.Repositories.ListTagProtection(ctx, "o", "r")
+	if err != nil {
+		t.Errorf("Repositories.ListTagProtection returned error: %v", err)
+	}
+
+	want := []*TagProtection{{ID: Int64(1), Pattern: String("tag1")}, {ID: Int64(2), Pattern: String("tag2")}}
+	if !cmp.Equal(tagProtections, want) {
+		t.Errorf("Repositories.ListTagProtection returned %+v, want %+v", tagProtections, want)
+	}
+
+	const methodName = "ListTagProtection"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Repositories.ListTagProtection(ctx, "\n", "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.ListTagProtection(ctx, "o", "r")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestRepositoriesService_ListTagProtection_invalidOwner(t *testing.T) {
+	client, _, _, teardown := setup()
+	defer teardown()
+
+	ctx := context.Background()
+	_, _, err := client.Repositories.ListTagProtection(ctx, "%", "r")
+	testURLParseError(t, err)
+}
+
+func TestRepositoriesService_CreateTagProtection(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	pattern := "tag*"
+
+	mux.HandleFunc("/repos/o/r/tags/protection", func(w http.ResponseWriter, r *http.Request) {
+		v := new(tagProtectionRequest)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, "POST")
+		want := &tagProtectionRequest{Pattern: "tag*"}
+		if !cmp.Equal(v, want) {
+			t.Errorf("Request body = %+v, want %+v", v, want)
+		}
+
+		fmt.Fprint(w, `{"id":1,"pattern":"tag*"}`)
+	})
+
+	ctx := context.Background()
+	got, _, err := client.Repositories.CreateTagProtection(ctx, "o", "r", pattern)
+	if err != nil {
+		t.Errorf("Repositories.CreateTagProtection returned error: %v", err)
+	}
+
+	want := &TagProtection{ID: Int64(1), Pattern: String("tag*")}
+	if !cmp.Equal(got, want) {
+		t.Errorf("Repositories.CreateTagProtection returned %+v, want %+v", got, want)
+	}
+
+	const methodName = "CreateTagProtection"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Repositories.CreateTagProtection(ctx, "\n", "\n", pattern)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.CreateTagProtection(ctx, "o", "r", pattern)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestRepositoriesService_DeleteTagProtection(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/tags/protection/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	ctx := context.Background()
+	_, err := client.Repositories.DeleteTagProtection(ctx, "o", "r", 1)
+	if err != nil {
+		t.Errorf("Repositories.DeleteTagProtection returned error: %v", err)
+	}
+
+	const methodName = "DeleteTagProtection"
+	testBadOptions(t, methodName, func() (err error) {
+		_, err = client.Repositories.DeleteTagProtection(ctx, "\n", "\n", 1)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Repositories.DeleteTagProtection(ctx, "o", "r", 1)
+	})
+}
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{
diff --git a/github/search_test.go b/github/search_test.go
index 4562fcf5..0d49b36c 100644
--- a/github/search_test.go
+++ b/github/search_test.go
@@ -9,6 +9,7 @@ import (
 	"context"
 	"fmt"
 	"net/http"
+	"strings"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
@@ -61,6 +62,85 @@ func TestSearchService_Repositories_coverage(t *testing.T) {
 	})
 }
 
+func TestSearchService_RepositoriesTextMatch(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/search/repositories", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		textMatchResponse := `
+			{
+				"total_count": 1,
+				"incomplete_results": false,
+				"items": [
+					{
+						"name":"gopher1"
+					}
+				]
+			}
+		`
+		list := strings.Split(r.Header.Get("Accept"), ",")
+		aMap := make(map[string]struct{})
+		for _, s := range list {
+			aMap[strings.TrimSpace(s)] = struct{}{}
+		}
+		if _, ok := aMap["application/vnd.github.v3.text-match+json"]; ok {
+			textMatchResponse = `
+					{
+						"total_count": 1,
+						"incomplete_results": false,
+						"items": [
+							{
+								"name":"gopher1",
+								"text_matches": [
+									{
+										"fragment": "I'm afraid my friend what you have found\nIs a gopher who lives to feed",
+										"matches": [
+											{
+												"text": "gopher",
+												"indices": [
+													14,
+													21
+											]
+											}
+									  ]
+								  }
+							  ]
+							}
+						]
+					}
+				`
+		}
+
+		fmt.Fprint(w, textMatchResponse)
+	})
+
+	opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}, TextMatch: true}
+	ctx := context.Background()
+	result, _, err := client.Search.Repositories(ctx, "blah", opts)
+	if err != nil {
+		t.Errorf("Search.Code returned error: %v", err)
+	}
+
+	wantedRepoResult := &Repository{
+		Name: String("gopher1"),
+		TextMatches: []*TextMatch{{
+			Fragment: String("I'm afraid my friend what you have found\nIs a gopher who lives to feed"),
+			Matches:  []*Match{{Text: String("gopher"), Indices: []int{14, 21}}},
+		},
+		},
+	}
+
+	want := &RepositoriesSearchResult{
+		Total:             Int(1),
+		IncompleteResults: Bool(false),
+		Repositories:      []*Repository{wantedRepoResult},
+	}
+	if !cmp.Equal(result, want) {
+		t.Errorf("Search.Repo returned %+v, want %+v", result, want)
+	}
+}
+
 func TestSearchService_Topics(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()