Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c26a812

Browse files
committed
Improve workspace create flow
1 parent c50d170 commit c26a812

File tree

10 files changed

+198
-77
lines changed

10 files changed

+198
-77
lines changed

cli/cliui/prompt.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,33 @@ func Prompt(cmd *cobra.Command, opts PromptOptions) (string, error) {
6565
// it parse properly.
6666
if err == nil && (strings.HasPrefix(line, "{") || strings.HasPrefix(line, "[")) {
6767
var data bytes.Buffer
68-
_, _ = data.WriteString(line)
6968
for {
70-
// Read line-by-line. We can't use a JSON decoder
71-
// here because it doesn't work by newline, so
72-
// reads will block.
73-
line, err = reader.ReadString('\n')
74-
if err != nil {
75-
break
76-
}
7769
_, _ = data.WriteString(line)
7870
var rawMessage json.RawMessage
7971
err = json.Unmarshal(data.Bytes(), &rawMessage)
8072
if err != nil {
73+
if err.Error() != "unexpected end of JSON input" {
74+
err = nil
75+
line = data.String()
76+
break
77+
}
78+
79+
// Read line-by-line. We can't use a JSON decoder
80+
// here because it doesn't work by newline, so
81+
// reads will block.
82+
line, err = reader.ReadString('\n')
83+
if err != nil {
84+
break
85+
}
8186
continue
8287
}
88+
// Compacting the JSON makes it easier for parsing and testing.
89+
bytes := data.Bytes()
90+
data.Reset()
91+
err = json.Compact(&data, bytes)
92+
if err != nil {
93+
break
94+
}
8395
line = data.String()
8496
break
8597
}

cli/cliui/select.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ func Select(cmd *cobra.Command, opts SelectOptions) (string, error) {
5050
if flag.Lookup("test.v") != nil {
5151
return opts.Options[0], nil
5252
}
53-
opts.HideSearch = false
5453
var value string
5554
err := survey.AskOne(&survey.Select{
5655
Options: opts.Options,

cli/start.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,8 @@ func newProvisionerDaemon(ctx context.Context, client *codersdk.Client, logger s
423423

424424
return provisionerd.New(client.ListenProvisionerDaemon, &provisionerd.Options{
425425
Logger: logger,
426-
PollInterval: 50 * time.Millisecond,
427-
UpdateInterval: 50 * time.Millisecond,
426+
PollInterval: 500 * time.Millisecond,
427+
UpdateInterval: 500 * time.Millisecond,
428428
Provisioners: provisionerd.Provisioners{
429429
string(database.ProvisionerTypeTerraform): proto.NewDRPCProvisionerClient(provisionersdk.Conn(terraformClient)),
430430
},

cli/templatecreate.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package cli
22

33
import (
4-
"errors"
54
"fmt"
65
"os"
76
"path/filepath"
@@ -10,7 +9,6 @@ import (
109
"time"
1110

1211
"github.com/briandowns/spinner"
13-
"github.com/manifoldco/promptui"
1412
"github.com/spf13/cobra"
1513
"golang.org/x/xerrors"
1614

@@ -98,9 +96,6 @@ func templateCreate() *cobra.Command {
9896
IsConfirm: true,
9997
})
10098
if err != nil {
101-
if errors.Is(err, promptui.ErrAbort) {
102-
return nil
103-
}
10499
return err
105100
}
106101
}

cli/templatecreate_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ func TestTemplateCreate(t *testing.T) {
3535
require.NoError(t, err)
3636
}()
3737
matches := []string{
38-
"Create template?", "yes",
38+
"Create and upload", "yes",
39+
"Confirm create?", "yes",
3940
}
4041
for i := 0; i < len(matches); i += 2 {
4142
match := matches[i]

cli/templateinit.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ func templateInit() *cobra.Command {
2828
exampleByName[example.Name] = example
2929
}
3030

31-
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Wrap.Render("A template defines infrastructure as code to be provisioned for individual developer workspaces. Select an example to get started:\n"))
31+
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Wrap.Render(
32+
"A template defines infrastructure as code to be provisioned "+
33+
"for individual developer workspaces. Select an example to get started:\n"))
3234
option, err := cliui.Select(cmd, cliui.SelectOptions{
3335
Options: exampleNames,
3436
})

cli/workspacecreate.go

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,25 @@
11
package cli
22

33
import (
4-
"errors"
54
"fmt"
65
"sort"
76
"time"
87

9-
"github.com/fatih/color"
10-
"github.com/manifoldco/promptui"
118
"github.com/spf13/cobra"
129
"golang.org/x/xerrors"
1310

11+
"github.com/coder/coder/cli/cliflag"
1412
"github.com/coder/coder/cli/cliui"
1513
"github.com/coder/coder/coderd/database"
1614
"github.com/coder/coder/codersdk"
1715
)
1816

1917
func workspaceCreate() *cobra.Command {
2018
var (
21-
templateName string
19+
workspaceName string
2220
)
2321
cmd := &cobra.Command{
24-
Use: "create <name>",
25-
Args: cobra.ExactArgs(1),
22+
Use: "create [template]",
2623
Short: "Create a workspace from a template",
2724
RunE: func(cmd *cobra.Command, args []string) error {
2825
client, err := createClient(cmd)
@@ -34,9 +31,14 @@ func workspaceCreate() *cobra.Command {
3431
return err
3532
}
3633

34+
templateName := ""
35+
if len(args) >= 1 {
36+
templateName = args[0]
37+
}
38+
3739
var template codersdk.Template
3840
if templateName == "" {
39-
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Wrap.Render("Select a template:"))
41+
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Wrap.Render("Select a template below to preview the provisioned infrastructure:"))
4042

4143
templateNames := []string{}
4244
templateByName := map[string]codersdk.Template{}
@@ -45,8 +47,16 @@ func workspaceCreate() *cobra.Command {
4547
return err
4648
}
4749
for _, template := range templates {
48-
templateNames = append(templateNames, template.Name)
49-
templateByName[template.Name] = template
50+
templateName := template.Name
51+
if template.WorkspaceOwnerCount > 0 {
52+
developerText := "developer"
53+
if template.WorkspaceOwnerCount != 1 {
54+
developerText = "developers"
55+
}
56+
templateName += cliui.Styles.Placeholder.Render(fmt.Sprintf(" (used by %d %s)", template.WorkspaceOwnerCount, developerText))
57+
}
58+
templateNames = append(templateNames, templateName)
59+
templateByName[templateName] = template
5060
}
5161
sort.Slice(templateNames, func(i, j int) bool {
5262
return templateByName[templateNames[i]].WorkspaceOwnerCount > templateByName[templateNames[j]].WorkspaceOwnerCount
@@ -70,10 +80,22 @@ func workspaceCreate() *cobra.Command {
7080
}
7181
}
7282

73-
_, _ = fmt.Fprintln(cmd.OutOrStdout())
74-
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Prompt.String()+"Creating with the "+cliui.Styles.Field.Render(template.Name)+" template...")
83+
if workspaceName == "" {
84+
workspaceName, err = cliui.Prompt(cmd, cliui.PromptOptions{
85+
Text: "Specify a name for your workspace:",
86+
Validate: func(workspaceName string) error {
87+
_, err = client.WorkspaceByName(cmd.Context(), codersdk.Me, workspaceName)
88+
if err == nil {
89+
return xerrors.Errorf("A workspace already exists named %q!", workspaceName)
90+
}
91+
return nil
92+
},
93+
})
94+
if err != nil {
95+
return err
96+
}
97+
}
7598

76-
workspaceName := args[0]
7799
_, err = client.WorkspaceByName(cmd.Context(), codersdk.Me, workspaceName)
78100
if err == nil {
79101
return xerrors.Errorf("A workspace already exists named %q!", workspaceName)
@@ -95,10 +117,9 @@ func workspaceCreate() *cobra.Command {
95117
continue
96118
}
97119
if !printed {
98-
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Paragraph.Render("This template has customizable parameters! These can be changed after create, but may have unintended side effects (like data loss).")+"\r\n")
120+
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Paragraph.Render("This template has customizable parameters. Values can be changed after create, but may have unintended side effects (like data loss).")+"\r\n")
99121
printed = true
100122
}
101-
102123
value, err := cliui.ParameterSchema(cmd, parameterSchema)
103124
if err != nil {
104125
return err
@@ -110,11 +131,8 @@ func workspaceCreate() *cobra.Command {
110131
DestinationScheme: parameterSchema.DefaultDestinationScheme,
111132
})
112133
}
113-
if printed {
114-
_, _ = fmt.Fprintln(cmd.OutOrStdout())
115-
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.FocusedPrompt.String()+"Previewing resources...")
116-
_, _ = fmt.Fprintln(cmd.OutOrStdout())
117-
}
134+
_, _ = fmt.Fprintln(cmd.OutOrStdout())
135+
118136
resources, err := client.TemplateVersionResources(cmd.Context(), templateVersion.ID)
119137
if err != nil {
120138
return err
@@ -123,20 +141,17 @@ func workspaceCreate() *cobra.Command {
123141
WorkspaceName: workspaceName,
124142
// Since agent's haven't connected yet, hiding this makes more sense.
125143
HideAgentState: true,
144+
Title: "Workspace Preview",
126145
})
127146
if err != nil {
128147
return err
129148
}
130149

131150
_, err = cliui.Prompt(cmd, cliui.PromptOptions{
132-
Text: fmt.Sprintf("Create workspace %s?", color.HiCyanString(workspaceName)),
133-
Default: "yes",
151+
Text: "Confirm create?",
134152
IsConfirm: true,
135153
})
136154
if err != nil {
137-
if errors.Is(err, promptui.ErrAbort) {
138-
return nil
139-
}
140155
return err
141156
}
142157

@@ -149,29 +164,26 @@ func workspaceCreate() *cobra.Command {
149164
if err != nil {
150165
return err
151166
}
152-
err = cliui.ProvisionerJob(cmd.Context(), cmd.OutOrStdout(), cliui.ProvisionerJobOptions{
153-
Fetch: func() (codersdk.ProvisionerJob, error) {
154-
build, err := client.WorkspaceBuild(cmd.Context(), workspace.LatestBuild.ID)
155-
return build.Job, err
156-
},
157-
Cancel: func() error {
158-
return client.CancelWorkspaceBuild(cmd.Context(), workspace.LatestBuild.ID)
159-
},
160-
Logs: func() (<-chan codersdk.ProvisionerJobLog, error) {
161-
return client.WorkspaceBuildLogsAfter(cmd.Context(), workspace.LatestBuild.ID, before)
162-
},
163-
})
167+
err = cliui.WorkspaceBuild(cmd.Context(), cmd.OutOrStdout(), client, workspace.LatestBuild.ID, before)
168+
if err != nil {
169+
return err
170+
}
171+
resources, err = client.WorkspaceResourcesByBuild(cmd.Context(), workspace.LatestBuild.ID)
164172
if err != nil {
165173
return err
166174
}
167-
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace has been created!\n\n", cliui.Styles.Keyword.Render(workspace.Name))
168-
_, _ = fmt.Fprintln(cmd.OutOrStdout(), " "+cliui.Styles.Code.Render("coder ssh "+workspace.Name))
169-
_, _ = fmt.Fprintln(cmd.OutOrStdout())
170175

171-
return err
176+
err = cliui.WorkspaceResources(cmd.OutOrStdout(), resources, cliui.WorkspaceResourcesOptions{
177+
WorkspaceName: workspaceName,
178+
})
179+
if err != nil {
180+
return err
181+
}
182+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "The %s workspace has been created!\n", cliui.Styles.Keyword.Render(workspace.Name))
183+
return nil
172184
},
173185
}
174-
cmd.Flags().StringVarP(&templateName, "template", "p", "", "Specify a template name.")
186+
cliflag.StringVarP(cmd.Flags(), &workspaceName, "name", "n", "CODER_WORKSPACE_NAME", "", "Specify a workspace name.")
175187

176188
return cmd
177189
}

0 commit comments

Comments
 (0)