Files
coder/enterprise/cli/grouplist.go
T
Zach a46336c3ec fix(cli)!: coder groups list -o json returns empty values (#22923)
The groupsToRows function was not setting the Group field on
groupTableRow, causing JSON output to contain zero-value structs. Table
output was unaffected since it uses separate fields.

BREAKING CHANGE: The JSON output structure changes from `{"Group":
{"id": ...}}` to `{"id": ...}` (flat). This is technically a breaking
change, but JSON output never contained real data (all fields were
zero-valued), so no working consumer could exist. We're taking the
opportunity to flatten the structure to match other list commands like
`coder list -o json`.
2026-03-11 09:45:00 -06:00

99 lines
2.5 KiB
Go

package cli
import (
"fmt"
"github.com/fatih/color"
"github.com/google/uuid"
"golang.org/x/xerrors"
agpl "github.com/coder/coder/v2/cli"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)
func (r *RootCmd) groupList() *serpent.Command {
formatter := cliui.NewOutputFormatter(
cliui.TableFormat([]groupTableRow{}, nil),
cliui.JSONFormat(),
)
orgContext := agpl.NewOrganizationContext()
cmd := &serpent.Command{
Use: "list",
Short: "List user groups",
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
),
Handler: func(inv *serpent.Invocation) error {
ctx := inv.Context()
client, err := r.InitClient(inv)
if err != nil {
return err
}
org, err := orgContext.Selected(inv, client)
if err != nil {
return xerrors.Errorf("current organization: %w", err)
}
groups, err := client.GroupsByOrganization(ctx, org.ID)
if err != nil {
return xerrors.Errorf("get groups: %w", err)
}
rows := groupsToRows(groups...)
out, err := formatter.Format(inv.Context(), rows)
if err != nil {
return xerrors.Errorf("display groups: %w", err)
}
if out == "" {
_, _ = fmt.Fprintf(inv.Stderr, "%s No groups found in %s! Create one:\n\n", agpl.Caret, color.HiWhiteString(org.Name))
_, _ = fmt.Fprintln(inv.Stderr, color.HiMagentaString(" $ coder groups create <name>\n"))
return nil
}
_, _ = fmt.Fprintln(inv.Stdout, out)
return nil
},
}
formatter.AttachOptions(&cmd.Options)
orgContext.AttachOptions(cmd)
return cmd
}
type groupTableRow struct {
// For json output:
codersdk.Group `table:"-"`
// For table output:
Name string `json:"-" table:"name,default_sort"`
DisplayName string `json:"-" table:"display name"`
OrganizationID uuid.UUID `json:"-" table:"organization id"`
Members []string `json:"-" table:"members"`
AvatarURL string `json:"-" table:"avatar url"`
}
func groupsToRows(groups ...codersdk.Group) []groupTableRow {
rows := make([]groupTableRow, 0, len(groups))
for _, group := range groups {
members := make([]string, 0, len(group.Members))
for _, member := range group.Members {
members = append(members, member.Email)
}
rows = append(rows, groupTableRow{
Group: group,
Name: group.Name,
DisplayName: group.DisplayName,
OrganizationID: group.OrganizationID,
AvatarURL: group.AvatarURL,
Members: members,
})
}
return rows
}