diff --git a/cli/create.go b/cli/create.go index c9da10f49d..5ad4cbf317 100644 --- a/cli/create.go +++ b/cli/create.go @@ -46,6 +46,7 @@ func (r *RootCmd) Create(opts CreateOptions) *serpent.Command { autoUpdates string copyParametersFrom string useParameterDefaults bool + noWait bool // Organization context is only required if more than 1 template // shares the same name across multiple organizations. orgContext = NewOrganizationContext() @@ -372,6 +373,14 @@ func (r *RootCmd) Create(opts CreateOptions) *serpent.Command { cliutil.WarnMatchedProvisioners(inv.Stderr, workspace.LatestBuild.MatchedProvisioners, workspace.LatestBuild.Job) + if noWait { + _, _ = fmt.Fprintf(inv.Stdout, + "\nThe %s workspace has been created and is building in the background.\n", + cliui.Keyword(workspace.Name), + ) + return nil + } + err = cliui.WorkspaceBuild(inv.Context(), inv.Stdout, client, workspace.LatestBuild.ID) if err != nil { return xerrors.Errorf("watch build: %w", err) @@ -445,6 +454,12 @@ func (r *RootCmd) Create(opts CreateOptions) *serpent.Command { Description: "Automatically accept parameter defaults when no value is provided.", Value: serpent.BoolOf(&useParameterDefaults), }, + serpent.Option{ + Flag: "no-wait", + Env: "CODER_CREATE_NO_WAIT", + Description: "Return immediately after creating the workspace. The build will run in the background.", + Value: serpent.BoolOf(&noWait), + }, cliui.SkipPromptOption(), ) cmd.Options = append(cmd.Options, parameterFlags.cliParameters()...) diff --git a/cli/create_test.go b/cli/create_test.go index e97539b195..e7f387584e 100644 --- a/cli/create_test.go +++ b/cli/create_test.go @@ -603,6 +603,81 @@ func TestCreate(t *testing.T) { assert.Nil(t, ws.AutostartSchedule, "expected workspace autostart schedule to be nil") } }) + + t.Run("NoWait", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + owner := coderdtest.CreateFirstUser(t, client) + member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) + version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil) + coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID) + + ctx := testutil.Context(t, testutil.WaitLong) + inv, root := clitest.New(t, "create", "my-workspace", + "--template", template.Name, + "-y", + "--no-wait", + ) + clitest.SetupConfig(t, member, root) + doneChan := make(chan struct{}) + pty := ptytest.New(t).Attach(inv) + go func() { + defer close(doneChan) + err := inv.Run() + assert.NoError(t, err) + }() + + pty.ExpectMatchContext(ctx, "building in the background") + _ = testutil.TryReceive(ctx, t, doneChan) + + // Verify workspace was actually created. + ws, err := member.WorkspaceByOwnerAndName(ctx, codersdk.Me, "my-workspace", codersdk.WorkspaceOptions{}) + require.NoError(t, err) + assert.Equal(t, ws.TemplateName, template.Name) + }) + + t.Run("NoWaitWithParameterDefaults", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + owner := coderdtest.CreateFirstUser(t, client) + member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) + version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, prepareEchoResponses([]*proto.RichParameter{ + {Name: "region", Type: "string", DefaultValue: "us-east-1"}, + {Name: "instance_type", Type: "string", DefaultValue: "t3.micro"}, + })) + coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID) + + ctx := testutil.Context(t, testutil.WaitLong) + inv, root := clitest.New(t, "create", "my-workspace", + "--template", template.Name, + "-y", + "--use-parameter-defaults", + "--no-wait", + ) + clitest.SetupConfig(t, member, root) + doneChan := make(chan struct{}) + pty := ptytest.New(t).Attach(inv) + go func() { + defer close(doneChan) + err := inv.Run() + assert.NoError(t, err) + }() + + pty.ExpectMatchContext(ctx, "building in the background") + _ = testutil.TryReceive(ctx, t, doneChan) + + // Verify workspace was created and parameters were applied. + ws, err := member.WorkspaceByOwnerAndName(ctx, codersdk.Me, "my-workspace", codersdk.WorkspaceOptions{}) + require.NoError(t, err) + assert.Equal(t, ws.TemplateName, template.Name) + + buildParams, err := member.WorkspaceBuildParameters(ctx, ws.LatestBuild.ID) + require.NoError(t, err) + assert.Contains(t, buildParams, codersdk.WorkspaceBuildParameter{Name: "region", Value: "us-east-1"}) + assert.Contains(t, buildParams, codersdk.WorkspaceBuildParameter{Name: "instance_type", Value: "t3.micro"}) + }) } func prepareEchoResponses(parameters []*proto.RichParameter, presets ...*proto.Preset) *echo.Responses { diff --git a/cli/testdata/coder_create_--help.golden b/cli/testdata/coder_create_--help.golden index 1292af1777..b1f5968c7a 100644 --- a/cli/testdata/coder_create_--help.golden +++ b/cli/testdata/coder_create_--help.golden @@ -20,6 +20,10 @@ OPTIONS: --copy-parameters-from string, $CODER_WORKSPACE_COPY_PARAMETERS_FROM Specify the source workspace name to copy parameters from. + --no-wait bool, $CODER_CREATE_NO_WAIT + Return immediately after creating the workspace. The build will run in + the background. + --parameter string-array, $CODER_RICH_PARAMETER Rich parameter value in the format "name=value". diff --git a/docs/reference/cli/create.md b/docs/reference/cli/create.md index c3a2cbe352..0b13a4c94b 100644 --- a/docs/reference/cli/create.md +++ b/docs/reference/cli/create.md @@ -92,6 +92,15 @@ Specify the source workspace name to copy parameters from. Automatically accept parameter defaults when no value is provided. +### --no-wait + +| | | +|-------------|------------------------------------| +| Type | bool | +| Environment | $CODER_CREATE_NO_WAIT | + +Return immediately after creating the workspace. The build will run in the background. + ### -y, --yes | | | diff --git a/docs/reference/cli/external-workspaces_create.md b/docs/reference/cli/external-workspaces_create.md index 6d1f21df8f..86a33c2e48 100644 --- a/docs/reference/cli/external-workspaces_create.md +++ b/docs/reference/cli/external-workspaces_create.md @@ -92,6 +92,15 @@ Specify the source workspace name to copy parameters from. Automatically accept parameter defaults when no value is provided. +### --no-wait + +| | | +|-------------|------------------------------------| +| Type | bool | +| Environment | $CODER_CREATE_NO_WAIT | + +Return immediately after creating the workspace. The build will run in the background. + ### -y, --yes | | | diff --git a/enterprise/cli/testdata/coder_external-workspaces_create_--help.golden b/enterprise/cli/testdata/coder_external-workspaces_create_--help.golden index 9ec3834235..6f33cda59b 100644 --- a/enterprise/cli/testdata/coder_external-workspaces_create_--help.golden +++ b/enterprise/cli/testdata/coder_external-workspaces_create_--help.golden @@ -20,6 +20,10 @@ OPTIONS: --copy-parameters-from string, $CODER_WORKSPACE_COPY_PARAMETERS_FROM Specify the source workspace name to copy parameters from. + --no-wait bool, $CODER_CREATE_NO_WAIT + Return immediately after creating the workspace. The build will run in + the background. + --parameter string-array, $CODER_RICH_PARAMETER Rich parameter value in the format "name=value".