From c8f75c9975bd232a4dbf374c4a405c75c2738a4f Mon Sep 17 00:00:00 2001 From: Ross Golder Date: Thu, 30 Jul 2026 10:27:06 +0700 Subject: [PATCH] feat(teams): add members subcommands Provide list, add, and remove operations for team members via 'tea teams members {list,create,delete}'. The members package reuses the RequireTeamID helper exported by cmd/teams. --- cmd/teams.go | 2 + cmd/teams/members/create.go | 55 ++++++++++++++++++++++++++++ cmd/teams/members/delete.go | 71 ++++++++++++++++++++++++++++++++++++ cmd/teams/members/list.go | 58 +++++++++++++++++++++++++++++ cmd/teams/members/members.go | 26 +++++++++++++ modules/print/team.go | 27 ++++++++++++++ 6 files changed, 239 insertions(+) create mode 100644 cmd/teams/members/create.go create mode 100644 cmd/teams/members/delete.go create mode 100644 cmd/teams/members/list.go create mode 100644 cmd/teams/members/members.go diff --git a/cmd/teams.go b/cmd/teams.go index a15c215c..e6138f33 100644 --- a/cmd/teams.go +++ b/cmd/teams.go @@ -5,6 +5,7 @@ package cmd import ( "gitea.dev/tea/cmd/teams" + "gitea.dev/tea/cmd/teams/members" "github.com/urfave/cli/v3" ) @@ -21,6 +22,7 @@ var CmdTeams = cli.Command{ &teams.CmdTeamsCreate, &teams.CmdTeamsDelete, &teams.CmdTeamsEdit, + &members.CmdTeamsMembers, }, Flags: []cli.Flag{ &cli.StringFlag{ diff --git a/cmd/teams/members/create.go b/cmd/teams/members/create.go new file mode 100644 index 00000000..1c459836 --- /dev/null +++ b/cmd/teams/members/create.go @@ -0,0 +1,55 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package members + +import ( + stdctx "context" + "errors" + "fmt" + + "gitea.dev/tea/cmd/flags" + "gitea.dev/tea/cmd/teams" + "gitea.dev/tea/modules/context" + "github.com/urfave/cli/v3" +) + +// CmdTeamsMembersCreate adds a member to a team. +var CmdTeamsMembersCreate = cli.Command{ + Name: "create", + Aliases: []string{"add", "c"}, + Usage: "Add a member to a team", + Description: "Add a user to a team", + ArgsUsage: " ", + Action: runTeamsMembersCreate, + Flags: flags.AllDefaultFlags, +} + +func runTeamsMembersCreate(ctx stdctx.Context, cmd *cli.Command) error { + if cmd.Args().Len() < 2 { + return errors.New("team ID and username are required") + } + + teamID, err := teams.RequireTeamID(cmd) + if err != nil { + return err + } + username := cmd.Args().Get(1) + + 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 _, err = client.Organizations.AddTeamMember(ctx, teamID, username); err != nil { + return err + } + fmt.Printf("User '%s' added to team '%s' (ID: %d)\n", username, team.Name, team.ID) + return nil +} diff --git a/cmd/teams/members/delete.go b/cmd/teams/members/delete.go new file mode 100644 index 00000000..be49a48e --- /dev/null +++ b/cmd/teams/members/delete.go @@ -0,0 +1,71 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package members + +import ( + stdctx "context" + "errors" + "fmt" + + "gitea.dev/tea/cmd/flags" + "gitea.dev/tea/cmd/teams" + "gitea.dev/tea/modules/context" + "github.com/urfave/cli/v3" +) + +// CmdTeamsMembersDelete removes a member from a team. +var CmdTeamsMembersDelete = cli.Command{ + Name: "delete", + Aliases: []string{"rm", "remove"}, + Usage: "Remove a member from a team", + Description: "Remove a user from a team", + ArgsUsage: " ", + Action: runTeamsMembersDelete, + Flags: append([]cli.Flag{ + &cli.BoolFlag{ + Name: "confirm", + Aliases: []string{"y"}, + Usage: "confirm removal without prompting", + }, + }, flags.AllDefaultFlags...), +} + +func runTeamsMembersDelete(ctx stdctx.Context, cmd *cli.Command) error { + if cmd.Args().Len() < 2 { + return errors.New("team ID and username are required") + } + + teamID, err := teams.RequireTeamID(cmd) + if err != nil { + return err + } + username := cmd.Args().Get(1) + + 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 remove '%s' from team '%s' (ID: %d)? [y/N] ", username, team.Name, team.ID) + var response string + fmt.Scanln(&response) + if response != "y" && response != "Y" && response != "yes" { + fmt.Println("Removal canceled.") + return nil + } + } + + if _, err = client.Organizations.RemoveTeamMember(ctx, teamID, username); err != nil { + return err + } + fmt.Printf("User '%s' removed from team '%s' (ID: %d)\n", username, team.Name, team.ID) + return nil +} diff --git a/cmd/teams/members/list.go b/cmd/teams/members/list.go new file mode 100644 index 00000000..e0a3358f --- /dev/null +++ b/cmd/teams/members/list.go @@ -0,0 +1,58 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package members + +import ( + stdctx "context" + + gitea "gitea.dev/sdk" + + "gitea.dev/tea/cmd/flags" + "gitea.dev/tea/cmd/teams" + "gitea.dev/tea/modules/context" + "gitea.dev/tea/modules/print" + "github.com/urfave/cli/v3" +) + +// CmdTeamsMembersList lists all members of a team. +var CmdTeamsMembersList = cli.Command{ + Name: "list", + Aliases: []string{"ls"}, + Usage: "List team members", + Description: "List all members of a team", + ArgsUsage: "", + Action: runTeamsMembersList, + Flags: append([]cli.Flag{ + &flags.PaginationPageFlag, + &flags.PaginationLimitFlag, + }, flags.AllDefaultFlags...), +} + +func runTeamsMembersList(ctx stdctx.Context, cmd *cli.Command) error { + teamID, err := teams.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 + } + + members, _, err := client.Organizations.ListTeamMembers(ctx, teamID, gitea.ListTeamMembersOptions{ + ListOptions: flags.GetListOptions(cmd), + }) + if err != nil { + return err + } + + print.TeamMembersList(members, c.Output, team.Name) + return nil +} diff --git a/cmd/teams/members/members.go b/cmd/teams/members/members.go new file mode 100644 index 00000000..4b248269 --- /dev/null +++ b/cmd/teams/members/members.go @@ -0,0 +1,26 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package members + +import ( + stdctx "context" + + "github.com/urfave/cli/v3" +) + +// CmdTeamsMembers is the parent command for managing team membership. +var CmdTeamsMembers = cli.Command{ + Name: "members", + Aliases: []string{"member"}, + Usage: "Manage team members", + Description: "List, add, or remove team members", + Commands: []*cli.Command{ + &CmdTeamsMembersList, + &CmdTeamsMembersCreate, + &CmdTeamsMembersDelete, + }, + Action: func(ctx stdctx.Context, cmd *cli.Command) error { + return cli.ShowCommandHelp(ctx, cmd, "members") + }, +} diff --git a/modules/print/team.go b/modules/print/team.go index ff44a412..0cd9dcfc 100644 --- a/modules/print/team.go +++ b/modules/print/team.go @@ -68,3 +68,30 @@ func yesNo(b bool) string { } return "no" } + +// TeamMembersList prints a list of team members. +func TeamMembersList(members []*gitea.User, output, teamName string) { + if len(members) == 0 { + fmt.Printf("No members found in team '%s'\n", teamName) + return + } + + t := tableWithHeader( + "ID", + "Username", + "Full Name", + "Email", + ) + + for _, m := range members { + t.addRow( + strconv.FormatInt(m.ID, 10), + m.UserName, + m.FullName, + m.Email, + ) + } + + fmt.Printf("Members of team '%s':\n", teamName) + t.print(output) +}