Repository: google/go-github
Pull request #3050 (2024-01-14 07:02:26)

Add support for deployment protection rules
Fixes https://github.com/google/go-github/issues/3022

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

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

For the most up to date status, view the checks section at the bottom of the pull request.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/google/go-github/pull/3050?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
Attention: `6 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`9231a0f`)](https://app.codecov.io/gh/google/go-github/commit/9231a0f0bc205f0535a29361d773a77da5e39294?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) 97.71% compared to head [(`dd3407a`)](https://app.codecov.io/gh/google/go-github/pull/3050?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) 97.68%.
> Report is 2 commits behind head on master.

| [Files](https://app.codecov.io/gh/google/go-github/pull/3050?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) | Patch % | Lines |
|---|---|---|
| [github/code-scanning.go](https://app.codecov.io/gh/google/go-github/pull/3050?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google#diff-Z2l0aHViL2NvZGUtc2Nhbm5pbmcuZ28=) | 75.00% | [2 Missing and 1 partial :warning: ](https://app.codecov.io/gh/google/go-github/pull/3050?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) |
| [github/repos\_deployment\_protection\_rules.go](https://app.codecov.io/gh/google/go-github/pull/3050?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google#diff-Z2l0aHViL3JlcG9zX2RlcGxveW1lbnRfcHJvdGVjdGlvbl9ydWxlcy5nbw==) | 95.00% | [2 Missing and 1 partial :warning: ](https://app.codecov.io/gh/google/go-github/pull/3050?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #3050      +/-   ##
==========================================
- Coverage   97.71%   97.68%   -0.04%     
==========================================
  Files         152      153       +1     
  Lines       13241    13310      +69     
==========================================
+ Hits        12939    13002      +63     
- Misses        213      217       +4     
- Partials       89       91       +2     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/google/go-github/pull/3050?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).


Patch:
diff --git a/github/actions_permissions_enterprise.go b/github/actions_permissions_enterprise.go
index 7e10444a..a6a85aa3 100644
--- a/github/actions_permissions_enterprise.go
+++ b/github/actions_permissions_enterprise.go
@@ -29,6 +29,14 @@ func (a ActionsPermissionsEnterprise) String() string {
 	return Stringify(a)
 }
 
+// DefaultWorkflowPermissionEnterprise represents the default permissions for GitHub Actions workflows for an enterprise.
+//
+// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions
+type DefaultWorkflowPermissionEnterprise struct {
+	DefaultWorkflowPermissions   *string `json:"default_workflow_permissions,omitempty"`
+	CanApprovePullRequestReviews *bool   `json:"can_approve_pull_request_reviews,omitempty"`
+}
+
 // GetActionsPermissionsInEnterprise gets the GitHub Actions permissions policy for an enterprise.
 //
 // GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#get-github-actions-permissions-for-an-enterprise
@@ -205,3 +213,46 @@ func (s *ActionsService) EditActionsAllowedInEnterprise(ctx context.Context, ent
 
 	return p, resp, nil
 }
+
+// GetDefaultWorkflowPermissionsInEnterprise gets the GitHub Actions default workflow permissions for an enterprise.
+//
+// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#get-default-workflow-permissions-for-an-enterprise
+//
+//meta:operation GET /enterprises/{enterprise}/actions/permissions/workflow
+func (s *ActionsService) GetDefaultWorkflowPermissionsInEnterprise(ctx context.Context, enterprise string) (*DefaultWorkflowPermissionEnterprise, *Response, error) {
+	u := fmt.Sprintf("enterprises/%v/actions/permissions/workflow", enterprise)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	permissions := new(DefaultWorkflowPermissionEnterprise)
+	resp, err := s.client.Do(ctx, req, permissions)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return permissions, resp, nil
+}
+
+// EditDefaultWorkflowPermissionsInEnterprise sets the GitHub Actions default workflow permissions for an enterprise.
+//
+// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#set-default-workflow-permissions-for-an-enterprise
+//
+//meta:operation PUT /enterprises/{enterprise}/actions/permissions/workflow
+func (s *ActionsService) EditDefaultWorkflowPermissionsInEnterprise(ctx context.Context, enterprise string, permissions DefaultWorkflowPermissionEnterprise) (*DefaultWorkflowPermissionEnterprise, *Response, error) {
+	u := fmt.Sprintf("enterprises/%v/actions/permissions/workflow", enterprise)
+	req, err := s.client.NewRequest("PUT", u, permissions)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(DefaultWorkflowPermissionEnterprise)
+	resp, err := s.client.Do(ctx, req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, nil
+}
diff --git a/github/actions_permissions_orgs.go b/github/actions_permissions_orgs.go
index 1a31e4c6..2f555f24 100644
--- a/github/actions_permissions_orgs.go
+++ b/github/actions_permissions_orgs.go
@@ -42,6 +42,14 @@ func (a ActionsAllowed) String() string {
 	return Stringify(a)
 }
 
+// DefaultWorkflowPermissionOrganization represents the default permissions for GitHub Actions workflows for an organization.
+//
+// GitHub API docs: https://docs.github.com/rest/actions/permissions
+type DefaultWorkflowPermissionOrganization struct {
+	DefaultWorkflowPermissions   *string `json:"default_workflow_permissions,omitempty"`
+	CanApprovePullRequestReviews *bool   `json:"can_approve_pull_request_reviews,omitempty"`
+}
+
 // GetActionsPermissions gets the GitHub Actions permissions policy for repositories and allowed actions in an organization.
 //
 // GitHub API docs: https://docs.github.com/rest/actions/permissions#get-github-actions-permissions-for-an-organization
@@ -218,3 +226,46 @@ func (s *ActionsService) EditActionsAllowed(ctx context.Context, org string, act
 
 	return p, resp, nil
 }
+
+// GetDefaultWorkflowPermissionsInOrganization gets the GitHub Actions default workflow permissions for an organization.
+//
+// GitHub API docs: https://docs.github.com/rest/actions/permissions#get-default-workflow-permissions-for-an-organization
+//
+//meta:operation GET /orgs/{org}/actions/permissions/workflow
+func (s *ActionsService) GetDefaultWorkflowPermissionsInOrganization(ctx context.Context, org string) (*DefaultWorkflowPermissionOrganization, *Response, error) {
+	u := fmt.Sprintf("orgs/%v/actions/permissions/workflow", org)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	permissions := new(DefaultWorkflowPermissionOrganization)
+	resp, err := s.client.Do(ctx, req, permissions)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return permissions, resp, nil
+}
+
+// EditDefaultWorkflowPermissionsInOrganization sets the GitHub Actions default workflow permissions for an organization.
+//
+// GitHub API docs: https://docs.github.com/rest/actions/permissions#set-default-workflow-permissions-for-an-organization
+//
+//meta:operation PUT /orgs/{org}/actions/permissions/workflow
+func (s *ActionsService) EditDefaultWorkflowPermissionsInOrganization(ctx context.Context, org string, permissions DefaultWorkflowPermissionOrganization) (*DefaultWorkflowPermissionOrganization, *Response, error) {
+	u := fmt.Sprintf("orgs/%v/actions/permissions/workflow", org)
+	req, err := s.client.NewRequest("PUT", u, permissions)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(DefaultWorkflowPermissionOrganization)
+	resp, err := s.client.Do(ctx, req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, nil
+}
diff --git a/github/actions_secrets.go b/github/actions_secrets.go
index 2d4ba98d..d8d405c0 100644
--- a/github/actions_secrets.go
+++ b/github/actions_secrets.go
@@ -138,6 +138,17 @@ func (s *ActionsService) ListRepoSecrets(ctx context.Context, owner, repo string
 	return s.listSecrets(ctx, url, opts)
 }
 
+// ListRepoOrgSecrets lists all organization secrets available in a repository
+// without revealing their encrypted values.
+//
+// GitHub API docs: https://docs.github.com/rest/actions/secrets#list-repository-organization-secrets
+//
+//meta:operation GET /repos/{owner}/{repo}/actions/organization-secrets
+func (s *ActionsService) ListRepoOrgSecrets(ctx context.Context, owner, repo string, opts *ListOptions) (*Secrets, *Response, error) {
+	url := fmt.Sprintf("repos/%v/%v/actions/organization-secrets", owner, repo)
+	return s.listSecrets(ctx, url, opts)
+}
+
 // ListOrgSecrets lists all secrets available in an organization
 // without revealing their encrypted values.
 //
diff --git a/github/actions_variables.go b/github/actions_variables.go
index 244d1590..aa0f23ca 100644
--- a/github/actions_variables.go
+++ b/github/actions_variables.go
@@ -59,6 +59,16 @@ func (s *ActionsService) ListRepoVariables(ctx context.Context, owner, repo stri
 	return s.listVariables(ctx, url, opts)
 }
 
+// ListRepoOrgVariables lists all organization variables available in a repository.
+//
+// GitHub API docs: https://docs.github.com/rest/actions/variables#list-repository-organization-variables
+//
+//meta:operation GET /repos/{owner}/{repo}/actions/organization-variables
+func (s *ActionsService) ListRepoOrgVariables(ctx context.Context, owner, repo string, opts *ListOptions) (*ActionsVariables, *Response, error) {
+	url := fmt.Sprintf("repos/%v/%v/actions/organization-variables", owner, repo)
+	return s.listVariables(ctx, url, opts)
+}
+
 // ListOrgVariables lists all variables available in an organization.
 //
 // GitHub API docs: https://docs.github.com/rest/actions/variables#list-organization-variables
diff --git a/github/event_types.go b/github/event_types.go
index db7c7c69..b820dddf 100644
--- a/github/event_types.go
+++ b/github/event_types.go
@@ -394,6 +394,7 @@ type EditChange struct {
 	Repo          *EditRepo          `json:"repository,omitempty"`
 	Owner         *EditOwner         `json:"owner,omitempty"`
 	DefaultBranch *EditDefaultBranch `json:"default_branch,omitempty"`
+	Topics        *EditTopics        `json:"topics,omitempty"`
 }
 
 // EditTitle represents a pull-request title change.
@@ -438,6 +439,11 @@ type RepoName struct {
 	From *string `json:"from,omitempty"`
 }
 
+// EditTopics represents a change of repository topics.
+type EditTopics struct {
+	From []string `json:"from,omitempty"`
+}
+
 // EditSHA represents a sha change of a pull-request.
 type EditSHA struct {
 	From *string `json:"from,omitempty"`
@@ -1321,43 +1327,44 @@ func (h HeadCommit) String() string {
 
 // PushEventRepository represents the repo object in a PushEvent payload.
 type PushEventRepository struct {
-	ID              *int64     `json:"id,omitempty"`
-	NodeID          *string    `json:"node_id,omitempty"`
-	Name            *string    `json:"name,omitempty"`
-	FullName        *string    `json:"full_name,omitempty"`
-	Owner           *User      `json:"owner,omitempty"`
-	Private         *bool      `json:"private,omitempty"`
-	Description     *string    `json:"description,omitempty"`
-	Fork            *bool      `json:"fork,omitempty"`
-	CreatedAt       *Timestamp `json:"created_at,omitempty"`
-	PushedAt        *Timestamp `json:"pushed_at,omitempty"`
-	UpdatedAt       *Timestamp `json:"updated_at,omitempty"`
-	Homepage        *string    `json:"homepage,omitempty"`
-	PullsURL        *string    `json:"pulls_url,omitempty"`
-	Size            *int       `json:"size,omitempty"`
-	StargazersCount *int       `json:"stargazers_count,omitempty"`
-	WatchersCount   *int       `json:"watchers_count,omitempty"`
-	Language        *string    `json:"language,omitempty"`
-	HasIssues       *bool      `json:"has_issues,omitempty"`
-	HasDownloads    *bool      `json:"has_downloads,omitempty"`
-	HasWiki         *bool      `json:"has_wiki,omitempty"`
-	HasPages        *bool      `json:"has_pages,omitempty"`
-	ForksCount      *int       `json:"forks_count,omitempty"`
-	Archived        *bool      `json:"archived,omitempty"`
-	Disabled        *bool      `json:"disabled,omitempty"`
-	OpenIssuesCount *int       `json:"open_issues_count,omitempty"`
-	DefaultBranch   *string    `json:"default_branch,omitempty"`
-	MasterBranch    *string    `json:"master_branch,omitempty"`
-	Organization    *string    `json:"organization,omitempty"`
-	URL             *string    `json:"url,omitempty"`
-	ArchiveURL      *string    `json:"archive_url,omitempty"`
-	HTMLURL         *string    `json:"html_url,omitempty"`
-	StatusesURL     *string    `json:"statuses_url,omitempty"`
-	GitURL          *string    `json:"git_url,omitempty"`
-	SSHURL          *string    `json:"ssh_url,omitempty"`
-	CloneURL        *string    `json:"clone_url,omitempty"`
-	SVNURL          *string    `json:"svn_url,omitempty"`
-	Topics          []string   `json:"topics,omitempty"`
+	ID               *int64            `json:"id,omitempty"`
+	NodeID           *string           `json:"node_id,omitempty"`
+	Name             *string           `json:"name,omitempty"`
+	FullName         *string           `json:"full_name,omitempty"`
+	Owner            *User             `json:"owner,omitempty"`
+	Private          *bool             `json:"private,omitempty"`
+	Description      *string           `json:"description,omitempty"`
+	Fork             *bool             `json:"fork,omitempty"`
+	CreatedAt        *Timestamp        `json:"created_at,omitempty"`
+	PushedAt         *Timestamp        `json:"pushed_at,omitempty"`
+	UpdatedAt        *Timestamp        `json:"updated_at,omitempty"`
+	Homepage         *string           `json:"homepage,omitempty"`
+	PullsURL         *string           `json:"pulls_url,omitempty"`
+	Size             *int              `json:"size,omitempty"`
+	StargazersCount  *int              `json:"stargazers_count,omitempty"`
+	WatchersCount    *int              `json:"watchers_count,omitempty"`
+	Language         *string           `json:"language,omitempty"`
+	HasIssues        *bool             `json:"has_issues,omitempty"`
+	HasDownloads     *bool             `json:"has_downloads,omitempty"`
+	HasWiki          *bool             `json:"has_wiki,omitempty"`
+	HasPages         *bool             `json:"has_pages,omitempty"`
+	ForksCount       *int              `json:"forks_count,omitempty"`
+	Archived         *bool             `json:"archived,omitempty"`
+	Disabled         *bool             `json:"disabled,omitempty"`
+	OpenIssuesCount  *int              `json:"open_issues_count,omitempty"`
+	DefaultBranch    *string           `json:"default_branch,omitempty"`
+	MasterBranch     *string           `json:"master_branch,omitempty"`
+	Organization     *string           `json:"organization,omitempty"`
+	URL              *string           `json:"url,omitempty"`
+	ArchiveURL       *string           `json:"archive_url,omitempty"`
+	HTMLURL          *string           `json:"html_url,omitempty"`
+	StatusesURL      *string           `json:"statuses_url,omitempty"`
+	GitURL           *string           `json:"git_url,omitempty"`
+	SSHURL           *string           `json:"ssh_url,omitempty"`
+	CloneURL         *string           `json:"clone_url,omitempty"`
+	SVNURL           *string           `json:"svn_url,omitempty"`
+	Topics           []string          `json:"topics,omitempty"`
+	CustomProperties map[string]string `json:"custom_properties,omitempty"`
 }
 
 // PushEventRepoOwner is a basic representation of user/org in a PushEvent payload.
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 256bfacf..00211cd7 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -4590,6 +4590,78 @@ func (c *Credit) GetUser() *User {
 	return c.User
 }
 
+// GetApp returns the App field.
+func (c *CustomDeploymentProtectionRule) GetApp() *CustomDeploymentProtectionRuleApp {
+	if c == nil {
+		return nil
+	}
+	return c.App
+}
+
+// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise.
+func (c *CustomDeploymentProtectionRule) GetEnabled() bool {
+	if c == nil || c.Enabled == nil {
+		return false
+	}
+	return *c.Enabled
+}
+
+// GetID returns the ID field if it's non-nil, zero value otherwise.
+func (c *CustomDeploymentProtectionRule) GetID() int64 {
+	if c == nil || c.ID == nil {
+		return 0
+	}
+	return *c.ID
+}
+
+// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise.
+func (c *CustomDeploymentProtectionRule) GetNodeID() string {
+	if c == nil || c.NodeID == nil {
+		return ""
+	}
+	return *c.NodeID
+}
+
+// GetID returns the ID field if it's non-nil, zero value otherwise.
+func (c *CustomDeploymentProtectionRuleApp) GetID() int64 {
+	if c == nil || c.ID == nil {
+		return 0
+	}
+	return *c.ID
+}
+
+// GetIntegrationURL returns the IntegrationURL field if it's non-nil, zero value otherwise.
+func (c *CustomDeploymentProtectionRuleApp) GetIntegrationURL() string {
+	if c == nil || c.IntegrationURL == nil {
+		return ""
+	}
+	return *c.IntegrationURL
+}
+
+// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise.
+func (c *CustomDeploymentProtectionRuleApp) GetNodeID() string {
+	if c == nil || c.NodeID == nil {
+		return ""
+	}
+	return *c.NodeID
+}
+
+// GetSlug returns the Slug field if it's non-nil, zero value otherwise.
+func (c *CustomDeploymentProtectionRuleApp) GetSlug() string {
+	if c == nil || c.Slug == nil {
+		return ""
+	}
+	return *c.Slug
+}
+
+// GetIntegrationID returns the IntegrationID field if it's non-nil, zero value otherwise.
+func (c *CustomDeploymentProtectionRuleRequest) GetIntegrationID() int64 {
+	if c == nil || c.IntegrationID == nil {
+		return 0
+	}
+	return *c.IntegrationID
+}
+
 // GetDefaultValue returns the DefaultValue field if it's non-nil, zero value otherwise.
 func (c *CustomProperty) GetDefaultValue() string {
 	if c == nil || c.DefaultValue == nil {
@@ -4686,6 +4758,54 @@ func (d *DefaultSetupConfiguration) GetUpdatedAt() Timestamp {
 	return *d.UpdatedAt
 }
 
+// GetCanApprovePullRequestReviews returns the CanApprovePullRequestReviews field if it's non-nil, zero value otherwise.
+func (d *DefaultWorkflowPermissionEnterprise) GetCanApprovePullRequestReviews() bool {
+	if d == nil || d.CanApprovePullRequestReviews == nil {
+		return false
+	}
+	return *d.CanApprovePullRequestReviews
+}
+
+// GetDefaultWorkflowPermissions returns the DefaultWorkflowPermissions field if it's non-nil, zero value otherwise.
+func (d *DefaultWorkflowPermissionEnterprise) GetDefaultWorkflowPermissions() string {
+	if d == nil || d.DefaultWorkflowPermissions == nil {
+		return ""
+	}
+	return *d.DefaultWorkflowPermissions
+}
+
+// GetCanApprovePullRequestReviews returns the CanApprovePullRequestReviews field if it's non-nil, zero value otherwise.
+func (d *DefaultWorkflowPermissionOrganization) GetCanApprovePullRequestReviews() bool {
+	if d == nil || d.CanApprovePullRequestReviews == nil {
+		return false
+	}
+	return *d.CanApprovePullRequestReviews
+}
+
+// GetDefaultWorkflowPermissions returns the DefaultWorkflowPermissions field if it's non-nil, zero value otherwise.
+func (d *DefaultWorkflowPermissionOrganization) GetDefaultWorkflowPermissions() string {
+	if d == nil || d.DefaultWorkflowPermissions == nil {
+		return ""
+	}
+	return *d.DefaultWorkflowPermissions
+}
+
+// GetCanApprovePullRequestReviews returns the CanApprovePullRequestReviews field if it's non-nil, zero value otherwise.
+func (d *DefaultWorkflowPermissionRepository) GetCanApprovePullRequestReviews() bool {
+	if d == nil || d.CanApprovePullRequestReviews == nil {
+		return false
+	}
+	return *d.CanApprovePullRequestReviews
+}
+
+// GetDefaultWorkflowPermissions returns the DefaultWorkflowPermissions field if it's non-nil, zero value otherwise.
+func (d *DefaultWorkflowPermissionRepository) GetDefaultWorkflowPermissions() string {
+	if d == nil || d.DefaultWorkflowPermissions == nil {
+		return ""
+	}
+	return *d.DefaultWorkflowPermissions
+}
+
 // GetConfirmDeleteURL returns the ConfirmDeleteURL field if it's non-nil, zero value otherwise.
 func (d *DeleteAnalysis) GetConfirmDeleteURL() string {
 	if d == nil || d.ConfirmDeleteURL == nil {
@@ -6310,6 +6430,14 @@ func (e *EditChange) GetTitle() *EditTitle {
 	return e.Title
 }
 
+// GetTopics returns the Topics field.
+func (e *EditChange) GetTopics() *EditTopics {
+	if e == nil {
+		return nil
+	}
+	return e.Topics
+}
+
 // GetFrom returns the From field if it's non-nil, zero value otherwise.
 func (e *EditDefaultBranch) GetFrom() string {
 	if e == nil || e.From == nil {
@@ -10470,6 +10598,22 @@ func (l *ListCollaboratorOptions) GetAffiliation() string {
 	return *l.Affiliation
 }
 
+// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise.
+func (l *ListCustomDeploymentRuleIntegrationsResponse) GetTotalCount() int {
+	if l == nil || l.TotalCount == nil {
+		return 0
+	}
+	return *l.TotalCount
+}
+
+// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise.
+func (l *ListDeploymentProtectionRuleResponse) GetTotalCount() int {
+	if l == nil || l.TotalCount == nil {
+		return 0
+	}
+	return *l.TotalCount
+}
+
 // GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise.
 func (l *ListExternalGroupsOptions) GetDisplayName() string {
 	if l == nil || l.DisplayName == nil {
@@ -16262,6 +16406,14 @@ func (p *PullRequestLinks) GetHTMLURL() string {
 	return *p.HTMLURL
 }
 
+// GetMergedAt returns the MergedAt field if it's non-nil, zero value otherwise.
+func (p *PullRequestLinks) GetMergedAt() Timestamp {
+	if p == nil || p.MergedAt == nil {
+		return Timestamp{}
+	}
+	return *p.MergedAt
+}
+
 // GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise.
 func (p *PullRequestLinks) GetPatchURL() string {
 	if p == nil || p.PatchURL == nil {
@@ -17086,6 +17238,14 @@ func (p *PushEventRepository) GetCreatedAt() Timestamp {
 	return *p.CreatedAt
 }
 
+// GetCustomProperties returns the CustomProperties map if it's non-nil, an empty map otherwise.
+func (p *PushEventRepository) GetCustomProperties() map[string]string {
+	if p == nil || p.CustomProperties == nil {
+		return map[string]string{}
+	}
+	return p.CustomProperties
+}
+
 // GetDefaultBranch returns the DefaultBranch field if it's non-nil, zero value otherwise.
 func (p *PushEventRepository) GetDefaultBranch() string {
 	if p == nil || p.DefaultBranch == nil {
@@ -18222,6 +18382,14 @@ func (r *Repository) GetCreatedAt() Timestamp {
 	return *r.CreatedAt
 }
 
+// GetCustomProperties returns the CustomProperties map if it's non-nil, an empty map otherwise.
+func (r *Repository) GetCustomProperties() map[string]string {
+	if r == nil || r.CustomProperties == nil {
+		return map[string]string{}
+	}
+	return r.CustomProperties
+}
+
 // GetDefaultBranch returns the DefaultBranch field if it's non-nil, zero value otherwise.
 func (r *Repository) GetDefaultBranch() string {
 	if r == nil || r.DefaultBranch == nil {
diff --git a/github/github.go b/github/github.go
index 8bc35e5e..a7326a7b 100644
--- a/github/github.go
+++ b/github/github.go
@@ -450,15 +450,18 @@ func (c *Client) copy() *Client {
 	c.clientMu.Lock()
 	// can't use *c here because that would copy mutexes by value.
 	clone := Client{
-		client:                  c.client,
+		client:                  &http.Client{},
 		UserAgent:               c.UserAgent,
 		BaseURL:                 c.BaseURL,
 		UploadURL:               c.UploadURL,
 		secondaryRateLimitReset: c.secondaryRateLimitReset,
 	}
 	c.clientMu.Unlock()
-	if clone.client == nil {
-		clone.client = &http.Client{}
+	if c.client != nil {
+		clone.client.Transport = c.client.Transport
+		clone.client.CheckRedirect = c.client.CheckRedirect
+		clone.client.Jar = c.client.Jar
+		clone.client.Timeout = c.client.Timeout
 	}
 	c.rateMu.Lock()
 	copy(clone.rateLimits[:], c.rateLimits[:])
diff --git a/github/issues.go b/github/issues.go
index 1c07fef8..a2652b34 100644
--- a/github/issues.go
+++ b/github/issues.go
@@ -122,10 +122,11 @@ type IssueListOptions struct {
 // PullRequestLinks object is added to the Issue object when it's an issue included
 // in the IssueCommentEvent webhook payload, if the webhook is fired by a comment on a PR.
 type PullRequestLinks struct {
-	URL      *string `json:"url,omitempty"`
-	HTMLURL  *string `json:"html_url,omitempty"`
-	DiffURL  *string `json:"diff_url,omitempty"`
-	PatchURL *string `json:"patch_url,omitempty"`
+	URL      *string    `json:"url,omitempty"`
+	HTMLURL  *string    `json:"html_url,omitempty"`
+	DiffURL  *string    `json:"diff_url,omitempty"`
+	PatchURL *string    `json:"patch_url,omitempty"`
+	MergedAt *Timestamp `json:"merged_at,omitempty"`
 }
 
 // List the issues for the authenticated user. If all is true, list issues
diff --git a/github/repos.go b/github/repos.go
index a7574d72..b492e55b 100644
--- a/github/repos.go
+++ b/github/repos.go
@@ -27,58 +27,59 @@ type RepositoriesService service
 
 // Repository represents a GitHub repository.
 type Repository struct {
-	ID                        *int64          `json:"id,omitempty"`
-	NodeID                    *string         `json:"node_id,omitempty"`
-	Owner                     *User           `json:"owner,omitempty"`
-	Name                      *string         `json:"name,omitempty"`
-	FullName                  *string         `json:"full_name,omitempty"`
-	Description               *string         `json:"description,omitempty"`
-	Homepage                  *string         `json:"homepage,omitempty"`
-	CodeOfConduct             *CodeOfConduct  `json:"code_of_conduct,omitempty"`
-	DefaultBranch             *string         `json:"default_branch,omitempty"`
-	MasterBranch              *string         `json:"master_branch,omitempty"`
-	CreatedAt                 *Timestamp      `json:"created_at,omitempty"`
-	PushedAt                  *Timestamp      `json:"pushed_at,omitempty"`
-	UpdatedAt                 *Timestamp      `json:"updated_at,omitempty"`
-	HTMLURL                   *string         `json:"html_url,omitempty"`
-	CloneURL                  *string         `json:"clone_url,omitempty"`
-	GitURL                    *string         `json:"git_url,omitempty"`
-	MirrorURL                 *string         `json:"mirror_url,omitempty"`
-	SSHURL                    *string         `json:"ssh_url,omitempty"`
-	SVNURL                    *string         `json:"svn_url,omitempty"`
-	Language                  *string         `json:"language,omitempty"`
-	Fork                      *bool           `json:"fork,omitempty"`
-	ForksCount                *int            `json:"forks_count,omitempty"`
-	NetworkCount              *int            `json:"network_count,omitempty"`
-	OpenIssuesCount           *int            `json:"open_issues_count,omitempty"`
-	OpenIssues                *int            `json:"open_issues,omitempty"` // Deprecated: Replaced by OpenIssuesCount. For backward compatibility OpenIssues is still populated.
-	StargazersCount           *int            `json:"stargazers_count,omitempty"`
-	SubscribersCount          *int            `json:"subscribers_count,omitempty"`
-	WatchersCount             *int            `json:"watchers_count,omitempty"` // Deprecated: Replaced by StargazersCount. For backward compatibility WatchersCount is still populated.
-	Watchers                  *int            `json:"watchers,omitempty"`       // Deprecated: Replaced by StargazersCount. For backward compatibility Watchers is still populated.
-	Size                      *int            `json:"size,omitempty"`
-	AutoInit                  *bool           `json:"auto_init,omitempty"`
-	Parent                    *Repository     `json:"parent,omitempty"`
-	Source                    *Repository     `json:"source,omitempty"`
-	TemplateRepository        *Repository     `json:"template_repository,omitempty"`
-	Organization              *Organization   `json:"organization,omitempty"`
-	Permissions               map[string]bool `json:"permissions,omitempty"`
-	AllowRebaseMerge          *bool           `json:"allow_rebase_merge,omitempty"`
-	AllowUpdateBranch         *bool           `json:"allow_update_branch,omitempty"`
-	AllowSquashMerge          *bool           `json:"allow_squash_merge,omitempty"`
-	AllowMergeCommit          *bool           `json:"allow_merge_commit,omitempty"`
-	AllowAutoMerge            *bool           `json:"allow_auto_merge,omitempty"`
-	AllowForking              *bool           `json:"allow_forking,omitempty"`
-	WebCommitSignoffRequired  *bool           `json:"web_commit_signoff_required,omitempty"`
-	DeleteBranchOnMerge       *bool           `json:"delete_branch_on_merge,omitempty"`
-	UseSquashPRTitleAsDefault *bool           `json:"use_squash_pr_title_as_default,omitempty"`
-	SquashMergeCommitTitle    *string         `json:"squash_merge_commit_title,omitempty"`   // Can be one of: "PR_TITLE", "COMMIT_OR_PR_TITLE"
-	SquashMergeCommitMessage  *string         `json:"squash_merge_commit_message,omitempty"` // Can be one of: "PR_BODY", "COMMIT_MESSAGES", "BLANK"
-	MergeCommitTitle          *string         `json:"merge_commit_title,omitempty"`          // Can be one of: "PR_TITLE", "MERGE_MESSAGE"
-	MergeCommitMessage        *string         `json:"merge_commit_message,omitempty"`        // Can be one of: "PR_BODY", "PR_TITLE", "BLANK"
-	Topics                    []string        `json:"topics,omitempty"`
-	Archived                  *bool           `json:"archived,omitempty"`
-	Disabled                  *bool           `json:"disabled,omitempty"`
+	ID                        *int64            `json:"id,omitempty"`
+	NodeID                    *string           `json:"node_id,omitempty"`
+	Owner                     *User             `json:"owner,omitempty"`
+	Name                      *string           `json:"name,omitempty"`
+	FullName                  *string           `json:"full_name,omitempty"`
+	Description               *string           `json:"description,omitempty"`
+	Homepage                  *string           `json:"homepage,omitempty"`
+	CodeOfConduct             *CodeOfConduct    `json:"code_of_conduct,omitempty"`
+	DefaultBranch             *string           `json:"default_branch,omitempty"`
+	MasterBranch              *string           `json:"master_branch,omitempty"`
+	CreatedAt                 *Timestamp        `json:"created_at,omitempty"`
+	PushedAt                  *Timestamp        `json:"pushed_at,omitempty"`
+	UpdatedAt                 *Timestamp        `json:"updated_at,omitempty"`
+	HTMLURL                   *string           `json:"html_url,omitempty"`
+	CloneURL                  *string           `json:"clone_url,omitempty"`
+	GitURL                    *string           `json:"git_url,omitempty"`
+	MirrorURL                 *string           `json:"mirror_url,omitempty"`
+	SSHURL                    *string           `json:"ssh_url,omitempty"`
+	SVNURL                    *string           `json:"svn_url,omitempty"`
+	Language                  *string           `json:"language,omitempty"`
+	Fork                      *bool             `json:"fork,omitempty"`
+	ForksCount                *int              `json:"forks_count,omitempty"`
+	NetworkCount              *int              `json:"network_count,omitempty"`
+	OpenIssuesCount           *int              `json:"open_issues_count,omitempty"`
+	OpenIssues                *int              `json:"open_issues,omitempty"` // Deprecated: Replaced by OpenIssuesCount. For backward compatibility OpenIssues is still populated.
+	StargazersCount           *int              `json:"stargazers_count,omitempty"`
+	SubscribersCount          *int              `json:"subscribers_count,omitempty"`
+	WatchersCount             *int              `json:"watchers_count,omitempty"` // Deprecated: Replaced by StargazersCount. For backward compatibility WatchersCount is still populated.
+	Watchers                  *int              `json:"watchers,omitempty"`       // Deprecated: Replaced by StargazersCount. For backward compatibility Watchers is still populated.
+	Size                      *int              `json:"size,omitempty"`
+	AutoInit                  *bool             `json:"auto_init,omitempty"`
+	Parent                    *Repository       `json:"parent,omitempty"`
+	Source                    *Repository       `json:"source,omitempty"`
+	TemplateRepository        *Repository       `json:"template_repository,omitempty"`
+	Organization              *Organization     `json:"organization,omitempty"`
+	Permissions               map[string]bool   `json:"permissions,omitempty"`
+	AllowRebaseMerge          *bool             `json:"allow_rebase_merge,omitempty"`
+	AllowUpdateBranch         *bool             `json:"allow_update_branch,omitempty"`
+	AllowSquashMerge          *bool             `json:"allow_squash_merge,omitempty"`
+	AllowMergeCommit          *bool             `json:"allow_merge_commit,omitempty"`
+	AllowAutoMerge            *bool             `json:"allow_auto_merge,omitempty"`
+	AllowForking              *bool             `json:"allow_forking,omitempty"`
+	WebCommitSignoffRequired  *bool             `json:"web_commit_signoff_required,omitempty"`
+	DeleteBranchOnMerge       *bool             `json:"delete_branch_on_merge,omitempty"`
+	UseSquashPRTitleAsDefault *bool             `json:"use_squash_pr_title_as_default,omitempty"`
+	SquashMergeCommitTitle    *string           `json:"squash_merge_commit_title,omitempty"`   // Can be one of: "PR_TITLE", "COMMIT_OR_PR_TITLE"
+	SquashMergeCommitMessage  *string           `json:"squash_merge_commit_message,omitempty"` // Can be one of: "PR_BODY", "COMMIT_MESSAGES", "BLANK"
+	MergeCommitTitle          *string           `json:"merge_commit_title,omitempty"`          // Can be one of: "PR_TITLE", "MERGE_MESSAGE"
+	MergeCommitMessage        *string           `json:"merge_commit_message,omitempty"`        // Can be one of: "PR_BODY", "PR_TITLE", "BLANK"
+	Topics                    []string          `json:"topics,omitempty"`
+	CustomProperties          map[string]string `json:"custom_properties,omitempty"`
+	Archived                  *bool             `json:"archived,omitempty"`
+	Disabled                  *bool             `json:"disabled,omitempty"`
 
 	// Only provided when using RepositoriesService.Get while in preview
 	License *License `json:"license,omitempty"`
diff --git a/github/repos_actions_permissions.go b/github/repos_actions_permissions.go
index 2dcc367d..9abd32a7 100644
--- a/github/repos_actions_permissions.go
+++ b/github/repos_actions_permissions.go
@@ -23,6 +23,14 @@ func (a ActionsPermissionsRepository) String() string {
 	return Stringify(a)
 }
 
+// DefaultWorkflowPermissionRepository represents the default permissions for GitHub Actions workflows for a repository.
+//
+// GitHub API docs: https://docs.github.com/rest/actions/permissions
+type DefaultWorkflowPermissionRepository struct {
+	DefaultWorkflowPermissions   *string `json:"default_workflow_permissions,omitempty"`
+	CanApprovePullRequestReviews *bool   `json:"can_approve_pull_request_reviews,omitempty"`
+}
+
 // GetActionsPermissions gets the GitHub Actions permissions policy for repositories and allowed actions in a repository.
 //
 // GitHub API docs: https://docs.github.com/rest/actions/permissions#get-github-actions-permissions-for-a-repository
@@ -30,6 +38,7 @@ func (a ActionsPermissionsRepository) String() string {
 //meta:operation GET /repos/{owner}/{repo}/actions/permissions
 func (s *RepositoriesService) GetActionsPermissions(ctx context.Context, owner, repo string) (*ActionsPermissionsRepository, *Response, error) {
 	u := fmt.Sprintf("repos/%v/%v/actions/permissions", owner, repo)
+
 	req, err := s.client.NewRequest("GET", u, nil)
 	if err != nil {
 		return nil, nil, err
@@ -64,3 +73,46 @@ func (s *RepositoriesService) EditActionsPermissions(ctx context.Context, owner,
 
 	return permissions, resp, nil
 }
+
+// GetDefaultWorkflowPermissions gets the GitHub Actions default workflow permissions in a repository.
+//
+// GitHub API docs: https://docs.github.com/rest/actions/permissions#get-default-workflow-permissions-for-a-repository
+//
+//meta:operation GET /repos/{owner}/{repo}/actions/permissions/workflow
+func (s *RepositoriesService) GetDefaultWorkflowPermissions(ctx context.Context, owner, repo string) (*DefaultWorkflowPermissionRepository, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/actions/permissions/workflow", owner, repo)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	permissions := new(DefaultWorkflowPermissionRepository)
+	resp, err := s.client.Do(ctx, req, permissions)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return permissions, resp, nil
+}
+
+// EditDefaultWorkflowPermissions sets the GitHub Actions default workflow permissions in a repository.
+//
+// GitHub API docs: https://docs.github.com/rest/actions/permissions#set-default-workflow-permissions-for-a-repository
+//
+//meta:operation PUT /repos/{owner}/{repo}/actions/permissions/workflow
+func (s *RepositoriesService) EditDefaultWorkflowPermissions(ctx context.Context, owner, repo string, permissions DefaultWorkflowPermissionRepository) (*DefaultWorkflowPermissionRepository, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/actions/permissions/workflow", owner, repo)
+	req, err := s.client.NewRequest("PUT", u, permissions)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p := new(DefaultWorkflowPermissionRepository)
+	resp, err := s.client.Do(ctx, req, p)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return p, resp, nil
+}
diff --git a/github/repos_deployment_protection_rules.go b/github/repos_deployment_protection_rules.go
new file mode 100644
index 00000000..29d49032
--- /dev/null
+++ b/github/repos_deployment_protection_rules.go
@@ -0,0 +1,148 @@
+// Copyright 2024 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"
+)
+
+// CustomDeploymentProtectionRuleApp represents a single deployment protection rule app for an environment.
+type CustomDeploymentProtectionRuleApp struct {
+	ID             *int64  `json:"id,omitempty"`
+	Slug           *string `json:"slug,omitempty"`
+	IntegrationURL *string `json:"integration_url,omitempty"`
+	NodeID         *string `json:"node_id,omitempty"`
+}
+
+// CustomDeploymentProtectionRule represents a single deployment protection rule for an environment.
+type CustomDeploymentProtectionRule struct {
+	ID      *int64                             `json:"id,omitempty"`
+	NodeID  *string                            `json:"node_id,omitempty"`
+	Enabled *bool                              `json:"enabled,omitempty"`
+	App     *CustomDeploymentProtectionRuleApp `json:"app,omitempty"`
+}
+
+// ListDeploymentProtectionRuleResponse represents the response that comes back when you list deployment protection rules.
+type ListDeploymentProtectionRuleResponse struct {
+	TotalCount      *int                              `json:"total_count,omitempty"`
+	ProtectionRules []*CustomDeploymentProtectionRule `json:"custom_deployment_protection_rules,omitempty"`
+}
+
+// ListCustomDeploymentRuleIntegrationsResponse represents the slightly different response that comes back when you list custom deployment rule integrations.
+type ListCustomDeploymentRuleIntegrationsResponse struct {
+	TotalCount            *int                                 `json:"total_count,omitempty"`
+	AvailableIntegrations []*CustomDeploymentProtectionRuleApp `json:"available_custom_deployment_protection_rule_integrations,omitempty"`
+}
+
+// CustomDeploymentProtectionRuleRequest represents a deployment protection rule request.
+type CustomDeploymentProtectionRuleRequest struct {
+	IntegrationID *int64 `json:"integration_id,omitempty"`
+}
+
+// GetAllDeploymentProtectionRules gets all the deployment protection rules for an environment.
+//
+// GitHub API docs: https://docs.github.com/rest/deployments/protection-rules#get-all-deployment-protection-rules-for-an-environment
+//
+//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules
+func (s *RepositoriesService) GetAllDeploymentProtectionRules(ctx context.Context, owner, repo, environment string) (*ListDeploymentProtectionRuleResponse, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment_protection_rules", owner, repo, environment)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var list *ListDeploymentProtectionRuleResponse
+	resp, err := s.client.Do(ctx, req, &list)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return list, resp, nil
+}
+
+// CreateCustomDeploymentProtectionRule creates a custom deployment protection rule on an environment.
+//
+// GitHub API docs: https://docs.github.com/rest/deployments/protection-rules#create-a-custom-deployment-protection-rule-on-an-environment
+//
+//meta:operation POST /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules
+func (s *RepositoriesService) CreateCustomDeploymentProtectionRule(ctx context.Context, owner, repo, environment string, request *CustomDeploymentProtectionRuleRequest) (*CustomDeploymentProtectionRule, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment_protection_rules", owner, repo, environment)
+
+	req, err := s.client.NewRequest("POST", u, request)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	protectionRule := new(CustomDeploymentProtectionRule)
+	resp, err := s.client.Do(ctx, req, protectionRule)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return protectionRule, resp, nil
+}
+
+// ListCustomDeploymentRuleIntegrations lists the custom deployment rule integrations for an environment.
+//
+// GitHub API docs: https://docs.github.com/rest/deployments/protection-rules#list-custom-deployment-rule-integrations-available-for-an-environment
+//
+//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/apps
+func (s *RepositoriesService) ListCustomDeploymentRuleIntegrations(ctx context.Context, owner, repo, environment string) (*ListCustomDeploymentRuleIntegrationsResponse, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment_protection_rules/apps", owner, repo, environment)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var list *ListCustomDeploymentRuleIntegrationsResponse
+	resp, err := s.client.Do(ctx, req, &list)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return list, resp, nil
+}
+
+// GetCustomDeploymentProtectionRule gets a custom deployment protection rule for an environment.
+//
+// GitHub API docs: https://docs.github.com/rest/deployments/protection-rules#get-a-custom-deployment-protection-rule
+//
+//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/{protection_rule_id}
+func (s *RepositoriesService) GetCustomDeploymentProtectionRule(ctx context.Context, owner, repo, environment string, protectionRuleID int64) (*CustomDeploymentProtectionRule, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment_protection_rules/%v", owner, repo, environment, protectionRuleID)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var protectionRule *CustomDeploymentProtectionRule
+	resp, err := s.client.Do(ctx, req, &protectionRule)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return protectionRule, resp, nil
+}
+
+// DisableCustomDeploymentProtectionRule disables a custom deployment protection rule for an environment.
+//
+// GitHub API docs: https://docs.github.com/rest/deployments/protection-rules#disable-a-custom-protection-rule-for-an-environment
+//
+//meta:operation DELETE /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/{protection_rule_id}
+func (s *RepositoriesService) DisableCustomDeploymentProtectionRule(ctx context.Context, owner, repo, environment string, protectionRuleID int64) (*Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment_protection_rules/%v", owner, repo, environment, protectionRuleID)
+
+	req, err := s.client.NewRequest("DELETE", u, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(ctx, req, nil)
+}
diff --git a/tools/go.mod b/tools/go.mod
index c36b9fec..2fbca2de 100644
--- a/tools/go.mod
+++ b/tools/go.mod
@@ -4,7 +4,7 @@ go 1.20
 
 require (
 	github.com/alecthomas/kong v0.8.1
-	github.com/getkin/kin-openapi v0.122.0
+	github.com/getkin/kin-openapi v0.123.0
 	github.com/google/go-cmp v0.6.0
 	github.com/google/go-github/v58 v58.0.0
 	golang.org/x/sync v0.6.0
@@ -12,15 +12,14 @@ require (
 )
 
 require (
-	github.com/go-openapi/jsonpointer v0.19.6 // indirect
-	github.com/go-openapi/swag v0.22.4 // indirect
+	github.com/go-openapi/jsonpointer v0.20.2 // indirect
+	github.com/go-openapi/swag v0.22.8 // indirect
 	github.com/google/go-querystring v1.1.0 // indirect
 	github.com/invopop/yaml v0.2.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
 	github.com/perimeterx/marshmallow v1.1.5 // indirect
-	github.com/stretchr/testify v1.8.4 // indirect
 )
 
 // Use version at HEAD, not the latest published.
diff --git a/tools/go.sum b/tools/go.sum
index 7ba38eee..d4a1fd8b 100644
--- a/tools/go.sum
+++ b/tools/go.sum
@@ -2,17 +2,13 @@ github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2o
 github.com/alecthomas/kong v0.8.1 h1:acZdn3m4lLRobeh3Zi2S2EpnXTd1mOL6U7xVml+vfkY=
 github.com/alecthomas/kong v0.8.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
 github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/getkin/kin-openapi v0.122.0 h1:WB9Jbl0Hp/T79/JF9xlSW5Kl9uYdk/AWD0yAd9HOM10=
-github.com/getkin/kin-openapi v0.122.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw=
-github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
-github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
-github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
-github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
-github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/getkin/kin-openapi v0.123.0 h1:zIik0mRwFNLyvtXK274Q6ut+dPh6nlxBp0x7mNrPhs8=
+github.com/getkin/kin-openapi v0.123.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
+github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
+github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
+github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw=
+github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
 github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@@ -24,12 +20,8 @@ github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
 github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
@@ -37,23 +29,14 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwd
 github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
 github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
 golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
 golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 3067b1b0..aab03d75 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -51,7 +51,7 @@ jobs:
         echo "go-mod-cache=$(go env GOMODCACHE)" >> $GITHUB_OUTPUT
 
     - name: Cache go modules
-      uses: actions/cache@v3
+      uses: actions/cache@v4
       with:
         path: |
           ${{ steps.cache-paths.outputs.go-cache }}
@@ -73,4 +73,4 @@ jobs:
 
     - name: Upload coverage to Codecov
       if: ${{ matrix.update-coverage }}
-      uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d #v3.1.4
+      uses: codecov/codecov-action@4fe8c5f003fae66aa5ebb77cfd3e7bfbbda0b6b0 #v3.1.5
diff --git a/github/actions_permissions_enterprise_test.go b/github/actions_permissions_enterprise_test.go
index 14e8e3a6..70f7d2c0 100644
--- a/github/actions_permissions_enterprise_test.go
+++ b/github/actions_permissions_enterprise_test.go
@@ -296,3 +296,80 @@ func TestActionsService_EditActionsAllowedInEnterprise(t *testing.T) {
 		return resp, err
 	})
 }
+
+func TestActionsService_GetDefaultWorkflowPermissionsInEnterprise(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/enterprises/e/actions/permissions/workflow", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{ "default_workflow_permissions": "read", "can_approve_pull_request_reviews": true }`)
+	})
+
+	ctx := context.Background()
+	ent, _, err := client.Actions.GetDefaultWorkflowPermissionsInEnterprise(ctx, "e")
+	if err != nil {
+		t.Errorf("Actions.GetDefaultWorkflowPermissionsInEnterprise returned error: %v", err)
+	}
+	want := &DefaultWorkflowPermissionEnterprise{DefaultWorkflowPermissions: String("read"), CanApprovePullRequestReviews: Bool(true)}
+	if !cmp.Equal(ent, want) {
+		t.Errorf("Actions.GetDefaultWorkflowPermissionsInEnterprise returned %+v, want %+v", ent, want)
+	}
+
+	const methodName = "GetDefaultWorkflowPermissionsInEnterprise"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Actions.GetDefaultWorkflowPermissionsInEnterprise(ctx, "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Actions.GetDefaultWorkflowPermissionsInEnterprise(ctx, "e")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestActionsService_EditDefaultWorkflowPermissionsInEnterprise(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+	input := &DefaultWorkflowPermissionEnterprise{DefaultWorkflowPermissions: String("read"), CanApprovePullRequestReviews: Bool(true)}
+
+	mux.HandleFunc("/enterprises/e/actions/permissions/workflow", func(w http.ResponseWriter, r *http.Request) {
+		v := new(DefaultWorkflowPermissionEnterprise)
+		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
+
+		testMethod(t, r, "PUT")
+		if !cmp.Equal(v, input) {
+			t.Errorf("Request body = %+v, want %+v", v, input)
+		}
+
+		fmt.Fprint(w, `{ "default_workflow_permissions": "read", "can_approve_pull_request_reviews": true }`)
+	})
+
+	ctx := context.Background()
+	ent, _, err := client.Actions.EditDefaultWorkflowPermissionsInEnterprise(ctx, "e", *input)
+	if err != nil {
+		t.Errorf("Actions.EditDefaultWorkflowPermissionsInEnterprise returned error: %v", err)
+	}
+
+	want := &DefaultWorkflowPermissionEnterprise{DefaultWorkflowPermissions: String("read"), CanApprovePullRequestReviews: Bool(true)}
+	if !cmp.Equal(ent, want) {
+		t.Errorf("Actions.EditDefaultWorkflowPermissionsInEnterprise returned %+v, want %+v", ent, want)
+	}
+
+	const methodName = "EditDefaultWorkflowPermissionsInEnterprise"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Actions.EditDefaultWorkflowPermissionsInEnterprise(ctx, "\n", *input)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Actions.EditDefaultWorkflowPermissionsInEnterprise(ctx, "e", *input)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
diff --git a/github/actions_permissions_orgs_test.go b/github/actions_permissions_orgs_test.go
index 36a241f6..fd35c519 100644
--- a/github/actions_permissions_orgs_test.go
+++ b/github/actions_permissions_orgs_test.go
@@ -334,3 +334,80 @@ func TestActionsPermissions_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, u, want)
 }
+
+func TestActionsService_GetDefaultWorkflowPermissionsInOrganization(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/orgs/o/actions/permissions/workflow", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{ "default_workflow_permissions": "read", "can_approve_pull_request_reviews": true }`)
+	})
+
+	ctx := context.Background()
+	org, _, err := client.Actions.GetDefaultWorkflowPermissionsInOrganization(ctx, "o")
+	if err != nil {
+		t.Errorf("Actions.GetDefaultWorkflowPermissionsInOrganization returned error: %v", err)
+	}
+	want := &DefaultWorkflowPermissionOrganization{DefaultWorkflowPermissions: String("read"), CanApprovePullRequestReviews: Bool(true)}
+	if !cmp.Equal(org, want) {
+		t.Errorf("Actions.GetDefaultWorkflowPermissionsInOrganization returned %+v, want %+v", org, want)
+	}
+
+	const methodName = "GetDefaultWorkflowPermissionsInOrganization"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Actions.GetDefaultWorkflowPermissionsInOrganization(ctx, "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Actions.GetDefaultWorkflowPermissionsInOrganization(ctx, "o")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestActionsService_EditDefaultWorkflowPermissionsInOrganization(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+	input := &DefaultWorkflowPermissionOrganization{DefaultWorkflowPermissions: String("read"), CanApprovePullRequestReviews: Bool(true)}
+
+	mux.HandleFunc("/orgs/o/actions/permissions/workflow", func(w http.ResponseWriter, r *http.Request) {
+		v := new(DefaultWorkflowPermissionOrganization)
+		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
+
+		testMethod(t, r, "PUT")
+		if !cmp.Equal(v, input) {
+			t.Errorf("Request body = %+v, want %+v", v, input)
+		}
+
+		fmt.Fprint(w, `{ "default_workflow_permissions": "read", "can_approve_pull_request_reviews": true }`)
+	})
+
+	ctx := context.Background()
+	org, _, err := client.Actions.EditDefaultWorkflowPermissionsInOrganization(ctx, "o", *input)
+	if err != nil {
+		t.Errorf("Actions.EditDefaultWorkflowPermissionsInOrganization returned error: %v", err)
+	}
+
+	want := &DefaultWorkflowPermissionOrganization{DefaultWorkflowPermissions: String("read"), CanApprovePullRequestReviews: Bool(true)}
+	if !cmp.Equal(org, want) {
+		t.Errorf("Actions.EditDefaultWorkflowPermissionsInOrganization returned %+v, want %+v", org, want)
+	}
+
+	const methodName = "EditDefaultWorkflowPermissionsInOrganization"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Actions.EditDefaultWorkflowPermissionsInOrganization(ctx, "\n", *input)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Actions.EditDefaultWorkflowPermissionsInOrganization(ctx, "o", *input)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
diff --git a/github/actions_secrets_test.go b/github/actions_secrets_test.go
index a1266d6b..a2ac169c 100644
--- a/github/actions_secrets_test.go
+++ b/github/actions_secrets_test.go
@@ -205,6 +205,49 @@ func TestActionsService_ListRepoSecrets(t *testing.T) {
 	})
 }
 
+func TestActionsService_ListRepoOrgSecrets(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/actions/organization-secrets", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testFormValues(t, r, values{"per_page": "2", "page": "2"})
+		fmt.Fprint(w, `{"total_count":4,"secrets":[{"name":"A","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"},{"name":"B","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"}]}`)
+	})
+
+	opts := &ListOptions{Page: 2, PerPage: 2}
+	ctx := context.Background()
+	secrets, _, err := client.Actions.ListRepoOrgSecrets(ctx, "o", "r", opts)
+	if err != nil {
+		t.Errorf("Actions.ListRepoOrgSecrets returned error: %v", err)
+	}
+
+	want := &Secrets{
+		TotalCount: 4,
+		Secrets: []*Secret{
+			{Name: "A", CreatedAt: Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, UpdatedAt: Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}},
+			{Name: "B", CreatedAt: Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, UpdatedAt: Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}},
+		},
+	}
+	if !cmp.Equal(secrets, want) {
+		t.Errorf("Actions.ListRepoOrgSecrets returned %+v, want %+v", secrets, want)
+	}
+
+	const methodName = "ListRepoOrgSecrets"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Actions.ListRepoOrgSecrets(ctx, "\n", "\n", opts)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Actions.ListRepoOrgSecrets(ctx, "o", "r", opts)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
 func TestActionsService_GetRepoSecret(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
diff --git a/github/actions_variables_test.go b/github/actions_variables_test.go
index a9f773c2..dcd648c2 100644
--- a/github/actions_variables_test.go
+++ b/github/actions_variables_test.go
@@ -58,6 +58,49 @@ func TestActionsService_ListRepoVariables(t *testing.T) {
 	})
 }
 
+func TestActionsService_ListRepoOrgVariables(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/actions/organization-variables", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testFormValues(t, r, values{"per_page": "2", "page": "2"})
+		fmt.Fprint(w, `{"total_count":4,"variables":[{"name":"A","value":"AA","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"},{"name":"B","value":"BB","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"}]}`)
+	})
+
+	opts := &ListOptions{Page: 2, PerPage: 2}
+	ctx := context.Background()
+	variables, _, err := client.Actions.ListRepoOrgVariables(ctx, "o", "r", opts)
+	if err != nil {
+		t.Errorf("Actions.ListRepoOrgVariables returned error: %v", err)
+	}
+
+	want := &ActionsVariables{
+		TotalCount: 4,
+		Variables: []*ActionsVariable{
+			{Name: "A", Value: "AA", CreatedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, UpdatedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}},
+			{Name: "B", Value: "BB", CreatedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, UpdatedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}},
+		},
+	}
+	if !cmp.Equal(variables, want) {
+		t.Errorf("Actions.ListRepoOrgVariables returned %+v, want %+v", variables, want)
+	}
+
+	const methodName = "ListRepoOrgVariables"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Actions.ListRepoOrgVariables(ctx, "\n", "\n", opts)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Actions.ListRepoOrgVariables(ctx, "o", "r", opts)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
 func TestActionsService_GetRepoVariable(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
diff --git a/github/event_types_test.go b/github/event_types_test.go
index 80eca61d..8578b922 100644
--- a/github/event_types_test.go
+++ b/github/event_types_test.go
@@ -91,6 +91,9 @@ func TestEditChange_Marshal_Repo(t *testing.T) {
 				From: String("old-repo-name"),
 			},
 		},
+		Topics: &EditTopics{
+			From: []string{"topic1", "topic2"},
+		},
 	}
 
 	want := `{
@@ -98,6 +101,12 @@ func TestEditChange_Marshal_Repo(t *testing.T) {
 			"name": {
 				"from": "old-repo-name"
 			}
+		},
+		"topics": {
+			"from": [
+				"topic1",
+				"topic2"
+			]
 		}
 	}`
 
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index 461ce8ed..7f7962cd 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -5399,6 +5399,93 @@ func TestCredit_GetUser(tt *testing.T) {
 	c.GetUser()
 }
 
+func TestCustomDeploymentProtectionRule_GetApp(tt *testing.T) {
+	c := &CustomDeploymentProtectionRule{}
+	c.GetApp()
+	c = nil
+	c.GetApp()
+}
+
+func TestCustomDeploymentProtectionRule_GetEnabled(tt *testing.T) {
+	var zeroValue bool
+	c := &CustomDeploymentProtectionRule{Enabled: &zeroValue}
+	c.GetEnabled()
+	c = &CustomDeploymentProtectionRule{}
+	c.GetEnabled()
+	c = nil
+	c.GetEnabled()
+}
+
+func TestCustomDeploymentProtectionRule_GetID(tt *testing.T) {
+	var zeroValue int64
+	c := &CustomDeploymentProtectionRule{ID: &zeroValue}
+	c.GetID()
+	c = &CustomDeploymentProtectionRule{}
+	c.GetID()
+	c = nil
+	c.GetID()
+}
+
+func TestCustomDeploymentProtectionRule_GetNodeID(tt *testing.T) {
+	var zeroValue string
+	c := &CustomDeploymentProtectionRule{NodeID: &zeroValue}
+	c.GetNodeID()
+	c = &CustomDeploymentProtectionRule{}
+	c.GetNodeID()
+	c = nil
+	c.GetNodeID()
+}
+
+func TestCustomDeploymentProtectionRuleApp_GetID(tt *testing.T) {
+	var zeroValue int64
+	c := &CustomDeploymentProtectionRuleApp{ID: &zeroValue}
+	c.GetID()
+	c = &CustomDeploymentProtectionRuleApp{}
+	c.GetID()
+	c = nil
+	c.GetID()
+}
+
+func TestCustomDeploymentProtectionRuleApp_GetIntegrationURL(tt *testing.T) {
+	var zeroValue string
+	c := &CustomDeploymentProtectionRuleApp{IntegrationURL: &zeroValue}
+	c.GetIntegrationURL()
+	c = &CustomDeploymentProtectionRuleApp{}
+	c.GetIntegrationURL()
+	c = nil
+	c.GetIntegrationURL()
+}
+
+func TestCustomDeploymentProtectionRuleApp_GetNodeID(tt *testing.T) {
+	var zeroValue string
+	c := &CustomDeploymentProtectionRuleApp{NodeID: &zeroValue}
+	c.GetNodeID()
+	c = &CustomDeploymentProtectionRuleApp{}
+	c.GetNodeID()
+	c = nil
+	c.GetNodeID()
+}
+
+func TestCustomDeploymentProtectionRuleApp_GetSlug(tt *testing.T) {
+	var zeroValue string
+	c := &CustomDeploymentProtectionRuleApp{Slug: &zeroValue}
+	c.GetSlug()
+	c = &CustomDeploymentProtectionRuleApp{}
+	c.GetSlug()
+	c = nil
+	c.GetSlug()
+}
+
+func TestCustomDeploymentProtectionRuleRequest_GetIntegrationID(tt *testing.T) {
+	var zeroValue int64
+	c := &CustomDeploymentProtectionRuleRequest{IntegrationID: &zeroValue}
+	c.GetIntegrationID()
+	c = &CustomDeploymentProtectionRuleRequest{}
+	c.GetIntegrationID()
+	c = nil
+	c.GetIntegrationID()
+}
+
 func TestCustomProperty_GetDefaultValue(tt *testing.T) {
 	var zeroValue string
 	c := &CustomProperty{DefaultValue: &zeroValue}
@@ -5519,6 +5606,66 @@ func TestDefaultSetupConfiguration_GetUpdatedAt(tt *testing.T) {
 	d.GetUpdatedAt()
 }
 
+func TestDefaultWorkflowPermissionEnterprise_GetCanApprovePullRequestReviews(tt *testing.T) {
+	var zeroValue bool
+	d := &DefaultWorkflowPermissionEnterprise{CanApprovePullRequestReviews: &zeroValue}
+	d.GetCanApprovePullRequestReviews()
+	d = &DefaultWorkflowPermissionEnterprise{}
+	d.GetCanApprovePullRequestReviews()
+	d = nil
+	d.GetCanApprovePullRequestReviews()
+}
+
+func TestDefaultWorkflowPermissionEnterprise_GetDefaultWorkflowPermissions(tt *testing.T) {
+	var zeroValue string
+	d := &DefaultWorkflowPermissionEnterprise{DefaultWorkflowPermissions: &zeroValue}
+	d.GetDefaultWorkflowPermissions()
+	d = &DefaultWorkflowPermissionEnterprise{}
+	d.GetDefaultWorkflowPermissions()
+	d = nil
+	d.GetDefaultWorkflowPermissions()
+}
+
+func TestDefaultWorkflowPermissionOrganization_GetCanApprovePullRequestReviews(tt *testing.T) {
+	var zeroValue bool
+	d := &DefaultWorkflowPermissionOrganization{CanApprovePullRequestReviews: &zeroValue}
+	d.GetCanApprovePullRequestReviews()
+	d = &DefaultWorkflowPermissionOrganization{}
+	d.GetCanApprovePullRequestReviews()
+	d = nil
+	d.GetCanApprovePullRequestReviews()
+}
+
+func TestDefaultWorkflowPermissionOrganization_GetDefaultWorkflowPermissions(tt *testing.T) {
+	var zeroValue string
+	d := &DefaultWorkflowPermissionOrganization{DefaultWorkflowPermissions: &zeroValue}
+	d.GetDefaultWorkflowPermissions()
+	d = &DefaultWorkflowPermissionOrganization{}
+	d.GetDefaultWorkflowPermissions()
+	d = nil
+	d.GetDefaultWorkflowPermissions()
+}
+
+func TestDefaultWorkflowPermissionRepository_GetCanApprovePullRequestReviews(tt *testing.T) {
+	var zeroValue bool
+	d := &DefaultWorkflowPermissionRepository{CanApprovePullRequestReviews: &zeroValue}
+	d.GetCanApprovePullRequestReviews()
+	d = &DefaultWorkflowPermissionRepository{}
+	d.GetCanApprovePullRequestReviews()
+	d = nil
+	d.GetCanApprovePullRequestReviews()
+}
+
+func TestDefaultWorkflowPermissionRepository_GetDefaultWorkflowPermissions(tt *testing.T) {
+	var zeroValue string
+	d := &DefaultWorkflowPermissionRepository{DefaultWorkflowPermissions: &zeroValue}
+	d.GetDefaultWorkflowPermissions()
+	d = &DefaultWorkflowPermissionRepository{}
+	d.GetDefaultWorkflowPermissions()
+	d = nil
+	d.GetDefaultWorkflowPermissions()
+}
+
 func TestDeleteAnalysis_GetConfirmDeleteURL(tt *testing.T) {
 	var zeroValue string
 	d := &DeleteAnalysis{ConfirmDeleteURL: &zeroValue}
@@ -7354,6 +7501,13 @@ func TestEditChange_GetTitle(tt *testing.T) {
 	e.GetTitle()
 }
 
+func TestEditChange_GetTopics(tt *testing.T) {
+	e := &EditChange{}
+	e.GetTopics()
+	e = nil
+	e.GetTopics()
+}
+
 func TestEditDefaultBranch_GetFrom(tt *testing.T) {
 	var zeroValue string
 	e := &EditDefaultBranch{From: &zeroValue}
@@ -12233,6 +12387,26 @@ func TestListCollaboratorOptions_GetAffiliation(tt *testing.T) {
 	l.GetAffiliation()
 }
 
+func TestListCustomDeploymentRuleIntegrationsResponse_GetTotalCount(tt *testing.T) {
+	var zeroValue int
+	l := &ListCustomDeploymentRuleIntegrationsResponse{TotalCount: &zeroValue}
+	l.GetTotalCount()
+	l = &ListCustomDeploymentRuleIntegrationsResponse{}
+	l.GetTotalCount()
+	l = nil
+	l.GetTotalCount()
+}
+
+func TestListDeploymentProtectionRuleResponse_GetTotalCount(tt *testing.T) {
+	var zeroValue int
+	l := &ListDeploymentProtectionRuleResponse{TotalCount: &zeroValue}
+	l.GetTotalCount()
+	l = &ListDeploymentProtectionRuleResponse{}
+	l.GetTotalCount()
+	l = nil
+	l.GetTotalCount()
+}
+
 func TestListExternalGroupsOptions_GetDisplayName(tt *testing.T) {
 	var zeroValue string
 	l := &ListExternalGroupsOptions{DisplayName: &zeroValue}
@@ -18864,6 +19038,16 @@ func TestPullRequestLinks_GetHTMLURL(tt *testing.T) {
 	p.GetHTMLURL()
 }
 
+func TestPullRequestLinks_GetMergedAt(tt *testing.T) {
+	var zeroValue Timestamp
+	p := &PullRequestLinks{MergedAt: &zeroValue}
+	p.GetMergedAt()
+	p = &PullRequestLinks{}
+	p.GetMergedAt()
+	p = nil
+	p.GetMergedAt()
+}
+
 func TestPullRequestLinks_GetPatchURL(tt *testing.T) {
 	var zeroValue string
 	p := &PullRequestLinks{PatchURL: &zeroValue}
@@ -19765,6 +19949,16 @@ func TestPushEventRepository_GetCreatedAt(tt *testing.T) {
 	p.GetCreatedAt()
 }
 
+func TestPushEventRepository_GetCustomProperties(tt *testing.T) {
+	zeroValue := map[string]string{}
+	p := &PushEventRepository{CustomProperties: zeroValue}
+	p.GetCustomProperties()
+	p = &PushEventRepository{}
+	p.GetCustomProperties()
+	p = nil
+	p.GetCustomProperties()
+}
+
 func TestPushEventRepository_GetDefaultBranch(tt *testing.T) {
 	var zeroValue string
 	p := &PushEventRepository{DefaultBranch: &zeroValue}
@@ -21119,6 +21313,16 @@ func TestRepository_GetCreatedAt(tt *testing.T) {
 	r.GetCreatedAt()
 }
 
+func TestRepository_GetCustomProperties(tt *testing.T) {
+	zeroValue := map[string]string{}
+	r := &Repository{CustomProperties: zeroValue}
+	r.GetCustomProperties()
+	r = &Repository{}
+	r.GetCustomProperties()
+	r = nil
+	r.GetCustomProperties()
+}
+
 func TestRepository_GetDefaultBranch(tt *testing.T) {
 	var zeroValue string
 	r := &Repository{DefaultBranch: &zeroValue}
diff --git a/github/github_test.go b/github/github_test.go
index 45ac57c0..9a369a25 100644
--- a/github/github_test.go
+++ b/github/github_test.go
@@ -2599,3 +2599,32 @@ func TestParseTokenExpiration(t *testing.T) {
 		}
 	}
 }
+
+func TestClientCopy_leak_transport(t *testing.T) {
+	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "application/json")
+		accessToken := r.Header.Get("Authorization")
+		_, _ = fmt.Fprintf(w, `{"login": "%s"}`, accessToken)
+	}))
+	clientPreconfiguredWithURLs, err := NewClient(nil).WithEnterpriseURLs(srv.URL, srv.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	aliceClient := clientPreconfiguredWithURLs.WithAuthToken("alice")
+	bobClient := clientPreconfiguredWithURLs.WithAuthToken("bob")
+
+	alice, _, err := aliceClient.Users.Get(context.Background(), "")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	assertNoDiff(t, "Bearer alice", alice.GetLogin())
+
+	bob, _, err := bobClient.Users.Get(context.Background(), "")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	assertNoDiff(t, "Bearer bob", bob.GetLogin())
+}
diff --git a/github/issues_test.go b/github/issues_test.go
index aa767d65..d64ef88d 100644
--- a/github/issues_test.go
+++ b/github/issues_test.go
@@ -499,13 +499,15 @@ func TestPullRequestLinks_Marshal(t *testing.T) {
 		HTMLURL:  String("hurl"),
 		DiffURL:  String("durl"),
 		PatchURL: String("purl"),
+		MergedAt: &Timestamp{referenceTime},
 	}
 
 	want := `{
 		"url": "url",
 		"html_url": "hurl",
 		"diff_url": "durl",
-		"patch_url": "purl"
+		"patch_url": "purl",
+		"merged_at": ` + referenceTimeStr + `
 		}`
 
 	testJSONMarshal(t, u, want)
diff --git a/github/repos_actions_permissions_test.go b/github/repos_actions_permissions_test.go
index ecff0d5f..9959a6e7 100644
--- a/github/repos_actions_permissions_test.go
+++ b/github/repos_actions_permissions_test.go
@@ -110,3 +110,81 @@ func TestActionsPermissionsRepository_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, u, want)
 }
+
+func TestRepositoriesService_GetDefaultWorkflowPermissions(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/actions/permissions/workflow", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{ "default_workflow_permissions": "read", "can_approve_pull_request_reviews": true }`)
+	})
+
+	ctx := context.Background()
+	org, _, err := client.Repositories.GetDefaultWorkflowPermissions(ctx, "o", "r")
+	if err != nil {
+		t.Errorf("Repositories.GetDefaultWorkflowPermissions returned error: %v", err)
+	}
+	want := &DefaultWorkflowPermissionRepository{DefaultWorkflowPermissions: String("read"), CanApprovePullRequestReviews: Bool(true)}
+	if !cmp.Equal(org, want) {
+		t.Errorf("Repositories.GetDefaultWorkflowPermissions returned %+v, want %+v", org, want)
+	}
+
+	const methodName = "GetDefaultWorkflowPermissions"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Repositories.GetDefaultWorkflowPermissions(ctx, "\n", "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.GetDefaultWorkflowPermissions(ctx, "o", "r")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestRepositoriesService_EditDefaultWorkflowPermissions(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	input := &DefaultWorkflowPermissionRepository{DefaultWorkflowPermissions: String("read"), CanApprovePullRequestReviews: Bool(true)}
+
+	mux.HandleFunc("/repos/o/r/actions/permissions/workflow", func(w http.ResponseWriter, r *http.Request) {
+		v := new(DefaultWorkflowPermissionRepository)
+		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
+
+		testMethod(t, r, "PUT")
+		if !cmp.Equal(v, input) {
+			t.Errorf("Request body = %+v, want %+v", v, input)
+		}
+
+		fmt.Fprint(w, `{ "default_workflow_permissions": "read", "can_approve_pull_request_reviews": true }`)
+	})
+
+	ctx := context.Background()
+	org, _, err := client.Repositories.EditDefaultWorkflowPermissions(ctx, "o", "r", *input)
+	if err != nil {
+		t.Errorf("Repositories.EditDefaultWorkflowPermissions returned error: %v", err)
+	}
+
+	want := &DefaultWorkflowPermissionRepository{DefaultWorkflowPermissions: String("read"), CanApprovePullRequestReviews: Bool(true)}
+	if !cmp.Equal(org, want) {
+		t.Errorf("Repositories.EditDefaultWorkflowPermissions returned %+v, want %+v", org, want)
+	}
+
+	const methodName = "EditDefaultWorkflowPermissions"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Repositories.EditDefaultWorkflowPermissions(ctx, "\n", "\n", *input)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.EditDefaultWorkflowPermissions(ctx, "o", "r", *input)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
diff --git a/github/repos_deployment_protection_rules_test.go b/github/repos_deployment_protection_rules_test.go
new file mode 100644
index 00000000..d5a6b87a
--- /dev/null
+++ b/github/repos_deployment_protection_rules_test.go
@@ -0,0 +1,213 @@
+// Copyright 2024 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"
+	"reflect"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestRepositoriesService_GetAllDeploymentProtectionRules(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/environments/e/deployment_protection_rules", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{"total_count":2, "custom_deployment_protection_rules":[{ "id": 3, "node_id": "IEH37kRlcGxveW1lbnRTdGF0ddiv", "enabled": true, "app": { "id": 1, "node_id": "GHT58kRlcGxveW1lbnRTdTY!bbcy", "slug": "a-custom-app", "integration_url": "https://api.github.com/apps/a-custom-app"}}, { "id": 4, "node_id": "MDE2OkRlcGxveW1lbnRTdHJ41128", "enabled": true, "app": { "id": 1, "node_id": "UHVE67RlcGxveW1lbnRTdTY!jfeuy", "slug": "another-custom-app", "integration_url": "https://api.github.com/apps/another-custom-app"}}]}`)
+	})
+
+	ctx := context.Background()
+	got, _, err := client.Repositories.GetAllDeploymentProtectionRules(ctx, "o", "r", "e")
+	if err != nil {
+		t.Errorf("Repositories.GetAllDeploymentProtectionRules returned error: %v", err)
+	}
+
+	want := &ListDeploymentProtectionRuleResponse{
+		ProtectionRules: []*CustomDeploymentProtectionRule{
+			{ID: Int64(3), NodeID: String("IEH37kRlcGxveW1lbnRTdGF0ddiv"), Enabled: Bool(true), App: &CustomDeploymentProtectionRuleApp{ID: Int64(1), NodeID: String("GHT58kRlcGxveW1lbnRTdTY!bbcy"), Slug: String("a-custom-app"), IntegrationURL: String("https://api.github.com/apps/a-custom-app")}},
+			{ID: Int64(4), NodeID: String("MDE2OkRlcGxveW1lbnRTdHJ41128"), Enabled: Bool(true), App: &CustomDeploymentProtectionRuleApp{ID: Int64(1), NodeID: String("UHVE67RlcGxveW1lbnRTdTY!jfeuy"), Slug: String("another-custom-app"), IntegrationURL: String("https://api.github.com/apps/another-custom-app")}},
+		},
+		TotalCount: Int(2),
+	}
+	if !reflect.DeepEqual(got, want) {
+		t.Errorf("Repositories.GetAllDeploymentProtectionRules = %+v, want %+v", got, want)
+	}
+
+	const methodName = "GetAllDeploymentProtectionRules"
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.GetAllDeploymentProtectionRules(ctx, "o", "r", "e")
+		if got != nil {
+			t.Errorf("got non-nil Repositories.GetAllDeploymentProtectionRules response: %+v", got)
+		}
+		return resp, err
+	})
+}
+
+func TestRepositoriesService_CreateCustomDeploymentProtectionRule(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	input := &CustomDeploymentProtectionRuleRequest{
+		IntegrationID: Int64(5),
+	}
+
+	mux.HandleFunc("/repos/o/r/environments/e/deployment_protection_rules", func(w http.ResponseWriter, r *http.Request) {
+		v := new(CustomDeploymentProtectionRuleRequest)
+		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
+
+		testMethod(t, r, "POST")
+		want := input
+		if !reflect.DeepEqual(v, want) {
+			t.Errorf("Request body = %+v, want %+v", v, want)
+		}
+
+		fmt.Fprint(w, `{"id":3, "node_id": "IEH37kRlcGxveW1lbnRTdGF0ddiv", "enabled": true, "app": {"id": 1, "node_id": "GHT58kRlcGxveW1lbnRTdTY!bbcy", "slug": "a-custom-app", "integration_url": "https://api.github.com/apps/a-custom-app"}}`)
+	})
+
+	ctx := context.Background()
+	got, _, err := client.Repositories.CreateCustomDeploymentProtectionRule(ctx, "o", "r", "e", input)
+	if err != nil {
+		t.Errorf("Repositories.CreateCustomDeploymentProtectionRule returned error: %v", err)
+	}
+
+	want := &CustomDeploymentProtectionRule{
+		ID:      Int64(3),
+		NodeID:  String("IEH37kRlcGxveW1lbnRTdGF0ddiv"),
+		Enabled: Bool(true),
+		App: &CustomDeploymentProtectionRuleApp{
+			ID:             Int64(1),
+			NodeID:         String("GHT58kRlcGxveW1lbnRTdTY!bbcy"),
+			Slug:           String("a-custom-app"),
+			IntegrationURL: String("https://api.github.com/apps/a-custom-app"),
+		},
+	}
+	if !reflect.DeepEqual(got, want) {
+		t.Errorf("Repositories.CreateCustomDeploymentProtectionRule = %+v, want %+v", got, want)
+	}
+
+	const methodName = "CreateCustomDeploymentProtectionRule"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Repositories.CreateCustomDeploymentProtectionRule(ctx, "\n", "\n", "\n", input)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.CreateCustomDeploymentProtectionRule(ctx, "o", "r", "e", input)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestRepositoriesService_ListCustomDeploymentRuleIntegrations(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/environments/e/deployment_protection_rules/apps", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{"total_count": 2, "available_custom_deployment_protection_rule_integrations": [{"id": 1, "node_id": "GHT58kRlcGxveW1lbnRTdTY!bbcy", "slug": "a-custom-app", "integration_url": "https://api.github.com/apps/a-custom-app"}, {"id": 2, "node_id": "UHVE67RlcGxveW1lbnRTdTY!jfeuy", "slug": "another-custom-app", "integration_url": "https://api.github.com/apps/another-custom-app"}]}`)
+	})
+
+	ctx := context.Background()
+	got, _, err := client.Repositories.ListCustomDeploymentRuleIntegrations(ctx, "o", "r", "e")
+	if err != nil {
+		t.Errorf("Repositories.ListCustomDeploymentRuleIntegrations returned error: %v", err)
+	}
+
+	want := &ListCustomDeploymentRuleIntegrationsResponse{
+		TotalCount: Int(2),
+		AvailableIntegrations: []*CustomDeploymentProtectionRuleApp{
+			{ID: Int64(1), NodeID: String("GHT58kRlcGxveW1lbnRTdTY!bbcy"), Slug: String("a-custom-app"), IntegrationURL: String("https://api.github.com/apps/a-custom-app")},
+			{ID: Int64(2), NodeID: String("UHVE67RlcGxveW1lbnRTdTY!jfeuy"), Slug: String("another-custom-app"), IntegrationURL: String("https://api.github.com/apps/another-custom-app")},
+		},
+	}
+	if !reflect.DeepEqual(got, want) {
+		t.Errorf("Repositories.ListCustomDeploymentRuleIntegrations = %+v, want %+v", got, want)
+	}
+
+	const methodName = "ListCustomDeploymentRuleIntegrations"
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.ListCustomDeploymentRuleIntegrations(ctx, "o", "r", "e")
+		if got != nil {
+			t.Errorf("got non-nil Repositories.ListCustomDeploymentRuleIntegrations response: %+v", got)
+		}
+		return resp, err
+	})
+}
+
+func TestRepositoriesService_GetCustomDeploymentProtectionRule(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/environments/e/deployment_protection_rules/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{"id":1, "node_id": "IEH37kRlcGxveW1lbnRTdGF0ddiv", "enabled": true, "app": {"id": 1, "node_id": "GHT58kRlcGxveW1lbnRTdTY!bbcy", "slug": "a-custom-app", "integration_url": "https://api.github.com/apps/a-custom-app"}}`)
+	})
+
+	ctx := context.Background()
+	got, _, err := client.Repositories.GetCustomDeploymentProtectionRule(ctx, "o", "r", "e", 1)
+	if err != nil {
+		t.Errorf("Repositories.GetCustomDeploymentProtectionRule returned error: %v", err)
+	}
+
+	want := &CustomDeploymentProtectionRule{
+		ID:      Int64(1),
+		NodeID:  String("IEH37kRlcGxveW1lbnRTdGF0ddiv"),
+		Enabled: Bool(true),
+		App: &CustomDeploymentProtectionRuleApp{
+			ID:             Int64(1),
+			NodeID:         String("GHT58kRlcGxveW1lbnRTdTY!bbcy"),
+			Slug:           String("a-custom-app"),
+			IntegrationURL: String("https://api.github.com/apps/a-custom-app"),
+		},
+	}
+
+	if !reflect.DeepEqual(got, want) {
+		t.Errorf("Repositories.GetCustomDeploymentProtectionRule = %+v, want %+v", got, want)
+	}
+
+	const methodName = "GetCustomDeploymentProtectionRule"
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.GetCustomDeploymentProtectionRule(ctx, "o", "r", "e", 1)
+		if got != nil {
+			t.Errorf("got non-nil Repositories.GetCustomDeploymentProtectionRule response: %+v", got)
+		}
+		return resp, err
+	})
+}
+
+func TestRepositoriesService_DisableCustomDeploymentProtectionRule(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/environments/e/deployment_protection_rules/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	ctx := context.Background()
+	resp, err := client.Repositories.DisableCustomDeploymentProtectionRule(ctx, "o", "r", "e", 1)
+	if err != nil {
+		t.Errorf("Repositories.DisableCustomDeploymentProtectionRule returned error: %v", err)
+	}
+
+	if !cmp.Equal(resp.StatusCode, 204) {
+		t.Errorf("Repositories.DisableCustomDeploymentProtectionRule returned  status code %+v, want %+v", resp.StatusCode, "204")
+	}
+
+	const methodName = "DisableCustomDeploymentProtectionRule"
+	testBadOptions(t, methodName, func() (err error) {
+		_, err = client.Repositories.DisableCustomDeploymentProtectionRule(ctx, "\n", "\n", "\n", 1)
+		return err
+	})
+}