-
Notifications
You must be signed in to change notification settings - Fork 888
feat: Add "coder projects create" command #246
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7167fa0
65380db
94eb484
a6ce22d
e53f0be
4466836
8fe05d6
79a56b6
dc86c0e
bff96b6
8766a33
aac220f
c493bf9
485c07b
b5a774a
d2cbf36
a8d00d8
9c7746f
e4770bb
c6cee94
f9814be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,7 @@ | |
"nhooyr", | ||
"nolint", | ||
"nosec", | ||
"ntqry", | ||
"oneof", | ||
"parameterscopeid", | ||
"promptui", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,34 @@ | ||
package clitest | ||
|
||
import ( | ||
"archive/tar" | ||
"bufio" | ||
"bytes" | ||
"errors" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"testing" | ||
|
||
"github.com/Netflix/go-expect" | ||
"github.com/spf13/cobra" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/coder/coder/cli" | ||
"github.com/coder/coder/cli/config" | ||
"github.com/coder/coder/codersdk" | ||
"github.com/coder/coder/provisioner/echo" | ||
) | ||
|
||
var ( | ||
// Used to ensure terminal output doesn't have anything crazy! | ||
// See: https://stackoverflow.com/a/29497680 | ||
stripAnsi = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))") | ||
kylecarbs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
|
||
// New creates a CLI instance with a configuration pointed to a | ||
// temporary testing directory. | ||
func New(t *testing.T, args ...string) (*cobra.Command, config.Root) { | ||
cmd := cli.Root() | ||
dir := t.TempDir() | ||
|
@@ -19,7 +37,27 @@ func New(t *testing.T, args ...string) (*cobra.Command, config.Root) { | |
return cmd, root | ||
} | ||
|
||
func StdoutLogs(t *testing.T) io.Writer { | ||
// SetupConfig applies the URL and SessionToken of the client to the config. | ||
func SetupConfig(t *testing.T, client *codersdk.Client, root config.Root) { | ||
err := root.Session().Write(client.SessionToken) | ||
require.NoError(t, err) | ||
err = root.URL().Write(client.URL.String()) | ||
require.NoError(t, err) | ||
} | ||
|
||
// CreateProjectVersionSource writes the echo provisioner responses into a | ||
// new temporary testing directory. | ||
func CreateProjectVersionSource(t *testing.T, responses *echo.Responses) string { | ||
directory := t.TempDir() | ||
data, err := echo.Tar(responses) | ||
require.NoError(t, err) | ||
extractTar(t, data, directory) | ||
return directory | ||
} | ||
|
||
// NewConsole creates a new TTY bound to the command provided. | ||
// All ANSI escape codes are stripped to provide clean output. | ||
func NewConsole(t *testing.T, cmd *cobra.Command) *expect.Console { | ||
reader, writer := io.Pipe() | ||
scanner := bufio.NewScanner(reader) | ||
t.Cleanup(func() { | ||
|
@@ -31,8 +69,46 @@ func StdoutLogs(t *testing.T) io.Writer { | |
if scanner.Err() != nil { | ||
return | ||
} | ||
t.Log(scanner.Text()) | ||
t.Log(stripAnsi.ReplaceAllString(scanner.Text(), "")) | ||
} | ||
}() | ||
return writer | ||
|
||
console, err := expect.NewConsole(expect.WithStdout(writer)) | ||
require.NoError(t, err) | ||
cmd.SetIn(console.Tty()) | ||
cmd.SetOut(console.Tty()) | ||
return console | ||
} | ||
|
||
func extractTar(t *testing.T, data []byte, directory string) { | ||
reader := tar.NewReader(bytes.NewBuffer(data)) | ||
for { | ||
header, err := reader.Next() | ||
if errors.Is(err, io.EOF) { | ||
break | ||
} | ||
require.NoError(t, err) | ||
// #nosec | ||
path := filepath.Join(directory, header.Name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should have a check here to sanitize the This would be bad, for example, if a malicious actor could write over a critical config file, a binary (ie, they overwrite our Kind of a similar to the example called out here: securego/gosec#439 (comment) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, and we handle this from provisionerd. But I didn't feel it was necessary for We should abstract the tar/untar logic out so it's in one place, then add tests for that specific functionality. |
||
mode := header.FileInfo().Mode() | ||
if mode == 0 { | ||
mode = 0600 | ||
} | ||
switch header.Typeflag { | ||
case tar.TypeDir: | ||
err = os.MkdirAll(path, mode) | ||
require.NoError(t, err) | ||
case tar.TypeReg: | ||
file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, mode) | ||
require.NoError(t, err) | ||
// Max file size of 10MB. | ||
_, err = io.CopyN(file, reader, (1<<20)*10) | ||
if errors.Is(err, io.EOF) { | ||
err = nil | ||
} | ||
require.NoError(t, err) | ||
err = file.Close() | ||
require.NoError(t, err) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
//go:build !windows | ||
|
||
package clitest_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/coder/coder/cli/clitest" | ||
"github.com/coder/coder/coderd/coderdtest" | ||
"github.com/stretchr/testify/require" | ||
"go.uber.org/goleak" | ||
) | ||
|
||
func TestMain(m *testing.M) { | ||
goleak.VerifyTestMain(m) | ||
} | ||
|
||
func TestCli(t *testing.T) { | ||
t.Parallel() | ||
clitest.CreateProjectVersionSource(t, nil) | ||
client := coderdtest.New(t) | ||
cmd, config := clitest.New(t) | ||
clitest.SetupConfig(t, client, config) | ||
console := clitest.NewConsole(t, cmd) | ||
go func() { | ||
err := cmd.Execute() | ||
require.NoError(t, err) | ||
}() | ||
_, err := console.ExpectString("coder") | ||
require.NoError(t, err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neat 🎉