Support setting user Codespaces secrets (#4699)
Co-authored-by: Mislav Marohnić <mislav@github.com>
This commit is contained in:
parent
577f29ae0d
commit
e82958b4e0
5 changed files with 208 additions and 88 deletions
|
|
@ -53,6 +53,12 @@ func SetDefaultHost(host string) {
|
|||
// FromFullName extracts the GitHub repository information from the following
|
||||
// formats: "OWNER/REPO", "HOST/OWNER/REPO", and a full URL.
|
||||
func FromFullName(nwo string) (Interface, error) {
|
||||
return FromFullNameWithHost(nwo, defaultHost())
|
||||
}
|
||||
|
||||
// FromFullNameWithHost is like FromFullName that defaults to a specific host for values that don't
|
||||
// explicitly include a hostname.
|
||||
func FromFullNameWithHost(nwo, fallbackHost string) (Interface, error) {
|
||||
if git.IsURL(nwo) {
|
||||
u, err := git.ParseURL(nwo)
|
||||
if err != nil {
|
||||
|
|
@ -71,7 +77,7 @@ func FromFullName(nwo string) (Interface, error) {
|
|||
case 3:
|
||||
return NewWithHost(parts[1], parts[2], parts[0]), nil
|
||||
case 2:
|
||||
return NewWithHost(parts[0], parts[1], defaultHost()), nil
|
||||
return NewWithHost(parts[0], parts[1], fallbackHost), nil
|
||||
default:
|
||||
return nil, fmt.Errorf(`expected the "[HOST/]OWNER/REPO" format, got %q`, nwo)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,9 @@ func NewCmdSecret(f *cmdutil.Factory) *cobra.Command {
|
|||
Short: "Manage GitHub secrets",
|
||||
Long: heredoc.Doc(`
|
||||
Secrets can be set at the repository, environment, or organization level for use in
|
||||
GitHub Actions. Run "gh help secret set" to learn how to get started.
|
||||
`),
|
||||
Annotations: map[string]string{
|
||||
"IsActions": "true",
|
||||
},
|
||||
GitHub Actions. User secrets can be set for use in GitHub Codespaces.
|
||||
Run "gh help secret set" to learn how to get started.
|
||||
`),
|
||||
}
|
||||
|
||||
cmdutil.EnableRepoOverride(cmd, f)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cli/cli/v2/api"
|
||||
|
|
@ -20,6 +21,13 @@ type SecretPayload struct {
|
|||
KeyID string `json:"key_id"`
|
||||
}
|
||||
|
||||
// The Codespaces Secret API currently expects repositories IDs as strings
|
||||
type CodespacesSecretPayload struct {
|
||||
EncryptedValue string `json:"encrypted_value"`
|
||||
Repositories []string `json:"selected_repository_ids,omitempty"`
|
||||
KeyID string `json:"key_id"`
|
||||
}
|
||||
|
||||
type PubKey struct {
|
||||
Raw [32]byte
|
||||
ID string `json:"key_id"`
|
||||
|
|
@ -50,6 +58,10 @@ func getOrgPublicKey(client *api.Client, host, orgName string) (*PubKey, error)
|
|||
return getPubKey(client, host, fmt.Sprintf("orgs/%s/actions/secrets/public-key", orgName))
|
||||
}
|
||||
|
||||
func getUserPublicKey(client *api.Client, host string) (*PubKey, error) {
|
||||
return getPubKey(client, host, "user/codespaces/secrets/public-key")
|
||||
}
|
||||
|
||||
func getRepoPubKey(client *api.Client, repo ghrepo.Interface) (*PubKey, error) {
|
||||
return getPubKey(client, repo.RepoHost(), fmt.Sprintf("repos/%s/actions/secrets/public-key",
|
||||
ghrepo.FullName(repo)))
|
||||
|
|
@ -60,7 +72,7 @@ func getEnvPubKey(client *api.Client, repo ghrepo.Interface, envName string) (*P
|
|||
ghrepo.FullName(repo), envName))
|
||||
}
|
||||
|
||||
func putSecret(client *api.Client, host, path string, payload SecretPayload) error {
|
||||
func putSecret(client *api.Client, host, path string, payload interface{}) error {
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize: %w", err)
|
||||
|
|
@ -78,7 +90,20 @@ func putOrgSecret(client *api.Client, host string, pk *PubKey, opts SetOptions,
|
|||
var repositoryIDs []int
|
||||
var err error
|
||||
if orgName != "" && visibility == shared.Selected {
|
||||
repositoryIDs, err = mapRepoNameToID(client, host, orgName, opts.RepositoryNames)
|
||||
repos := make([]ghrepo.Interface, 0, len(opts.RepositoryNames))
|
||||
for _, repositoryName := range opts.RepositoryNames {
|
||||
var repo ghrepo.Interface
|
||||
if strings.Contains(repositoryName, "/") {
|
||||
repo, err = ghrepo.FromFullNameWithHost(repositoryName, host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid repository name: %w", err)
|
||||
}
|
||||
} else {
|
||||
repo = ghrepo.NewWithHost(opts.OrgName, repositoryName, host)
|
||||
}
|
||||
repos = append(repos, repo)
|
||||
}
|
||||
repositoryIDs, err = mapRepoToID(client, host, repos)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to look up IDs for repositories %v: %w", opts.RepositoryNames, err)
|
||||
}
|
||||
|
|
@ -95,6 +120,38 @@ func putOrgSecret(client *api.Client, host string, pk *PubKey, opts SetOptions,
|
|||
return putSecret(client, host, path, payload)
|
||||
}
|
||||
|
||||
func putUserSecret(client *api.Client, host string, pk *PubKey, opts SetOptions, eValue string) error {
|
||||
payload := CodespacesSecretPayload{
|
||||
EncryptedValue: eValue,
|
||||
KeyID: pk.ID,
|
||||
}
|
||||
|
||||
if len(opts.RepositoryNames) > 0 {
|
||||
repos := make([]ghrepo.Interface, len(opts.RepositoryNames))
|
||||
for i, repo := range opts.RepositoryNames {
|
||||
// For user secrets, repository names should be fully qualifed (e.g. "owner/repo")
|
||||
repoNWO, err := ghrepo.FromFullNameWithHost(repo, host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repos[i] = repoNWO
|
||||
}
|
||||
|
||||
repositoryIDs, err := mapRepoToID(client, host, repos)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to look up repository IDs: %w", err)
|
||||
}
|
||||
repositoryStringIDs := make([]string, len(repositoryIDs))
|
||||
for i, id := range repositoryIDs {
|
||||
repositoryStringIDs[i] = strconv.Itoa(id)
|
||||
}
|
||||
payload.Repositories = repositoryStringIDs
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("user/codespaces/secrets/%s", opts.SecretName)
|
||||
return putSecret(client, host, path, payload)
|
||||
}
|
||||
|
||||
func putEnvSecret(client *api.Client, pk *PubKey, repo ghrepo.Interface, envName string, secretName, eValue string) error {
|
||||
payload := SecretPayload{
|
||||
EncryptedValue: eValue,
|
||||
|
|
@ -114,14 +171,14 @@ func putRepoSecret(client *api.Client, pk *PubKey, repo ghrepo.Interface, secret
|
|||
}
|
||||
|
||||
// This does similar logic to `api.RepoNetwork`, but without the overfetching.
|
||||
func mapRepoNameToID(client *api.Client, host, orgName string, repositoryNames []string) ([]int, error) {
|
||||
queries := make([]string, 0, len(repositoryNames))
|
||||
for i, repoName := range repositoryNames {
|
||||
func mapRepoToID(client *api.Client, host string, repositories []ghrepo.Interface) ([]int, error) {
|
||||
queries := make([]string, 0, len(repositories))
|
||||
for i, repo := range repositories {
|
||||
queries = append(queries, fmt.Sprintf(`
|
||||
repo_%03d: repository(owner: %q, name: %q) {
|
||||
databaseId
|
||||
}
|
||||
`, i, orgName, repoName))
|
||||
`, i, repo.RepoOwner(), repo.RepoName()))
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`query MapRepositoryNames { %s }`, strings.Join(queries, ""))
|
||||
|
|
@ -134,13 +191,13 @@ func mapRepoNameToID(client *api.Client, host, orgName string, repositoryNames [
|
|||
return nil, fmt.Errorf("failed to look up repositories: %w", err)
|
||||
}
|
||||
|
||||
repoKeys := make([]string, 0, len(repositoryNames))
|
||||
repoKeys := make([]string, 0, len(repositories))
|
||||
for k := range graphqlResult {
|
||||
repoKeys = append(repoKeys, k)
|
||||
}
|
||||
sort.Strings(repoKeys)
|
||||
|
||||
result := make([]int, len(repositoryNames))
|
||||
result := make([]int, len(repositories))
|
||||
for i, k := range repoKeys {
|
||||
result[i] = graphqlResult[k].DatabaseID
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,10 @@ package set
|
|||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
|
|
@ -34,6 +31,7 @@ type SetOptions struct {
|
|||
SecretName string
|
||||
OrgName string
|
||||
EnvName string
|
||||
UserSecrets bool
|
||||
Body string
|
||||
Visibility string
|
||||
RepositoryNames []string
|
||||
|
|
@ -49,25 +47,39 @@ func NewCmdSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command
|
|||
cmd := &cobra.Command{
|
||||
Use: "set <secret-name>",
|
||||
Short: "Create or update secrets",
|
||||
Long: "Locally encrypt a new or updated secret at either the repository, environment, or organization level and send it to GitHub for storage.",
|
||||
Long: heredoc.Doc(`
|
||||
Set a value for a secret on one of the following levels:
|
||||
- repository (default): available to Actions runs in a repository
|
||||
- environment: available to Actions runs for a deployment environment in a repository
|
||||
- organization: available to Actions runs within an organization
|
||||
- user: available to Codespaces for your user
|
||||
|
||||
Organization and user secrets can optionally be restricted to only be available to
|
||||
specific repositories.
|
||||
|
||||
Secret values are locally encrypted before being sent to GitHub.
|
||||
`),
|
||||
Example: heredoc.Doc(`
|
||||
Paste secret in prompt
|
||||
# Paste secret value for the current repository in an interactive prompt
|
||||
$ gh secret set MYSECRET
|
||||
|
||||
Use environment variable as secret value
|
||||
$ gh secret set MYSECRET -b"${ENV_VALUE}"
|
||||
# Read secret value from an environment variable
|
||||
$ gh secret set MYSECRET --body "$ENV_VALUE"
|
||||
|
||||
Use file as secret value
|
||||
$ gh secret set MYSECRET < file.json
|
||||
# Read secret value from a file
|
||||
$ gh secret set MYSECRET < myfile.txt
|
||||
|
||||
Set environment level secret
|
||||
$ gh secret set MYSECRET -bval --env=anEnv
|
||||
# Set secret for a deployment environment in the current repository
|
||||
$ gh secret set MYSECRET --env myenvironment
|
||||
|
||||
Set organization level secret visible to entire organization
|
||||
$ gh secret set MYSECRET -bval --org=anOrg --visibility=all
|
||||
# Set organization-level secret visible to both public and private repositories
|
||||
$ gh secret set MYSECRET --org myOrg --visibility all
|
||||
|
||||
Set organization level secret visible only to certain repositories
|
||||
$ gh secret set MYSECRET -bval --org=anOrg --repos="repo1,repo2,repo3"
|
||||
# Set organization-level secret visible to specific repositories
|
||||
$ gh secret set MYSECRET --org myOrg --repos repo1,repo2,repo3
|
||||
|
||||
# Set user-level secret for Codespaces
|
||||
$ gh secret set MYSECRET --user
|
||||
`),
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
|
|
@ -79,35 +91,30 @@ func NewCmdSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command
|
|||
// support `-R, --repo` override
|
||||
opts.BaseRepo = f.BaseRepo
|
||||
|
||||
if err := cmdutil.MutuallyExclusive("specify only one of `--org` or `--env`", opts.OrgName != "", opts.EnvName != ""); err != nil {
|
||||
if err := cmdutil.MutuallyExclusive("specify only one of `--org`, `--env`, or `--user`", opts.OrgName != "", opts.EnvName != "", opts.UserSecrets); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts.SecretName = args[0]
|
||||
|
||||
err := validSecretName(opts.SecretName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("visibility") {
|
||||
if opts.OrgName == "" {
|
||||
return cmdutil.FlagErrorf("--visibility not supported for repository secrets; did you mean to pass --org?")
|
||||
return cmdutil.FlagErrorf("`--visibility` is only supported with `--org`")
|
||||
}
|
||||
|
||||
if opts.Visibility != shared.All && opts.Visibility != shared.Private && opts.Visibility != shared.Selected {
|
||||
return cmdutil.FlagErrorf("--visibility must be one of `all`, `private`, or `selected`")
|
||||
return cmdutil.FlagErrorf("`--visibility` must be one of \"all\", \"private\", or \"selected\"")
|
||||
}
|
||||
|
||||
if opts.Visibility != shared.Selected && cmd.Flags().Changed("repos") {
|
||||
return cmdutil.FlagErrorf("--repos only supported when --visibility='selected'")
|
||||
if opts.Visibility != shared.Selected && len(opts.RepositoryNames) > 0 {
|
||||
return cmdutil.FlagErrorf("`--repos` is only supported with `--visibility=selected`")
|
||||
}
|
||||
|
||||
if opts.Visibility == shared.Selected && !cmd.Flags().Changed("repos") {
|
||||
return cmdutil.FlagErrorf("--repos flag required when --visibility='selected'")
|
||||
if opts.Visibility == shared.Selected && len(opts.RepositoryNames) == 0 {
|
||||
return cmdutil.FlagErrorf("`--repos` list required with `--visibility=selected`")
|
||||
}
|
||||
} else {
|
||||
if cmd.Flags().Changed("repos") {
|
||||
if len(opts.RepositoryNames) > 0 {
|
||||
opts.Visibility = shared.Selected
|
||||
}
|
||||
}
|
||||
|
|
@ -119,11 +126,13 @@ func NewCmdSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command
|
|||
return setRun(opts)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVarP(&opts.OrgName, "org", "o", "", "Set a secret for an organization")
|
||||
cmd.Flags().StringVarP(&opts.EnvName, "env", "e", "", "Set a secret for an environment")
|
||||
cmd.Flags().StringVarP(&opts.Visibility, "visibility", "v", "private", "Set visibility for an organization secret: `all`, `private`, or `selected`")
|
||||
cmd.Flags().StringSliceVarP(&opts.RepositoryNames, "repos", "r", []string{}, "List of repository names for `selected` visibility")
|
||||
cmd.Flags().StringVarP(&opts.Body, "body", "b", "", "A value for the secret. Reads from STDIN if not specified.")
|
||||
|
||||
cmd.Flags().StringVarP(&opts.OrgName, "org", "o", "", "Set `organization` secret")
|
||||
cmd.Flags().StringVarP(&opts.EnvName, "env", "e", "", "Set deployment `environment` secret")
|
||||
cmd.Flags().BoolVarP(&opts.UserSecrets, "user", "u", false, "Set a secret for your user")
|
||||
cmd.Flags().StringVarP(&opts.Visibility, "visibility", "v", "private", "Set visibility for an organization secret: `{all|private|selected}`")
|
||||
cmd.Flags().StringSliceVarP(&opts.RepositoryNames, "repos", "r", []string{}, "List of `repositories` that can access an organization or user secret")
|
||||
cmd.Flags().StringVarP(&opts.Body, "body", "b", "", "The value for the secret (reads from standard input if not specified)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
@ -144,7 +153,7 @@ func setRun(opts *SetOptions) error {
|
|||
envName := opts.EnvName
|
||||
|
||||
var baseRepo ghrepo.Interface
|
||||
if orgName == "" {
|
||||
if orgName == "" && !opts.UserSecrets {
|
||||
baseRepo, err = opts.BaseRepo()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not determine base repo: %w", err)
|
||||
|
|
@ -166,6 +175,8 @@ func setRun(opts *SetOptions) error {
|
|||
pk, err = getOrgPublicKey(client, host, orgName)
|
||||
} else if envName != "" {
|
||||
pk, err = getEnvPubKey(client, baseRepo, envName)
|
||||
} else if opts.UserSecrets {
|
||||
pk, err = getUserPublicKey(client, host)
|
||||
} else {
|
||||
pk, err = getRepoPubKey(client, baseRepo)
|
||||
}
|
||||
|
|
@ -184,6 +195,8 @@ func setRun(opts *SetOptions) error {
|
|||
err = putOrgSecret(client, host, pk, *opts, encoded)
|
||||
} else if envName != "" {
|
||||
err = putEnvSecret(client, pk, baseRepo, envName, opts.SecretName, encoded)
|
||||
} else if opts.UserSecrets {
|
||||
err = putUserSecret(client, host, pk, *opts, encoded)
|
||||
} else {
|
||||
err = putRepoSecret(client, pk, baseRepo, opts.SecretName, encoded)
|
||||
}
|
||||
|
|
@ -193,7 +206,9 @@ func setRun(opts *SetOptions) error {
|
|||
|
||||
if opts.IO.IsStdoutTTY() {
|
||||
target := orgName
|
||||
if orgName == "" {
|
||||
if opts.UserSecrets {
|
||||
target = "your user"
|
||||
} else if orgName == "" {
|
||||
target = ghrepo.FullName(baseRepo)
|
||||
}
|
||||
cs := opts.IO.ColorScheme()
|
||||
|
|
@ -203,28 +218,6 @@ func setRun(opts *SetOptions) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func validSecretName(name string) error {
|
||||
if name == "" {
|
||||
return errors.New("secret name cannot be blank")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(name, "GITHUB_") {
|
||||
return errors.New("secret name cannot begin with GITHUB_")
|
||||
}
|
||||
|
||||
leadingNumber := regexp.MustCompile(`^[0-9]`)
|
||||
if leadingNumber.MatchString(name) {
|
||||
return errors.New("secret name cannot start with a number")
|
||||
}
|
||||
|
||||
validChars := regexp.MustCompile(`^([0-9]|[a-z]|[A-Z]|_)+$`)
|
||||
if !validChars.MatchString(name) {
|
||||
return errors.New("secret name can only contain letters, numbers, and _")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getBody(opts *SetOptions) ([]byte, error) {
|
||||
if opts.Body == "" {
|
||||
if opts.IO.CanPrompt() {
|
||||
|
|
|
|||
|
|
@ -90,6 +90,16 @@ func TestNewCmdSet(t *testing.T) {
|
|||
OrgName: "coolOrg",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user with selected repos",
|
||||
cli: `-u -bs -r"monalisa/coolRepo,cli/cli,github/hub" cool_secret`,
|
||||
wants: SetOptions{
|
||||
SecretName: "cool_secret",
|
||||
Visibility: shared.Selected,
|
||||
RepositoryNames: []string{"monalisa/coolRepo", "cli/cli", "github/hub"},
|
||||
Body: "s",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "repo",
|
||||
cli: `cool_secret -b"a secret"`,
|
||||
|
|
@ -121,21 +131,6 @@ func TestNewCmdSet(t *testing.T) {
|
|||
OrgName: "coolOrg",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad name prefix",
|
||||
cli: `GITHUB_SECRET -b"cool"`,
|
||||
wantsErr: true,
|
||||
},
|
||||
{
|
||||
name: "leading numbers in name",
|
||||
cli: `123_SECRET -b"cool"`,
|
||||
wantsErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid characters in name",
|
||||
cli: `BAD-SECRET -b"cool"`,
|
||||
wantsErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -275,7 +270,7 @@ func Test_setRun_org(t *testing.T) {
|
|||
opts: &SetOptions{
|
||||
OrgName: "UmbrellaCorporation",
|
||||
Visibility: shared.Selected,
|
||||
RepositoryNames: []string{"birkin", "wesker"},
|
||||
RepositoryNames: []string{"birkin", "UmbrellaCorporation/wesker"},
|
||||
},
|
||||
wantRepositories: []int{1, 2},
|
||||
},
|
||||
|
|
@ -297,7 +292,7 @@ func Test_setRun_org(t *testing.T) {
|
|||
|
||||
if len(tt.opts.RepositoryNames) > 0 {
|
||||
reg.Register(httpmock.GraphQL(`query MapRepositoryNames\b`),
|
||||
httpmock.StringResponse(`{"data":{"birkin":{"databaseId":1},"wesker":{"databaseId":2}}}`))
|
||||
httpmock.StringResponse(`{"data":{"repo_0001":{"databaseId":1},"repo_0002":{"databaseId":2}}}`))
|
||||
}
|
||||
|
||||
io, _, _, _ := iostreams.Test()
|
||||
|
|
@ -335,6 +330,77 @@ func Test_setRun_org(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func Test_setRun_user(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
opts *SetOptions
|
||||
wantVisibility shared.Visibility
|
||||
wantRepositories []string
|
||||
}{
|
||||
{
|
||||
name: "all vis",
|
||||
opts: &SetOptions{
|
||||
UserSecrets: true,
|
||||
Visibility: shared.All,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "selected visibility",
|
||||
opts: &SetOptions{
|
||||
UserSecrets: true,
|
||||
Visibility: shared.Selected,
|
||||
RepositoryNames: []string{"cli/cli", "github/hub"},
|
||||
},
|
||||
wantRepositories: []string{"212613049", "401025"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reg := &httpmock.Registry{}
|
||||
|
||||
reg.Register(httpmock.REST("GET", "user/codespaces/secrets/public-key"),
|
||||
httpmock.JSONResponse(PubKey{ID: "123", Key: "CDjXqf7AJBXWhMczcy+Fs7JlACEptgceysutztHaFQI="}))
|
||||
|
||||
reg.Register(httpmock.REST("PUT", "user/codespaces/secrets/cool_secret"),
|
||||
httpmock.StatusStringResponse(201, `{}`))
|
||||
|
||||
if len(tt.opts.RepositoryNames) > 0 {
|
||||
reg.Register(httpmock.GraphQL(`query MapRepositoryNames\b`),
|
||||
httpmock.StringResponse(`{"data":{"repo_0001":{"databaseId":212613049},"repo_0002":{"databaseId":401025}}}`))
|
||||
}
|
||||
|
||||
io, _, _, _ := iostreams.Test()
|
||||
|
||||
tt.opts.HttpClient = func() (*http.Client, error) {
|
||||
return &http.Client{Transport: reg}, nil
|
||||
}
|
||||
tt.opts.Config = func() (config.Config, error) {
|
||||
return config.NewBlankConfig(), nil
|
||||
}
|
||||
tt.opts.IO = io
|
||||
tt.opts.SecretName = "cool_secret"
|
||||
tt.opts.Body = "a secret"
|
||||
// Cribbed from https://github.com/golang/crypto/commit/becbf705a91575484002d598f87d74f0002801e7
|
||||
tt.opts.RandomOverride = bytes.NewReader([]byte{5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5})
|
||||
|
||||
err := setRun(tt.opts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
reg.Verify(t)
|
||||
|
||||
data, err := ioutil.ReadAll(reg.Requests[len(reg.Requests)-1].Body)
|
||||
assert.NoError(t, err)
|
||||
var payload CodespacesSecretPayload
|
||||
err = json.Unmarshal(data, &payload)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, payload.KeyID, "123")
|
||||
assert.Equal(t, payload.EncryptedValue, "UKYUCbHd0DJemxa3AOcZ6XcsBwALG9d4bpB8ZT0gSV39vl3BHiGSgj8zJapDxgB2BwqNqRhpjC4=")
|
||||
assert.ElementsMatch(t, payload.Repositories, tt.wantRepositories)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getBody(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue