Skip to content

Commit 256b8e3

Browse files
rgarciacursoragent
andcommitted
Add deploy delete and app delete commands
- `kernel deploy delete <id>`: deletes a single deployment by ID with confirmation prompt (skippable via --yes) - `kernel app delete <name>`: deletes all deployments for an app, optionally scoped to a specific --version, with parallel deletion and a progress spinner Both commands use the new Deployments.Delete SDK method added in the go-sdk bump. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent d36bb6c commit 256b8e3

3 files changed

Lines changed: 123 additions & 2 deletions

File tree

cmd/app.go

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/pterm/pterm"
1010
"github.com/samber/lo"
1111
"github.com/spf13/cobra"
12+
"golang.org/x/sync/errgroup"
1213
)
1314

1415
var appCmd = &cobra.Command{
@@ -25,7 +26,14 @@ var appListCmd = &cobra.Command{
2526
RunE: runAppList,
2627
}
2728

28-
// --- app history subcommand (scaffold)
29+
var appDeleteCmd = &cobra.Command{
30+
Use: "delete <app_name>",
31+
Short: "Delete an app and all its deployments",
32+
Long: "Deletes all deployments for an application. Use --version to scope deletion to a specific version.",
33+
Args: cobra.ExactArgs(1),
34+
RunE: runAppDelete,
35+
}
36+
2937
var appHistoryCmd = &cobra.Command{
3038
Use: "history <app_name>",
3139
Short: "Show deployment history for an application",
@@ -36,8 +44,13 @@ var appHistoryCmd = &cobra.Command{
3644
func init() {
3745
// register subcommands under app
3846
appCmd.AddCommand(appListCmd)
47+
appCmd.AddCommand(appDeleteCmd)
3948
appCmd.AddCommand(appHistoryCmd)
4049

50+
// Flags for delete
51+
appDeleteCmd.Flags().String("version", "", "Only delete deployments for this version (default: all versions)")
52+
appDeleteCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt")
53+
4154
// Add optional filters for list
4255
appListCmd.Flags().String("name", "", "Filter by application name")
4356
appListCmd.Flags().String("version", "", "Filter by version label")
@@ -206,6 +219,76 @@ func runAppList(cmd *cobra.Command, args []string) error {
206219
return nil
207220
}
208221

222+
func runAppDelete(cmd *cobra.Command, args []string) error {
223+
client := getKernelClient(cmd)
224+
appName := args[0]
225+
version, _ := cmd.Flags().GetString("version")
226+
skipConfirm, _ := cmd.Flags().GetBool("yes")
227+
228+
params := kernel.DeploymentListParams{
229+
AppName: kernel.Opt(appName),
230+
Limit: kernel.Opt(int64(100)),
231+
Offset: kernel.Opt(int64(0)),
232+
}
233+
if version != "" {
234+
params.AppVersion = kernel.Opt(version)
235+
}
236+
237+
initial, err := client.Deployments.List(cmd.Context(), params)
238+
if err != nil {
239+
return util.CleanedUpSdkError{Err: err}
240+
}
241+
if initial == nil || len(initial.Items) == 0 {
242+
pterm.Info.Printf("No deployments found for app '%s'\n", appName)
243+
return nil
244+
}
245+
246+
if !skipConfirm {
247+
scope := "all versions"
248+
if version != "" {
249+
scope = fmt.Sprintf("version '%s'", version)
250+
}
251+
msg := fmt.Sprintf("Delete all deployments for app '%s' (%s)? This cannot be undone.", appName, scope)
252+
pterm.DefaultInteractiveConfirm.DefaultText = msg
253+
ok, _ := pterm.DefaultInteractiveConfirm.Show()
254+
if !ok {
255+
pterm.Info.Println("Deletion cancelled")
256+
return nil
257+
}
258+
}
259+
260+
spinner, _ := pterm.DefaultSpinner.Start(fmt.Sprintf("Deleting deployments for app '%s'...", appName))
261+
deleted := 0
262+
263+
for {
264+
page, err := client.Deployments.List(cmd.Context(), params)
265+
if err != nil {
266+
spinner.Fail("Failed to list deployments")
267+
return util.CleanedUpSdkError{Err: err}
268+
}
269+
items := page.Items
270+
if len(items) == 0 {
271+
break
272+
}
273+
274+
g, gctx := errgroup.WithContext(cmd.Context())
275+
for _, dep := range items {
276+
g.Go(func() error {
277+
return client.Deployments.Delete(gctx, dep.ID)
278+
})
279+
}
280+
if err := g.Wait(); err != nil {
281+
spinner.Fail("Failed to delete deployments")
282+
return util.CleanedUpSdkError{Err: err}
283+
}
284+
deleted += len(items)
285+
spinner.UpdateText(fmt.Sprintf("Deleted %d deployment(s) so far...", deleted))
286+
}
287+
288+
spinner.Success(fmt.Sprintf("Deleted %d deployment(s) for app '%s'", deleted, appName))
289+
return nil
290+
}
291+
209292
func runAppHistory(cmd *cobra.Command, args []string) error {
210293
client := getKernelClient(cmd)
211294
appName := args[0]

cmd/deploy.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ import (
2323
"github.com/spf13/cobra"
2424
)
2525

26+
var deployDeleteCmd = &cobra.Command{
27+
Use: "delete <deployment_id>",
28+
Short: "Delete a deployment",
29+
Long: "Stops a running deployment and marks it for deletion. If already stopped or failed, deletes immediately.",
30+
Args: cobra.ExactArgs(1),
31+
RunE: runDeployDelete,
32+
}
33+
2634
var deployLogsCmd = &cobra.Command{
2735
Use: "logs <deployment_id>",
2836
Short: "Stream logs for a deployment",
@@ -65,6 +73,9 @@ func init() {
6573
deployLogsCmd.Flags().BoolP("with-timestamps", "t", false, "Include timestamps in each log line")
6674
deployCmd.AddCommand(deployLogsCmd)
6775

76+
deployDeleteCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt")
77+
deployCmd.AddCommand(deployDeleteCmd)
78+
6879
deployHistoryCmd.Flags().Int("limit", 20, "Max deployments to return (default 20)")
6980
deployHistoryCmd.Flags().Int("per-page", 20, "Items per page (alias of --limit)")
7081
deployHistoryCmd.Flags().Int("page", 1, "Page number (1-based)")
@@ -306,6 +317,33 @@ func quoteIfNeeded(s string) string {
306317
return s
307318
}
308319

320+
func runDeployDelete(cmd *cobra.Command, args []string) error {
321+
client := getKernelClient(cmd)
322+
deploymentID := args[0]
323+
skipConfirm, _ := cmd.Flags().GetBool("yes")
324+
325+
if !skipConfirm {
326+
msg := fmt.Sprintf("Are you sure you want to delete deployment '%s'? This cannot be undone.", deploymentID)
327+
pterm.DefaultInteractiveConfirm.DefaultText = msg
328+
ok, _ := pterm.DefaultInteractiveConfirm.Show()
329+
if !ok {
330+
pterm.Info.Println("Deletion cancelled")
331+
return nil
332+
}
333+
}
334+
335+
if err := client.Deployments.Delete(cmd.Context(), deploymentID); err != nil {
336+
if util.IsNotFound(err) {
337+
pterm.Warning.Printf("Deployment '%s' not found\n", deploymentID)
338+
return nil
339+
}
340+
return util.CleanedUpSdkError{Err: err}
341+
}
342+
343+
pterm.Success.Printf("Deleted deployment %s\n", deploymentID)
344+
return nil
345+
}
346+
309347
func runDeployLogs(cmd *cobra.Command, args []string) error {
310348
client := getKernelClient(cmd)
311349

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ require (
1919
github.com/zalando/go-keyring v0.2.6
2020
golang.org/x/crypto v0.47.0
2121
golang.org/x/oauth2 v0.30.0
22+
golang.org/x/sync v0.19.0
2223
)
2324

2425
require (
@@ -53,7 +54,6 @@ require (
5354
github.com/tidwall/pretty v1.2.1 // indirect
5455
github.com/tidwall/sjson v1.2.5 // indirect
5556
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
56-
golang.org/x/sync v0.19.0 // indirect
5757
golang.org/x/sys v0.40.0 // indirect
5858
golang.org/x/term v0.39.0 // indirect
5959
golang.org/x/text v0.33.0 // indirect

0 commit comments

Comments
 (0)