Skip to content

Commit 55f0dad

Browse files
authored
merge upstream
2 parents 76aca39 + 9cb8200 commit 55f0dad

File tree

15 files changed

+384
-158
lines changed

15 files changed

+384
-158
lines changed

cmd/ghcs/code.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import (
1414

1515
func NewCodeCmd() *cobra.Command {
1616
return &cobra.Command{
17-
Use: "code",
18-
Short: "Open a GitHub Codespace in VSCode.",
17+
Use: "code [<codespace>]",
18+
Short: "Open a Codespace in VS Code",
19+
Args: cobra.MaximumNArgs(1),
1920
RunE: func(cmd *cobra.Command, args []string) error {
2021
var codespaceName string
2122
if len(args) > 0 {
@@ -43,8 +44,7 @@ func Code(codespaceName string) error {
4344
codespace, err := codespaces.ChooseCodespace(ctx, apiClient, user)
4445
if err != nil {
4546
if err == codespaces.ErrNoCodespaces {
46-
fmt.Println(err.Error())
47-
return nil
47+
return err
4848
}
4949
return fmt.Errorf("error choosing codespace: %v", err)
5050
}

cmd/ghcs/create.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ package main
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"os"
78
"strings"
89

910
"github.com/AlecAivazis/survey/v2"
1011
"github.com/fatih/camelcase"
1112
"github.com/github/ghcs/api"
13+
"github.com/github/ghcs/cmd/ghcs/output"
1214
"github.com/github/ghcs/internal/codespaces"
1315
"github.com/spf13/cobra"
1416
)
@@ -18,7 +20,8 @@ var repo, branch, machine string
1820
func newCreateCmd() *cobra.Command {
1921
createCmd := &cobra.Command{
2022
Use: "create",
21-
Short: "Create a GitHub Codespace.",
23+
Short: "Create a Codespace",
24+
Args: cobra.NoArgs,
2225
RunE: func(cmd *cobra.Command, args []string) error {
2326
return Create()
2427
},
@@ -40,6 +43,7 @@ func Create() error {
4043
apiClient := api.New(os.Getenv("GITHUB_TOKEN"))
4144
locationCh := getLocation(ctx, apiClient)
4245
userCh := getUser(ctx, apiClient)
46+
log := output.NewLogger(os.Stdout, os.Stderr, false)
4347

4448
repo, err := getRepoName()
4549
if err != nil {
@@ -70,18 +74,17 @@ func Create() error {
7074
return fmt.Errorf("error getting machine type: %v", err)
7175
}
7276
if machine == "" {
73-
fmt.Println("There are no available machine types for this repository")
74-
return nil
77+
return errors.New("There are no available machine types for this repository")
7578
}
7679

77-
fmt.Println("Creating your codespace...")
80+
log.Println("Creating your codespace...")
7881

7982
codespace, err := apiClient.CreateCodespace(ctx, userResult.User, repository, machine, branch, locationResult.Location)
8083
if err != nil {
8184
return fmt.Errorf("error creating codespace: %v", err)
8285
}
8386

84-
states, err := codespaces.PollPostCreateStates(ctx, apiClient, userResult.User, codespace)
87+
states, err := codespaces.PollPostCreateStates(ctx, log, apiClient, userResult.User, codespace)
8588
if err != nil {
8689
return fmt.Errorf("poll post create states: %v", err)
8790
}
@@ -132,7 +135,7 @@ PollStates:
132135
}
133136
}
134137

135-
fmt.Println("Codespace created: " + codespace.Name)
138+
log.Printf("Codespace created: %s\n", codespace.Name)
136139

137140
return nil
138141
}

cmd/ghcs/delete.go

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,44 @@ package main
22

33
import (
44
"context"
5-
"errors"
65
"fmt"
76
"os"
7+
"strings"
88

99
"github.com/github/ghcs/api"
10+
"github.com/github/ghcs/cmd/ghcs/output"
11+
"github.com/github/ghcs/internal/codespaces"
1012
"github.com/spf13/cobra"
1113
)
1214

1315
func NewDeleteCmd() *cobra.Command {
1416
deleteCmd := &cobra.Command{
15-
Use: "delete CODESPACE_NAME",
16-
Short: "Delete a GitHub Codespace.",
17+
Use: "delete [<codespace>]",
18+
Short: "Delete a Codespace",
19+
Args: cobra.MaximumNArgs(1),
1720
RunE: func(cmd *cobra.Command, args []string) error {
18-
if len(args) == 0 {
19-
return errors.New("A Codespace name is required.")
21+
var codespaceName string
22+
if len(args) > 0 {
23+
codespaceName = args[0]
2024
}
21-
return Delete(args[0])
25+
return Delete(codespaceName)
2226
},
2327
}
2428

2529
deleteAllCmd := &cobra.Command{
2630
Use: "all",
27-
Short: "delete all codespaces",
28-
Long: "delete all codespaces for the user with the current token",
31+
Short: "Delete all Codespaces for the current user",
32+
Args: cobra.NoArgs,
2933
RunE: func(cmd *cobra.Command, args []string) error {
3034
return DeleteAll()
3135
},
3236
}
3337

3438
deleteByRepoCmd := &cobra.Command{
35-
Use: "repo REPO_NAME",
36-
Short: "delete all codespaces for the repo",
37-
Long: `delete all the codespaces that the user with the current token has in this repo.
38-
This includes all codespaces in all states.`,
39+
Use: "repo <repo>",
40+
Short: "Delete all Codespaces for a repository",
41+
Args: cobra.ExactArgs(1),
3942
RunE: func(cmd *cobra.Command, args []string) error {
40-
if len(args) == 0 {
41-
return errors.New("A Repository name is required.")
42-
}
4343
return DeleteByRepo(args[0])
4444
},
4545
}
@@ -56,29 +56,31 @@ func init() {
5656
func Delete(codespaceName string) error {
5757
apiClient := api.New(os.Getenv("GITHUB_TOKEN"))
5858
ctx := context.Background()
59+
log := output.NewLogger(os.Stdout, os.Stderr, false)
5960

6061
user, err := apiClient.GetUser(ctx)
6162
if err != nil {
6263
return fmt.Errorf("error getting user: %v", err)
6364
}
6465

65-
token, err := apiClient.GetCodespaceToken(ctx, user.Login, codespaceName)
66+
codespace, token, err := codespaces.GetOrChooseCodespace(ctx, apiClient, user, codespaceName)
6667
if err != nil {
67-
return fmt.Errorf("error getting codespace token: %v", err)
68+
return fmt.Errorf("get or choose codespace: %v", err)
6869
}
6970

70-
if err := apiClient.DeleteCodespace(ctx, user, token, codespaceName); err != nil {
71+
if err := apiClient.DeleteCodespace(ctx, user, token, codespace.Name); err != nil {
7172
return fmt.Errorf("error deleting codespace: %v", err)
7273
}
7374

74-
fmt.Println("Codespace deleted.")
75+
log.Println("Codespace deleted.")
7576

76-
return List()
77+
return List(&ListOptions{})
7778
}
7879

7980
func DeleteAll() error {
8081
apiClient := api.New(os.Getenv("GITHUB_TOKEN"))
8182
ctx := context.Background()
83+
log := output.NewLogger(os.Stdout, os.Stderr, false)
8284

8385
user, err := apiClient.GetUser(ctx)
8486
if err != nil {
@@ -100,15 +102,16 @@ func DeleteAll() error {
100102
return fmt.Errorf("error deleting codespace: %v", err)
101103
}
102104

103-
fmt.Printf("Codespace deleted: %s\n", c.Name)
105+
log.Printf("Codespace deleted: %s\n", c.Name)
104106
}
105107

106-
return List()
108+
return List(&ListOptions{})
107109
}
108110

109111
func DeleteByRepo(repo string) error {
110112
apiClient := api.New(os.Getenv("GITHUB_TOKEN"))
111113
ctx := context.Background()
114+
log := output.NewLogger(os.Stdout, os.Stderr, false)
112115

113116
user, err := apiClient.GetUser(ctx)
114117
if err != nil {
@@ -122,7 +125,7 @@ func DeleteByRepo(repo string) error {
122125

123126
var deleted bool
124127
for _, c := range codespaces {
125-
if c.RepositoryNWO != repo {
128+
if !strings.EqualFold(c.RepositoryNWO, repo) {
126129
continue
127130
}
128131
deleted = true
@@ -136,12 +139,12 @@ func DeleteByRepo(repo string) error {
136139
return fmt.Errorf("error deleting codespace: %v", err)
137140
}
138141

139-
fmt.Printf("Codespace deleted: %s\n", c.Name)
142+
log.Printf("Codespace deleted: %s\n", c.Name)
140143
}
141144

142145
if !deleted {
143-
fmt.Printf("No codespace was found for repository: %s\n", repo)
146+
return fmt.Errorf("No codespace was found for repository: %s", repo)
144147
}
145148

146-
return List()
149+
return List(&ListOptions{})
147150
}

cmd/ghcs/list.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,37 @@ import (
55
"fmt"
66
"os"
77

8-
"github.com/olekukonko/tablewriter"
9-
108
"github.com/github/ghcs/api"
9+
"github.com/github/ghcs/cmd/ghcs/output"
1110
"github.com/spf13/cobra"
1211
)
1312

13+
type ListOptions struct {
14+
AsJSON bool
15+
}
16+
1417
func NewListCmd() *cobra.Command {
18+
opts := &ListOptions{}
19+
1520
listCmd := &cobra.Command{
1621
Use: "list",
17-
Short: "List GitHub Codespaces you have on your account.",
22+
Short: "List your Codespaces",
23+
Args: cobra.NoArgs,
1824
RunE: func(cmd *cobra.Command, args []string) error {
19-
return List()
25+
return List(opts)
2026
},
2127
}
2228

29+
listCmd.Flags().BoolVar(&opts.AsJSON, "json", false, "Output as JSON")
30+
2331
return listCmd
2432
}
2533

2634
func init() {
2735
rootCmd.AddCommand(NewListCmd())
2836
}
2937

30-
func List() error {
38+
func List(opts *ListOptions) error {
3139
apiClient := api.New(os.Getenv("GITHUB_TOKEN"))
3240
ctx := context.Background()
3341

@@ -41,12 +49,7 @@ func List() error {
4149
return fmt.Errorf("error getting codespaces: %v", err)
4250
}
4351

44-
if len(codespaces) == 0 {
45-
fmt.Println("You have no codespaces.")
46-
return nil
47-
}
48-
49-
table := tablewriter.NewWriter(os.Stdout)
52+
table := output.NewTable(os.Stdout, opts.AsJSON)
5053
table.SetHeader([]string{"Name", "Repository", "Branch", "State", "Created At"})
5154
for _, codespace := range codespaces {
5255
table.Append([]string{

cmd/ghcs/logs.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88

99
"github.com/github/ghcs/api"
10+
"github.com/github/ghcs/cmd/ghcs/output"
1011
"github.com/github/ghcs/internal/codespaces"
1112
"github.com/spf13/cobra"
1213
)
@@ -15,8 +16,9 @@ func NewLogsCmd() *cobra.Command {
1516
var tail bool
1617

1718
logsCmd := &cobra.Command{
18-
Use: "logs",
19+
Use: "logs [<codespace>]",
1920
Short: "Access Codespace logs",
21+
Args: cobra.MaximumNArgs(1),
2022
RunE: func(cmd *cobra.Command, args []string) error {
2123
var codespaceName string
2224
if len(args) > 0 {
@@ -38,6 +40,7 @@ func init() {
3840
func Logs(tail bool, codespaceName string) error {
3941
apiClient := api.New(os.Getenv("GITHUB_TOKEN"))
4042
ctx := context.Background()
43+
log := output.NewLogger(os.Stdout, os.Stderr, false)
4144

4245
user, err := apiClient.GetUser(ctx)
4346
if err != nil {
@@ -49,7 +52,7 @@ func Logs(tail bool, codespaceName string) error {
4952
return fmt.Errorf("get or choose codespace: %v", err)
5053
}
5154

52-
lsclient, err := codespaces.ConnectToLiveshare(ctx, apiClient, user.Login, token, codespace)
55+
lsclient, err := codespaces.ConnectToLiveshare(ctx, log, apiClient, user.Login, token, codespace)
5356
if err != nil {
5457
return fmt.Errorf("connecting to liveshare: %v", err)
5558
}

cmd/ghcs/main.go

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,46 @@
11
package main
22

33
import (
4+
"errors"
45
"fmt"
6+
"io"
57
"os"
68

79
"github.com/spf13/cobra"
810
)
911

1012
func main() {
11-
Execute()
13+
if err := rootCmd.Execute(); err != nil {
14+
explainError(os.Stderr, err)
15+
os.Exit(1)
16+
}
1217
}
1318

1419
var Version = "DEV"
1520

1621
var rootCmd = &cobra.Command{
17-
Use: "ghcs",
18-
Short: "Unofficial GitHub Codespaces CLI.",
19-
Long: "Unofficial CLI tool to manage and interact with GitHub Codespaces.",
22+
Use: "ghcs",
23+
Long: `Unofficial CLI tool to manage GitHub Codespaces.
24+
25+
Running commands requires the GITHUB_TOKEN environment variable to be set to a
26+
token to access the GitHub API with.`,
2027
Version: Version,
28+
29+
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
30+
if os.Getenv("GITHUB_TOKEN") == "" {
31+
return tokenError
32+
}
33+
return nil
34+
},
2135
}
2236

23-
func Execute() {
24-
if os.Getenv("GITHUB_TOKEN") == "" {
25-
fmt.Println("The GITHUB_TOKEN environment variable is required. Create a Personal Access Token at https://github.com/settings/tokens/new?scopes=repo and make sure to enable SSO for the GitHub organization after creating the token.")
26-
os.Exit(1)
27-
}
37+
var tokenError = errors.New("GITHUB_TOKEN is missing")
2838

29-
if err := rootCmd.Execute(); err != nil {
30-
fmt.Fprintln(os.Stderr, err)
31-
os.Exit(1)
39+
func explainError(w io.Writer, err error) {
40+
if errors.Is(err, tokenError) {
41+
fmt.Fprintln(w, "The GITHUB_TOKEN environment variable is required. Create a Personal Access Token at https://github.com/settings/tokens/new?scopes=repo")
42+
fmt.Fprintln(w, "Make sure to enable SSO for your organizations after creating the token.")
43+
return
3244
}
45+
// fmt.Fprintf(w, "%v\n", err)
3346
}

0 commit comments

Comments
 (0)