consolidate survey functions

This commit is contained in:
Alan Donovan 2021-09-09 10:28:55 -04:00
parent 6b0510f81a
commit cbb8253544
8 changed files with 108 additions and 94 deletions

View file

@ -7,7 +7,6 @@ import (
"os"
"github.com/github/ghcs/api"
"github.com/github/ghcs/internal/codespaces"
"github.com/skratchdot/open-golang/open"
"github.com/spf13/cobra"
)
@ -47,9 +46,9 @@ func code(codespaceName string, useInsiders bool) error {
}
if codespaceName == "" {
codespace, err := codespaces.ChooseCodespace(ctx, apiClient, user)
codespace, err := chooseCodespace(ctx, apiClient, user)
if err != nil {
if err == codespaces.ErrNoCodespaces {
if err == errNoCodespaces {
return err
}
return fmt.Errorf("error choosing codespace: %v", err)

101
cmd/ghcs/common.go Normal file
View file

@ -0,0 +1,101 @@
package main
// This file defines functions common to the entire ghcs command set.
import (
"context"
"errors"
"fmt"
"sort"
"github.com/AlecAivazis/survey/v2"
"github.com/github/ghcs/api"
"golang.org/x/term"
)
var errNoCodespaces = errors.New("You have no codespaces.")
func chooseCodespace(ctx context.Context, apiClient *api.API, user *api.User) (*api.Codespace, error) {
codespaces, err := apiClient.ListCodespaces(ctx, user)
if err != nil {
return nil, fmt.Errorf("error getting codespaces: %v", err)
}
if len(codespaces) == 0 {
return nil, errNoCodespaces
}
sort.Slice(codespaces, func(i, j int) bool {
return codespaces[i].CreatedAt > codespaces[j].CreatedAt
})
codespacesByName := make(map[string]*api.Codespace)
codespacesNames := make([]string, 0, len(codespaces))
for _, codespace := range codespaces {
codespacesByName[codespace.Name] = codespace
codespacesNames = append(codespacesNames, codespace.Name)
}
sshSurvey := []*survey.Question{
{
Name: "codespace",
Prompt: &survey.Select{
Message: "Choose codespace:",
Options: codespacesNames,
Default: codespacesNames[0],
},
Validate: survey.Required,
},
}
var answers struct {
Codespace string
}
if err := ask(sshSurvey, &answers); err != nil {
return nil, fmt.Errorf("error getting answers: %v", err)
}
codespace := codespacesByName[answers.Codespace]
return codespace, nil
}
func getOrChooseCodespace(ctx context.Context, apiClient *api.API, user *api.User, codespaceName string) (codespace *api.Codespace, token string, err error) {
if codespaceName == "" {
codespace, err = chooseCodespace(ctx, apiClient, user)
if err != nil {
if err == errNoCodespaces {
return nil, "", err
}
return nil, "", fmt.Errorf("choosing codespace: %v", err)
}
codespaceName = codespace.Name
token, err = apiClient.GetCodespaceToken(ctx, user.Login, codespaceName)
if err != nil {
return nil, "", fmt.Errorf("getting codespace token: %v", err)
}
} else {
token, err = apiClient.GetCodespaceToken(ctx, user.Login, codespaceName)
if err != nil {
return nil, "", fmt.Errorf("getting codespace token for given codespace: %v", err)
}
codespace, err = apiClient.GetCodespace(ctx, token, user.Login, codespaceName)
if err != nil {
return nil, "", fmt.Errorf("getting full codespace details: %v", err)
}
}
return codespace, token, nil
}
var hasTTY = term.IsTerminal(0) && term.IsTerminal(1) // is process connected to a terminal?
// ask asks survey questions on the terminal, using standard options.
// It fails unless hasTTY, but ideally callers should avoid calling it in that case.
func ask(qs []*survey.Question, response interface{}) error {
if !hasTTY {
return fmt.Errorf("no terminal")
}
return survey.Ask(qs, response, survey.WithShowCursor(true))
}

View file

@ -286,8 +286,3 @@ func getMachineName(ctx context.Context, machine string, user *api.User, repo *a
return machine, nil
}
// ask asks survery questions using standard options.
func ask(qs []*survey.Question, response interface{}) error {
return survey.Ask(qs, response, survey.WithShowCursor(true))
}

View file

@ -8,7 +8,6 @@ import (
"github.com/github/ghcs/api"
"github.com/github/ghcs/cmd/ghcs/output"
"github.com/github/ghcs/internal/codespaces"
"github.com/spf13/cobra"
)
@ -63,7 +62,7 @@ func delete_(codespaceName string) error {
return fmt.Errorf("error getting user: %v", err)
}
codespace, token, err := codespaces.GetOrChooseCodespace(ctx, apiClient, user, codespaceName)
codespace, token, err := getOrChooseCodespace(ctx, apiClient, user, codespaceName)
if err != nil {
return fmt.Errorf("get or choose codespace: %v", err)
}

View file

@ -51,7 +51,7 @@ func logs(ctx context.Context, tail bool, codespaceName string) error {
return fmt.Errorf("getting user: %v", err)
}
codespace, token, err := codespaces.GetOrChooseCodespace(ctx, apiClient, user, codespaceName)
codespace, token, err := getOrChooseCodespace(ctx, apiClient, user, codespaceName)
if err != nil {
return fmt.Errorf("get or choose codespace: %v", err)
}

View file

@ -67,9 +67,9 @@ func ports(opts *portsOptions) error {
return fmt.Errorf("error getting user: %v", err)
}
codespace, token, err := codespaces.GetOrChooseCodespace(ctx, apiClient, user, opts.codespaceName)
codespace, token, err := getOrChooseCodespace(ctx, apiClient, user, opts.codespaceName)
if err != nil {
if err == codespaces.ErrNoCodespaces {
if err == errNoCodespaces {
return err
}
return fmt.Errorf("error choosing codespace: %v", err)

View file

@ -52,7 +52,7 @@ func ssh(ctx context.Context, sshProfile, codespaceName string, localSSHServerPo
return fmt.Errorf("error getting user: %v", err)
}
codespace, token, err := codespaces.GetOrChooseCodespace(ctx, apiClient, user, codespaceName)
codespace, token, err := getOrChooseCodespace(ctx, apiClient, user, codespaceName)
if err != nil {
return fmt.Errorf("get or choose codespace: %v", err)
}

View file

@ -4,62 +4,12 @@ import (
"context"
"errors"
"fmt"
"sort"
"time"
"github.com/AlecAivazis/survey/v2"
"github.com/github/ghcs/api"
"github.com/github/go-liveshare"
)
var (
ErrNoCodespaces = errors.New("You have no codespaces.")
)
func ChooseCodespace(ctx context.Context, apiClient *api.API, user *api.User) (*api.Codespace, error) {
codespaces, err := apiClient.ListCodespaces(ctx, user)
if err != nil {
return nil, fmt.Errorf("error getting codespaces: %v", err)
}
if len(codespaces) == 0 {
return nil, ErrNoCodespaces
}
sort.Slice(codespaces, func(i, j int) bool {
return codespaces[i].CreatedAt > codespaces[j].CreatedAt
})
codespacesByName := make(map[string]*api.Codespace)
codespacesNames := make([]string, 0, len(codespaces))
for _, codespace := range codespaces {
codespacesByName[codespace.Name] = codespace
codespacesNames = append(codespacesNames, codespace.Name)
}
sshSurvey := []*survey.Question{
{
Name: "codespace",
Prompt: &survey.Select{
Message: "Choose codespace:",
Options: codespacesNames,
Default: codespacesNames[0],
},
Validate: survey.Required,
},
}
answers := struct {
Codespace string
}{}
if err := survey.Ask(sshSurvey, &answers); err != nil {
return nil, fmt.Errorf("error getting answers: %v", err)
}
codespace := codespacesByName[answers.Codespace]
return codespace, nil
}
type logger interface {
Print(v ...interface{}) (int, error)
Println(v ...interface{}) (int, error)
@ -123,33 +73,3 @@ func ConnectToLiveshare(ctx context.Context, log logger, apiClient *api.API, use
return lsclient.JoinWorkspace(ctx)
}
func GetOrChooseCodespace(ctx context.Context, apiClient *api.API, user *api.User, codespaceName string) (codespace *api.Codespace, token string, err error) {
if codespaceName == "" {
codespace, err = ChooseCodespace(ctx, apiClient, user)
if err != nil {
if err == ErrNoCodespaces {
return nil, "", err
}
return nil, "", fmt.Errorf("choosing codespace: %v", err)
}
codespaceName = codespace.Name
token, err = apiClient.GetCodespaceToken(ctx, user.Login, codespaceName)
if err != nil {
return nil, "", fmt.Errorf("getting codespace token: %v", err)
}
} else {
token, err = apiClient.GetCodespaceToken(ctx, user.Login, codespaceName)
if err != nil {
return nil, "", fmt.Errorf("getting codespace token for given codespace: %v", err)
}
codespace, err = apiClient.GetCodespace(ctx, token, user.Login, codespaceName)
if err != nil {
return nil, "", fmt.Errorf("getting full codespace details: %v", err)
}
}
return codespace, token, nil
}