diff --git a/cli/templatepush.go b/cli/templatepush.go index 02884aed360b3..ef511f595898f 100644 --- a/cli/templatepush.go +++ b/cli/templatepush.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "io" + "net/http" "path/filepath" "time" @@ -13,6 +14,7 @@ import ( "github.com/coder/coder/cli/clibase" "github.com/coder/coder/cli/cliui" "github.com/coder/coder/coderd/database" + "github.com/coder/coder/coderd/util/ptr" "github.com/coder/coder/codersdk" "github.com/coder/coder/provisionersdk" ) @@ -142,6 +144,7 @@ func (r *RootCmd) templatePush() *clibase.Cmd { provisionerTags []string uploadFlags templateUploadFlags activate bool + create bool ) client := new(codersdk.Client) cmd := &clibase.Cmd{ @@ -164,24 +167,48 @@ func (r *RootCmd) templatePush() *clibase.Cmd { return err } - template, err := client.TemplateByName(inv.Context(), organization.ID, name) + resp, err := uploadFlags.upload(inv, client) if err != nil { return err } - err = uploadFlags.checkForLockfile(inv) - if err != nil { - return xerrors.Errorf("check for lockfile: %w", err) - } - - resp, err := uploadFlags.upload(inv, client) + tags, err := ParseProvisionerTags(provisionerTags) if err != nil { return err } - tags, err := ParseProvisionerTags(provisionerTags) + template, err := client.TemplateByName(inv.Context(), organization.ID, name) if err != nil { - return err + if !create { + if sdkErr, ok := codersdk.AsError(err); ok && sdkErr.StatusCode() != http.StatusNotFound { + return xerrors.Errorf("get template: %w", err) + } + _, _ = fmt.Fprintf(inv.Stdout, "Create a new template with `coder templates create %s`.\n", name) + _, _ = fmt.Fprintf(inv.Stdout, "Or use `coder templates push %s --create`.\n", name) + return xerrors.Errorf("template %q not found: %w", name, err) + } + _, _ = fmt.Fprintf(inv.Stdout, "Creating a new template: %s\n", name) + job, err := createValidTemplateVersion(inv, createValidTemplateVersionArgs{ + Client: client, + Organization: organization, + Provisioner: database.ProvisionerType(provisioner), + FileID: resp.ID, + ProvisionerTags: tags, + VariablesFile: variablesFile, + Variables: variables, + }) + if err != nil { + return err + } + defaultTTL := 24 * time.Hour + failureTTL := 0 * time.Hour + inactivityTTL := 0 * time.Hour + disableEveryone := false + createReq := codersdk.CreateTemplateRequest{Name: name, VersionID: job.ID, DefaultTTLMillis: ptr.Ref(defaultTTL.Milliseconds()), FailureTTLMillis: ptr.Ref(failureTTL.Milliseconds()), InactivityTTLMillis: ptr.Ref(inactivityTTL.Milliseconds()), DisableEveryoneGroupAccess: disableEveryone} + template, err = client.CreateTemplate(inv.Context(), organization.ID, createReq) + if err != nil { + return err + } } job, err := createValidTemplateVersion(inv, createValidTemplateVersionArgs{ @@ -212,7 +239,6 @@ func (r *RootCmd) templatePush() *clibase.Cmd { return err } } - _, _ = fmt.Fprintf(inv.Stdout, "Updated version at %s!\n", cliui.DefaultStyles.DateTimeStamp.Render(time.Now().Format(time.Stamp))) return nil }, @@ -266,6 +292,12 @@ func (r *RootCmd) templatePush() *clibase.Cmd { Default: "true", Value: clibase.BoolOf(&activate), }, + { + Flag: "create", + Description: "Create a new template if one does not already 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 22f5b6a38f1f5..75d7cd1007cd4 100644 --- a/cli/templatepush_test.go +++ b/cli/templatepush_test.go @@ -192,6 +192,43 @@ func TestTemplatePush(t *testing.T) { require.NotEqual(t, "example", templateVersions[0].Name) }) + t.Run("PushTemplateWithCreate", func(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium) + t.Cleanup(cancel) + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + user := coderdtest.CreateFirstUser(t, client) + + source := clitest.CreateTemplateVersionSource(t, &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionApply: echo.ProvisionComplete, + }) + + inv, root := clitest.New(t, "templates", "push", "--create", "--directory", source, "--name", "example") + clitest.SetupConfig(t, client, root) + pty := ptytest.New(t).Attach(inv) + + clitest.Start(t, inv) + + matches := []struct { + match string + write string + }{ + {match: "Upload", write: "yes"}, + {match: "Creating a new template"}, + } + for _, m := range matches { + pty.ExpectMatch(m.match) + if len(m.write) > 0 { + pty.WriteLine(m.write) + } + // Assert that the template was created. + template, err := client.TemplateByName(ctx, user.OrganizationID, "example") + require.NoError(t, err) + require.NotNil(t, template.ID) + } + }) + t.Run("UseWorkingDir", func(t *testing.T) { t.Parallel() diff --git a/cli/testdata/coder_templates_push_--help.golden b/cli/testdata/coder_templates_push_--help.golden index ebe0a73e7014a..4e47ad5e3714e 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 a new template if one does not already 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 692887b6921d2..294b0acf9f911 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 a new template if one does not already exist. + ### -d, --directory | | |