Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 154b9bc

Browse files
authored
feat: Add "coder projects create" command (#246)
* Refactor parameter parsing to return nil values if none computed * Refactor parameter to allow for hiding redisplay * Refactor parameters to enable schema matching * Refactor provisionerd to dynamically update parameter schemas * Refactor job update for provisionerd * Handle multiple states correctly when provisioning a project * Add project import job resource table * Basic creation flow works! * Create project fully works!!! * Only show job status if completed * Add create workspace support * Replace Netflix/go-expect with ActiveState * Fix linting errors * Use forked chzyer/readline * Add create workspace CLI * Add CLI test * Move jobs to their own APIs * Remove go-expect * Fix requested changes * Skip workspacecreate test on windows
1 parent df13fef commit 154b9bc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+3192
-2218
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"nhooyr",
4545
"nolint",
4646
"nosec",
47+
"ntqry",
4748
"oneof",
4849
"parameterscopeid",
4950
"promptui",

cli/clitest/clitest.go

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
11
package clitest
22

33
import (
4+
"archive/tar"
45
"bufio"
6+
"bytes"
7+
"errors"
58
"io"
9+
"os"
10+
"path/filepath"
11+
"regexp"
612
"testing"
713

14+
"github.com/Netflix/go-expect"
815
"github.com/spf13/cobra"
16+
"github.com/stretchr/testify/require"
917

1018
"github.com/coder/coder/cli"
1119
"github.com/coder/coder/cli/config"
20+
"github.com/coder/coder/codersdk"
21+
"github.com/coder/coder/provisioner/echo"
1222
)
1323

24+
var (
25+
// Used to ensure terminal output doesn't have anything crazy!
26+
// See: https://stackoverflow.com/a/29497680
27+
stripAnsi = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))")
28+
)
29+
30+
// New creates a CLI instance with a configuration pointed to a
31+
// temporary testing directory.
1432
func New(t *testing.T, args ...string) (*cobra.Command, config.Root) {
1533
cmd := cli.Root()
1634
dir := t.TempDir()
@@ -19,7 +37,27 @@ func New(t *testing.T, args ...string) (*cobra.Command, config.Root) {
1937
return cmd, root
2038
}
2139

22-
func StdoutLogs(t *testing.T) io.Writer {
40+
// SetupConfig applies the URL and SessionToken of the client to the config.
41+
func SetupConfig(t *testing.T, client *codersdk.Client, root config.Root) {
42+
err := root.Session().Write(client.SessionToken)
43+
require.NoError(t, err)
44+
err = root.URL().Write(client.URL.String())
45+
require.NoError(t, err)
46+
}
47+
48+
// CreateProjectVersionSource writes the echo provisioner responses into a
49+
// new temporary testing directory.
50+
func CreateProjectVersionSource(t *testing.T, responses *echo.Responses) string {
51+
directory := t.TempDir()
52+
data, err := echo.Tar(responses)
53+
require.NoError(t, err)
54+
extractTar(t, data, directory)
55+
return directory
56+
}
57+
58+
// NewConsole creates a new TTY bound to the command provided.
59+
// All ANSI escape codes are stripped to provide clean output.
60+
func NewConsole(t *testing.T, cmd *cobra.Command) *expect.Console {
2361
reader, writer := io.Pipe()
2462
scanner := bufio.NewScanner(reader)
2563
t.Cleanup(func() {
@@ -31,8 +69,46 @@ func StdoutLogs(t *testing.T) io.Writer {
3169
if scanner.Err() != nil {
3270
return
3371
}
34-
t.Log(scanner.Text())
72+
t.Log(stripAnsi.ReplaceAllString(scanner.Text(), ""))
3573
}
3674
}()
37-
return writer
75+
76+
console, err := expect.NewConsole(expect.WithStdout(writer))
77+
require.NoError(t, err)
78+
cmd.SetIn(console.Tty())
79+
cmd.SetOut(console.Tty())
80+
return console
81+
}
82+
83+
func extractTar(t *testing.T, data []byte, directory string) {
84+
reader := tar.NewReader(bytes.NewBuffer(data))
85+
for {
86+
header, err := reader.Next()
87+
if errors.Is(err, io.EOF) {
88+
break
89+
}
90+
require.NoError(t, err)
91+
// #nosec
92+
path := filepath.Join(directory, header.Name)
93+
mode := header.FileInfo().Mode()
94+
if mode == 0 {
95+
mode = 0600
96+
}
97+
switch header.Typeflag {
98+
case tar.TypeDir:
99+
err = os.MkdirAll(path, mode)
100+
require.NoError(t, err)
101+
case tar.TypeReg:
102+
file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, mode)
103+
require.NoError(t, err)
104+
// Max file size of 10MB.
105+
_, err = io.CopyN(file, reader, (1<<20)*10)
106+
if errors.Is(err, io.EOF) {
107+
err = nil
108+
}
109+
require.NoError(t, err)
110+
err = file.Close()
111+
require.NoError(t, err)
112+
}
113+
}
38114
}

cli/clitest/clitest_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//go:build !windows
2+
3+
package clitest_test
4+
5+
import (
6+
"testing"
7+
8+
"github.com/coder/coder/cli/clitest"
9+
"github.com/coder/coder/coderd/coderdtest"
10+
"github.com/stretchr/testify/require"
11+
"go.uber.org/goleak"
12+
)
13+
14+
func TestMain(m *testing.M) {
15+
goleak.VerifyTestMain(m)
16+
}
17+
18+
func TestCli(t *testing.T) {
19+
t.Parallel()
20+
clitest.CreateProjectVersionSource(t, nil)
21+
client := coderdtest.New(t)
22+
cmd, config := clitest.New(t)
23+
clitest.SetupConfig(t, client, config)
24+
console := clitest.NewConsole(t, cmd)
25+
go func() {
26+
err := cmd.Execute()
27+
require.NoError(t, err)
28+
}()
29+
_, err := console.ExpectString("coder")
30+
require.NoError(t, err)
31+
}

cli/login.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func login() *cobra.Command {
4949
}
5050
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Your Coder deployment hasn't been set up!\n", color.HiBlackString(">"))
5151

52-
_, err := runPrompt(cmd, &promptui.Prompt{
52+
_, err := prompt(cmd, &promptui.Prompt{
5353
Label: "Would you like to create the first user?",
5454
IsConfirm: true,
5555
Default: "y",
@@ -61,23 +61,23 @@ func login() *cobra.Command {
6161
if err != nil {
6262
return xerrors.Errorf("get current user: %w", err)
6363
}
64-
username, err := runPrompt(cmd, &promptui.Prompt{
64+
username, err := prompt(cmd, &promptui.Prompt{
6565
Label: "What username would you like?",
6666
Default: currentUser.Username,
6767
})
6868
if err != nil {
6969
return xerrors.Errorf("pick username prompt: %w", err)
7070
}
7171

72-
organization, err := runPrompt(cmd, &promptui.Prompt{
72+
organization, err := prompt(cmd, &promptui.Prompt{
7373
Label: "What is the name of your organization?",
7474
Default: "acme-corp",
7575
})
7676
if err != nil {
7777
return xerrors.Errorf("pick organization prompt: %w", err)
7878
}
7979

80-
email, err := runPrompt(cmd, &promptui.Prompt{
80+
email, err := prompt(cmd, &promptui.Prompt{
8181
Label: "What's your email?",
8282
Validate: func(s string) error {
8383
err := validator.New().Var(s, "email")
@@ -91,7 +91,7 @@ func login() *cobra.Command {
9191
return xerrors.Errorf("specify email prompt: %w", err)
9292
}
9393

94-
password, err := runPrompt(cmd, &promptui.Prompt{
94+
password, err := prompt(cmd, &promptui.Prompt{
9595
Label: "Enter a password:",
9696
Mask: '*',
9797
})

cli/login_test.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import (
88
"github.com/coder/coder/cli/clitest"
99
"github.com/coder/coder/coderd/coderdtest"
1010
"github.com/stretchr/testify/require"
11-
12-
"github.com/Netflix/go-expect"
1311
)
1412

1513
func TestLogin(t *testing.T) {
@@ -24,12 +22,9 @@ func TestLogin(t *testing.T) {
2422

2523
t.Run("InitialUserTTY", func(t *testing.T) {
2624
t.Parallel()
27-
console, err := expect.NewConsole(expect.WithStdout(clitest.StdoutLogs(t)))
28-
require.NoError(t, err)
2925
client := coderdtest.New(t)
3026
root, _ := clitest.New(t, "login", client.URL.String())
31-
root.SetIn(console.Tty())
32-
root.SetOut(console.Tty())
27+
console := clitest.NewConsole(t, root)
3328
go func() {
3429
err := root.Execute()
3530
require.NoError(t, err)
@@ -45,12 +40,12 @@ func TestLogin(t *testing.T) {
4540
for i := 0; i < len(matches); i += 2 {
4641
match := matches[i]
4742
value := matches[i+1]
48-
_, err = console.ExpectString(match)
43+
_, err := console.ExpectString(match)
4944
require.NoError(t, err)
5045
_, err = console.SendLine(value)
5146
require.NoError(t, err)
5247
}
53-
_, err = console.ExpectString("Welcome to Coder")
48+
_, err := console.ExpectString("Welcome to Coder")
5449
require.NoError(t, err)
5550
})
5651
}

0 commit comments

Comments
 (0)