cli/pkg/cmd/secret/delete/delete.go
Andy Feller c088951944 Fix host handling in variable and secret delete
This change updates `gh variable delete` and `gh secret delete` to use the host associated with the repository rather than the default host.  Along with these changes is minor refactoring of the tests to ensure appropriate Dotcom vs GHES targeting works as expected.

Minor edit to `gh variable get` testing to simplify some conditional logic in test setup.
2024-08-02 15:11:22 -04:00

155 lines
4.5 KiB
Go

package delete
import (
"fmt"
"net/http"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/gh"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/pkg/cmd/secret/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/spf13/cobra"
)
type DeleteOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams
Config func() (gh.Config, error)
BaseRepo func() (ghrepo.Interface, error)
SecretName string
OrgName string
EnvName string
UserSecrets bool
Application string
}
func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Command {
opts := &DeleteOptions{
IO: f.IOStreams,
Config: f.Config,
HttpClient: f.HttpClient,
}
cmd := &cobra.Command{
Use: "delete <secret-name>",
Short: "Delete secrets",
Long: heredoc.Doc(`
Delete a secret on one of the following levels:
- repository (default): available to GitHub Actions runs or Dependabot in a repository
- environment: available to GitHub Actions runs for a deployment environment in a repository
- organization: available to GitHub Actions runs, Dependabot, or Codespaces within an organization
- user: available to Codespaces for your user
`),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
// support `-R, --repo` override
opts.BaseRepo = f.BaseRepo
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]
if runF != nil {
return runF(opts)
}
return removeRun(opts)
},
Aliases: []string{
"remove",
},
}
cmd.Flags().StringVarP(&opts.OrgName, "org", "o", "", "Delete a secret for an organization")
cmd.Flags().StringVarP(&opts.EnvName, "env", "e", "", "Delete a secret for an environment")
cmd.Flags().BoolVarP(&opts.UserSecrets, "user", "u", false, "Delete a secret for your user")
cmdutil.StringEnumFlag(cmd, &opts.Application, "app", "a", "", []string{shared.Actions, shared.Codespaces, shared.Dependabot}, "Delete a secret for a specific application")
return cmd
}
func removeRun(opts *DeleteOptions) error {
c, err := opts.HttpClient()
if err != nil {
return fmt.Errorf("could not create http client: %w", err)
}
client := api.NewClientFromHTTP(c)
orgName := opts.OrgName
envName := opts.EnvName
secretEntity, err := shared.GetSecretEntity(orgName, envName, opts.UserSecrets)
if err != nil {
return err
}
secretApp, err := shared.GetSecretApp(opts.Application, secretEntity)
if err != nil {
return err
}
if !shared.IsSupportedSecretEntity(secretApp, secretEntity) {
return fmt.Errorf("%s secrets are not supported for %s", secretEntity, secretApp)
}
var baseRepo ghrepo.Interface
if secretEntity == shared.Repository || secretEntity == shared.Environment {
baseRepo, err = opts.BaseRepo()
if err != nil {
return err
}
}
cfg, err := opts.Config()
if err != nil {
return err
}
var path string
var host string
switch secretEntity {
case shared.Organization:
path = fmt.Sprintf("orgs/%s/%s/secrets/%s", orgName, secretApp, opts.SecretName)
host, _ = cfg.Authentication().DefaultHost()
case shared.Environment:
path = fmt.Sprintf("repos/%s/environments/%s/secrets/%s", ghrepo.FullName(baseRepo), envName, opts.SecretName)
host = baseRepo.RepoHost()
case shared.User:
path = fmt.Sprintf("user/codespaces/secrets/%s", opts.SecretName)
host, _ = cfg.Authentication().DefaultHost()
case shared.Repository:
path = fmt.Sprintf("repos/%s/%s/secrets/%s", ghrepo.FullName(baseRepo), secretApp, opts.SecretName)
host = baseRepo.RepoHost()
}
err = client.REST(host, "DELETE", path, nil, nil)
if err != nil {
return fmt.Errorf("failed to delete secret %s: %w", opts.SecretName, err)
}
if opts.IO.IsStdoutTTY() {
var target string
switch secretEntity {
case shared.Organization:
target = orgName
case shared.User:
target = "your user"
case shared.Repository, shared.Environment:
target = ghrepo.FullName(baseRepo)
}
cs := opts.IO.ColorScheme()
if envName != "" {
fmt.Fprintf(opts.IO.Out, "%s Deleted secret %s from %s environment on %s\n", cs.SuccessIconWithColor(cs.Red), opts.SecretName, envName, target)
} else {
fmt.Fprintf(opts.IO.Out, "%s Deleted %s secret %s from %s\n", cs.SuccessIconWithColor(cs.Red), secretApp.Title(), opts.SecretName, target)
}
}
return nil
}