diff --git a/internal/codespaces/api/api.go b/internal/codespaces/api/api.go index 49b41b7e5..e151dfb54 100644 --- a/internal/codespaces/api/api.go +++ b/internal/codespaces/api/api.go @@ -183,6 +183,7 @@ type Codespace struct { VSCSTarget string `json:"vscs_target"` PendingOperation bool `json:"pending_operation"` PendingOperationDisabledReason string `json:"pending_operation_disabled_reason"` + IdleTimeoutNotice string `json:"idle_timeout_notice"` } type CodespaceGitStatus struct { diff --git a/pkg/cmd/codespace/create.go b/pkg/cmd/codespace/create.go index 81bb57bc4..f32b0fde9 100644 --- a/pkg/cmd/codespace/create.go +++ b/pkg/cmd/codespace/create.go @@ -48,7 +48,6 @@ func newCreateCmd(app *App) *cobra.Command { // Create creates a new Codespace func (a *App) Create(ctx context.Context, opts createOptions) error { - // Overrides for Codespace developers to target test environments vscsLocation := os.Getenv("VSCS_LOCATION") vscsTarget := os.Getenv("VSCS_TARGET") @@ -153,7 +152,14 @@ func (a *App) Create(ctx context.Context, opts createOptions) error { } } + cs := a.io.ColorScheme() + fmt.Fprintln(a.io.Out, codespace.Name) + + if a.io.IsStderrTTY() && codespace.IdleTimeoutNotice != "" { + fmt.Fprintln(a.io.ErrOut, cs.Yellow("Notice:"), codespace.IdleTimeoutNotice) + } + return nil } diff --git a/pkg/cmd/codespace/create_test.go b/pkg/cmd/codespace/create_test.go index 2d417620d..0c2ff96bc 100644 --- a/pkg/cmd/codespace/create_test.go +++ b/pkg/cmd/codespace/create_test.go @@ -22,6 +22,7 @@ func TestApp_Create(t *testing.T) { wantErr error wantStdout string wantStderr string + isTTY bool }{ { name: "create codespace with default branch and 30m idle timeout", @@ -70,6 +71,106 @@ func TestApp_Create(t *testing.T) { }, wantStdout: "monalisa-dotfiles-abcd1234\n", }, + { + name: "create codespace with default branch shows idle timeout notice if present", + fields: fields{ + apiClient: &apiClientMock{ + GetCodespaceRegionLocationFunc: func(ctx context.Context) (string, error) { + return "EUROPE", nil + }, + GetRepositoryFunc: func(ctx context.Context, nwo string) (*api.Repository, error) { + return &api.Repository{ + ID: 1234, + FullName: nwo, + DefaultBranch: "main", + }, nil + }, + GetCodespacesMachinesFunc: func(ctx context.Context, repoID int, branch, location string) ([]*api.Machine, error) { + return []*api.Machine{ + { + Name: "GIGA", + DisplayName: "Gigabits of a machine", + }, + }, nil + }, + CreateCodespaceFunc: func(ctx context.Context, params *api.CreateCodespaceParams) (*api.Codespace, error) { + if params.Branch != "main" { + return nil, fmt.Errorf("got branch %q, want %q", params.Branch, "main") + } + if params.IdleTimeoutMinutes != 30 { + return nil, fmt.Errorf("idle timeout minutes was %v", params.IdleTimeoutMinutes) + } + return &api.Codespace{ + Name: "monalisa-dotfiles-abcd1234", + IdleTimeoutNotice: "Idle timeout for this codespace is set to 10 minutes in compliance with your organization's policy", + }, nil + }, + GetCodespaceRepoSuggestionsFunc: func(ctx context.Context, partialSearch string, params api.RepoSearchParameters) ([]string, error) { + return nil, nil // We can't ask for suggestions without a terminal. + }, + }, + }, + opts: createOptions{ + repo: "monalisa/dotfiles", + branch: "", + machine: "GIGA", + showStatus: false, + idleTimeout: 30 * time.Minute, + }, + wantStdout: "monalisa-dotfiles-abcd1234\n", + wantStderr: "Notice: Idle timeout for this codespace is set to 10 minutes in compliance with your organization's policy\n", + isTTY: true, + }, + { + name: "create codespace with default branch does not show idle timeout notice if not conntected to terminal", + fields: fields{ + apiClient: &apiClientMock{ + GetCodespaceRegionLocationFunc: func(ctx context.Context) (string, error) { + return "EUROPE", nil + }, + GetRepositoryFunc: func(ctx context.Context, nwo string) (*api.Repository, error) { + return &api.Repository{ + ID: 1234, + FullName: nwo, + DefaultBranch: "main", + }, nil + }, + GetCodespacesMachinesFunc: func(ctx context.Context, repoID int, branch, location string) ([]*api.Machine, error) { + return []*api.Machine{ + { + Name: "GIGA", + DisplayName: "Gigabits of a machine", + }, + }, nil + }, + CreateCodespaceFunc: func(ctx context.Context, params *api.CreateCodespaceParams) (*api.Codespace, error) { + if params.Branch != "main" { + return nil, fmt.Errorf("got branch %q, want %q", params.Branch, "main") + } + if params.IdleTimeoutMinutes != 30 { + return nil, fmt.Errorf("idle timeout minutes was %v", params.IdleTimeoutMinutes) + } + return &api.Codespace{ + Name: "monalisa-dotfiles-abcd1234", + IdleTimeoutNotice: "Idle timeout for this codespace is set to 10 minutes in compliance with your organization's policy", + }, nil + }, + GetCodespaceRepoSuggestionsFunc: func(ctx context.Context, partialSearch string, params api.RepoSearchParameters) ([]string, error) { + return nil, nil // We can't ask for suggestions without a terminal. + }, + }, + }, + opts: createOptions{ + repo: "monalisa/dotfiles", + branch: "", + machine: "GIGA", + showStatus: false, + idleTimeout: 30 * time.Minute, + }, + wantStdout: "monalisa-dotfiles-abcd1234\n", + wantStderr: "", + isTTY: false, + }, { name: "create codespace that requires accepting additional permissions", fields: fields{ @@ -125,6 +226,10 @@ Alternatively, you can run "create" with the "--default-permissions" option to c for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { io, _, stdout, stderr := iostreams.Test() + io.SetStdoutTTY(tt.isTTY) + io.SetStdinTTY(tt.isTTY) + io.SetStderrTTY(tt.isTTY) + a := &App{ io: io, apiClient: tt.fields.apiClient,