Add gh cs view command

This commit is contained in:
David Gardiner 2023-05-26 09:54:28 -07:00
parent 79128a23c8
commit 54560c19dd
4 changed files with 135 additions and 3 deletions

View file

@ -192,6 +192,13 @@ type Codespace struct {
PendingOperationDisabledReason string `json:"pending_operation_disabled_reason"`
IdleTimeoutNotice string `json:"idle_timeout_notice"`
WebURL string `json:"web_url"`
DevContainerPath string `json:"devcontainer_path"`
Prebuild bool `json:"prebuild"`
Location string `json:"location"`
IdleTimeoutMinutes int `json:"idle_timeout_minutes"`
RetentionExpiresAt string `json:"retention_expires_at"`
RecentFolders []string `json:"recent_folders"`
BillableOwner User `json:"billable_owner"`
}
type CodespaceGitStatus struct {
@ -230,8 +237,8 @@ type CodespaceConnection struct {
HostPublicKeys []string `json:"hostPublicKeys"`
}
// CodespaceFields is the list of exportable fields for a codespace.
var CodespaceFields = []string{
// ListCodespaceFields is the list of exportable fields for a codespace when using the `gh cs list` command.
var ListCodespaceFields = []string{
"displayName",
"name",
"owner",
@ -244,6 +251,27 @@ var CodespaceFields = []string{
"vscsTarget",
}
// ViewCodespaceFields is the list of exportable fields for a codespace when using the `gh cs view` command.
var ViewCodespaceFields = []string{
"name",
"displayName",
"state",
"owner",
"billableOwner",
"location",
"repository",
"gitStatus",
"devcontainerPath",
"machineName",
"machineDisplayName",
"prebuild",
"createdAt",
"lastUsedAt",
"idleTimeoutMinutes",
"retentionExpiresAt",
"recentFolders",
}
func (c *Codespace) ExportData(fields []string) map[string]interface{} {
v := reflect.ValueOf(c).Elem()
data := map[string]interface{}{}
@ -256,11 +284,15 @@ func (c *Codespace) ExportData(fields []string) map[string]interface{} {
data[f] = c.Repository.FullName
case "machineName":
data[f] = c.Machine.Name
case "machineDisplayName":
data[f] = c.Machine.DisplayName
case "gitStatus":
data[f] = map[string]interface{}{
"ref": c.GitStatus.Ref,
"hasUnpushedChanges": c.GitStatus.HasUnpushedChanges,
"hasUncommittedChanges": c.GitStatus.HasUncommittedChanges,
"ahead": c.GitStatus.Ahead,
"behind": c.GitStatus.Behind,
}
case "vscsTarget":
if c.VSCSTarget != "" && c.VSCSTarget != VSCSTargetProduction {

View file

@ -70,7 +70,7 @@ func newListCmd(app *App) *cobra.Command {
listCmd.Flags().StringVarP(&opts.orgName, "org", "o", "", "The `login` handle of the organization to list codespaces for (admin-only)")
listCmd.Flags().StringVarP(&opts.userName, "user", "u", "", "The `username` to list codespaces for (used with --org)")
cmdutil.AddJSONFlags(listCmd, &exporter, api.CodespaceFields)
cmdutil.AddJSONFlags(listCmd, &exporter, api.ListCodespaceFields)
listCmd.Flags().BoolVarP(&opts.useWeb, "web", "w", false, "List codespaces in the web browser, cannot be used with --user or --org")

View file

@ -16,6 +16,7 @@ func NewRootCmd(app *App) *cobra.Command {
root.AddCommand(newDeleteCmd(app))
root.AddCommand(newJupyterCmd(app))
root.AddCommand(newListCmd(app))
root.AddCommand(newViewCmd(app))
root.AddCommand(newLogsCmd(app))
root.AddCommand(newPortsCmd(app))
root.AddCommand(newSSHCmd(app))

99
pkg/cmd/codespace/view.go Normal file
View file

@ -0,0 +1,99 @@
package codespace
import (
"context"
"strconv"
"strings"
"github.com/cli/cli/v2/internal/codespaces/api"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/utils"
"github.com/spf13/cobra"
)
type viewOptions struct {
selector *CodespaceSelector
}
func newViewCmd(app *App) *cobra.Command {
opts := &viewOptions{}
var exporter cmdutil.Exporter
viewCmd := &cobra.Command{
Use: "view",
Short: "View details about a codespace",
Args: noArgsConstraint,
RunE: func(cmd *cobra.Command, args []string) error {
return app.ViewCodespace(cmd.Context(), opts, exporter)
},
}
opts.selector = AddCodespaceSelector(viewCmd, app.apiClient)
cmdutil.AddJSONFlags(viewCmd, &exporter, api.ViewCodespaceFields)
return viewCmd
}
func (a *App) ViewCodespace(ctx context.Context, opts *viewOptions, exporter cmdutil.Exporter) error {
selectedCodespace, err := opts.selector.Select(ctx)
if err != nil {
return err
}
if err := a.io.StartPager(); err != nil {
a.errLogger.Printf("error starting pager: %v", err)
}
defer a.io.StopPager()
if exporter != nil {
return exporter.Write(a.io, selectedCodespace)
}
//nolint:staticcheck // SA1019: utils.NewTablePrinter is deprecated: use internal/tableprinter
tp := utils.NewTablePrinter(a.io)
c := codespace{selectedCodespace}
formattedName := formatNameForVSCSTarget(c.Name, c.VSCSTarget)
// Create an array of fields to display in the table with their values
fields := []struct {
name string
value string
}{
{"Name", formattedName},
{"Display Name", c.DisplayName},
{"State", c.State},
{"Machine Name", c.Machine.Name},
{"Machine Display Name", c.Machine.DisplayName},
{"Prebuild", strconv.FormatBool(c.Prebuild)},
{"Owner", c.Owner.Login},
{"BillableOwner", c.BillableOwner.Login},
{"Location", c.Location},
{"Repository", c.Repository.FullName},
{"Branch", c.GitStatus.Ref},
{"Devcontainer Path", c.DevContainerPath},
{"Commits Ahead", strconv.Itoa(c.GitStatus.Ahead)},
{"Commits Behind", strconv.Itoa(c.GitStatus.Behind)},
{"Has Uncommitted Changes", strconv.FormatBool(c.GitStatus.HasUncommittedChanges)},
{"Has Unpushed Changes", strconv.FormatBool(c.GitStatus.HasUnpushedChanges)},
{"Created At", c.CreatedAt},
{"Last Used At", c.LastUsedAt},
{"Idle Timeout Minutes", strconv.Itoa(c.IdleTimeoutMinutes)},
{"Retention Expires At", c.RetentionExpiresAt},
{"Recent Folders", strings.Join(c.RecentFolders, ", ")},
}
for _, field := range fields {
// Only display the field if it has a value.
if field.value != "" {
tp.AddField(field.name, nil, nil)
tp.AddField(field.value, nil, nil)
tp.EndRow()
}
}
err = tp.Render()
if err != nil {
return err
}
return nil
}