mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: Unify cli behavior for templates create and update (#1385)
Fixes #565
This commit is contained in:
+25
-16
@@ -27,7 +27,8 @@ func templateCreate() *cobra.Command {
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "create [name]",
|
||||
Short: "Create a template from the current directory",
|
||||
Short: "Create a template from the current directory or as specified by flag",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
@@ -51,19 +52,10 @@ func templateCreate() *cobra.Command {
|
||||
return xerrors.Errorf("A template already exists named %q!", templateName)
|
||||
}
|
||||
|
||||
// Confirm upload of the users current directory.
|
||||
// Truncate if in the home directory, because a shorter path looks nicer.
|
||||
displayDirectory := directory
|
||||
userHomeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get home dir: %w", err)
|
||||
}
|
||||
if strings.HasPrefix(displayDirectory, userHomeDir) {
|
||||
displayDirectory = strings.TrimPrefix(displayDirectory, userHomeDir)
|
||||
displayDirectory = "~" + displayDirectory
|
||||
}
|
||||
// Confirm upload of the directory.
|
||||
prettyDir := prettyDirectoryPath(directory)
|
||||
_, err = cliui.Prompt(cmd, cliui.PromptOptions{
|
||||
Text: fmt.Sprintf("Create and upload %q?", displayDirectory),
|
||||
Text: fmt.Sprintf("Create and upload %q?", prettyDir),
|
||||
IsConfirm: true,
|
||||
Default: "yes",
|
||||
})
|
||||
@@ -73,7 +65,7 @@ func templateCreate() *cobra.Command {
|
||||
|
||||
spin := spinner.New(spinner.CharSets[5], 100*time.Millisecond)
|
||||
spin.Writer = cmd.OutOrStdout()
|
||||
spin.Suffix = cliui.Styles.Keyword.Render(" Uploading current directory...")
|
||||
spin.Suffix = cliui.Styles.Keyword.Render(" Uploading directory...")
|
||||
spin.Start()
|
||||
defer spin.Stop()
|
||||
archive, err := provisionersdk.Tar(directory, provisionersdk.TemplateArchiveLimit)
|
||||
@@ -123,9 +115,9 @@ func templateCreate() *cobra.Command {
|
||||
}
|
||||
currentDirectory, _ := os.Getwd()
|
||||
cmd.Flags().StringVarP(&directory, "directory", "d", currentDirectory, "Specify the directory to create from")
|
||||
cmd.Flags().StringVarP(&provisioner, "provisioner", "p", "terraform", "Customize the provisioner backend")
|
||||
cmd.Flags().StringVarP(&provisioner, "test.provisioner", "", "terraform", "Customize the provisioner backend")
|
||||
// This is for testing!
|
||||
err := cmd.Flags().MarkHidden("provisioner")
|
||||
err := cmd.Flags().MarkHidden("test.provisioner")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -228,3 +220,20 @@ func createValidTemplateVersion(cmd *cobra.Command, client *codersdk.Client, org
|
||||
|
||||
return &version, parameters, nil
|
||||
}
|
||||
|
||||
// prettyDirectoryPath returns a prettified path when inside the users
|
||||
// home directory. Falls back to dir if the users home directory cannot
|
||||
// discerned. This function calls filepath.Clean on the result.
|
||||
func prettyDirectoryPath(dir string) string {
|
||||
dir = filepath.Clean(dir)
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return dir
|
||||
}
|
||||
pretty := dir
|
||||
if strings.HasPrefix(pretty, homeDir) {
|
||||
pretty = strings.TrimPrefix(pretty, homeDir)
|
||||
pretty = "~" + pretty
|
||||
}
|
||||
return pretty
|
||||
}
|
||||
|
||||
+16
-14
@@ -22,28 +22,30 @@ func TestTemplateCreate(t *testing.T) {
|
||||
Parse: echo.ParseComplete,
|
||||
Provision: echo.ProvisionComplete,
|
||||
})
|
||||
cmd, root := clitest.New(t, "templates", "create", "my-template", "--directory", source, "--provisioner", string(database.ProvisionerTypeEcho))
|
||||
cmd, root := clitest.New(t, "templates", "create", "my-template", "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho))
|
||||
clitest.SetupConfig(t, client, root)
|
||||
_ = coderdtest.NewProvisionerDaemon(t, client)
|
||||
doneChan := make(chan struct{})
|
||||
pty := ptytest.New(t)
|
||||
cmd.SetIn(pty.Input())
|
||||
cmd.SetOut(pty.Output())
|
||||
|
||||
execDone := make(chan error)
|
||||
go func() {
|
||||
defer close(doneChan)
|
||||
err := cmd.Execute()
|
||||
require.NoError(t, err)
|
||||
execDone <- cmd.Execute()
|
||||
}()
|
||||
matches := []string{
|
||||
"Create and upload", "yes",
|
||||
"Confirm create?", "yes",
|
||||
|
||||
matches := []struct {
|
||||
match string
|
||||
write string
|
||||
}{
|
||||
{match: "Create and upload", write: "yes"},
|
||||
{match: "Confirm create?", write: "yes"},
|
||||
}
|
||||
for i := 0; i < len(matches); i += 2 {
|
||||
match := matches[i]
|
||||
value := matches[i+1]
|
||||
pty.ExpectMatch(match)
|
||||
pty.WriteLine(value)
|
||||
for _, m := range matches {
|
||||
pty.ExpectMatch(m.match)
|
||||
pty.WriteLine(m.write)
|
||||
}
|
||||
<-doneChan
|
||||
|
||||
require.NoError(t, <-execDone)
|
||||
})
|
||||
}
|
||||
|
||||
+38
-14
@@ -3,22 +3,28 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/briandowns/spinner"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/cli/cliui"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/provisionersdk"
|
||||
)
|
||||
|
||||
func templateUpdate() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "update <template> [directory]",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Short: "Update the source-code of a template from a directory.",
|
||||
var (
|
||||
directory string
|
||||
provisioner string
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "update <template>",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "Update the source-code of a template from the current directory or as specified by flag",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
@@ -33,16 +39,22 @@ func templateUpdate() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
directory, err := os.Getwd()
|
||||
// Confirm upload of the directory.
|
||||
prettyDir := prettyDirectoryPath(directory)
|
||||
_, err = cliui.Prompt(cmd, cliui.PromptOptions{
|
||||
Text: fmt.Sprintf("Upload %q?", prettyDir),
|
||||
IsConfirm: true,
|
||||
Default: "yes",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(args) >= 2 {
|
||||
directory, err = filepath.Abs(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
spin := spinner.New(spinner.CharSets[5], 100*time.Millisecond)
|
||||
spin.Writer = cmd.OutOrStdout()
|
||||
spin.Suffix = cliui.Styles.Keyword.Render(" Uploading directory...")
|
||||
spin.Start()
|
||||
defer spin.Stop()
|
||||
content, err := provisionersdk.Tar(directory, provisionersdk.TemplateArchiveLimit)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -51,13 +63,14 @@ func templateUpdate() *cobra.Command {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
spin.Stop()
|
||||
|
||||
before := time.Now()
|
||||
templateVersion, err := client.CreateTemplateVersion(cmd.Context(), organization.ID, codersdk.CreateTemplateVersionRequest{
|
||||
TemplateID: template.ID,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
StorageSource: resp.Hash,
|
||||
Provisioner: database.ProvisionerTypeTerraform,
|
||||
Provisioner: database.ProvisionerType(provisioner),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -71,7 +84,7 @@ func templateUpdate() *cobra.Command {
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
_, _ = fmt.Printf("terraform (%s): %s\n", log.Level, log.Output)
|
||||
_, _ = fmt.Printf("%s (%s): %s\n", provisioner, log.Level, log.Output)
|
||||
}
|
||||
templateVersion, err = client.TemplateVersion(cmd.Context(), templateVersion.ID)
|
||||
if err != nil {
|
||||
@@ -92,4 +105,15 @@ func templateUpdate() *cobra.Command {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
currentDirectory, _ := os.Getwd()
|
||||
cmd.Flags().StringVarP(&directory, "directory", "d", currentDirectory, "Specify the directory to create from")
|
||||
cmd.Flags().StringVarP(&provisioner, "test.provisioner", "", "terraform", "Customize the provisioner backend")
|
||||
// This is for testing!
|
||||
err := cmd.Flags().MarkHidden("test.provisioner")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/cli/clitest"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/pty/ptytest"
|
||||
)
|
||||
|
||||
func TestTemplateUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := coderdtest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
_ = coderdtest.NewProvisionerDaemon(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
||||
// Test the cli command.
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
Provision: echo.ProvisionComplete,
|
||||
})
|
||||
cmd, root := clitest.New(t, "templates", "update", template.Name, "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho))
|
||||
clitest.SetupConfig(t, client, root)
|
||||
pty := ptytest.New(t)
|
||||
cmd.SetIn(pty.Input())
|
||||
cmd.SetOut(pty.Output())
|
||||
|
||||
execDone := make(chan error)
|
||||
go func() {
|
||||
execDone <- cmd.Execute()
|
||||
}()
|
||||
|
||||
matches := []struct {
|
||||
match string
|
||||
write string
|
||||
}{
|
||||
{match: "Upload", write: "yes"},
|
||||
}
|
||||
for _, m := range matches {
|
||||
pty.ExpectMatch(m.match)
|
||||
pty.WriteLine(m.write)
|
||||
}
|
||||
|
||||
require.NoError(t, <-execDone)
|
||||
|
||||
// Assert that the template version changed.
|
||||
templateVersions, err := client.TemplateVersionsByTemplate(context.Background(), codersdk.TemplateVersionsByTemplateRequest{
|
||||
TemplateID: template.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, templateVersions, 2)
|
||||
assert.NotEqual(t, template.ActiveVersionID, templateVersions[1].ID)
|
||||
}
|
||||
Reference in New Issue
Block a user