merge upstream + pr feedback
This commit is contained in:
commit
fe2d5ebf37
10 changed files with 213 additions and 132 deletions
32
api/api.go
32
api/api.go
|
|
@ -18,6 +18,8 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
const githubAPI = "https://api.github.com"
|
||||
|
|
@ -42,7 +44,7 @@ func (a *API) GetUser(ctx context.Context) (*User, error) {
|
|||
}
|
||||
|
||||
a.setHeaders(req)
|
||||
resp, err := a.client.Do(req)
|
||||
resp, err := a.do(ctx, req, "/user")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
|
|
@ -87,7 +89,7 @@ func (a *API) GetRepository(ctx context.Context, nwo string) (*Repository, error
|
|||
}
|
||||
|
||||
a.setHeaders(req)
|
||||
resp, err := a.client.Do(req)
|
||||
resp, err := a.do(ctx, req, "/repos/*")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
|
|
@ -156,7 +158,7 @@ func (a *API) ListCodespaces(ctx context.Context, user *User) ([]*Codespace, err
|
|||
}
|
||||
|
||||
a.setHeaders(req)
|
||||
resp, err := a.client.Do(req)
|
||||
resp, err := a.do(ctx, req, "/vscs_internal/user/*/codespaces")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
|
|
@ -204,7 +206,7 @@ func (a *API) GetCodespaceToken(ctx context.Context, ownerLogin, codespaceName s
|
|||
}
|
||||
|
||||
a.setHeaders(req)
|
||||
resp, err := a.client.Do(req)
|
||||
resp, err := a.do(ctx, req, "/vscs_internal/user/*/codespaces/*/token")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
|
|
@ -238,7 +240,7 @@ func (a *API) GetCodespace(ctx context.Context, token, owner, codespace string)
|
|||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
resp, err := a.client.Do(req)
|
||||
resp, err := a.do(ctx, req, "/vscs_internal/user/*/codespaces/*")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
|
|
@ -272,7 +274,7 @@ func (a *API) StartCodespace(ctx context.Context, token string, codespace *Codes
|
|||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
resp, err := a.client.Do(req)
|
||||
resp, err := a.do(ctx, req, "/vscs_internal/proxy/environments/*/start")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
|
|
@ -309,7 +311,7 @@ func (a *API) GetCodespaceRegionLocation(ctx context.Context) (string, error) {
|
|||
return "", fmt.Errorf("error creating request: %v", err)
|
||||
}
|
||||
|
||||
resp, err := a.client.Do(req)
|
||||
resp, err := a.do(ctx, req, req.URL.String())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
|
|
@ -350,7 +352,7 @@ func (a *API) GetCodespacesSKUs(ctx context.Context, user *User, repository *Rep
|
|||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
a.setHeaders(req)
|
||||
resp, err := a.client.Do(req)
|
||||
resp, err := a.do(ctx, req, "/vscs_internal/user/*/skus")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
|
|
@ -394,7 +396,7 @@ func (a *API) CreateCodespace(ctx context.Context, user *User, repository *Repos
|
|||
}
|
||||
|
||||
a.setHeaders(req)
|
||||
resp, err := a.client.Do(req)
|
||||
resp, err := a.do(ctx, req, "/vscs_internal/user/*/codespaces")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
|
|
@ -424,7 +426,7 @@ func (a *API) DeleteCodespace(ctx context.Context, user *User, token, codespaceN
|
|||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
resp, err := a.client.Do(req)
|
||||
resp, err := a.do(ctx, req, "/vscs_internal/user/*/codespaces/*")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
|
|
@ -456,7 +458,7 @@ func (a *API) GetCodespaceRepositoryContents(ctx context.Context, codespace *Cod
|
|||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
a.setHeaders(req)
|
||||
resp, err := a.client.Do(req)
|
||||
resp, err := a.do(ctx, req, "/repos/*/contents/*")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making request: %v", err)
|
||||
}
|
||||
|
|
@ -488,6 +490,14 @@ func (a *API) GetCodespaceRepositoryContents(ctx context.Context, codespace *Cod
|
|||
return decoded, nil
|
||||
}
|
||||
|
||||
func (a *API) do(ctx context.Context, req *http.Request, spanName string) (*http.Response, error) {
|
||||
// TODO(adonovan): use NewRequestWithContext(ctx) and drop ctx parameter.
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, spanName)
|
||||
defer span.Finish()
|
||||
req = req.WithContext(ctx)
|
||||
return a.client.Do(req)
|
||||
}
|
||||
|
||||
func (a *API) setHeaders(req *http.Request) {
|
||||
req.Header.Set("Authorization", "Bearer "+a.token)
|
||||
req.Header.Set("Accept", "application/vnd.github.v3+json")
|
||||
|
|
|
|||
|
|
@ -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/skratchdot/open-golang/open"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
|
@ -54,9 +53,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)
|
||||
|
|
|
|||
103
cmd/ghcs/common.go
Normal file
103
cmd/ghcs/common.go
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
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
|
||||
}
|
||||
|
||||
// getOrChooseCodespace prompts the user to choose a codespace if the codespaceName is empty.
|
||||
// It then fetches the codespace token and the codespace record.
|
||||
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))
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ func logs(ctx context.Context, log *output.Logger, codespaceName string, follow
|
|||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,13 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/lightstep/lightstep-tracer-go"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
@ -18,22 +23,32 @@ func main() {
|
|||
|
||||
var version = "DEV"
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "ghcs",
|
||||
SilenceUsage: true, // don't print usage message after each error (see #80)
|
||||
SilenceErrors: false, // print errors automatically so that main need not
|
||||
Long: `Unofficial CLI tool to manage GitHub Codespaces.
|
||||
var rootCmd = newRootCmd()
|
||||
|
||||
func newRootCmd() *cobra.Command {
|
||||
var lightstep string
|
||||
|
||||
root := &cobra.Command{
|
||||
Use: "ghcs",
|
||||
SilenceUsage: true, // don't print usage message after each error (see #80)
|
||||
SilenceErrors: false, // print errors automatically so that main need not
|
||||
Long: `Unofficial CLI tool to manage GitHub Codespaces.
|
||||
|
||||
Running commands requires the GITHUB_TOKEN environment variable to be set to a
|
||||
token to access the GitHub API with.`,
|
||||
Version: version,
|
||||
Version: version,
|
||||
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if os.Getenv("GITHUB_TOKEN") == "" {
|
||||
return tokenError
|
||||
}
|
||||
return nil
|
||||
},
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if os.Getenv("GITHUB_TOKEN") == "" {
|
||||
return tokenError
|
||||
}
|
||||
return initLightstep(lightstep)
|
||||
},
|
||||
}
|
||||
|
||||
root.PersistentFlags().StringVar(&lightstep, "lightstep", "", "Lightstep tracing endpoint (service:token@host:port)")
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
var tokenError = errors.New("GITHUB_TOKEN is missing")
|
||||
|
|
@ -45,3 +60,51 @@ func explainError(w io.Writer, err error) {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
// initLightstep parses the --lightstep=service:token@host:port flag and
|
||||
// enables tracing if non-empty.
|
||||
func initLightstep(config string) error {
|
||||
if config == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
cut := func(s, sep string) (pre, post string) {
|
||||
if i := strings.Index(s, sep); i >= 0 {
|
||||
return s[:i], s[i+len(sep):]
|
||||
}
|
||||
return s, ""
|
||||
}
|
||||
|
||||
// Parse service:token@host:port.
|
||||
serviceToken, hostPort := cut(config, "@")
|
||||
service, token := cut(serviceToken, ":")
|
||||
host, port := cut(hostPort, ":")
|
||||
portI, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid Lightstep configuration: %s", config)
|
||||
}
|
||||
|
||||
opentracing.SetGlobalTracer(lightstep.NewTracer(lightstep.Options{
|
||||
AccessToken: token,
|
||||
Collector: lightstep.Endpoint{
|
||||
Host: host,
|
||||
Port: portI,
|
||||
Plaintext: false,
|
||||
},
|
||||
Tags: opentracing.Tags{
|
||||
lightstep.ComponentNameKey: service,
|
||||
},
|
||||
}))
|
||||
|
||||
// Report failure to record traces.
|
||||
lightstep.SetGlobalEventHandler(func(ev lightstep.Event) {
|
||||
switch ev := ev.(type) {
|
||||
case lightstep.EventStatusReport, lightstep.MetricEventStatusReport:
|
||||
// ignore
|
||||
default:
|
||||
log.Printf("[trace] %s", ev)
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,9 +61,9 @@ func ports(codespaceName string, asJSON bool) 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 {
|
||||
if err == codespaces.ErrNoCodespaces {
|
||||
if err == errNoCodespaces {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("error choosing codespace: %v", err)
|
||||
|
|
@ -157,7 +157,7 @@ func getDevContainer(ctx context.Context, apiClient *api.API, codespace *api.Cod
|
|||
|
||||
// newPortsPublicCmd returns a Cobra "ports public" subcommand, which makes a given port public.
|
||||
func newPortsPublicCmd() *cobra.Command {
|
||||
newPortsPublicCmd := &cobra.Command{
|
||||
return &cobra.Command{
|
||||
Use: "public <port>",
|
||||
Short: "Mark port as public",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
|
|
@ -181,13 +181,11 @@ func newPortsPublicCmd() *cobra.Command {
|
|||
return updatePortVisibility(log, codespace, port, true)
|
||||
},
|
||||
}
|
||||
|
||||
return newPortsPublicCmd
|
||||
}
|
||||
|
||||
// newPortsPrivateCmd returns a Cobra "ports private" subcommand, which makes a given port private.
|
||||
func newPortsPrivateCmd() *cobra.Command {
|
||||
newPortsPrivateCmd := &cobra.Command{
|
||||
return &cobra.Command{
|
||||
Use: "private <port>",
|
||||
Short: "Mark port as private",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
|
|
@ -211,8 +209,6 @@ func newPortsPrivateCmd() *cobra.Command {
|
|||
return updatePortVisibility(log, codespace, port, false)
|
||||
},
|
||||
}
|
||||
|
||||
return newPortsPrivateCmd
|
||||
}
|
||||
|
||||
func updatePortVisibility(log *output.Logger, codespaceName, sourcePort string, public bool) error {
|
||||
|
|
@ -224,9 +220,9 @@ func updatePortVisibility(log *output.Logger, codespaceName, sourcePort string,
|
|||
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 {
|
||||
if err == codespaces.ErrNoCodespaces {
|
||||
if err == errNoCodespaces {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("error getting codespace: %v", err)
|
||||
|
|
@ -258,7 +254,7 @@ func updatePortVisibility(log *output.Logger, codespaceName, sourcePort string,
|
|||
// NewPortsForwardCmd returns a Cobra "ports forward" subcommand, which forwards a set of
|
||||
// port pairs from the codespace to localhost.
|
||||
func newPortsForwardCmd() *cobra.Command {
|
||||
newPortsForwardCmd := &cobra.Command{
|
||||
return &cobra.Command{
|
||||
Use: "forward <remote-port>:<local-port>...",
|
||||
Short: "Forward ports",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
|
|
@ -284,8 +280,6 @@ func newPortsForwardCmd() *cobra.Command {
|
|||
return forwardPorts(log, codespace, ports)
|
||||
},
|
||||
}
|
||||
|
||||
return newPortsForwardCmd
|
||||
}
|
||||
|
||||
func forwardPorts(log *output.Logger, codespaceName string, ports []string) error {
|
||||
|
|
@ -302,9 +296,9 @@ func forwardPorts(log *output.Logger, codespaceName string, ports []string) erro
|
|||
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 {
|
||||
if err == codespaces.ErrNoCodespaces {
|
||||
if err == errNoCodespaces {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("error getting codespace: %v", err)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,35 +73,3 @@ func ConnectToLiveshare(ctx context.Context, log logger, apiClient *api.API, use
|
|||
|
||||
return lsclient.JoinWorkspace(ctx)
|
||||
}
|
||||
|
||||
// GetOrChooseCodespace prompts the user to choose a codespace if the codespaceName is empty.
|
||||
// It then fetches the codespace token and the codespace record.
|
||||
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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue