Repository: google/go-github
Pull request #2401 (2022-06-28 12:04:09)

Change DependabotSecretsSelectedRepoIDs to []string
Fixes: #2400.

gmlewis: @lneves75 - could you please review and approve if you agree to this change when you have a chance?
codecov[bot]: # [Codecov](https://codecov.io/gh/google/go-github/pull/2401?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
> Merging [#2401](https://codecov.io/gh/google/go-github/pull/2401?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (e3028fb) into [master](https://codecov.io/gh/google/go-github/commit/fd22ee93382e50d36592170f63c6a08d7c35ed8d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (fd22ee9) will **not change** coverage.
> The diff coverage is `n/a`.

```diff
@@           Coverage Diff           @@
##           master    #2401   +/-   ##
=======================================
  Coverage   98.06%   98.06%           
=======================================
  Files         119      119           
  Lines       10546    10546           
=======================================
  Hits        10342    10342           
  Misses        140      140           
  Partials       64       64           
```


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

------

[Continue to review full report at Codecov](https://codecov.io/gh/google/go-github/pull/2401?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/google/go-github/pull/2401?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google). Last update [fd22ee9...e3028fb](https://codecov.io/gh/google/go-github/pull/2401?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).

gmlewis: Awaiting LGTM+Approval from two contributors to this repo before merging.
lneves75: @gmlewis apparently the problem lies on github's API implementation for both endpoints.
If we call the endpoint `/orgs/myorg/actions/secrets/BAR` with a set of strings we get a 422
```json
{
	"message": "Invalid request.\n\nFor 'items', \"123\" is not an integer.\nFor 'items', \"456\" is not an integer.",
	"documentation_url": "https://docs.github.com/rest/reference/actions#create-or-update-an-organization-secret"
}
```

So I'm afraid that we need to have two distinct types 😞 
```go
type ActionSecretsSelectedRepoIDs []int64
type DependabotSecretsSelectedRepoIDs []string
```  
WDYT ?
timrogers: > OK, that is really weird. Can you please contact GitHub tech support and verify this correct?

👋🏻 Hey there! I'm the PM for the API at GitHub, and I happened to just spot this thread.

I can confirm that the current behaviour of the API is that:

* the Actions "[Create or update an organization secret](https://docs.github.com/en/rest/actions/secrets#create-or-update-an-organization-secret)" API only has documented support for an array of integers, but it actually accepts strings or integers. (Until recently, it said it was an array of strings.)
* the Dependabot "[Create or update an organization secret](https://docs.github.com/en/rest/dependabot/secrets#create-or-update-an-organization-secret)" API only supports an array of strings

This is obviously a bit weird at the moment and is something we need to clean up - but in a way that avoids breaking changes for users!
gmlewis: > 👋🏻 Hey there! I'm the PM for the API at GitHub, and I happened to just spot this thread.

Hi @timrogers - it's great to have you here!  Thank you to you and your team for an amazing product... it is greatly appreciated!

> I can confirm that the current behaviour of the API is that:
> 
> * the Actions "[Create or update an organization secret](https://docs.github.com/en/rest/actions/secrets#create-or-update-an-organization-secret)" API only has documented support for an array of integers (although in practice it actually accepts strings or integers). (Until recently, the docs said it should be an array of strings.)
> * the Dependabot "[Create or update an organization secret](https://docs.github.com/en/rest/dependabot/secrets#create-or-update-an-organization-secret)" API only supports an array of strings

If I'm reading this correctly, then making them both use an array of strings should work.
However, when we tried this here: https://github.com/google/go-github/pull/2401/commits/e3028fbc563b35173008debb2bcabd9eddf63c83
it failed... see comment from @lneves75 above: https://github.com/google/go-github/pull/2401#issuecomment-1168648355

So then we tried splitting it into two separate implementations, as you see this PR now.

> Sorry for the pain! This is obviously a bit weird at the moment and is something we need to clean up - but in a way that minimises breaking changes for users.

No problem... we would just like to get it working, so if you have recommendations on how to proceed, that would be great.
We don't mind updating the repo when/if it changes... we are updating this repo all the time.  :smile:

timrogers: @gmlewis So sorry - you’re right, I showed too much confidence and I actually misread the internal PR 😅: the Actions API really does only accept integers, whereas before it claimed incorrectly in the docs to accept strings.

Looking at the inputs, integers seem like the right data type, so I will raise an issue with the Dependabot team to look at making this more consistent in a non-breaking way.  

Very glad I came by this repo to discover this and see all of the great work you’re doing to make the API accessible to Go developers ❤️
timrogers: In other words, it looks like you’re doing exactly what you need to do in this PR!
gmlewis: > Very glad I came by this repo to discover this and see all of the great work you’re doing to make the API accessible to Go developers

Thank you so much, @timrogers !

We really appreciate your input, and we always love to hear from GitHub team members and tech support whenever we can.  :smile: 

Patch:
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 da146bcb..013780bf 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -1030,6 +1030,14 @@ func (a *AuditEntry) GetRepositoryPublic() bool {
 	return *a.RepositoryPublic
 }
 
+// GetRunAttempt returns the RunAttempt field if it's non-nil, zero value otherwise.
+func (a *AuditEntry) GetRunAttempt() int64 {
+	if a == nil || a.RunAttempt == nil {
+		return 0
+	}
+	return *a.RunAttempt
+}
+
 // GetRunnerGroupID returns the RunnerGroupID field if it's non-nil, zero value otherwise.
 func (a *AuditEntry) GetRunnerGroupID() int64 {
 	if a == nil || a.RunnerGroupID == nil {
@@ -2286,6 +2294,14 @@ func (c *CodeOfConduct) GetURL() string {
 	return *c.URL
 }
 
+// GetSuggestion returns the Suggestion field if it's non-nil, zero value otherwise.
+func (c *CodeownersError) GetSuggestion() string {
+	if c == nil || c.Suggestion == nil {
+		return ""
+	}
+	return *c.Suggestion
+}
+
 // GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise.
 func (c *CodeResult) GetHTMLURL() string {
 	if c == nil || c.HTMLURL == nil {
@@ -10790,12 +10806,12 @@ func (p *PagesUpdate) GetPublic() bool {
 	return *p.Public
 }
 
-// GetSource returns the Source field if it's non-nil, zero value otherwise.
-func (p *PagesUpdate) GetSource() string {
-	if p == nil || p.Source == nil {
-		return ""
+// GetSource returns the Source field.
+func (p *PagesUpdate) GetSource() *PagesSource {
+	if p == nil {
+		return nil
 	}
-	return *p.Source
+	return p.Source
 }
 
 // GetHook returns the Hook field.
@@ -12894,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 {
@@ -12902,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 {
@@ -12910,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 {
@@ -17286,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/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/orgs_audit_log.go b/github/orgs_audit_log.go
index 52bacfed..be5fd24d 100644
--- a/github/orgs_audit_log.go
+++ b/github/orgs_audit_log.go
@@ -70,6 +70,7 @@ type AuditEntry struct {
 	Repo                  *string     `json:"repo,omitempty"`
 	Repository            *string     `json:"repository,omitempty"`
 	RepositoryPublic      *bool       `json:"repository_public,omitempty"`
+	RunAttempt            *int64      `json:"run_attempt,omitempty"`
 	RunnerGroupID         *int64      `json:"runner_group_id,omitempty"`
 	RunnerGroupName       *string     `json:"runner_group_name,omitempty"`
 	RunnerID              *int64      `json:"runner_id,omitempty"`
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_codeowners.go b/github/repos_codeowners.go
new file mode 100644
index 00000000..835d56e1
--- /dev/null
+++ b/github/repos_codeowners.go
@@ -0,0 +1,46 @@
+// 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"
+)
+
+// CodeownersErrors represents a list of syntax errors detected in the CODEOWNERS file.
+type CodeownersErrors struct {
+	Errors []*CodeownersError `json:"errors"`
+}
+
+// CodeownersError represents a syntax error detected in the CODEOWNERS file.
+type CodeownersError struct {
+	Line       int     `json:"line"`
+	Column     int     `json:"column"`
+	Kind       string  `json:"kind"`
+	Source     string  `json:"source"`
+	Suggestion *string `json:"suggestion,omitempty"`
+	Message    string  `json:"message"`
+	Path       string  `json:"path"`
+}
+
+// GetCodeownersErrors lists any syntax errors that are detected in the CODEOWNERS file.
+//
+// GitHub API docs: https://docs.github.com/en/rest/repos/repos#list-codeowners-errors
+func (s *RepositoriesService) GetCodeownersErrors(ctx context.Context, owner, repo string) (*CodeownersErrors, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/codeowners/errors", owner, repo)
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	codeownersErrors := &CodeownersErrors{}
+	resp, err := s.client.Do(ctx, req, codeownersErrors)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return codeownersErrors, resp, nil
+}
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
+}
diff --git a/github/repos_pages.go b/github/repos_pages.go
index 9b864eb0..737cec0f 100644
--- a/github/repos_pages.go
+++ b/github/repos_pages.go
@@ -93,8 +93,10 @@ type PagesUpdate struct {
 	// Leaving CNAME empty will remove the custom domain.
 	CNAME *string `json:"cname"`
 	// Source must include the branch name, and may optionally specify the subdirectory "/docs".
-	// Possible values are: "gh-pages", "master", and "master /docs".
-	Source *string `json:"source,omitempty"`
+	// Possible values for Source.Branch are usually "gh-pages", "main", and "master",
+	// or any other existing branch name.
+	// Possible values for Source.Path are: "/", and "/docs".
+	Source *PagesSource `json:"source,omitempty"`
 	// Public configures access controls for the site.
 	// If "true", the site will be accessible to anyone on the internet. If "false",
 	// the site will be accessible to anyone with read access to the repository that
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 5d30ec96..984a69b7 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -1216,6 +1216,16 @@ func TestAuditEntry_GetRepositoryPublic(tt *testing.T) {
 	a.GetRepositoryPublic()
 }
 
+func TestAuditEntry_GetRunAttempt(tt *testing.T) {
+	var zeroValue int64
+	a := &AuditEntry{RunAttempt: &zeroValue}
+	a.GetRunAttempt()
+	a = &AuditEntry{}
+	a.GetRunAttempt()
+	a = nil
+	a.GetRunAttempt()
+}
+
 func TestAuditEntry_GetRunnerGroupID(tt *testing.T) {
 	var zeroValue int64
 	a := &AuditEntry{RunnerGroupID: &zeroValue}
@@ -2699,6 +2709,16 @@ func TestCodeOfConduct_GetURL(tt *testing.T) {
 	c.GetURL()
 }
 
+func TestCodeownersError_GetSuggestion(tt *testing.T) {
+	var zeroValue string
+	c := &CodeownersError{Suggestion: &zeroValue}
+	c.GetSuggestion()
+	c = &CodeownersError{}
+	c.GetSuggestion()
+	c = nil
+	c.GetSuggestion()
+}
+
 func TestCodeResult_GetHTMLURL(tt *testing.T) {
 	var zeroValue string
 	c := &CodeResult{HTMLURL: &zeroValue}
@@ -12643,10 +12663,7 @@ func TestPagesUpdate_GetPublic(tt *testing.T) {
 }
 
 func TestPagesUpdate_GetSource(tt *testing.T) {
-	var zeroValue string
-	p := &PagesUpdate{Source: &zeroValue}
-	p.GetSource()
-	p = &PagesUpdate{}
+	p := &PagesUpdate{}
 	p.GetSource()
 	p = nil
 	p.GetSource()
@@ -15002,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()
@@ -15009,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()
@@ -15016,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()
@@ -20165,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/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/orgs_audit_log_test.go b/github/orgs_audit_log_test.go
index c2beca2b..1f060072 100644
--- a/github/orgs_audit_log_test.go
+++ b/github/orgs_audit_log_test.go
@@ -207,6 +207,7 @@ func TestAuditEntry_Marshal(t *testing.T) {
 		Repo:                  String("r"),
 		Repository:            String("repo"),
 		RepositoryPublic:      Bool(false),
+		RunAttempt:            Int64(1),
 		RunnerGroupID:         Int64(1),
 		RunnerGroupName:       String("rgn"),
 		RunnerID:              Int64(1),
@@ -275,6 +276,7 @@ func TestAuditEntry_Marshal(t *testing.T) {
 		"repo": "r",
 		"repository": "repo",
 		"repository_public": false,
+		"run_attempt": 1,
 		"runner_group_id": 1,
 		"runner_group_name": "rgn",
 		"runner_id": 1,
diff --git a/github/repos_codeowners_test.go b/github/repos_codeowners_test.go
new file mode 100644
index 00000000..62c58c5b
--- /dev/null
+++ b/github/repos_codeowners_test.go
@@ -0,0 +1,110 @@
+// 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"
+	"net/http"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestRepositoriesService_GetCodeownersErrors(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/codeowners/errors", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testHeader(t, r, "Accept", mediaTypeV3)
+		fmt.Fprint(w, `{
+		  "errors": [
+			{
+			  "line": 1,
+			  "column": 1,
+			  "kind": "Invalid pattern",
+			  "source": "***/*.rb @monalisa",
+			  "suggestion": "Did you mean **/*.rb?",
+			  "message": "Invalid pattern on line 3: Did you mean **/*.rb?\n\n  ***/*.rb @monalisa\n  ^",
+			  "path": ".github/CODEOWNERS"
+			}
+		  ]
+		}
+	`)
+	})
+
+	ctx := context.Background()
+	codeownersErrors, _, err := client.Repositories.GetCodeownersErrors(ctx, "o", "r")
+	if err != nil {
+		t.Errorf("Repositories.GetCodeownersErrors returned error: %v", err)
+	}
+
+	want := &CodeownersErrors{
+		Errors: []*CodeownersError{
+			{
+				Line:       1,
+				Column:     1,
+				Kind:       "Invalid pattern",
+				Source:     "***/*.rb @monalisa",
+				Suggestion: String("Did you mean **/*.rb?"),
+				Message:    "Invalid pattern on line 3: Did you mean **/*.rb?\n\n  ***/*.rb @monalisa\n  ^",
+				Path:       ".github/CODEOWNERS",
+			},
+		},
+	}
+	if !cmp.Equal(codeownersErrors, want) {
+		t.Errorf("Repositories.GetCodeownersErrors returned %+v, want %+v", codeownersErrors, want)
+	}
+
+	const methodName = "GetCodeownersErrors"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Repositories.GetCodeownersErrors(ctx, "\n", "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.GetCodeownersErrors(ctx, "o", "r")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestCodeownersErrors_Marshal(t *testing.T) {
+	testJSONMarshal(t, &CodeownersErrors{}, "{}")
+
+	u := &CodeownersErrors{
+		Errors: []*CodeownersError{
+			{
+				Line:       1,
+				Column:     1,
+				Kind:       "Invalid pattern",
+				Source:     "***/*.rb @monalisa",
+				Suggestion: String("Did you mean **/*.rb?"),
+				Message:    "Invalid pattern on line 3: Did you mean **/*.rb?\n\n  ***/*.rb @monalisa\n  ^",
+				Path:       ".github/CODEOWNERS",
+			},
+		},
+	}
+
+	want := `{
+	  "errors": [
+		{
+		  "line": 1,
+		  "column": 1,
+		  "kind": "Invalid pattern",
+		  "source": "***/*.rb @monalisa",
+		  "suggestion": "Did you mean **/*.rb?",
+		  "message": "Invalid pattern on line 3: Did you mean **/*.rb?\n\n  ***/*.rb @monalisa\n  ^",
+		  "path": ".github/CODEOWNERS"
+		}
+	  ]
+	}
+`
+	testJSONMarshal(t, u, want)
+}
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_pages_test.go b/github/repos_pages_test.go
index 46bffcff..ed17e735 100644
--- a/github/repos_pages_test.go
+++ b/github/repos_pages_test.go
@@ -76,7 +76,7 @@ func TestRepositoriesService_UpdatePages(t *testing.T) {
 
 	input := &PagesUpdate{
 		CNAME:  String("www.my-domain.com"),
-		Source: String("gh-pages"),
+		Source: &PagesSource{Branch: String("gh-pages")},
 	}
 
 	mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) {
@@ -84,12 +84,12 @@ func TestRepositoriesService_UpdatePages(t *testing.T) {
 		json.NewDecoder(r.Body).Decode(v)
 
 		testMethod(t, r, "PUT")
-		want := &PagesUpdate{CNAME: String("www.my-domain.com"), Source: String("gh-pages")}
+		want := &PagesUpdate{CNAME: String("www.my-domain.com"), Source: &PagesSource{Branch: String("gh-pages")}}
 		if !cmp.Equal(v, want) {
 			t.Errorf("Request body = %+v, want %+v", v, want)
 		}
 
-		fmt.Fprint(w, `{"cname":"www.my-domain.com","source":"gh-pages"}`)
+		fmt.Fprint(w, `{"cname":"www.my-domain.com","source":{"branch":"gh-pages"}}`)
 	})
 
 	ctx := context.Background()
@@ -114,7 +114,7 @@ func TestRepositoriesService_UpdatePages_NullCNAME(t *testing.T) {
 	defer teardown()
 
 	input := &PagesUpdate{
-		Source: String("gh-pages"),
+		Source: &PagesSource{Branch: String("gh-pages")},
 	}
 
 	mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) {
@@ -123,12 +123,12 @@ func TestRepositoriesService_UpdatePages_NullCNAME(t *testing.T) {
 			t.Fatalf("unable to read body: %v", err)
 		}
 
-		want := []byte(`{"cname":null,"source":"gh-pages"}` + "\n")
+		want := []byte(`{"cname":null,"source":{"branch":"gh-pages"}}` + "\n")
 		if !bytes.Equal(got, want) {
 			t.Errorf("Request body = %+v, want %+v", got, want)
 		}
 
-		fmt.Fprint(w, `{"cname":null,"source":"gh-pages"}`)
+		fmt.Fprint(w, `{"cname":null,"source":{"branch":"gh-pages"}}`)
 	})
 
 	ctx := context.Background()
@@ -393,12 +393,12 @@ func TestPagesUpdate_Marshal(t *testing.T) {
 
 	u := &PagesUpdate{
 		CNAME:  String("cname"),
-		Source: String("src"),
+		Source: &PagesSource{Path: String("src")},
 	}
 
 	want := `{
 		"cname": "cname",
-		"source": "src"
+		"source": { "path": "src" }
 	}`
 
 	testJSONMarshal(t, u, want)
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()