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
| | |