mirror of
https://gitea.com/gitea/tea.git
synced 2026-09-10 07:26:33 -04:00
feat(teams): add teams list, create, delete, edit subcommands
Provide basic CRUD operations for organization teams via 'tea teams'. Defaults to listing teams for the configured organization when invoked with no subcommand. Subsequent PRs will add 'tea teams members' and 'tea teams repos' as nested subcommands.
This commit is contained in:
parent
58931b5d17
commit
6c40906cb4
|
|
@ -41,6 +41,7 @@ func App() *cli.Command {
|
||||||
&CmdActions,
|
&CmdActions,
|
||||||
&CmdWiki,
|
&CmdWiki,
|
||||||
&CmdWebhooks,
|
&CmdWebhooks,
|
||||||
|
&CmdTeams,
|
||||||
&CmdComments,
|
&CmdComments,
|
||||||
|
|
||||||
&CmdOpen,
|
&CmdOpen,
|
||||||
|
|
|
||||||
40
cmd/teams.go
Normal file
40
cmd/teams.go
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.dev/tea/cmd/teams"
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdTeams represents the teams command for managing organization teams.
|
||||||
|
var CmdTeams = cli.Command{
|
||||||
|
Name: "teams",
|
||||||
|
Aliases: []string{"team"},
|
||||||
|
Category: catEntities,
|
||||||
|
Usage: "Manage organization teams",
|
||||||
|
Description: "Manage organization teams",
|
||||||
|
Action: teams.RunTeamsList,
|
||||||
|
Commands: []*cli.Command{
|
||||||
|
&teams.CmdTeamsList,
|
||||||
|
&teams.CmdTeamsCreate,
|
||||||
|
&teams.CmdTeamsDelete,
|
||||||
|
&teams.CmdTeamsEdit,
|
||||||
|
},
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "org",
|
||||||
|
Usage: "organization to operate on",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "login",
|
||||||
|
Usage: "gitea login instance to use",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "output",
|
||||||
|
Aliases: []string{"o"},
|
||||||
|
Usage: "output format [table, csv, simple, tsv, yaml, json]",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
126
cmd/teams/create.go
Normal file
126
cmd/teams/create.go
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package teams
|
||||||
|
|
||||||
|
import (
|
||||||
|
stdctx "context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
gitea "gitea.dev/sdk"
|
||||||
|
|
||||||
|
"gitea.dev/tea/cmd/flags"
|
||||||
|
"gitea.dev/tea/modules/context"
|
||||||
|
"gitea.dev/tea/modules/print"
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdTeamsCreate represents a sub command of teams to create a new team.
|
||||||
|
var CmdTeamsCreate = cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
Aliases: []string{"add", "c"},
|
||||||
|
Usage: "Create a new team",
|
||||||
|
Description: "Create a new team in the specified organization",
|
||||||
|
ArgsUsage: "<team-name>",
|
||||||
|
Action: runTeamsCreate,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "org",
|
||||||
|
Usage: "organization to create the team in",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "description",
|
||||||
|
Usage: "team description",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "permission",
|
||||||
|
Value: "read",
|
||||||
|
Usage: "team permission level: read, write, admin",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "can-create-repo",
|
||||||
|
Usage: "allow team members to create repositories",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "all-repos",
|
||||||
|
Usage: "grant access to all organization repositories",
|
||||||
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "units",
|
||||||
|
Usage: "repository units the team can access (code, issues, pulls, wiki, releases, projects, packages, actions)",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTeamsCreate(ctx stdctx.Context, cmd *cli.Command) error {
|
||||||
|
if cmd.Args().Len() == 0 {
|
||||||
|
return errors.New("team name is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := context.InitCommand(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client := c.Login.Client()
|
||||||
|
|
||||||
|
opt := gitea.CreateTeamOption{
|
||||||
|
Name: cmd.Args().First(),
|
||||||
|
Description: cmd.String("description"),
|
||||||
|
Permission: gitea.AccessMode(cmd.String("permission")),
|
||||||
|
CanCreateOrgRepo: cmd.Bool("can-create-repo"),
|
||||||
|
IncludesAllRepositories: cmd.Bool("all-repos"),
|
||||||
|
Units: ParseRepoUnits(cmd.StringSlice("units")),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := opt.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("invalid team options: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
team, _, err := client.Organizations.CreateTeam(ctx, cmd.String("org"), opt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Team '%s' created in organization '%s' (ID: %d)\n", team.Name, cmd.String("org"), team.ID)
|
||||||
|
print.TeamDetails(team, c.Output)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseRepoUnits maps user-supplied unit names to RepoUnitType values.
|
||||||
|
// An empty input yields the Gitea default unit set (code, issues, pulls, releases).
|
||||||
|
// Exported for reuse by edit.go and the teams test suite.
|
||||||
|
func ParseRepoUnits(units []string) []gitea.RepoUnitType {
|
||||||
|
if len(units) == 0 {
|
||||||
|
return []gitea.RepoUnitType{
|
||||||
|
gitea.RepoUnitCode,
|
||||||
|
gitea.RepoUnitIssues,
|
||||||
|
gitea.RepoUnitPulls,
|
||||||
|
gitea.RepoUnitReleases,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result := make([]gitea.RepoUnitType, 0, len(units))
|
||||||
|
for _, unit := range units {
|
||||||
|
switch strings.ToLower(unit) {
|
||||||
|
case "code":
|
||||||
|
result = append(result, gitea.RepoUnitCode)
|
||||||
|
case "issues":
|
||||||
|
result = append(result, gitea.RepoUnitIssues)
|
||||||
|
case "pulls":
|
||||||
|
result = append(result, gitea.RepoUnitPulls)
|
||||||
|
case "wiki":
|
||||||
|
result = append(result, gitea.RepoUnitWiki)
|
||||||
|
case "releases":
|
||||||
|
result = append(result, gitea.RepoUnitReleases)
|
||||||
|
case "projects":
|
||||||
|
result = append(result, gitea.RepoUnitProjects)
|
||||||
|
case "packages":
|
||||||
|
result = append(result, gitea.RepoUnitPackages)
|
||||||
|
case "actions":
|
||||||
|
result = append(result, gitea.RepoUnitActions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
64
cmd/teams/delete.go
Normal file
64
cmd/teams/delete.go
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package teams
|
||||||
|
|
||||||
|
import (
|
||||||
|
stdctx "context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.dev/tea/cmd/flags"
|
||||||
|
"gitea.dev/tea/modules/context"
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdTeamsDelete represents a sub command of teams to delete a team by ID.
|
||||||
|
var CmdTeamsDelete = cli.Command{
|
||||||
|
Name: "delete",
|
||||||
|
Aliases: []string{"rm", "remove"},
|
||||||
|
Usage: "Delete a team",
|
||||||
|
Description: "Delete a team by ID",
|
||||||
|
ArgsUsage: "<team-id>",
|
||||||
|
Action: runTeamsDelete,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "confirm",
|
||||||
|
Aliases: []string{"y"},
|
||||||
|
Usage: "confirm deletion without prompting",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTeamsDelete(ctx stdctx.Context, cmd *cli.Command) error {
|
||||||
|
teamID, err := RequireTeamID(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := context.InitCommand(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client := c.Login.Client()
|
||||||
|
|
||||||
|
team, _, err := client.Organizations.GetTeam(ctx, teamID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.Bool("confirm") {
|
||||||
|
fmt.Printf("Are you sure you want to delete team '%s' (ID: %d)? [y/N] ", team.Name, team.ID)
|
||||||
|
var response string
|
||||||
|
fmt.Scanln(&response)
|
||||||
|
if response != "y" && response != "Y" && response != "yes" {
|
||||||
|
fmt.Println("Deletion canceled.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = client.Organizations.DeleteTeam(ctx, teamID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Team '%s' (ID: %d) deleted successfully\n", team.Name, team.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
128
cmd/teams/edit.go
Normal file
128
cmd/teams/edit.go
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package teams
|
||||||
|
|
||||||
|
import (
|
||||||
|
stdctx "context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
gitea "gitea.dev/sdk"
|
||||||
|
|
||||||
|
"gitea.dev/tea/cmd/flags"
|
||||||
|
"gitea.dev/tea/modules/context"
|
||||||
|
"gitea.dev/tea/modules/print"
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdTeamsEdit represents a sub command of teams to edit an existing team's settings.
|
||||||
|
var CmdTeamsEdit = cli.Command{
|
||||||
|
Name: "edit",
|
||||||
|
Aliases: []string{"update"},
|
||||||
|
Usage: "Edit a team",
|
||||||
|
Description: "Edit an existing team's settings",
|
||||||
|
ArgsUsage: "<team-id>",
|
||||||
|
Action: runTeamsEdit,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Usage: "new team name",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "description",
|
||||||
|
Usage: "new team description",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "permission",
|
||||||
|
Usage: "team permission level: read, write, admin",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "can-create-repo",
|
||||||
|
Usage: "allow team members to create repositories",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "no-create-repo",
|
||||||
|
Usage: "disallow team members to create repositories",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "all-repos",
|
||||||
|
Usage: "grant access to all organization repositories",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "no-all-repos",
|
||||||
|
Usage: "remove access to all organization repositories",
|
||||||
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "units",
|
||||||
|
Usage: "repository units the team can access (code, issues, pulls, wiki, releases, projects, packages, actions)",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTeamsEdit(ctx stdctx.Context, cmd *cli.Command) error {
|
||||||
|
teamID, err := RequireTeamID(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := context.InitCommand(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client := c.Login.Client()
|
||||||
|
|
||||||
|
// Get the current team to use as the baseline for unspecified fields.
|
||||||
|
team, _, err := client.Organizations.GetTeam(ctx, teamID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
opt := gitea.EditTeamOption{
|
||||||
|
Name: team.Name,
|
||||||
|
Permission: team.Permission,
|
||||||
|
Units: team.Units,
|
||||||
|
Description: &team.Description,
|
||||||
|
CanCreateOrgRepo: &team.CanCreateOrgRepo,
|
||||||
|
IncludesAllRepositories: &team.IncludesAllRepositories,
|
||||||
|
}
|
||||||
|
|
||||||
|
if name := cmd.String("name"); name != "" {
|
||||||
|
opt.Name = name
|
||||||
|
}
|
||||||
|
if cmd.IsSet("description") {
|
||||||
|
desc := cmd.String("description")
|
||||||
|
opt.Description = &desc
|
||||||
|
}
|
||||||
|
if cmd.IsSet("permission") {
|
||||||
|
opt.Permission = gitea.AccessMode(cmd.String("permission"))
|
||||||
|
}
|
||||||
|
if cmd.Bool("can-create-repo") {
|
||||||
|
v := true
|
||||||
|
opt.CanCreateOrgRepo = &v
|
||||||
|
} else if cmd.Bool("no-create-repo") {
|
||||||
|
v := false
|
||||||
|
opt.CanCreateOrgRepo = &v
|
||||||
|
}
|
||||||
|
if cmd.Bool("all-repos") {
|
||||||
|
v := true
|
||||||
|
opt.IncludesAllRepositories = &v
|
||||||
|
} else if cmd.Bool("no-all-repos") {
|
||||||
|
v := false
|
||||||
|
opt.IncludesAllRepositories = &v
|
||||||
|
}
|
||||||
|
if cmd.IsSet("units") {
|
||||||
|
opt.Units = ParseRepoUnits(cmd.StringSlice("units"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = client.Organizations.EditTeam(ctx, teamID, opt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, _, err := client.Organizations.GetTeam(ctx, teamID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Team '%s' (ID: %d) updated successfully\n", updated.Name, updated.ID)
|
||||||
|
print.TeamDetails(updated, c.Output)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
77
cmd/teams/list.go
Normal file
77
cmd/teams/list.go
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package teams
|
||||||
|
|
||||||
|
import (
|
||||||
|
stdctx "context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
gitea "gitea.dev/sdk"
|
||||||
|
|
||||||
|
"gitea.dev/tea/cmd/flags"
|
||||||
|
"gitea.dev/tea/modules/context"
|
||||||
|
"gitea.dev/tea/modules/print"
|
||||||
|
"gitea.dev/tea/modules/utils"
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdTeamsList represents a sub command of teams to list teams in an organization.
|
||||||
|
var CmdTeamsList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List teams in an organization",
|
||||||
|
Description: "List all teams in the specified organization",
|
||||||
|
Action: RunTeamsList,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&flags.PaginationPageFlag,
|
||||||
|
&flags.PaginationLimitFlag,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "search",
|
||||||
|
Usage: "filter teams by name (substring match)",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunTeamsList lists teams in an organization.
|
||||||
|
func RunTeamsList(ctx stdctx.Context, cmd *cli.Command) error {
|
||||||
|
c, err := context.InitCommand(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.Ensure(context.CtxRequirement{Org: true}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := c.Login.Client()
|
||||||
|
if search := cmd.String("search"); search != "" {
|
||||||
|
teams, _, err := client.Organizations.SearchOrgTeams(ctx, c.Org, &gitea.SearchTeamsOptions{
|
||||||
|
ListOptions: flags.GetListOptions(cmd),
|
||||||
|
Query: search,
|
||||||
|
IncludeDescription: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print.TeamsList(teams, c.Output, c.Org)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
teams, _, err := client.Organizations.ListOrgTeams(ctx, c.Org, gitea.ListTeamsOptions{
|
||||||
|
ListOptions: flags.GetListOptions(cmd),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print.TeamsList(teams, c.Output, c.Org)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequireTeamID returns an error if no team ID was provided, or the
|
||||||
|
// parsed team ID. Exported so sub-packages (members, repos) can share it.
|
||||||
|
func RequireTeamID(cmd *cli.Command) (int64, error) {
|
||||||
|
if cmd.Args().Len() == 0 {
|
||||||
|
return 0, errors.New("team ID is required")
|
||||||
|
}
|
||||||
|
return utils.ArgToIndex(cmd.Args().First())
|
||||||
|
}
|
||||||
70
modules/print/team.go
Normal file
70
modules/print/team.go
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package print
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"gitea.dev/sdk"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TeamsList prints a list of teams in an organization.
|
||||||
|
func TeamsList(teams []*gitea.Team, output, orgName string) {
|
||||||
|
if len(teams) == 0 {
|
||||||
|
fmt.Printf("No teams found in organization '%s'\n", orgName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := tableWithHeader(
|
||||||
|
"ID",
|
||||||
|
"Name",
|
||||||
|
"Description",
|
||||||
|
"Permission",
|
||||||
|
"Can Create Repo",
|
||||||
|
"All Repos",
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, team := range teams {
|
||||||
|
t.addRow(
|
||||||
|
strconv.FormatInt(team.ID, 10),
|
||||||
|
team.Name,
|
||||||
|
team.Description,
|
||||||
|
string(team.Permission),
|
||||||
|
yesNo(team.CanCreateOrgRepo),
|
||||||
|
yesNo(team.IncludesAllRepositories),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Teams in organization '%s':\n", orgName)
|
||||||
|
t.print(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TeamDetails prints detailed information about a team.
|
||||||
|
func TeamDetails(team *gitea.Team, output string) {
|
||||||
|
fmt.Printf("# Team: %s (ID: %d)\n\n", team.Name, team.ID)
|
||||||
|
fmt.Printf("- **Description**: %s\n", team.Description)
|
||||||
|
fmt.Printf("- **Permission**: %s\n", team.Permission)
|
||||||
|
fmt.Printf("- **Can Create Org Repo**: %t\n", team.CanCreateOrgRepo)
|
||||||
|
fmt.Printf("- **Includes All Repositories**: %t\n", team.IncludesAllRepositories)
|
||||||
|
|
||||||
|
if len(team.Units) > 0 {
|
||||||
|
fmt.Printf("- **Repository Units**:\n")
|
||||||
|
for _, unit := range team.Units {
|
||||||
|
fmt.Printf(" - %s\n", unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if team.Organization != nil {
|
||||||
|
fmt.Printf("- **Organization**: %s\n", team.Organization.UserName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// yesNo maps a bool to a human-readable "yes"/"no" string for table output.
|
||||||
|
func yesNo(b bool) string {
|
||||||
|
if b {
|
||||||
|
return "yes"
|
||||||
|
}
|
||||||
|
return "no"
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue