Repository: google/go-github
Pull request #2248 (2022-01-11 01:39:09)

Add support for Dependabot secrets
I added handling for Dependabot specific secrets per #2246. This part of the API seems undocumented, but I don't see why it would change as it's almost identical to managing regular secrets. This file is pretty repetitive, and I doubled the number of repo and org methods, so I did some refactoring. Hopefully nothing too heinous.

codecov[bot]: # [Codecov](https://codecov.io/gh/google/go-github/pull/2248?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
> Merging [#2248](https://codecov.io/gh/google/go-github/pull/2248?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (ee5fb9c) into [master](https://codecov.io/gh/google/go-github/commit/6e66a89c7618957e0f95f82d94bfbe3164fd21dc?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (6e66a89) will **decrease** coverage by `0.41%`.
> The diff coverage is `64.10%`.

[![Impacted file tree graph](https://codecov.io/gh/google/go-github/pull/2248/graphs/tree.svg?width=650&height=150&src=pr&token=EgYjGjZlI0&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google)](https://codecov.io/gh/google/go-github/pull/2248?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google)

```diff
@@            Coverage Diff             @@
##           master    #2248      +/-   ##
==========================================
- Coverage   97.80%   97.38%   -0.42%     
==========================================
  Files         113      113              
  Lines       10207    10182      -25     
==========================================
- Hits         9983     9916      -67     
- Misses        156      198      +42     
  Partials       68       68              
```


| [Impacted Files](https://codecov.io/gh/google/go-github/pull/2248?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/2248/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google#diff-Z2l0aHViL2FjdGlvbnNfc2VjcmV0cy5nbw==) | `79.61% <64.10%> (-20.39%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/google/go-github/pull/2248?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/2248?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google). Last update [6e66a89...ee5fb9c](https://codecov.io/gh/google/go-github/pull/2248?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).

jurre: Slightly bike-sheddy, but I would suggest pulling this out of `github/actions_secrets.go` into `github/dependabot_secrets.go`. While the API shares a lot of similarities, it is it's own top-level resource that is geared towards Dependabot specifically, not Actions.
liath: Done! That feels much nicer, good suggestion :D
liath: Could we go a step further and make them `actions_secrets_dependabot*` so that all the secrets-management files are next to each other alphabetically?
gmlewis: > Could we go a step further and make them `actions_secrets_dependabot*` so that all the secrets-management files are next to each other alphabetically?

Genius!  Sorry I didn't think of that the first time, @liath !
jurre: Not super familiar with this project, but I do work on Dependabot (and implemented the API in question) and so I'm curious why this is part of the `ActionsService`, it seems more appropriate to add a new `DependabotService` struct, as I'd expect these APIs might diverge over time which could result in a breaking change to the library. Given that they are separate resources in the API, would it make sense for the client to follow that?
gmlewis: > Not super familiar with this project, but I do work on Dependabot (and implemented the API in question) and so I'm curious why this is part of the `ActionsService`, it seems more appropriate to add a new `DependabotService` struct, as I'd expect these APIs might diverge over time which could result in a breaking change to the library. Given that they are separate resources in the API, would it make sense for the client to follow that?
> 
> No strong opinion either way, confident y'all do what makes most sense for this project +1

Hi @jurre and thank you for the comments and questions (and for the API! :smile: )!

So in this repo, we try to tightly couple the layout and services to the GitHub v3 API documentation organization structure.

We initially placed these API endpoints under the `ActionsService` endpoints primarily because they appear to have a very high correlation with those endpoints, and because no `Dependabot` documentation was available at that time (if I'm not mistaken).

However, now that the API documentation exists and is indeed under a completely different section, I think you are right that we should create a new `DependabotService`.

@liath - would you like to tackle that, or should we address that in a follow-up PR (either by you or by another contributor)?
jurre: > very high correlation with those endpoints

You're right, that's definitely the case 👍 they share a lot of the same semantics, but might evolve separately

> no Dependabot documentation was available at that time

Yeah it took a few days to get those published so that's totally fair.

Patch:
diff --git a/github/actions_secrets.go b/github/actions_secrets.go
index 5bfb22ac..29f70a1a 100644
--- a/github/actions_secrets.go
+++ b/github/actions_secrets.go
@@ -47,12 +47,8 @@ func (p *PublicKey) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
-// GetRepoPublicKey gets a public key that should be used for secret encryption.
-//
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#get-a-repository-public-key
-func (s *ActionsService) GetRepoPublicKey(ctx context.Context, owner, repo string) (*PublicKey, *Response, error) {
-	u := fmt.Sprintf("repos/%v/%v/actions/secrets/public-key", owner, repo)
-	req, err := s.client.NewRequest("GET", u, nil)
+func (s *ActionsService) getPublicKey(ctx context.Context, url string) (*PublicKey, *Response, error) {
+	req, err := s.client.NewRequest("GET", url, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -66,42 +62,28 @@ func (s *ActionsService) GetRepoPublicKey(ctx context.Context, owner, repo strin
 	return pubKey, resp, nil
 }
 
+// GetRepoPublicKey gets a public key that should be used for secret encryption.
+//
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#get-a-repository-public-key
+func (s *ActionsService) GetRepoPublicKey(ctx context.Context, owner, repo string) (*PublicKey, *Response, error) {
+	url := fmt.Sprintf("repos/%v/%v/actions/secrets/public-key", owner, repo)
+	return s.getPublicKey(ctx, url)
+}
+
 // GetOrgPublicKey gets a public key that should be used for secret encryption.
 //
 // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#get-an-organization-public-key
 func (s *ActionsService) GetOrgPublicKey(ctx context.Context, org string) (*PublicKey, *Response, error) {
-	u := fmt.Sprintf("orgs/%v/actions/secrets/public-key", org)
-	req, err := s.client.NewRequest("GET", u, nil)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	pubKey := new(PublicKey)
-	resp, err := s.client.Do(ctx, req, pubKey)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return pubKey, resp, nil
+	url := fmt.Sprintf("orgs/%v/actions/secrets/public-key", org)
+	return s.getPublicKey(ctx, url)
 }
 
 // GetEnvPublicKey gets a public key that should be used for secret encryption.
 //
 // GitHub API docs: https://docs.github.com/en/rest/reference/actions#get-an-environment-public-key
 func (s *ActionsService) GetEnvPublicKey(ctx context.Context, repoID int, env string) (*PublicKey, *Response, error) {
-	u := fmt.Sprintf("repositories/%v/environments/%v/secrets/public-key", repoID, env)
-	req, err := s.client.NewRequest("GET", u, nil)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	pubKey := new(PublicKey)
-	resp, err := s.client.Do(ctx, req, pubKey)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return pubKey, resp, nil
+	url := fmt.Sprintf("repositories/%v/environments/%v/secrets/public-key", repoID, env)
+	return s.getPublicKey(ctx, url)
 }
 
 // Secret represents a repository action secret.
@@ -119,13 +101,8 @@ type Secrets struct {
 	Secrets    []*Secret `json:"secrets"`
 }
 
-// ListRepoSecrets lists all secrets available in a repository
-// without revealing their encrypted values.
-//
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#list-repository-secrets
-func (s *ActionsService) ListRepoSecrets(ctx context.Context, owner, repo string, opts *ListOptions) (*Secrets, *Response, error) {
-	u := fmt.Sprintf("repos/%v/%v/actions/secrets", owner, repo)
-	u, err := addOptions(u, opts)
+func (s *ActionsService) listSecrets(ctx context.Context, url string, opts *ListOptions) (*Secrets, *Response, error) {
+	u, err := addOptions(url, opts)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -144,12 +121,34 @@ func (s *ActionsService) ListRepoSecrets(ctx context.Context, owner, repo string
 	return secrets, resp, nil
 }
 
-// GetRepoSecret gets a single repository secret without revealing its encrypted value.
+// ListRepoSecrets lists all secrets available in a repository
+// without revealing their encrypted values.
 //
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#get-a-repository-secret
-func (s *ActionsService) GetRepoSecret(ctx context.Context, owner, repo, name string) (*Secret, *Response, error) {
-	u := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, name)
-	req, err := s.client.NewRequest("GET", u, nil)
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#list-repository-secrets
+func (s *ActionsService) ListRepoSecrets(ctx context.Context, owner, repo string, opts *ListOptions) (*Secrets, *Response, error) {
+	url := fmt.Sprintf("repos/%v/%v/actions/secrets", owner, repo)
+	return s.listSecrets(ctx, url, opts)
+}
+
+// ListOrgSecrets lists all secrets available in an organization
+// without revealing their encrypted values.
+//
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#list-organization-secrets
+func (s *ActionsService) ListOrgSecrets(ctx context.Context, org string, opts *ListOptions) (*Secrets, *Response, error) {
+	url := fmt.Sprintf("orgs/%v/actions/secrets", org)
+	return s.listSecrets(ctx, url, opts)
+}
+
+// ListEnvSecrets lists all secrets available in an environment.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/actions#list-environment-secrets
+func (s *ActionsService) ListEnvSecrets(ctx context.Context, repoID int, env string, opts *ListOptions) (*Secrets, *Response, error) {
+	url := fmt.Sprintf("repositories/%v/environments/%v/secrets", repoID, env)
+	return s.listSecrets(ctx, url, opts)
+}
+
+func (s *ActionsService) getSecret(ctx context.Context, url string) (*Secret, *Response, error) {
+	req, err := s.client.NewRequest("GET", url, nil)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -163,6 +162,30 @@ func (s *ActionsService) GetRepoSecret(ctx context.Context, owner, repo, name st
 	return secret, resp, nil
 }
 
+// GetRepoSecret gets a single repository secret without revealing its encrypted value.
+//
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#get-a-repository-secret
+func (s *ActionsService) GetRepoSecret(ctx context.Context, owner, repo, name string) (*Secret, *Response, error) {
+	url := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, name)
+	return s.getSecret(ctx, url)
+}
+
+// GetOrgSecret gets a single organization secret without revealing its encrypted value.
+//
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#get-an-organization-secret
+func (s *ActionsService) GetOrgSecret(ctx context.Context, org, name string) (*Secret, *Response, error) {
+	url := fmt.Sprintf("orgs/%v/actions/secrets/%v", org, name)
+	return s.getSecret(ctx, url)
+}
+
+// GetEnvSecret gets a single environment secret without revealing its encrypted value.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/actions#list-environment-secrets
+func (s *ActionsService) GetEnvSecret(ctx context.Context, repoID int, env, secretName string) (*Secret, *Response, error) {
+	url := fmt.Sprintf("repositories/%v/environments/%v/secrets/%v", repoID, env, secretName)
+	return s.getSecret(ctx, url)
+}
+
 // SelectedRepoIDs are the repository IDs that have access to the secret.
 type SelectedRepoIDs []int64
 
@@ -179,13 +202,8 @@ type EncryptedSecret struct {
 	SelectedRepositoryIDs SelectedRepoIDs `json:"selected_repository_ids,omitempty"`
 }
 
-// CreateOrUpdateRepoSecret creates or updates a repository secret with an encrypted value.
-//
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#create-or-update-a-repository-secret
-func (s *ActionsService) CreateOrUpdateRepoSecret(ctx context.Context, owner, repo string, eSecret *EncryptedSecret) (*Response, error) {
-	u := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, eSecret.Name)
-
-	req, err := s.client.NewRequest("PUT", u, eSecret)
+func (s *ActionsService) putSecret(ctx context.Context, url string, eSecret *EncryptedSecret) (*Response, error) {
+	req, err := s.client.NewRequest("PUT", url, eSecret)
 	if err != nil {
 		return nil, err
 	}
@@ -193,76 +211,61 @@ func (s *ActionsService) CreateOrUpdateRepoSecret(ctx context.Context, owner, re
 	return s.client.Do(ctx, req, nil)
 }
 
-// DeleteRepoSecret deletes a secret in a repository using the secret name.
+// CreateOrUpdateRepoSecret creates or updates a repository secret with an encrypted value.
 //
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#delete-a-repository-secret
-func (s *ActionsService) DeleteRepoSecret(ctx context.Context, owner, repo, name string) (*Response, error) {
-	u := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, name)
-
-	req, err := s.client.NewRequest("DELETE", u, nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return s.client.Do(ctx, req, nil)
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#create-or-update-a-repository-secret
+func (s *ActionsService) CreateOrUpdateRepoSecret(ctx context.Context, owner, repo string, eSecret *EncryptedSecret) (*Response, error) {
+	url := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, eSecret.Name)
+	return s.putSecret(ctx, url, eSecret)
 }
 
-// ListOrgSecrets lists all secrets available in an organization
-// without revealing their encrypted values.
+// CreateOrUpdateOrgSecret creates or updates an organization secret with an encrypted value.
 //
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#list-organization-secrets
-func (s *ActionsService) ListOrgSecrets(ctx context.Context, org string, opts *ListOptions) (*Secrets, *Response, error) {
-	u := fmt.Sprintf("orgs/%v/actions/secrets", org)
-	u, err := addOptions(u, opts)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	req, err := s.client.NewRequest("GET", u, nil)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	secrets := new(Secrets)
-	resp, err := s.client.Do(ctx, req, &secrets)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return secrets, resp, nil
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#create-or-update-an-organization-secret
+func (s *ActionsService) CreateOrUpdateOrgSecret(ctx context.Context, org string, eSecret *EncryptedSecret) (*Response, error) {
+	url := fmt.Sprintf("orgs/%v/actions/secrets/%v", org, eSecret.Name)
+	return s.putSecret(ctx, url, eSecret)
 }
 
-// GetOrgSecret gets a single organization secret without revealing its encrypted value.
+// CreateOrUpdateEnvSecret creates or updates a single environment secret with an encrypted value.
 //
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#get-an-organization-secret
-func (s *ActionsService) GetOrgSecret(ctx context.Context, org, name string) (*Secret, *Response, error) {
-	u := fmt.Sprintf("orgs/%v/actions/secrets/%v", org, name)
-	req, err := s.client.NewRequest("GET", u, nil)
-	if err != nil {
-		return nil, nil, err
-	}
+// GitHub API docs: https://docs.github.com/en/rest/reference/actions#create-or-update-an-environment-secret
+func (s *ActionsService) CreateOrUpdateEnvSecret(ctx context.Context, repoID int, env string, eSecret *EncryptedSecret) (*Response, error) {
+	url := fmt.Sprintf("repositories/%v/environments/%v/secrets/%v", repoID, env, eSecret.Name)
+	return s.putSecret(ctx, url, eSecret)
+}
 
-	secret := new(Secret)
-	resp, err := s.client.Do(ctx, req, secret)
+func (s *ActionsService) deleteSecret(ctx context.Context, url string) (*Response, error) {
+	req, err := s.client.NewRequest("DELETE", url, nil)
 	if err != nil {
-		return nil, resp, err
+		return nil, err
 	}
 
-	return secret, resp, nil
+	return s.client.Do(ctx, req, nil)
 }
 
-// CreateOrUpdateOrgSecret creates or updates an organization secret with an encrypted value.
+// DeleteRepoSecret deletes a secret in a repository using the secret name.
 //
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#create-or-update-an-organization-secret
-func (s *ActionsService) CreateOrUpdateOrgSecret(ctx context.Context, org string, eSecret *EncryptedSecret) (*Response, error) {
-	u := fmt.Sprintf("orgs/%v/actions/secrets/%v", org, eSecret.Name)
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#delete-a-repository-secret
+func (s *ActionsService) DeleteRepoSecret(ctx context.Context, owner, repo, name string) (*Response, error) {
+	url := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, name)
+	return s.deleteSecret(ctx, url)
+}
 
-	req, err := s.client.NewRequest("PUT", u, eSecret)
-	if err != nil {
-		return nil, err
-	}
+// DeleteOrgSecret deletes a secret in an organization using the secret name.
+//
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#delete-an-organization-secret
+func (s *ActionsService) DeleteOrgSecret(ctx context.Context, org, name string) (*Response, error) {
+	url := fmt.Sprintf("orgs/%v/actions/secrets/%v", org, name)
+	return s.deleteSecret(ctx, url)
+}
 
-	return s.client.Do(ctx, req, nil)
+// DeleteEnvSecret deletes a secret in an environment using the secret name.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/actions#delete-an-environment-secret
+func (s *ActionsService) DeleteEnvSecret(ctx context.Context, repoID int, env, secretName string) (*Response, error) {
+	url := fmt.Sprintf("repositories/%v/environments/%v/secrets/%v", repoID, env, secretName)
+	return s.deleteSecret(ctx, url)
 }
 
 // SelectedReposList represents the list of repositories selected for an organization secret.
@@ -271,12 +274,8 @@ type SelectedReposList struct {
 	Repositories []*Repository `json:"repositories,omitempty"`
 }
 
-// ListSelectedReposForOrgSecret lists all repositories that have access to a secret.
-//
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#list-selected-repositories-for-an-organization-secret
-func (s *ActionsService) ListSelectedReposForOrgSecret(ctx context.Context, org, name string, opts *ListOptions) (*SelectedReposList, *Response, error) {
-	u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories", org, name)
-	u, err := addOptions(u, opts)
+func (s *ActionsService) listSelectedReposForSecret(ctx context.Context, url string, opts *ListOptions) (*SelectedReposList, *Response, error) {
+	u, err := addOptions(url, opts)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -295,17 +294,20 @@ func (s *ActionsService) ListSelectedReposForOrgSecret(ctx context.Context, org,
 	return result, resp, nil
 }
 
-// SetSelectedReposForOrgSecret sets the repositories that have access to a secret.
+// ListSelectedReposForOrgSecret lists all repositories that have access to a secret.
 //
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#set-selected-repositories-for-an-organization-secret
-func (s *ActionsService) SetSelectedReposForOrgSecret(ctx context.Context, org, name string, ids SelectedRepoIDs) (*Response, error) {
-	u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories", org, name)
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#list-selected-repositories-for-an-organization-secret
+func (s *ActionsService) ListSelectedReposForOrgSecret(ctx context.Context, org, name string, opts *ListOptions) (*SelectedReposList, *Response, error) {
+	url := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories", org, name)
+	return s.listSelectedReposForSecret(ctx, url, opts)
+}
 
+func (s *ActionsService) setSelectedReposForSecret(ctx context.Context, url string, ids SelectedRepoIDs) (*Response, error) {
 	type repoIDs struct {
 		SelectedIDs SelectedRepoIDs `json:"selected_repository_ids"`
 	}
 
-	req, err := s.client.NewRequest("PUT", u, repoIDs{SelectedIDs: ids})
+	req, err := s.client.NewRequest("PUT", url, repoIDs{SelectedIDs: ids})
 	if err != nil {
 		return nil, err
 	}
@@ -313,39 +315,16 @@ func (s *ActionsService) SetSelectedReposForOrgSecret(ctx context.Context, org,
 	return s.client.Do(ctx, req, nil)
 }
 
-// AddSelectedRepoToOrgSecret adds a repository to an organization secret.
-//
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#add-selected-repository-to-an-organization-secret
-func (s *ActionsService) AddSelectedRepoToOrgSecret(ctx context.Context, org, name string, repo *Repository) (*Response, error) {
-	u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories/%v", org, name, *repo.ID)
-	req, err := s.client.NewRequest("PUT", u, nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return s.client.Do(ctx, req, nil)
-}
-
-// RemoveSelectedRepoFromOrgSecret removes a repository from an organization secret.
+// SetSelectedReposForOrgSecret sets the repositories that have access to a secret.
 //
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#remove-selected-repository-from-an-organization-secret
-func (s *ActionsService) RemoveSelectedRepoFromOrgSecret(ctx context.Context, org, name string, repo *Repository) (*Response, error) {
-	u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories/%v", org, name, *repo.ID)
-	req, err := s.client.NewRequest("DELETE", u, nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return s.client.Do(ctx, req, nil)
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#set-selected-repositories-for-an-organization-secret
+func (s *ActionsService) SetSelectedReposForOrgSecret(ctx context.Context, org, name string, ids SelectedRepoIDs) (*Response, error) {
+	url := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories", org, name)
+	return s.setSelectedReposForSecret(ctx, url, ids)
 }
 
-// DeleteOrgSecret deletes a secret in an organization using the secret name.
-//
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#delete-an-organization-secret
-func (s *ActionsService) DeleteOrgSecret(ctx context.Context, org, name string) (*Response, error) {
-	u := fmt.Sprintf("orgs/%v/actions/secrets/%v", org, name)
-
-	req, err := s.client.NewRequest("DELETE", u, nil)
+func (s *ActionsService) addSelectedRepoToSecret(ctx context.Context, url string) (*Response, error) {
+	req, err := s.client.NewRequest("PUT", url, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -353,56 +332,16 @@ func (s *ActionsService) DeleteOrgSecret(ctx context.Context, org, name string)
 	return s.client.Do(ctx, req, nil)
 }
 
-// ListEnvSecrets lists all secrets available in an environment.
-//
-// GitHub API docs: https://docs.github.com/en/rest/reference/actions#list-environment-secrets
-func (s *ActionsService) ListEnvSecrets(ctx context.Context, repoID int, env string, opts *ListOptions) (*Secrets, *Response, error) {
-	u := fmt.Sprintf("repositories/%v/environments/%v/secrets", repoID, env)
-	u, err := addOptions(u, opts)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	req, err := s.client.NewRequest("GET", u, nil)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	secrets := new(Secrets)
-	resp, err := s.client.Do(ctx, req, &secrets)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return secrets, resp, nil
-}
-
-// GetEnvSecret gets a single environment secret without revealing its encrypted value.
+// AddSelectedRepoToOrgSecret adds a repository to an organization secret.
 //
-// GitHub API docs: https://docs.github.com/en/rest/reference/actions#list-environment-secrets
-func (s *ActionsService) GetEnvSecret(ctx context.Context, repoID int, env, secretName string) (*Secret, *Response, error) {
-	u := fmt.Sprintf("repositories/%v/environments/%v/secrets/%v", repoID, env, secretName)
-	req, err := s.client.NewRequest("GET", u, nil)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	secret := new(Secret)
-	resp, err := s.client.Do(ctx, req, secret)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return secret, resp, nil
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#add-selected-repository-to-an-organization-secret
+func (s *ActionsService) AddSelectedRepoToOrgSecret(ctx context.Context, org, name string, repo *Repository) (*Response, error) {
+	url := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories/%v", org, name, *repo.ID)
+	return s.addSelectedRepoToSecret(ctx, url)
 }
 
-// CreateOrUpdateEnvSecret creates or updates a repository secret with an encrypted value.
-//
-// GitHub API docs: https://docs.github.com/en/rest/reference/actions#create-or-update-an-environment-secret
-func (s *ActionsService) CreateOrUpdateEnvSecret(ctx context.Context, repoID int, env string, eSecret *EncryptedSecret) (*Response, error) {
-	u := fmt.Sprintf("repositories/%v/environments/%v/secrets/%v", repoID, env, eSecret.Name)
-
-	req, err := s.client.NewRequest("PUT", u, eSecret)
+func (s *ActionsService) removeSelectedRepoFromSecret(ctx context.Context, url string) (*Response, error) {
+	req, err := s.client.NewRequest("DELETE", url, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -410,16 +349,10 @@ func (s *ActionsService) CreateOrUpdateEnvSecret(ctx context.Context, repoID int
 	return s.client.Do(ctx, req, nil)
 }
 
-// DeleteEnvSecret deletes a secret in an environment using the secret name.
+// RemoveSelectedRepoFromOrgSecret removes a repository from an organization secret.
 //
-// GitHub API docs: https://docs.github.com/en/rest/reference/actions#delete-an-environment-secret
-func (s *ActionsService) DeleteEnvSecret(ctx context.Context, repoID int, env, secretName string) (*Response, error) {
-	u := fmt.Sprintf("repositories/%v/environments/%v/secrets/%v", repoID, env, secretName)
-
-	req, err := s.client.NewRequest("DELETE", u, nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return s.client.Do(ctx, req, nil)
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#remove-selected-repository-from-an-organization-secret
+func (s *ActionsService) RemoveSelectedRepoFromOrgSecret(ctx context.Context, org, name string, repo *Repository) (*Response, error) {
+	url := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories/%v", org, name, *repo.ID)
+	return s.removeSelectedRepoFromSecret(ctx, url)
 }
diff --git a/github/dependabot.go b/github/dependabot.go
new file mode 100644
index 00000000..8ee0c0c7
--- /dev/null
+++ b/github/dependabot.go
@@ -0,0 +1,12 @@
+// 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
+
+// DependabotService handles communication with the Dependabot related
+// methods of the GitHub API.
+//
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/dependabot/
+type DependabotService service
diff --git a/github/dependabot_secrets.go b/github/dependabot_secrets.go
new file mode 100644
index 00000000..a6645339
--- /dev/null
+++ b/github/dependabot_secrets.go
@@ -0,0 +1,228 @@
+// 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"
+)
+
+func (s *DependabotService) getPublicKey(ctx context.Context, url string) (*PublicKey, *Response, error) {
+	req, err := s.client.NewRequest("GET", url, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pubKey := new(PublicKey)
+	resp, err := s.client.Do(ctx, req, pubKey)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pubKey, resp, nil
+}
+
+// GetRepoPublicKey gets a public key that should be used for Dependabot secret encryption.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#get-a-repository-public-key
+func (s *DependabotService) GetRepoPublicKey(ctx context.Context, owner, repo string) (*PublicKey, *Response, error) {
+	url := fmt.Sprintf("repos/%v/%v/dependabot/secrets/public-key", owner, repo)
+	return s.getPublicKey(ctx, url)
+}
+
+// GetOrgPublicKey gets a public key that should be used for Dependabot secret encryption.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#get-an-organization-public-key
+func (s *DependabotService) GetOrgPublicKey(ctx context.Context, org string) (*PublicKey, *Response, error) {
+	url := fmt.Sprintf("orgs/%v/dependabot/secrets/public-key", org)
+	return s.getPublicKey(ctx, url)
+}
+
+func (s *DependabotService) listSecrets(ctx context.Context, url string, opts *ListOptions) (*Secrets, *Response, error) {
+	u, err := addOptions(url, opts)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	secrets := new(Secrets)
+	resp, err := s.client.Do(ctx, req, &secrets)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return secrets, resp, nil
+}
+
+// ListRepoSecrets lists all Dependabot secrets available in a repository
+// without revealing their encrypted values.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#list-repository-secrets
+func (s *DependabotService) ListRepoSecrets(ctx context.Context, owner, repo string, opts *ListOptions) (*Secrets, *Response, error) {
+	url := fmt.Sprintf("repos/%v/%v/dependabot/secrets", owner, repo)
+	return s.listSecrets(ctx, url, opts)
+}
+
+// ListOrgSecrets lists all Dependabot secrets available in an organization
+// without revealing their encrypted values.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#list-organization-secrets
+func (s *DependabotService) ListOrgSecrets(ctx context.Context, org string, opts *ListOptions) (*Secrets, *Response, error) {
+	url := fmt.Sprintf("orgs/%v/dependabot/secrets", org)
+	return s.listSecrets(ctx, url, opts)
+}
+
+func (s *DependabotService) getSecret(ctx context.Context, url string) (*Secret, *Response, error) {
+	req, err := s.client.NewRequest("GET", url, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	secret := new(Secret)
+	resp, err := s.client.Do(ctx, req, secret)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return secret, resp, nil
+}
+
+// GetRepoSecret gets a single repository Dependabot secret without revealing its encrypted value.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#get-a-repository-secret
+func (s *DependabotService) GetRepoSecret(ctx context.Context, owner, repo, name string) (*Secret, *Response, error) {
+	url := fmt.Sprintf("repos/%v/%v/dependabot/secrets/%v", owner, repo, name)
+	return s.getSecret(ctx, url)
+}
+
+// GetOrgSecret gets a single organization Dependabot secret without revealing its encrypted value.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#get-an-organization-secret
+func (s *DependabotService) GetOrgSecret(ctx context.Context, org, name string) (*Secret, *Response, error) {
+	url := fmt.Sprintf("orgs/%v/dependabot/secrets/%v", org, name)
+	return s.getSecret(ctx, url)
+}
+
+func (s *DependabotService) putSecret(ctx context.Context, url string, eSecret *EncryptedSecret) (*Response, error) {
+	req, err := s.client.NewRequest("PUT", url, eSecret)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(ctx, req, nil)
+}
+
+// CreateOrUpdateRepoSecret creates or updates a repository Dependabot secret with an encrypted value.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#create-or-update-a-repository-secret
+func (s *DependabotService) CreateOrUpdateRepoSecret(ctx context.Context, owner, repo string, eSecret *EncryptedSecret) (*Response, error) {
+	url := fmt.Sprintf("repos/%v/%v/dependabot/secrets/%v", owner, repo, eSecret.Name)
+	return s.putSecret(ctx, url, eSecret)
+}
+
+// CreateOrUpdateOrgSecret creates or updates an organization Dependabot secret with an encrypted value.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#create-or-update-an-organization-secret
+func (s *DependabotService) CreateOrUpdateOrgSecret(ctx context.Context, org string, eSecret *EncryptedSecret) (*Response, error) {
+	url := fmt.Sprintf("orgs/%v/dependabot/secrets/%v", org, eSecret.Name)
+	return s.putSecret(ctx, url, eSecret)
+}
+
+func (s *DependabotService) deleteSecret(ctx context.Context, url string) (*Response, error) {
+	req, err := s.client.NewRequest("DELETE", url, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(ctx, req, nil)
+}
+
+// DeleteRepoSecret deletes a Dependabot secret in a repository using the secret name.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#delete-a-repository-secret
+func (s *DependabotService) DeleteRepoSecret(ctx context.Context, owner, repo, name string) (*Response, error) {
+	url := fmt.Sprintf("repos/%v/%v/dependabot/secrets/%v", owner, repo, name)
+	return s.deleteSecret(ctx, url)
+}
+
+// DeleteOrgSecret deletes a Dependabot secret in an organization using the secret name.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#delete-an-organization-secret
+func (s *DependabotService) DeleteOrgSecret(ctx context.Context, org, name string) (*Response, error) {
+	url := fmt.Sprintf("orgs/%v/dependabot/secrets/%v", org, name)
+	return s.deleteSecret(ctx, url)
+}
+
+// ListSelectedReposForOrgSecret lists all repositories that have access to a Dependabot secret.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#list-selected-repositories-for-an-organization-secret
+func (s *DependabotService) ListSelectedReposForOrgSecret(ctx context.Context, org, name string, opts *ListOptions) (*SelectedReposList, *Response, error) {
+	url := fmt.Sprintf("orgs/%v/dependabot/secrets/%v/repositories", org, name)
+	u, err := addOptions(url, opts)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	result := new(SelectedReposList)
+	resp, err := s.client.Do(ctx, req, result)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return result, resp, nil
+}
+
+// SetSelectedReposForOrgSecret sets the repositories that have access to a Dependabot secret.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#set-selected-repositories-for-an-organization-secret
+func (s *DependabotService) SetSelectedReposForOrgSecret(ctx context.Context, org, name string, ids SelectedRepoIDs) (*Response, error) {
+	url := fmt.Sprintf("orgs/%v/dependabot/secrets/%v/repositories", org, name)
+	type repoIDs struct {
+		SelectedIDs SelectedRepoIDs `json:"selected_repository_ids"`
+	}
+
+	req, err := s.client.NewRequest("PUT", url, repoIDs{SelectedIDs: ids})
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(ctx, req, nil)
+}
+
+// AddSelectedRepoToOrgSecret adds a repository to an organization Dependabot secret.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#add-selected-repository-to-an-organization-secret
+func (s *DependabotService) AddSelectedRepoToOrgSecret(ctx context.Context, org, name string, repo *Repository) (*Response, error) {
+	url := fmt.Sprintf("orgs/%v/dependabot/secrets/%v/repositories/%v", org, name, *repo.ID)
+	req, err := s.client.NewRequest("PUT", url, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(ctx, req, nil)
+}
+
+// RemoveSelectedRepoFromOrgSecret removes a repository from an organization Dependabot secret.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/dependabot#remove-selected-repository-from-an-organization-secret
+func (s *DependabotService) RemoveSelectedRepoFromOrgSecret(ctx context.Context, org, name string, repo *Repository) (*Response, error) {
+	url := fmt.Sprintf("orgs/%v/dependabot/secrets/%v/repositories/%v", org, name, *repo.ID)
+	req, err := s.client.NewRequest("DELETE", url, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(ctx, req, nil)
+}
diff --git a/github/github.go b/github/github.go
index 26bf78a9..4fb466e3 100644
--- a/github/github.go
+++ b/github/github.go
@@ -178,6 +178,7 @@ type Client struct {
 	Billing        *BillingService
 	Checks         *ChecksService
 	CodeScanning   *CodeScanningService
+	Dependabot     *DependabotService
 	Enterprise     *EnterpriseService
 	Gists          *GistsService
 	Git            *GitService
@@ -306,6 +307,7 @@ func NewClient(httpClient *http.Client) *Client {
 	c.Billing = (*BillingService)(&c.common)
 	c.Checks = (*ChecksService)(&c.common)
 	c.CodeScanning = (*CodeScanningService)(&c.common)
+	c.Dependabot = (*DependabotService)(&c.common)
 	c.Enterprise = (*EnterpriseService)(&c.common)
 	c.Gists = (*GistsService)(&c.common)
 	c.Git = (*GitService)(&c.common)
diff --git a/github/users_packages.go b/github/users_packages.go
index 4308b16e..cd20f8c1 100644
--- a/github/users_packages.go
+++ b/github/users_packages.go
@@ -115,13 +115,17 @@ func (s *UsersService) RestorePackage(ctx context.Context, user, packageType, pa
 //
 // GitHub API docs: https://docs.github.com/en/rest/reference/packages#get-all-package-versions-for-a-package-owned-by-the-authenticated-user
 // GitHub API docs: https://docs.github.com/en/rest/reference/users#delete-an-email-address-for-the-authenticated-user
-func (s *UsersService) PackageGetAllVersions(ctx context.Context, user, packageType, packageName string) ([]*PackageVersion, *Response, error) {
+func (s *UsersService) PackageGetAllVersions(ctx context.Context, user, packageType, packageName string, opts *PackageListOptions) ([]*PackageVersion, *Response, error) {
 	var u string
 	if user != "" {
 		u = fmt.Sprintf("users/%v/packages/%v/%v/versions", user, packageType, packageName)
 	} else {
 		u = fmt.Sprintf("user/packages/%v/%v/versions", packageType, packageName)
 	}
+	u, err := addOptions(u, opts)
+	if err != nil {
+		return nil, nil, err
+	}
 
 	req, err := s.client.NewRequest("GET", u, nil)
 	if err != nil {


Test patch:
diff --git a/github/dependabot_secrets_test.go b/github/dependabot_secrets_test.go
new file mode 100644
index 00000000..49b93e76
--- /dev/null
+++ b/github/dependabot_secrets_test.go
@@ -0,0 +1,526 @@
+// 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"
+	"time"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestDependabotService_GetRepoPublicKey(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/dependabot/secrets/public-key", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{"key_id":"1234","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`)
+	})
+
+	ctx := context.Background()
+	key, _, err := client.Dependabot.GetRepoPublicKey(ctx, "o", "r")
+	if err != nil {
+		t.Errorf("Dependabot.GetRepoPublicKey returned error: %v", err)
+	}
+
+	want := &PublicKey{KeyID: String("1234"), Key: String("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")}
+	if !cmp.Equal(key, want) {
+		t.Errorf("Dependabot.GetRepoPublicKey returned %+v, want %+v", key, want)
+	}
+
+	const methodName = "GetRepoPublicKey"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Dependabot.GetRepoPublicKey(ctx, "\n", "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Dependabot.GetRepoPublicKey(ctx, "o", "r")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestDependabotService_GetRepoPublicKeyNumeric(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/dependabot/secrets/public-key", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{"key_id":1234,"key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`)
+	})
+
+	ctx := context.Background()
+	key, _, err := client.Dependabot.GetRepoPublicKey(ctx, "o", "r")
+	if err != nil {
+		t.Errorf("Dependabot.GetRepoPublicKey returned error: %v", err)
+	}
+
+	want := &PublicKey{KeyID: String("1234"), Key: String("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")}
+	if !cmp.Equal(key, want) {
+		t.Errorf("Dependabot.GetRepoPublicKey returned %+v, want %+v", key, want)
+	}
+
+	const methodName = "GetRepoPublicKey"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Dependabot.GetRepoPublicKey(ctx, "\n", "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Dependabot.GetRepoPublicKey(ctx, "o", "r")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestDependabotService_ListRepoSecrets(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/dependabot/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.Dependabot.ListRepoSecrets(ctx, "o", "r", opts)
+	if err != nil {
+		t.Errorf("Dependabot.ListRepoSecrets 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("Dependabot.ListRepoSecrets returned %+v, want %+v", secrets, want)
+	}
+
+	const methodName = "ListRepoSecrets"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Dependabot.ListRepoSecrets(ctx, "\n", "\n", opts)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Dependabot.ListRepoSecrets(ctx, "o", "r", opts)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestDependabotService_GetRepoSecret(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/dependabot/secrets/NAME", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{"name":"NAME","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"}`)
+	})
+
+	ctx := context.Background()
+	secret, _, err := client.Dependabot.GetRepoSecret(ctx, "o", "r", "NAME")
+	if err != nil {
+		t.Errorf("Dependabot.GetRepoSecret returned error: %v", err)
+	}
+
+	want := &Secret{
+		Name:      "NAME",
+		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(secret, want) {
+		t.Errorf("Dependabot.GetRepoSecret returned %+v, want %+v", secret, want)
+	}
+
+	const methodName = "GetRepoSecret"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Dependabot.GetRepoSecret(ctx, "\n", "\n", "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Dependabot.GetRepoSecret(ctx, "o", "r", "NAME")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestDependabotService_CreateOrUpdateRepoSecret(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/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="}`+"\n")
+		w.WriteHeader(http.StatusCreated)
+	})
+
+	input := &EncryptedSecret{
+		Name:           "NAME",
+		EncryptedValue: "QIv=",
+		KeyID:          "1234",
+	}
+	ctx := context.Background()
+	_, err := client.Dependabot.CreateOrUpdateRepoSecret(ctx, "o", "r", input)
+	if err != nil {
+		t.Errorf("Dependabot.CreateOrUpdateRepoSecret returned error: %v", err)
+	}
+
+	const methodName = "CreateOrUpdateRepoSecret"
+	testBadOptions(t, methodName, func() (err error) {
+		_, err = client.Dependabot.CreateOrUpdateRepoSecret(ctx, "\n", "\n", input)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Dependabot.CreateOrUpdateRepoSecret(ctx, "o", "r", input)
+	})
+}
+
+func TestDependabotService_DeleteRepoSecret(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/dependabot/secrets/NAME", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+	})
+
+	ctx := context.Background()
+	_, err := client.Dependabot.DeleteRepoSecret(ctx, "o", "r", "NAME")
+	if err != nil {
+		t.Errorf("Dependabot.DeleteRepoSecret returned error: %v", err)
+	}
+
+	const methodName = "DeleteRepoSecret"
+	testBadOptions(t, methodName, func() (err error) {
+		_, err = client.Dependabot.DeleteRepoSecret(ctx, "\n", "\n", "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Dependabot.DeleteRepoSecret(ctx, "o", "r", "NAME")
+	})
+}
+
+func TestDependabotService_GetOrgPublicKey(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/orgs/o/dependabot/secrets/public-key", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{"key_id":"012345678","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`)
+	})
+
+	ctx := context.Background()
+	key, _, err := client.Dependabot.GetOrgPublicKey(ctx, "o")
+	if err != nil {
+		t.Errorf("Dependabot.GetOrgPublicKey returned error: %v", err)
+	}
+
+	want := &PublicKey{KeyID: String("012345678"), Key: String("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")}
+	if !cmp.Equal(key, want) {
+		t.Errorf("Dependabot.GetOrgPublicKey returned %+v, want %+v", key, want)
+	}
+
+	const methodName = "GetOrgPublicKey"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Dependabot.GetOrgPublicKey(ctx, "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Dependabot.GetOrgPublicKey(ctx, "o")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestDependabotService_ListOrgSecrets(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/orgs/o/dependabot/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":3,"secrets":[{"name":"GIST_ID","created_at":"2019-08-10T14:59:22Z","updated_at":"2020-01-10T14:59:22Z","visibility":"private"},{"name":"DEPLOY_TOKEN","created_at":"2019-08-10T14:59:22Z","updated_at":"2020-01-10T14:59:22Z","visibility":"all"},{"name":"GH_TOKEN","created_at":"2019-08-10T14:59:22Z","updated_at":"2020-01-10T14:59:22Z","visibility":"selected","selected_repositories_url":"https://api.github.com/orgs/octo-org/dependabot/secrets/SUPER_SECRET/repositories"}]}`)
+	})
+
+	opts := &ListOptions{Page: 2, PerPage: 2}
+	ctx := context.Background()
+	secrets, _, err := client.Dependabot.ListOrgSecrets(ctx, "o", opts)
+	if err != nil {
+		t.Errorf("Dependabot.ListOrgSecrets returned error: %v", err)
+	}
+
+	want := &Secrets{
+		TotalCount: 3,
+		Secrets: []*Secret{
+			{Name: "GIST_ID", CreatedAt: Timestamp{time.Date(2019, time.August, 10, 14, 59, 22, 0, time.UTC)}, UpdatedAt: Timestamp{time.Date(2020, time.January, 10, 14, 59, 22, 0, time.UTC)}, Visibility: "private"},
+			{Name: "DEPLOY_TOKEN", CreatedAt: Timestamp{time.Date(2019, time.August, 10, 14, 59, 22, 0, time.UTC)}, UpdatedAt: Timestamp{time.Date(2020, time.January, 10, 14, 59, 22, 0, time.UTC)}, Visibility: "all"},
+			{Name: "GH_TOKEN", CreatedAt: Timestamp{time.Date(2019, time.August, 10, 14, 59, 22, 0, time.UTC)}, UpdatedAt: Timestamp{time.Date(2020, time.January, 10, 14, 59, 22, 0, time.UTC)}, Visibility: "selected", SelectedRepositoriesURL: "https://api.github.com/orgs/octo-org/dependabot/secrets/SUPER_SECRET/repositories"},
+		},
+	}
+	if !cmp.Equal(secrets, want) {
+		t.Errorf("Dependabot.ListOrgSecrets returned %+v, want %+v", secrets, want)
+	}
+
+	const methodName = "ListOrgSecrets"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Dependabot.ListOrgSecrets(ctx, "\n", opts)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Dependabot.ListOrgSecrets(ctx, "o", opts)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestDependabotService_GetOrgSecret(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/orgs/o/dependabot/secrets/NAME", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{"name":"NAME","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z","visibility":"selected","selected_repositories_url":"https://api.github.com/orgs/octo-org/dependabot/secrets/SUPER_SECRET/repositories"}`)
+	})
+
+	ctx := context.Background()
+	secret, _, err := client.Dependabot.GetOrgSecret(ctx, "o", "NAME")
+	if err != nil {
+		t.Errorf("Dependabot.GetOrgSecret returned error: %v", err)
+	}
+
+	want := &Secret{
+		Name:                    "NAME",
+		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)},
+		Visibility:              "selected",
+		SelectedRepositoriesURL: "https://api.github.com/orgs/octo-org/dependabot/secrets/SUPER_SECRET/repositories",
+	}
+	if !cmp.Equal(secret, want) {
+		t.Errorf("Dependabot.GetOrgSecret returned %+v, want %+v", secret, want)
+	}
+
+	const methodName = "GetOrgSecret"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Dependabot.GetOrgSecret(ctx, "\n", "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Dependabot.GetOrgSecret(ctx, "o", "NAME")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestDependabotService_CreateOrUpdateOrgSecret(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	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")
+		w.WriteHeader(http.StatusCreated)
+	})
+
+	input := &EncryptedSecret{
+		Name:                  "NAME",
+		EncryptedValue:        "QIv=",
+		KeyID:                 "1234",
+		Visibility:            "selected",
+		SelectedRepositoryIDs: SelectedRepoIDs{1296269, 1269280},
+	}
+	ctx := context.Background()
+	_, err := client.Dependabot.CreateOrUpdateOrgSecret(ctx, "o", input)
+	if err != nil {
+		t.Errorf("Dependabot.CreateOrUpdateOrgSecret returned error: %v", err)
+	}
+
+	const methodName = "CreateOrUpdateOrgSecret"
+	testBadOptions(t, methodName, func() (err error) {
+		_, err = client.Dependabot.CreateOrUpdateOrgSecret(ctx, "\n", input)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Dependabot.CreateOrUpdateOrgSecret(ctx, "o", input)
+	})
+}
+
+func TestDependabotService_ListSelectedReposForOrgSecret(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/orgs/o/dependabot/secrets/NAME/repositories", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprintf(w, `{"total_count":1,"repositories":[{"id":1}]}`)
+	})
+
+	opts := &ListOptions{Page: 2, PerPage: 2}
+	ctx := context.Background()
+	repos, _, err := client.Dependabot.ListSelectedReposForOrgSecret(ctx, "o", "NAME", opts)
+	if err != nil {
+		t.Errorf("Dependabot.ListSelectedReposForOrgSecret returned error: %v", err)
+	}
+
+	want := &SelectedReposList{
+		TotalCount: Int(1),
+		Repositories: []*Repository{
+			{ID: Int64(1)},
+		},
+	}
+	if !cmp.Equal(repos, want) {
+		t.Errorf("Dependabot.ListSelectedReposForOrgSecret returned %+v, want %+v", repos, want)
+	}
+
+	const methodName = "ListSelectedReposForOrgSecret"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Dependabot.ListSelectedReposForOrgSecret(ctx, "\n", "\n", opts)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Dependabot.ListSelectedReposForOrgSecret(ctx, "o", "NAME", opts)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestDependabotService_SetSelectedReposForOrgSecret(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	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")
+	})
+
+	ctx := context.Background()
+	_, err := client.Dependabot.SetSelectedReposForOrgSecret(ctx, "o", "NAME", SelectedRepoIDs{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})
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Dependabot.SetSelectedReposForOrgSecret(ctx, "o", "NAME", SelectedRepoIDs{64780797})
+	})
+}
+
+func TestDependabotService_AddSelectedRepoToOrgSecret(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/orgs/o/dependabot/secrets/NAME/repositories/1234", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PUT")
+	})
+
+	repo := &Repository{ID: Int64(1234)}
+	ctx := context.Background()
+	_, err := client.Dependabot.AddSelectedRepoToOrgSecret(ctx, "o", "NAME", repo)
+	if err != nil {
+		t.Errorf("Dependabot.AddSelectedRepoToOrgSecret returned error: %v", err)
+	}
+
+	const methodName = "AddSelectedRepoToOrgSecret"
+	testBadOptions(t, methodName, func() (err error) {
+		_, err = client.Dependabot.AddSelectedRepoToOrgSecret(ctx, "\n", "\n", repo)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Dependabot.AddSelectedRepoToOrgSecret(ctx, "o", "NAME", repo)
+	})
+}
+
+func TestDependabotService_RemoveSelectedRepoFromOrgSecret(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/orgs/o/dependabot/secrets/NAME/repositories/1234", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+	})
+
+	repo := &Repository{ID: Int64(1234)}
+	ctx := context.Background()
+	_, err := client.Dependabot.RemoveSelectedRepoFromOrgSecret(ctx, "o", "NAME", repo)
+	if err != nil {
+		t.Errorf("Dependabot.RemoveSelectedRepoFromOrgSecret returned error: %v", err)
+	}
+
+	const methodName = "RemoveSelectedRepoFromOrgSecret"
+	testBadOptions(t, methodName, func() (err error) {
+		_, err = client.Dependabot.RemoveSelectedRepoFromOrgSecret(ctx, "\n", "\n", repo)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Dependabot.RemoveSelectedRepoFromOrgSecret(ctx, "o", "NAME", repo)
+	})
+}
+
+func TestDependabotService_DeleteOrgSecret(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/orgs/o/dependabot/secrets/NAME", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+	})
+
+	ctx := context.Background()
+	_, err := client.Dependabot.DeleteOrgSecret(ctx, "o", "NAME")
+	if err != nil {
+		t.Errorf("Dependabot.DeleteOrgSecret returned error: %v", err)
+	}
+
+	const methodName = "DeleteOrgSecret"
+	testBadOptions(t, methodName, func() (err error) {
+		_, err = client.Dependabot.DeleteOrgSecret(ctx, "\n", "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		return client.Dependabot.DeleteOrgSecret(ctx, "o", "NAME")
+	})
+}
diff --git a/github/users_packages_test.go b/github/users_packages_test.go
index 41e7698a..a0877593 100644
--- a/github/users_packages_test.go
+++ b/github/users_packages_test.go
@@ -363,7 +363,10 @@ func TestUsersService_Authenticated_ListPackagesVersions(t *testing.T) {
 	})
 
 	ctx := context.Background()
-	packages, _, err := client.Users.PackageGetAllVersions(ctx, "", "container", "hello_docker")
+	opts := &PackageListOptions{
+		String("internal"), String("container"), String("deleted"), ListOptions{Page: 1, PerPage: 2},
+	}
+	packages, _, err := client.Users.PackageGetAllVersions(ctx, "", "container", "hello_docker", opts)
 	if err != nil {
 		t.Errorf("Users.Authenticated_PackageGetAllVersions returned error: %v", err)
 	}
@@ -389,12 +392,12 @@ func TestUsersService_Authenticated_ListPackagesVersions(t *testing.T) {
 
 	const methodName = "PackageGetAllVersions"
 	testBadOptions(t, methodName, func() (err error) {
-		_, _, err = client.Users.PackageGetAllVersions(ctx, "\n", "", "")
+		_, _, err = client.Users.PackageGetAllVersions(ctx, "\n", "", "", &PackageListOptions{})
 		return err
 	})
 
 	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
-		got, resp, err := client.Users.PackageGetAllVersions(ctx, "", "", "")
+		got, resp, err := client.Users.PackageGetAllVersions(ctx, "", "", "", &PackageListOptions{})
 		if got != nil {
 			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
 		}
@@ -429,7 +432,10 @@ func TestUsersService_specifiedUser_ListPackagesVersions(t *testing.T) {
 	})
 
 	ctx := context.Background()
-	packages, _, err := client.Users.PackageGetAllVersions(ctx, "u", "container", "hello_docker")
+	opts := &PackageListOptions{
+		String("internal"), String("container"), String("deleted"), ListOptions{Page: 1, PerPage: 2},
+	}
+	packages, _, err := client.Users.PackageGetAllVersions(ctx, "u", "container", "hello_docker", opts)
 	if err != nil {
 		t.Errorf("Users.specifiedUser_PackageGetAllVersions returned error: %v", err)
 	}
@@ -455,12 +461,12 @@ func TestUsersService_specifiedUser_ListPackagesVersions(t *testing.T) {
 
 	const methodName = "PackageGetAllVersions"
 	testBadOptions(t, methodName, func() (err error) {
-		_, _, err = client.Users.PackageGetAllVersions(ctx, "\n", "", "")
+		_, _, err = client.Users.PackageGetAllVersions(ctx, "\n", "", "", &PackageListOptions{})
 		return err
 	})
 
 	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
-		got, resp, err := client.Users.PackageGetAllVersions(ctx, "", "", "")
+		got, resp, err := client.Users.PackageGetAllVersions(ctx, "", "", "", &PackageListOptions{})
 		if got != nil {
 			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
 		}