Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
28 changes: 23 additions & 5 deletions 28 pkg/cmd/cache/delete/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ type DeleteOptions struct {
HttpClient func() (*http.Client, error)
IO *iostreams.IOStreams

DeleteAll bool
Identifier string
DeleteAll bool
SucceedOnNoCaches bool
Identifier string
}

func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Command {
Expand All @@ -33,7 +34,7 @@ func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co
}

cmd := &cobra.Command{
Use: "delete [<cache-id>| <cache-key> | --all]",
Use: "delete [<cache-id> | <cache-key> | --all]",
Short: "Delete GitHub Actions caches",
Long: heredoc.Docf(`
Delete GitHub Actions caches.
Expand All @@ -50,8 +51,11 @@ func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co
# Delete a cache by id in a specific repo
$ gh cache delete 1234 --repo cli/cli

# Delete all caches
# Delete all caches (exit code 1 on no caches)
$ gh cache delete --all

# Delete all caches (exit code 0 on no caches)
$ gh cache delete --all --succeed-on-no-caches
`),
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -65,6 +69,10 @@ func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co
return err
}

if !opts.DeleteAll && opts.SucceedOnNoCaches {
return cmdutil.FlagErrorf("--succeed-on-no-caches must be used in conjunction with --all")
}

if !opts.DeleteAll && len(args) == 0 {
return cmdutil.FlagErrorf("must provide either cache id, cache key, or use --all")
}
Expand All @@ -82,6 +90,7 @@ func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co
}

cmd.Flags().BoolVarP(&opts.DeleteAll, "all", "a", false, "Delete all caches")
cmd.Flags().BoolVar(&opts.SucceedOnNoCaches, "succeed-on-no-caches", false, "Return exit code 0 if no caches found. Must be used in conjunction with `--all`")

return cmd
}
Expand All @@ -100,12 +109,21 @@ func deleteRun(opts *DeleteOptions) error {

var toDelete []string
if opts.DeleteAll {
opts.IO.StartProgressIndicator()
caches, err := shared.GetCaches(client, repo, shared.GetCachesOptions{Limit: -1})
opts.IO.StopProgressIndicator()
Comment thread
jtmcg marked this conversation as resolved.
if err != nil {
return err
}
if len(caches.ActionsCaches) == 0 {
return fmt.Errorf("%s No caches to delete", opts.IO.ColorScheme().FailureIcon())
if opts.SucceedOnNoCaches {
if opts.IO.IsStdoutTTY() {
fmt.Fprintf(opts.IO.Out, "%s No caches to delete\n", opts.IO.ColorScheme().SuccessIcon())
}
return nil
} else {
return fmt.Errorf("%s No caches to delete", opts.IO.ColorScheme().FailureIcon())
}
}
for _, cache := range caches.ActionsCaches {
toDelete = append(toDelete, strconv.Itoa(cache.Id))
Expand Down
77 changes: 77 additions & 0 deletions 77 pkg/cmd/cache/delete/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ func TestNewCmdDelete(t *testing.T) {
cli: "--all",
wants: DeleteOptions{DeleteAll: true},
},
{
name: "delete all and succeed-on-no-caches flags",
cli: "--all --succeed-on-no-caches",
wants: DeleteOptions{DeleteAll: true, SucceedOnNoCaches: true},
},
{
name: "succeed-on-no-caches flag",
cli: "--succeed-on-no-caches",
wantsErr: "--succeed-on-no-caches must be used in conjunction with --all",
},
{
name: "succeed-on-no-caches flag and id argument",
cli: "--succeed-on-no-caches 123",
wantsErr: "--succeed-on-no-caches must be used in conjunction with --all",
},
{
name: "id argument and delete all flag",
cli: "1 --all",
Expand Down Expand Up @@ -72,6 +87,7 @@ func TestNewCmdDelete(t *testing.T) {
}
assert.NoError(t, err)
assert.Equal(t, tt.wants.DeleteAll, gotOpts.DeleteAll)
assert.Equal(t, tt.wants.SucceedOnNoCaches, gotOpts.SucceedOnNoCaches)
assert.Equal(t, tt.wants.Identifier, gotOpts.Identifier)
})
}
Expand Down Expand Up @@ -160,6 +176,19 @@ func TestDeleteRun(t *testing.T) {
tty: true,
wantStdout: "✓ Deleted 2 caches from OWNER/REPO\n",
},
{
name: "attempts to delete all caches but api errors",
opts: DeleteOptions{DeleteAll: true},
stubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/caches"),
httpmock.StatusStringResponse(500, ""),
)
},
tty: true,
wantErr: true,
wantErrMsg: "HTTP 500 (https://api.github.com/repos/OWNER/REPO/actions/caches?per_page=100)",
},
{
name: "displays delete error",
opts: DeleteOptions{Identifier: "123"},
Expand All @@ -186,6 +215,54 @@ func TestDeleteRun(t *testing.T) {
tty: true,
wantStdout: "✓ Deleted 1 cache from OWNER/REPO\n",
},
{
name: "no caches to delete when deleting all",
opts: DeleteOptions{DeleteAll: true},
stubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/caches"),
httpmock.JSONResponse(shared.CachePayload{
ActionsCaches: []shared.Cache{},
TotalCount: 0,
}),
)
},
tty: false,
wantErr: true,
wantErrMsg: "X No caches to delete",
},
{
name: "no caches to delete when deleting all but succeed on no cache tty",
opts: DeleteOptions{DeleteAll: true, SucceedOnNoCaches: true},
stubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/caches"),
httpmock.JSONResponse(shared.CachePayload{
ActionsCaches: []shared.Cache{},
TotalCount: 0,
}),
)
},
tty: true,
wantErr: false,
wantStdout: "✓ No caches to delete\n",
},
{
name: "no caches to delete when deleting all but succeed on no cache non-tty",
opts: DeleteOptions{DeleteAll: true, SucceedOnNoCaches: true},
stubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/caches"),
httpmock.JSONResponse(shared.CachePayload{
ActionsCaches: []shared.Cache{},
TotalCount: 0,
}),
)
},
tty: false,
wantErr: false,
wantStdout: "",
},
}

for _, tt := range tests {
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.