feat: Add "projects list" command to the CLI (#333)

This adds a WorkspaceOwnerCount parameter returned from the
projects API. It's helpful to display the amount of usage
a specific project has.
This commit is contained in:
Kyle Carberry
2022-02-21 12:47:08 -06:00
committed by GitHub
parent 59ee22d368
commit 67613da86d
13 changed files with 311 additions and 16 deletions
+3 -3
View File
@@ -67,7 +67,7 @@ func login() *cobra.Command {
if !isTTY(cmd) {
return xerrors.New("the initial user cannot be created in non-interactive mode. use the API")
}
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Your Coder deployment hasn't been set up!\n", color.HiBlackString(">"))
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Your Coder deployment hasn't been set up!\n", caret)
_, err := prompt(cmd, &promptui.Prompt{
Label: "Would you like to create the first user?",
@@ -147,7 +147,7 @@ func login() *cobra.Command {
return xerrors.Errorf("write server url: %w", err)
}
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Welcome to Coder, %s! You're authenticated.\n", color.HiBlackString(">"), color.HiCyanString(username))
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Welcome to Coder, %s! You're authenticated.\n", caret, color.HiCyanString(username))
return nil
}
@@ -192,7 +192,7 @@ func login() *cobra.Command {
return xerrors.Errorf("write server url: %w", err)
}
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Welcome to Coder, %s! You're authenticated.\n", color.HiBlackString(">"), color.HiCyanString(resp.Username))
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Welcome to Coder, %s! You're authenticated.\n", caret, color.HiCyanString(resp.Username))
return nil
},
}
+1 -1
View File
@@ -92,7 +92,7 @@ func projectCreate() *cobra.Command {
return err
}
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s The %s project has been created!\n", color.HiBlackString(">"), color.HiCyanString(project.Name))
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s The %s project has been created!\n", caret, color.HiCyanString(project.Name))
_, err = prompt(cmd, &promptui.Prompt{
Label: "Create a new workspace?",
IsConfirm: true,
+63
View File
@@ -0,0 +1,63 @@
package cli
import (
"fmt"
"text/tabwriter"
"time"
"github.com/fatih/color"
"github.com/spf13/cobra"
)
func projectList() *cobra.Command {
return &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
RunE: func(cmd *cobra.Command, args []string) error {
client, err := createClient(cmd)
if err != nil {
return err
}
start := time.Now()
organization, err := currentOrganization(cmd, client)
if err != nil {
return err
}
projects, err := client.Projects(cmd.Context(), organization.Name)
if err != nil {
return err
}
if len(projects) == 0 {
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s No projects found in %s! Create one:\n\n", caret, color.HiWhiteString(organization.Name))
_, _ = fmt.Fprintln(cmd.OutOrStdout(), color.HiMagentaString(" $ coder projects create <directory>\n"))
return nil
}
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Projects found in %s %s\n\n",
caret,
color.HiWhiteString(organization.Name),
color.HiBlackString("[%dms]",
time.Since(start).Milliseconds()))
writer := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 0, 4, ' ', 0)
_, _ = fmt.Fprintf(writer, "%s\t%s\t%s\t%s\n",
color.HiBlackString("Project"),
color.HiBlackString("Source"),
color.HiBlackString("Last Updated"),
color.HiBlackString("Used By"))
for _, project := range projects {
suffix := ""
if project.WorkspaceOwnerCount != 1 {
suffix = "s"
}
_, _ = fmt.Fprintf(writer, "%s\t%s\t%s\t%s\n",
color.New(color.FgHiCyan).Sprint(project.Name),
color.WhiteString("Archive"),
color.WhiteString(project.UpdatedAt.Format("January 2, 2006")),
color.New(color.FgHiWhite).Sprintf("%d developer%s", project.WorkspaceOwnerCount, suffix))
}
return writer.Flush()
},
}
}
+56
View File
@@ -0,0 +1,56 @@
package cli_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/coder/coder/cli/clitest"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/pty/ptytest"
)
func TestProjectList(t *testing.T) {
t.Parallel()
t.Run("None", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t)
coderdtest.CreateInitialUser(t, client)
cmd, root := clitest.New(t, "projects", "list")
clitest.SetupConfig(t, client, root)
pty := ptytest.New(t)
cmd.SetIn(pty.Input())
cmd.SetOut(pty.Output())
closeChan := make(chan struct{})
go func() {
err := cmd.Execute()
require.NoError(t, err)
close(closeChan)
}()
pty.ExpectMatch("No projects found")
<-closeChan
})
t.Run("List", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t)
user := coderdtest.CreateInitialUser(t, client)
daemon := coderdtest.NewProvisionerDaemon(t, client)
job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil)
coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID)
_ = daemon.Close()
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
cmd, root := clitest.New(t, "projects", "list")
clitest.SetupConfig(t, client, root)
pty := ptytest.New(t)
cmd.SetIn(pty.Input())
cmd.SetOut(pty.Output())
closeChan := make(chan struct{})
go func() {
err := cmd.Execute()
require.NoError(t, err)
close(closeChan)
}()
pty.ExpectMatch(project.Name)
<-closeChan
})
}
+6 -3
View File
@@ -30,9 +30,12 @@ func projects() *cobra.Command {
` + color.New(color.FgHiMagenta).Sprint("$ coder projects update <name>"),
}
cmd.AddCommand(projectCreate())
cmd.AddCommand(projectPlan())
cmd.AddCommand(projectUpdate())
cmd.AddCommand(
projectCreate(),
projectList(),
projectPlan(),
projectUpdate(),
)
return cmd
}
+4
View File
@@ -18,6 +18,10 @@ import (
"github.com/coder/coder/codersdk"
)
var (
caret = color.HiBlackString(">")
)
const (
varGlobalConfig = "global-config"
varNoOpen = "no-open"
+1 -1
View File
@@ -54,7 +54,7 @@ func workspaceCreate() *cobra.Command {
}
}
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Previewing project create...\n", color.HiBlackString(">"))
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Previewing project create...\n", caret)
project, err := client.Project(cmd.Context(), organization.Name, args[0])
if err != nil {