Merge branch 'trunk' of github.com:cli/cli into jg/choose-codespace-prompt
This commit is contained in:
commit
a8d1718f21
14 changed files with 155 additions and 159 deletions
|
|
@ -34,7 +34,6 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -44,6 +43,7 @@ import (
|
|||
|
||||
const githubAPI = "https://api.github.com"
|
||||
|
||||
// API is the interface to the codespace service.
|
||||
type API struct {
|
||||
token string
|
||||
client httpClient
|
||||
|
|
@ -54,6 +54,7 @@ type httpClient interface {
|
|||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// New creates a new API client with the given token and HTTP client.
|
||||
func New(token string, httpClient httpClient) *API {
|
||||
return &API{
|
||||
token: token,
|
||||
|
|
@ -62,10 +63,12 @@ func New(token string, httpClient httpClient) *API {
|
|||
}
|
||||
}
|
||||
|
||||
// User represents a GitHub user.
|
||||
type User struct {
|
||||
Login string `json:"login"`
|
||||
}
|
||||
|
||||
// GetUser returns the user associated with the given token.
|
||||
func (a *API) GetUser(ctx context.Context) (*User, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, a.githubAPI+"/user", nil)
|
||||
if err != nil {
|
||||
|
|
@ -96,6 +99,7 @@ func (a *API) GetUser(ctx context.Context) (*User, error) {
|
|||
return &response, nil
|
||||
}
|
||||
|
||||
// jsonErrorResponse returns the error message from a JSON response.
|
||||
func jsonErrorResponse(b []byte) error {
|
||||
var response struct {
|
||||
Message string `json:"message"`
|
||||
|
|
@ -107,10 +111,12 @@ func jsonErrorResponse(b []byte) error {
|
|||
return errors.New(response.Message)
|
||||
}
|
||||
|
||||
// Repository represents a GitHub repository.
|
||||
type Repository struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
// GetRepository returns the repository associated with the given owner and name.
|
||||
func (a *API) GetRepository(ctx context.Context, nwo string) (*Repository, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, a.githubAPI+"/repos/"+strings.ToLower(nwo), nil)
|
||||
if err != nil {
|
||||
|
|
@ -174,6 +180,7 @@ func (a *API) ListCodespaces(ctx context.Context) ([]*codespace.Codespace, error
|
|||
return response.Codespaces, nil
|
||||
}
|
||||
|
||||
// getCodespaceTokenRequest is the request body for the get codespace token endpoint.
|
||||
type getCodespaceTokenRequest struct {
|
||||
MintRepositoryToken bool `json:"mint_repository_token"`
|
||||
}
|
||||
|
|
@ -186,6 +193,7 @@ type getCodespaceTokenResponse struct {
|
|||
// creation of a codespace is not yet complete and that the caller should try again.
|
||||
var ErrNotProvisioned = errors.New("codespace not provisioned")
|
||||
|
||||
// GetCodespaceToken returns a codespace token for the user.
|
||||
func (a *API) GetCodespaceToken(ctx context.Context, ownerLogin, codespaceName string) (string, error) {
|
||||
reqBody, err := json.Marshal(getCodespaceTokenRequest{true})
|
||||
if err != nil {
|
||||
|
|
@ -229,6 +237,7 @@ func (a *API) GetCodespaceToken(ctx context.Context, ownerLogin, codespaceName s
|
|||
return response.RepositoryToken, nil
|
||||
}
|
||||
|
||||
// GetCodespace returns a codespace for the user.
|
||||
func (a *API) GetCodespace(ctx context.Context, token, owner, codespaceName string) (*codespace.Codespace, error) {
|
||||
req, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
|
|
@ -264,19 +273,20 @@ func (a *API) GetCodespace(ctx context.Context, token, owner, codespaceName stri
|
|||
return &response, nil
|
||||
}
|
||||
|
||||
func (a *API) StartCodespace(ctx context.Context, token string, codespace *codespace.Codespace) error {
|
||||
// StartCodespace starts a codespace for the user.
|
||||
// If the codespace is already running, the returned error from the API is ignored.
|
||||
func (a *API) StartCodespace(ctx context.Context, codespaceName string) error {
|
||||
req, err := http.NewRequest(
|
||||
http.MethodPost,
|
||||
a.githubAPI+"/vscs_internal/proxy/environments/"+codespace.GUID+"/start",
|
||||
a.githubAPI+"/user/codespaces/"+codespaceName+"/start",
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating request: %w", err)
|
||||
}
|
||||
|
||||
// TODO: use a.setHeaders()
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
resp, err := a.do(ctx, req, "/vscs_internal/proxy/environments/*/start")
|
||||
a.setHeaders(req)
|
||||
resp, err := a.do(ctx, req, "/user/codespaces/*/start")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making request: %w", err)
|
||||
}
|
||||
|
|
@ -288,19 +298,20 @@ func (a *API) StartCodespace(ctx context.Context, token string, codespace *codes
|
|||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.StatusCode == http.StatusConflict {
|
||||
// 409 means the codespace is already running which we can safely ignore
|
||||
return nil
|
||||
}
|
||||
|
||||
// Error response may be a numeric code or a JSON {"message": "..."}.
|
||||
if bytes.HasPrefix(b, []byte("{")) {
|
||||
return jsonErrorResponse(b) // probably JSON
|
||||
}
|
||||
|
||||
if len(b) > 100 {
|
||||
b = append(b[:97], "..."...)
|
||||
}
|
||||
if strings.TrimSpace(string(b)) == "7" {
|
||||
// Non-HTTP 200 with error code 7 (EnvironmentNotShutdown) is benign.
|
||||
// Ignore it.
|
||||
} else {
|
||||
return fmt.Errorf("failed to start codespace: %s", b)
|
||||
}
|
||||
return fmt.Errorf("failed to start codespace: %s", b)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -310,6 +321,7 @@ type getCodespaceRegionLocationResponse struct {
|
|||
Current string `json:"current"`
|
||||
}
|
||||
|
||||
// GetCodespaceRegionLocation returns the closest codespace location for the user.
|
||||
func (a *API) GetCodespaceRegionLocation(ctx context.Context) (string, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, "https://online.visualstudio.com/api/v1/locations", nil)
|
||||
if err != nil {
|
||||
|
|
@ -339,13 +351,15 @@ func (a *API) GetCodespaceRegionLocation(ctx context.Context) (string, error) {
|
|||
return response.Current, nil
|
||||
}
|
||||
|
||||
type SKU struct {
|
||||
type Machine struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
}
|
||||
|
||||
func (a *API) GetCodespacesSKUs(ctx context.Context, user *User, repository *Repository, branch, location string) ([]*SKU, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, a.githubAPI+"/vscs_internal/user/"+user.Login+"/skus", nil)
|
||||
// GetCodespacesMachines returns the codespaces machines for the given repo, branch and location.
|
||||
func (a *API) GetCodespacesMachines(ctx context.Context, repoID int, branch, location string) ([]*Machine, error) {
|
||||
reqURL := fmt.Sprintf("%s/repositories/%d/codespaces/machines", a.githubAPI, repoID)
|
||||
req, err := http.NewRequest(http.MethodGet, reqURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating request: %w", err)
|
||||
}
|
||||
|
|
@ -353,11 +367,10 @@ func (a *API) GetCodespacesSKUs(ctx context.Context, user *User, repository *Rep
|
|||
q := req.URL.Query()
|
||||
q.Add("location", location)
|
||||
q.Add("ref", branch)
|
||||
q.Add("repository_id", strconv.Itoa(repository.ID))
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
a.setHeaders(req)
|
||||
resp, err := a.do(ctx, req, "/vscs_internal/user/*/skus")
|
||||
resp, err := a.do(ctx, req, "/repositories/*/codespaces/machines")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making request: %w", err)
|
||||
}
|
||||
|
|
@ -373,13 +386,13 @@ func (a *API) GetCodespacesSKUs(ctx context.Context, user *User, repository *Rep
|
|||
}
|
||||
|
||||
var response struct {
|
||||
SKUs []*SKU `json:"skus"`
|
||||
Machines []*Machine `json:"machines"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &response); err != nil {
|
||||
return nil, fmt.Errorf("error unmarshaling response: %w", err)
|
||||
}
|
||||
|
||||
return response.SKUs, nil
|
||||
return response.Machines, nil
|
||||
}
|
||||
|
||||
// CreateCodespaceParams are the required parameters for provisioning a Codespace.
|
||||
|
|
@ -482,20 +495,15 @@ func (a *API) startCreate(ctx context.Context, repoID int, machine, branch, loca
|
|||
return &response, nil
|
||||
}
|
||||
|
||||
func (a *API) DeleteCodespace(ctx context.Context, user string, codespaceName string) error {
|
||||
token, err := a.GetCodespaceToken(ctx, user, codespaceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting codespace token: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodDelete, a.githubAPI+"/vscs_internal/user/"+user+"/codespaces/"+codespaceName, nil)
|
||||
// DeleteCodespace deletes the given codespace.
|
||||
func (a *API) DeleteCodespace(ctx context.Context, codespaceName string) error {
|
||||
req, err := http.NewRequest(http.MethodDelete, a.githubAPI+"/user/codespaces/"+codespaceName, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating request: %w", err)
|
||||
}
|
||||
|
||||
// TODO: use a.setHeaders()
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
resp, err := a.do(ctx, req, "/vscs_internal/user/*/codespaces/*")
|
||||
a.setHeaders(req)
|
||||
resp, err := a.do(ctx, req, "/user/codespaces/*")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making request: %w", err)
|
||||
}
|
||||
|
|
@ -584,6 +592,8 @@ func (a *API) AuthorizedKeys(ctx context.Context, user string) ([]byte, error) {
|
|||
return b, nil
|
||||
}
|
||||
|
||||
// do executes the given request and returns the response. It creates an
|
||||
// opentracing span to track the length of the request.
|
||||
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)
|
||||
|
|
@ -592,6 +602,7 @@ func (a *API) do(ctx context.Context, req *http.Request, spanName string) (*http
|
|||
return a.client.Do(req)
|
||||
}
|
||||
|
||||
// setHeaders sets the required headers for the API.
|
||||
func (a *API) setHeaders(req *http.Request) {
|
||||
if a.token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+a.token)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ func connectionReady(cs *codespace.Codespace) bool {
|
|||
type apiClient interface {
|
||||
GetCodespace(ctx context.Context, token, user, name string) (*codespace.Codespace, error)
|
||||
GetCodespaceToken(ctx context.Context, user, codespace string) (string, error)
|
||||
StartCodespace(ctx context.Context, token string, codespace *codespace.Codespace) error
|
||||
StartCodespace(ctx context.Context, name string) error
|
||||
}
|
||||
|
||||
// ConnectToLiveshare waits for a Codespace to become running,
|
||||
|
|
@ -36,7 +36,7 @@ func ConnectToLiveshare(ctx context.Context, log logger, apiClient apiClient, us
|
|||
if cs.Environment.State != codespace.EnvironmentStateAvailable {
|
||||
startedCodespace = true
|
||||
log.Print("Starting your codespace...")
|
||||
if err := apiClient.StartCodespace(ctx, token, cs); err != nil {
|
||||
if err := apiClient.StartCodespace(ctx, cs.Name); err != nil {
|
||||
return nil, fmt.Errorf("error starting codespace: %w", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package ghcs
|
||||
package codespace
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package ghcs
|
||||
package codespace
|
||||
|
||||
// This file defines functions common to the entire ghcs command set.
|
||||
// This file defines functions common to the entire codespace command set.
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -38,13 +38,13 @@ type apiClient interface {
|
|||
GetCodespaceToken(ctx context.Context, user, name string) (string, error)
|
||||
GetCodespace(ctx context.Context, token, user, name string) (*codespace.Codespace, error)
|
||||
ListCodespaces(ctx context.Context) ([]*codespace.Codespace, error)
|
||||
DeleteCodespace(ctx context.Context, user, name string) error
|
||||
StartCodespace(ctx context.Context, token string, codespace *codespace.Codespace) error
|
||||
DeleteCodespace(ctx context.Context, name string) error
|
||||
StartCodespace(ctx context.Context, name string) error
|
||||
CreateCodespace(ctx context.Context, params *api.CreateCodespaceParams) (*codespace.Codespace, error)
|
||||
GetRepository(ctx context.Context, nwo string) (*api.Repository, error)
|
||||
AuthorizedKeys(ctx context.Context, user string) ([]byte, error)
|
||||
GetCodespaceRegionLocation(ctx context.Context) (string, error)
|
||||
GetCodespacesSKUs(ctx context.Context, user *api.User, repository *api.Repository, branch, location string) ([]*api.SKU, error)
|
||||
GetCodespacesMachines(ctx context.Context, repoID int, branch, location string) ([]*api.Machine, error)
|
||||
GetCodespaceRepositoryContents(ctx context.Context, codespace *codespace.Codespace, path string) ([]byte, error)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package ghcs
|
||||
package codespace
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -72,7 +72,7 @@ func (a *App) Create(ctx context.Context, opts createOptions) error {
|
|||
return fmt.Errorf("error getting codespace user: %w", userResult.Err)
|
||||
}
|
||||
|
||||
machine, err := getMachineName(ctx, opts.machine, userResult.User, repository, branch, locationResult.Location, a.apiClient)
|
||||
machine, err := getMachineName(ctx, a.apiClient, repository.ID, opts.machine, branch, locationResult.Location)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting machine type: %w", err)
|
||||
}
|
||||
|
|
@ -235,8 +235,8 @@ func getBranchName(branch string) (string, error) {
|
|||
}
|
||||
|
||||
// getMachineName prompts the user to select the machine type, or validates the machine if non-empty.
|
||||
func getMachineName(ctx context.Context, machine string, user *api.User, repo *api.Repository, branch, location string, apiClient apiClient) (string, error) {
|
||||
skus, err := apiClient.GetCodespacesSKUs(ctx, user, repo, branch, location)
|
||||
func getMachineName(ctx context.Context, apiClient apiClient, repoID int, machine, branch, location string) (string, error) {
|
||||
machines, err := apiClient.GetCodespacesMachines(ctx, repoID, branch, location)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error requesting machine instance types: %w", err)
|
||||
}
|
||||
|
|
@ -244,55 +244,55 @@ func getMachineName(ctx context.Context, machine string, user *api.User, repo *a
|
|||
// if user supplied a machine type, it must be valid
|
||||
// if no machine type was supplied, we don't error if there are no machine types for the current repo
|
||||
if machine != "" {
|
||||
for _, sku := range skus {
|
||||
if machine == sku.Name {
|
||||
for _, m := range machines {
|
||||
if machine == m.Name {
|
||||
return machine, nil
|
||||
}
|
||||
}
|
||||
|
||||
availableSKUs := make([]string, len(skus))
|
||||
for i := 0; i < len(skus); i++ {
|
||||
availableSKUs[i] = skus[i].Name
|
||||
availableMachines := make([]string, len(machines))
|
||||
for i := 0; i < len(machines); i++ {
|
||||
availableMachines[i] = machines[i].Name
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("there is no such machine for the repository: %s\nAvailable machines: %v", machine, availableSKUs)
|
||||
} else if len(skus) == 0 {
|
||||
return "", fmt.Errorf("there is no such machine for the repository: %s\nAvailable machines: %v", machine, availableMachines)
|
||||
} else if len(machines) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if len(skus) == 1 {
|
||||
return skus[0].Name, nil // VS Code does not prompt for SKU if there is only one, this makes us consistent with that behavior
|
||||
if len(machines) == 1 {
|
||||
// VS Code does not prompt for machine if there is only one, this makes us consistent with that behavior
|
||||
return machines[0].Name, nil
|
||||
}
|
||||
|
||||
skuNames := make([]string, 0, len(skus))
|
||||
skuByName := make(map[string]*api.SKU)
|
||||
for _, sku := range skus {
|
||||
nameParts := camelcase.Split(sku.Name)
|
||||
machineNames := make([]string, 0, len(machines))
|
||||
machineByName := make(map[string]*api.Machine)
|
||||
for _, m := range machines {
|
||||
nameParts := camelcase.Split(m.Name)
|
||||
machineName := strings.Title(strings.ToLower(nameParts[0]))
|
||||
skuName := fmt.Sprintf("%s - %s", machineName, sku.DisplayName)
|
||||
skuNames = append(skuNames, skuName)
|
||||
skuByName[skuName] = sku
|
||||
machineName = fmt.Sprintf("%s - %s", machineName, m.DisplayName)
|
||||
machineNames = append(machineNames, machineName)
|
||||
machineByName[machineName] = m
|
||||
}
|
||||
|
||||
skuSurvey := []*survey.Question{
|
||||
machineSurvey := []*survey.Question{
|
||||
{
|
||||
Name: "sku",
|
||||
Name: "machine",
|
||||
Prompt: &survey.Select{
|
||||
Message: "Choose Machine Type:",
|
||||
Options: skuNames,
|
||||
Default: skuNames[0],
|
||||
Options: machineNames,
|
||||
Default: machineNames[0],
|
||||
},
|
||||
Validate: survey.Required,
|
||||
},
|
||||
}
|
||||
|
||||
var skuAnswers struct{ SKU string }
|
||||
if err := ask(skuSurvey, &skuAnswers); err != nil {
|
||||
return "", fmt.Errorf("error getting SKU: %w", err)
|
||||
var machineAnswers struct{ Machine string }
|
||||
if err := ask(machineSurvey, &machineAnswers); err != nil {
|
||||
return "", fmt.Errorf("error getting machine: %w", err)
|
||||
}
|
||||
|
||||
sku := skuByName[skuAnswers.SKU]
|
||||
machine = sku.Name
|
||||
selectedMachine := machineByName[machineAnswers.Machine]
|
||||
|
||||
return machine, nil
|
||||
return selectedMachine.Name, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package ghcs
|
||||
package codespace
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -80,7 +80,6 @@ func (a *App) Delete(ctx context.Context, opts deleteOptions) error {
|
|||
nameFilter = c.Name
|
||||
}
|
||||
} else {
|
||||
// TODO: this token is discarded and then re-requested later in DeleteCodespace
|
||||
token, err := a.apiClient.GetCodespaceToken(ctx, user.Login, nameFilter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting codespace token: %w", err)
|
||||
|
|
@ -132,7 +131,7 @@ func (a *App) Delete(ctx context.Context, opts deleteOptions) error {
|
|||
for _, c := range codespacesToDelete {
|
||||
codespaceName := c.Name
|
||||
g.Go(func() error {
|
||||
if err := a.apiClient.DeleteCodespace(ctx, user.Login, codespaceName); err != nil {
|
||||
if err := a.apiClient.DeleteCodespace(ctx, codespaceName); err != nil {
|
||||
_, _ = a.logger.Errorf("error deleting codespace %q: %v\n", codespaceName, err)
|
||||
return err
|
||||
}
|
||||
|
|
@ -143,6 +142,13 @@ func (a *App) Delete(ctx context.Context, opts deleteOptions) error {
|
|||
if err := g.Wait(); err != nil {
|
||||
return errors.New("some codespaces failed to delete")
|
||||
}
|
||||
|
||||
noun := "Codespace"
|
||||
if len(codespacesToDelete) > 1 {
|
||||
noun = noun + "s"
|
||||
}
|
||||
a.logger.Println(noun + " deleted.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package ghcs
|
||||
package codespace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
@ -157,10 +157,7 @@ func TestDelete(t *testing.T) {
|
|||
GetUserFunc: func(_ context.Context) (*api.User, error) {
|
||||
return user, nil
|
||||
},
|
||||
DeleteCodespaceFunc: func(_ context.Context, userLogin, name string) error {
|
||||
if userLogin != user.Login {
|
||||
return fmt.Errorf("unexpected user %q", userLogin)
|
||||
}
|
||||
DeleteCodespaceFunc: func(_ context.Context, name string) error {
|
||||
if tt.deleteErr != nil {
|
||||
return tt.deleteErr
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package ghcs
|
||||
package codespace
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package ghcs
|
||||
package codespace
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Code generated by moq; DO NOT EDIT.
|
||||
// github.com/matryer/moq
|
||||
|
||||
package ghcs
|
||||
package codespace
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -23,7 +23,7 @@ import (
|
|||
// CreateCodespaceFunc: func(ctx context.Context, params *api.CreateCodespaceParams) (*codespace.Codespace, error) {
|
||||
// panic("mock out the CreateCodespace method")
|
||||
// },
|
||||
// DeleteCodespaceFunc: func(ctx context.Context, user string, name string) error {
|
||||
// DeleteCodespaceFunc: func(ctx context.Context, name string) error {
|
||||
// panic("mock out the DeleteCodespace method")
|
||||
// },
|
||||
// GetCodespaceFunc: func(ctx context.Context, token string, user string, name string) (*codespace.Codespace, error) {
|
||||
|
|
@ -38,8 +38,8 @@ import (
|
|||
// GetCodespaceTokenFunc: func(ctx context.Context, user string, name string) (string, error) {
|
||||
// panic("mock out the GetCodespaceToken method")
|
||||
// },
|
||||
// GetCodespacesSKUsFunc: func(ctx context.Context, user *api.User, repository *api.Repository, branch string, location string) ([]*api.SKU, error) {
|
||||
// panic("mock out the GetCodespacesSKUs method")
|
||||
// GetCodespacesMachinesFunc: func(ctx context.Context, repoID int, branch string, location string) ([]*api.Machine, error) {
|
||||
// panic("mock out the GetCodespacesMachines method")
|
||||
// },
|
||||
// GetRepositoryFunc: func(ctx context.Context, nwo string) (*api.Repository, error) {
|
||||
// panic("mock out the GetRepository method")
|
||||
|
|
@ -50,7 +50,7 @@ import (
|
|||
// ListCodespacesFunc: func(ctx context.Context) ([]*codespace.Codespace, error) {
|
||||
// panic("mock out the ListCodespaces method")
|
||||
// },
|
||||
// StartCodespaceFunc: func(ctx context.Context, token string, codespaceMoqParam *codespace.Codespace) error {
|
||||
// StartCodespaceFunc: func(ctx context.Context, name string) error {
|
||||
// panic("mock out the StartCodespace method")
|
||||
// },
|
||||
// }
|
||||
|
|
@ -67,7 +67,7 @@ type apiClientMock struct {
|
|||
CreateCodespaceFunc func(ctx context.Context, params *api.CreateCodespaceParams) (*codespace.Codespace, error)
|
||||
|
||||
// DeleteCodespaceFunc mocks the DeleteCodespace method.
|
||||
DeleteCodespaceFunc func(ctx context.Context, user string, name string) error
|
||||
DeleteCodespaceFunc func(ctx context.Context, name string) error
|
||||
|
||||
// GetCodespaceFunc mocks the GetCodespace method.
|
||||
GetCodespaceFunc func(ctx context.Context, token string, user string, name string) (*codespace.Codespace, error)
|
||||
|
|
@ -81,8 +81,8 @@ type apiClientMock struct {
|
|||
// GetCodespaceTokenFunc mocks the GetCodespaceToken method.
|
||||
GetCodespaceTokenFunc func(ctx context.Context, user string, name string) (string, error)
|
||||
|
||||
// GetCodespacesSKUsFunc mocks the GetCodespacesSKUs method.
|
||||
GetCodespacesSKUsFunc func(ctx context.Context, user *api.User, repository *api.Repository, branch string, location string) ([]*api.SKU, error)
|
||||
// GetCodespacesMachinesFunc mocks the GetCodespacesMachines method.
|
||||
GetCodespacesMachinesFunc func(ctx context.Context, repoID int, branch string, location string) ([]*api.Machine, error)
|
||||
|
||||
// GetRepositoryFunc mocks the GetRepository method.
|
||||
GetRepositoryFunc func(ctx context.Context, nwo string) (*api.Repository, error)
|
||||
|
|
@ -94,7 +94,7 @@ type apiClientMock struct {
|
|||
ListCodespacesFunc func(ctx context.Context) ([]*codespace.Codespace, error)
|
||||
|
||||
// StartCodespaceFunc mocks the StartCodespace method.
|
||||
StartCodespaceFunc func(ctx context.Context, token string, codespaceMoqParam *codespace.Codespace) error
|
||||
StartCodespaceFunc func(ctx context.Context, name string) error
|
||||
|
||||
// calls tracks calls to the methods.
|
||||
calls struct {
|
||||
|
|
@ -116,8 +116,6 @@ type apiClientMock struct {
|
|||
DeleteCodespace []struct {
|
||||
// Ctx is the ctx argument value.
|
||||
Ctx context.Context
|
||||
// User is the user argument value.
|
||||
User string
|
||||
// Name is the name argument value.
|
||||
Name string
|
||||
}
|
||||
|
|
@ -155,14 +153,12 @@ type apiClientMock struct {
|
|||
// Name is the name argument value.
|
||||
Name string
|
||||
}
|
||||
// GetCodespacesSKUs holds details about calls to the GetCodespacesSKUs method.
|
||||
GetCodespacesSKUs []struct {
|
||||
// GetCodespacesMachines holds details about calls to the GetCodespacesMachines method.
|
||||
GetCodespacesMachines []struct {
|
||||
// Ctx is the ctx argument value.
|
||||
Ctx context.Context
|
||||
// User is the user argument value.
|
||||
User *api.User
|
||||
// Repository is the repository argument value.
|
||||
Repository *api.Repository
|
||||
// RepoID is the repoID argument value.
|
||||
RepoID int
|
||||
// Branch is the branch argument value.
|
||||
Branch string
|
||||
// Location is the location argument value.
|
||||
|
|
@ -189,10 +185,8 @@ type apiClientMock struct {
|
|||
StartCodespace []struct {
|
||||
// Ctx is the ctx argument value.
|
||||
Ctx context.Context
|
||||
// Token is the token argument value.
|
||||
Token string
|
||||
// CodespaceMoqParam is the codespaceMoqParam argument value.
|
||||
CodespaceMoqParam *codespace.Codespace
|
||||
// Name is the name argument value.
|
||||
Name string
|
||||
}
|
||||
}
|
||||
lockAuthorizedKeys sync.RWMutex
|
||||
|
|
@ -202,7 +196,7 @@ type apiClientMock struct {
|
|||
lockGetCodespaceRegionLocation sync.RWMutex
|
||||
lockGetCodespaceRepositoryContents sync.RWMutex
|
||||
lockGetCodespaceToken sync.RWMutex
|
||||
lockGetCodespacesSKUs sync.RWMutex
|
||||
lockGetCodespacesMachines sync.RWMutex
|
||||
lockGetRepository sync.RWMutex
|
||||
lockGetUser sync.RWMutex
|
||||
lockListCodespaces sync.RWMutex
|
||||
|
|
@ -280,23 +274,21 @@ func (mock *apiClientMock) CreateCodespaceCalls() []struct {
|
|||
}
|
||||
|
||||
// DeleteCodespace calls DeleteCodespaceFunc.
|
||||
func (mock *apiClientMock) DeleteCodespace(ctx context.Context, user string, name string) error {
|
||||
func (mock *apiClientMock) DeleteCodespace(ctx context.Context, name string) error {
|
||||
if mock.DeleteCodespaceFunc == nil {
|
||||
panic("apiClientMock.DeleteCodespaceFunc: method is nil but apiClient.DeleteCodespace was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Ctx context.Context
|
||||
User string
|
||||
Name string
|
||||
}{
|
||||
Ctx: ctx,
|
||||
User: user,
|
||||
Name: name,
|
||||
}
|
||||
mock.lockDeleteCodespace.Lock()
|
||||
mock.calls.DeleteCodespace = append(mock.calls.DeleteCodespace, callInfo)
|
||||
mock.lockDeleteCodespace.Unlock()
|
||||
return mock.DeleteCodespaceFunc(ctx, user, name)
|
||||
return mock.DeleteCodespaceFunc(ctx, name)
|
||||
}
|
||||
|
||||
// DeleteCodespaceCalls gets all the calls that were made to DeleteCodespace.
|
||||
|
|
@ -304,12 +296,10 @@ func (mock *apiClientMock) DeleteCodespace(ctx context.Context, user string, nam
|
|||
// len(mockedapiClient.DeleteCodespaceCalls())
|
||||
func (mock *apiClientMock) DeleteCodespaceCalls() []struct {
|
||||
Ctx context.Context
|
||||
User string
|
||||
Name string
|
||||
} {
|
||||
var calls []struct {
|
||||
Ctx context.Context
|
||||
User string
|
||||
Name string
|
||||
}
|
||||
mock.lockDeleteCodespace.RLock()
|
||||
|
|
@ -470,50 +460,46 @@ func (mock *apiClientMock) GetCodespaceTokenCalls() []struct {
|
|||
return calls
|
||||
}
|
||||
|
||||
// GetCodespacesSKUs calls GetCodespacesSKUsFunc.
|
||||
func (mock *apiClientMock) GetCodespacesSKUs(ctx context.Context, user *api.User, repository *api.Repository, branch string, location string) ([]*api.SKU, error) {
|
||||
if mock.GetCodespacesSKUsFunc == nil {
|
||||
panic("apiClientMock.GetCodespacesSKUsFunc: method is nil but apiClient.GetCodespacesSKUs was just called")
|
||||
// GetCodespacesMachines calls GetCodespacesMachinesFunc.
|
||||
func (mock *apiClientMock) GetCodespacesMachines(ctx context.Context, repoID int, branch string, location string) ([]*api.Machine, error) {
|
||||
if mock.GetCodespacesMachinesFunc == nil {
|
||||
panic("apiClientMock.GetCodespacesMachinesFunc: method is nil but apiClient.GetCodespacesMachines was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Ctx context.Context
|
||||
User *api.User
|
||||
Repository *api.Repository
|
||||
Branch string
|
||||
Location string
|
||||
Ctx context.Context
|
||||
RepoID int
|
||||
Branch string
|
||||
Location string
|
||||
}{
|
||||
Ctx: ctx,
|
||||
User: user,
|
||||
Repository: repository,
|
||||
Branch: branch,
|
||||
Location: location,
|
||||
Ctx: ctx,
|
||||
RepoID: repoID,
|
||||
Branch: branch,
|
||||
Location: location,
|
||||
}
|
||||
mock.lockGetCodespacesSKUs.Lock()
|
||||
mock.calls.GetCodespacesSKUs = append(mock.calls.GetCodespacesSKUs, callInfo)
|
||||
mock.lockGetCodespacesSKUs.Unlock()
|
||||
return mock.GetCodespacesSKUsFunc(ctx, user, repository, branch, location)
|
||||
mock.lockGetCodespacesMachines.Lock()
|
||||
mock.calls.GetCodespacesMachines = append(mock.calls.GetCodespacesMachines, callInfo)
|
||||
mock.lockGetCodespacesMachines.Unlock()
|
||||
return mock.GetCodespacesMachinesFunc(ctx, repoID, branch, location)
|
||||
}
|
||||
|
||||
// GetCodespacesSKUsCalls gets all the calls that were made to GetCodespacesSKUs.
|
||||
// GetCodespacesMachinesCalls gets all the calls that were made to GetCodespacesMachines.
|
||||
// Check the length with:
|
||||
// len(mockedapiClient.GetCodespacesSKUsCalls())
|
||||
func (mock *apiClientMock) GetCodespacesSKUsCalls() []struct {
|
||||
Ctx context.Context
|
||||
User *api.User
|
||||
Repository *api.Repository
|
||||
Branch string
|
||||
Location string
|
||||
// len(mockedapiClient.GetCodespacesMachinesCalls())
|
||||
func (mock *apiClientMock) GetCodespacesMachinesCalls() []struct {
|
||||
Ctx context.Context
|
||||
RepoID int
|
||||
Branch string
|
||||
Location string
|
||||
} {
|
||||
var calls []struct {
|
||||
Ctx context.Context
|
||||
User *api.User
|
||||
Repository *api.Repository
|
||||
Branch string
|
||||
Location string
|
||||
Ctx context.Context
|
||||
RepoID int
|
||||
Branch string
|
||||
Location string
|
||||
}
|
||||
mock.lockGetCodespacesSKUs.RLock()
|
||||
calls = mock.calls.GetCodespacesSKUs
|
||||
mock.lockGetCodespacesSKUs.RUnlock()
|
||||
mock.lockGetCodespacesMachines.RLock()
|
||||
calls = mock.calls.GetCodespacesMachines
|
||||
mock.lockGetCodespacesMachines.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
|
|
@ -615,37 +601,33 @@ func (mock *apiClientMock) ListCodespacesCalls() []struct {
|
|||
}
|
||||
|
||||
// StartCodespace calls StartCodespaceFunc.
|
||||
func (mock *apiClientMock) StartCodespace(ctx context.Context, token string, codespaceMoqParam *codespace.Codespace) error {
|
||||
func (mock *apiClientMock) StartCodespace(ctx context.Context, name string) error {
|
||||
if mock.StartCodespaceFunc == nil {
|
||||
panic("apiClientMock.StartCodespaceFunc: method is nil but apiClient.StartCodespace was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Ctx context.Context
|
||||
Token string
|
||||
CodespaceMoqParam *codespace.Codespace
|
||||
Ctx context.Context
|
||||
Name string
|
||||
}{
|
||||
Ctx: ctx,
|
||||
Token: token,
|
||||
CodespaceMoqParam: codespaceMoqParam,
|
||||
Ctx: ctx,
|
||||
Name: name,
|
||||
}
|
||||
mock.lockStartCodespace.Lock()
|
||||
mock.calls.StartCodespace = append(mock.calls.StartCodespace, callInfo)
|
||||
mock.lockStartCodespace.Unlock()
|
||||
return mock.StartCodespaceFunc(ctx, token, codespaceMoqParam)
|
||||
return mock.StartCodespaceFunc(ctx, name)
|
||||
}
|
||||
|
||||
// StartCodespaceCalls gets all the calls that were made to StartCodespace.
|
||||
// Check the length with:
|
||||
// len(mockedapiClient.StartCodespaceCalls())
|
||||
func (mock *apiClientMock) StartCodespaceCalls() []struct {
|
||||
Ctx context.Context
|
||||
Token string
|
||||
CodespaceMoqParam *codespace.Codespace
|
||||
Ctx context.Context
|
||||
Name string
|
||||
} {
|
||||
var calls []struct {
|
||||
Ctx context.Context
|
||||
Token string
|
||||
CodespaceMoqParam *codespace.Codespace
|
||||
Ctx context.Context
|
||||
Name string
|
||||
}
|
||||
mock.lockStartCodespace.RLock()
|
||||
calls = mock.calls.StartCodespace
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Code generated by moq; DO NOT EDIT.
|
||||
// github.com/matryer/moq
|
||||
|
||||
package ghcs
|
||||
package codespace
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package ghcs
|
||||
package codespace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
@ -79,7 +79,7 @@ func (a *App) ListPorts(ctx context.Context, codespaceName string, asJSON bool)
|
|||
|
||||
devContainerResult := <-devContainerCh
|
||||
if devContainerResult.err != nil {
|
||||
// Warn about failure to read the devcontainer file. Not a ghcs command error.
|
||||
// Warn about failure to read the devcontainer file. Not a codespace command error.
|
||||
_, _ = a.logger.Errorf("Failed to get port names: %v\n", devContainerResult.err.Error())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package ghcs
|
||||
package codespace
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
|
@ -8,7 +8,7 @@ var version = "DEV" // Replaced in the release build process (by GoReleaser or H
|
|||
|
||||
func NewRootCmd(app *App) *cobra.Command {
|
||||
root := &cobra.Command{
|
||||
Use: "ghcs",
|
||||
Use: "codespace",
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package ghcs
|
||||
package codespace
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue