@@ -2,13 +2,13 @@ package ghcs
22
33import (
44 "context"
5+ "errors"
56 "fmt"
67 "os"
78 "strings"
89 "time"
910
1011 "github.com/AlecAivazis/survey/v2"
11- "github.com/github/ghcs/cmd/ghcs/output"
1212 "github.com/github/ghcs/internal/api"
1313 "github.com/spf13/cobra"
1414 "golang.org/x/sync/errgroup"
@@ -17,35 +17,43 @@ import (
1717type deleteOptions struct {
1818 deleteAll bool
1919 skipConfirm bool
20- isInteractive bool
2120 codespaceName string
2221 repoFilter string
2322 keepDays uint16
23+
24+ isInteractive bool
2425 now func () time.Time
25- apiClient * api.API
26+ apiClient apiClient
27+ prompter prompter
28+ }
29+
30+ type prompter interface {
31+ Confirm (message string ) (bool , error )
32+ }
33+
34+ type apiClient interface {
35+ GetUser (ctx context.Context ) (* api.User , error )
36+ ListCodespaces (ctx context.Context , user string ) ([]* api.Codespace , error )
37+ DeleteCodespace (ctx context.Context , user , name string ) error
2638}
2739
2840func newDeleteCmd () * cobra.Command {
2941 opts := deleteOptions {
30- apiClient : api .New (os .Getenv ("GITHUB_TOKEN" )),
31- now : time .Now ,
3242 isInteractive : hasTTY ,
43+ now : time .Now ,
44+ apiClient : api .New (os .Getenv ("GITHUB_TOKEN" )),
45+ prompter : & surveyPrompter {},
3346 }
3447
3548 deleteCmd := & cobra.Command {
3649 Use : "delete" ,
3750 Short : "Delete a codespace" ,
3851 Args : cobra .NoArgs ,
3952 RunE : func (cmd * cobra.Command , args []string ) error {
40- // switch {
41- // case allCodespaces && repo != "":
42- // return errors.New("both --all and --repo is not supported")
43- // case allCodespaces:
44- // return deleteAll(log, force, keepThresholdDays)
45- // case repo != "":
46- // return deleteByRepo(log, repo, force, keepThresholdDays)
47- log := output .NewLogger (os .Stdout , os .Stderr , false )
48- return delete (context .Background (), log , opts )
53+ if opts .deleteAll && opts .repoFilter != "" {
54+ return errors .New ("both --all and --repo is not supported" )
55+ }
56+ return delete (context .Background (), opts )
4957 },
5058 }
5159
@@ -58,13 +66,13 @@ func newDeleteCmd() *cobra.Command {
5866 return deleteCmd
5967}
6068
61- func delete (ctx context.Context , log * output. Logger , opts deleteOptions ) error {
69+ func delete (ctx context.Context , opts deleteOptions ) error {
6270 user , err := opts .apiClient .GetUser (ctx )
6371 if err != nil {
6472 return fmt .Errorf ("error getting user: %w" , err )
6573 }
6674
67- codespaces , err := opts .apiClient .ListCodespaces (ctx , user )
75+ codespaces , err := opts .apiClient .ListCodespaces (ctx , user . Login )
6876 if err != nil {
6977 return fmt .Errorf ("error getting codespaces: %w" , err )
7078 }
@@ -78,7 +86,7 @@ func delete(ctx context.Context, log *output.Logger, opts deleteOptions) error {
7886 nameFilter = c .Name
7987 }
8088
81- var codespacesToDelete []* api.Codespace
89+ codespacesToDelete := make ( []* api.Codespace , 0 , len ( codespaces ))
8290 lastUpdatedCutoffTime := opts .now ().AddDate (0 , 0 , - int (opts .keepDays ))
8391 for _ , c := range codespaces {
8492 if nameFilter != "" && c .Name != nameFilter {
@@ -97,9 +105,9 @@ func delete(ctx context.Context, log *output.Logger, opts deleteOptions) error {
97105 }
98106 }
99107 if nameFilter == "" || ! opts .skipConfirm {
100- confirmed , err := confirmDeletion (c )
108+ confirmed , err := confirmDeletion (opts . prompter , c , opts . isInteractive )
101109 if err != nil {
102- return fmt .Errorf ("deletion could not be confirmed : %w" , err )
110+ return fmt .Errorf ("unable to confirm : %w" , err )
103111 }
104112 if ! confirmed {
105113 continue
@@ -112,11 +120,7 @@ func delete(ctx context.Context, log *output.Logger, opts deleteOptions) error {
112120 for _ , c := range codespacesToDelete {
113121 codespaceName := c .Name
114122 g .Go (func () error {
115- token , err := opts .apiClient .GetCodespaceToken (ctx , user .Login , codespaceName )
116- if err != nil {
117- return fmt .Errorf ("error getting codespace token: %w" , err )
118- }
119- if err := opts .apiClient .DeleteCodespace (ctx , user , token , codespaceName ); err != nil {
123+ if err := opts .apiClient .DeleteCodespace (ctx , user .Login , codespaceName ); err != nil {
120124 return fmt .Errorf ("error deleting codespace: %w" , err )
121125 }
122126 return nil
@@ -126,24 +130,29 @@ func delete(ctx context.Context, log *output.Logger, opts deleteOptions) error {
126130 return g .Wait ()
127131}
128132
129- func confirmDeletion (codespace * api.Codespace ) (bool , error ) {
133+ func confirmDeletion (p prompter , codespace * api.Codespace , isInteractive bool ) (bool , error ) {
130134 gs := codespace .Environment .GitStatus
131135 hasUnsavedChanges := gs .HasUncommitedChanges || gs .HasUnpushedChanges
132136 if ! hasUnsavedChanges {
133137 return true , nil
134138 }
135- if ! hasTTY {
139+ if ! isInteractive {
136140 return false , fmt .Errorf ("codespace %s has unsaved changes (use --force to override)" , codespace .Name )
137141 }
142+ return p .Confirm (fmt .Sprintf ("Codespace %s has unsaved changes. OK to delete?" , codespace .Name ))
143+ }
144+
145+ type surveyPrompter struct {}
138146
147+ func (p * surveyPrompter ) Confirm (message string ) (bool , error ) {
139148 var confirmed struct {
140149 Confirmed bool
141150 }
142151 q := []* survey.Question {
143152 {
144153 Name : "confirmed" ,
145154 Prompt : & survey.Confirm {
146- Message : fmt . Sprintf ( "Codespace %s has unsaved changes. OK to delete?" , codespace . Name ) ,
155+ Message : message ,
147156 },
148157 },
149158 }
0 commit comments