diff --git a/cli/templatepush.go b/cli/templatepush.go index bc5516b092ef9..1b0639e55c2af 100644 --- a/cli/templatepush.go +++ b/cli/templatepush.go @@ -163,6 +163,7 @@ func (r *RootCmd) templatePush() *clibase.Cmd { provisionerTags []string uploadFlags templateUploadFlags activate bool + create bool ) client := new(codersdk.Client) cmd := &clibase.Cmd{ @@ -185,9 +186,13 @@ func (r *RootCmd) templatePush() *clibase.Cmd { return err } + var createTemplate bool template, err := client.TemplateByName(inv.Context(), organization.ID, name) if err != nil { - return err + if !create { + return err + } + createTemplate = true } err = uploadFlags.checkForLockfile(inv) @@ -207,19 +212,24 @@ func (r *RootCmd) templatePush() *clibase.Cmd { return err } - job, err := createValidTemplateVersion(inv, createValidTemplateVersionArgs{ - Name: versionName, + args := createValidTemplateVersionArgs{ Message: message, Client: client, Organization: organization, Provisioner: database.ProvisionerType(provisioner), FileID: resp.ID, + ProvisionerTags: tags, VariablesFile: variablesFile, Variables: variables, - Template: &template, - ReuseParameters: !alwaysPrompt, - ProvisionerTags: tags, - }) + } + + if !createTemplate { + args.Name = versionName + args.Template = &template + args.ReuseParameters = !alwaysPrompt + } + + job, err := createValidTemplateVersion(inv, args) if err != nil { return err } @@ -228,7 +238,19 @@ func (r *RootCmd) templatePush() *clibase.Cmd { return xerrors.Errorf("job failed: %s", job.Job.Status) } - if activate { + if createTemplate { + _, err = client.CreateTemplate(inv.Context(), organization.ID, codersdk.CreateTemplateRequest{ + Name: name, + VersionID: job.ID, + }) + if err != nil { + return err + } + + _, _ = fmt.Fprintln(inv.Stdout, "\n"+cliui.DefaultStyles.Wrap.Render( + "The "+cliui.DefaultStyles.Keyword.Render(name)+" template has been created at "+cliui.DefaultStyles.DateTimeStamp.Render(time.Now().Format(time.Stamp))+"! "+ + "Developers can provision a workspace with this template using:")+"\n") + } else if activate { err = client.UpdateActiveTemplateVersion(inv.Context(), template.ID, codersdk.UpdateActiveTemplateVersion{ ID: job.ID, }) @@ -290,6 +312,12 @@ func (r *RootCmd) templatePush() *clibase.Cmd { Default: "true", Value: clibase.BoolOf(&activate), }, + { + Flag: "create", + Description: "Create the template if it does not exist.", + Default: "false", + Value: clibase.BoolOf(&create), + }, cliui.SkipPromptOption(), } cmd.Options = append(cmd.Options, uploadFlags.options()...) diff --git a/cli/templatepush_test.go b/cli/templatepush_test.go index c890ea6593b94..88a1ce250543c 100644 --- a/cli/templatepush_test.go +++ b/cli/templatepush_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -613,6 +614,52 @@ func TestTemplatePush(t *testing.T) { require.Equal(t, "second_variable", templateVariables[1].Name) require.Equal(t, "foobar", templateVariables[1].Value) }) + + t.Run("CreateTemplate", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + user := coderdtest.CreateFirstUser(t, client) + source := clitest.CreateTemplateVersionSource(t, &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionApply: provisionCompleteWithAgent, + }) + + const templateName = "my-template" + args := []string{ + "templates", + "push", + templateName, + "--directory", source, + "--test.provisioner", string(database.ProvisionerTypeEcho), + "--create", + } + inv, root := clitest.New(t, args...) + clitest.SetupConfig(t, client, root) + pty := ptytest.New(t).Attach(inv) + + waiter := clitest.StartWithWaiter(t, inv) + + matches := []struct { + match string + write string + }{ + {match: "Upload", write: "yes"}, + {match: "template has been created"}, + } + for _, m := range matches { + pty.ExpectMatch(m.match) + if m.write != "" { + pty.WriteLine(m.write) + } + } + + waiter.RequireSuccess() + + template, err := client.TemplateByName(context.Background(), user.OrganizationID, templateName) + require.NoError(t, err) + require.Equal(t, templateName, template.Name) + require.NotEqual(t, uuid.Nil, template.ActiveVersionID) + }) }) } diff --git a/cli/testdata/coder_templates_push_--help.golden b/cli/testdata/coder_templates_push_--help.golden index 6aeff1641fd0d..7dc7b2e87ff7f 100644 --- a/cli/testdata/coder_templates_push_--help.golden +++ b/cli/testdata/coder_templates_push_--help.golden @@ -10,6 +10,9 @@ Push a new template version from the current directory or as specified by flag Always prompt all parameters. Does not pull parameter values from active template version. + --create bool (default: false) + Create the template if it does not exist. + -d, --directory string (default: .) Specify the directory to create from, use '-' to read tar from stdin. diff --git a/docs/cli/templates_push.md b/docs/cli/templates_push.md index 4043de558198b..fb3068ed5b15d 100644 --- a/docs/cli/templates_push.md +++ b/docs/cli/templates_push.md @@ -29,6 +29,15 @@ Whether the new template will be marked active. Always prompt all parameters. Does not pull parameter values from active template version. +### --create + +| | | +| ------- | ------------------ | +| Type | bool | +| Default | false | + +Create the template if it does not exist. + ### -d, --directory | | |