feat(teams): add repos subcommands

Provide list, add, and remove operations for repositories accessible
by a team via 'tea teams repos {list,create,delete}'. The repos
package reuses the RequireTeamID helper exported by cmd/teams and
introduces a parseRepoSlug helper for '<owner>/<repo>' arguments.
This commit is contained in:
Ross Golder 2026-07-30 10:27:58 +07:00
parent 3346617581
commit f877621715
No known key found for this signature in database
GPG key ID: 253A7E508D2D59CD
6 changed files with 263 additions and 0 deletions

View file

@ -6,6 +6,7 @@ package cmd
import (
"gitea.dev/tea/cmd/teams"
"gitea.dev/tea/cmd/teams/members"
"gitea.dev/tea/cmd/teams/repos"
"github.com/urfave/cli/v3"
)
@ -23,6 +24,7 @@ var CmdTeams = cli.Command{
&teams.CmdTeamsDelete,
&teams.CmdTeamsEdit,
&members.CmdTeamsMembers,
&repos.CmdTeamsRepos,
},
Flags: []cli.Flag{
&cli.StringFlag{

69
cmd/teams/repos/create.go Normal file
View file

@ -0,0 +1,69 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repos
import (
stdctx "context"
"errors"
"fmt"
"strings"
"gitea.dev/tea/cmd/flags"
"gitea.dev/tea/cmd/teams"
"gitea.dev/tea/modules/context"
"github.com/urfave/cli/v3"
)
// CmdTeamsReposCreate grants a team access to a repository.
var CmdTeamsReposCreate = cli.Command{
Name: "create",
Aliases: []string{"add", "c"},
Usage: "Grant a team access to a repository",
Description: "Grant a team access to a repository",
ArgsUsage: "<team-id> <owner>/<repo>",
Action: runTeamsReposCreate,
Flags: flags.AllDefaultFlags,
}
func runTeamsReposCreate(ctx stdctx.Context, cmd *cli.Command) error {
if cmd.Args().Len() < 2 {
return errors.New("team ID and <owner>/<repo> are required")
}
teamID, err := teams.RequireTeamID(cmd)
if err != nil {
return err
}
owner, repo, err := parseRepoSlug(cmd.Args().Get(1))
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 _, err = client.Organizations.AddTeamRepository(ctx, teamID, owner, repo); err != nil {
return err
}
fmt.Printf("Repository '%s/%s' granted to team '%s' (ID: %d)\n", owner, repo, team.Name, team.ID)
return nil
}
// parseRepoSlug splits "owner/repo" into its parts.
func parseRepoSlug(slug string) (owner, repo string, err error) {
parts := strings.SplitN(slug, "/", 2)
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
return "", "", fmt.Errorf("invalid repository %q: expected <owner>/<repo>", slug)
}
return parts[0], parts[1], nil
}

75
cmd/teams/repos/delete.go Normal file
View file

@ -0,0 +1,75 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repos
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"
)
// CmdTeamsReposDelete revokes a team's access to a repository.
var CmdTeamsReposDelete = cli.Command{
Name: "delete",
Aliases: []string{"rm", "remove"},
Usage: "Revoke a team's access to a repository",
Description: "Revoke a team's access to a repository",
ArgsUsage: "<team-id> <owner>/<repo>",
Action: runTeamsReposDelete,
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "confirm",
Aliases: []string{"y"},
Usage: "confirm removal without prompting",
},
}, flags.AllDefaultFlags...),
}
func runTeamsReposDelete(ctx stdctx.Context, cmd *cli.Command) error {
if cmd.Args().Len() < 2 {
return errors.New("team ID and <owner>/<repo> are required")
}
teamID, err := teams.RequireTeamID(cmd)
if err != nil {
return err
}
owner, repo, err := parseRepoSlug(cmd.Args().Get(1))
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 revoke '%s/%s' access from team '%s' (ID: %d)? [y/N] ", owner, repo, 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.RemoveTeamRepository(ctx, teamID, owner, repo); err != nil {
return err
}
fmt.Printf("Repository '%s/%s' access revoked from team '%s' (ID: %d)\n", owner, repo, team.Name, team.ID)
return nil
}

58
cmd/teams/repos/list.go Normal file
View file

@ -0,0 +1,58 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repos
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"
)
// CmdTeamsReposList lists the repositories a team has access to.
var CmdTeamsReposList = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List team repositories",
Description: "List all repositories a team has access to",
ArgsUsage: "<team-id>",
Action: runTeamsReposList,
Flags: append([]cli.Flag{
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
}, flags.AllDefaultFlags...),
}
func runTeamsReposList(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
}
repos, _, err := client.Organizations.ListTeamRepositories(ctx, teamID, gitea.ListTeamRepositoriesOptions{
ListOptions: flags.GetListOptions(cmd),
})
if err != nil {
return err
}
print.TeamRepositoriesList(repos, c.Output, team.Name)
return nil
}

26
cmd/teams/repos/repos.go Normal file
View file

@ -0,0 +1,26 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repos
import (
stdctx "context"
"github.com/urfave/cli/v3"
)
// CmdTeamsRepos is the parent command for managing which repositories a team can access.
var CmdTeamsRepos = cli.Command{
Name: "repos",
Aliases: []string{"repo", "repositories"},
Usage: "Manage team repository access",
Description: "List, add, or remove repositories a team can access",
Commands: []*cli.Command{
&CmdTeamsReposList,
&CmdTeamsReposCreate,
&CmdTeamsReposDelete,
},
Action: func(ctx stdctx.Context, cmd *cli.Command) error {
return cli.ShowCommandHelp(ctx, cmd, "repos")
},
}

View file

@ -95,3 +95,36 @@ func TeamMembersList(members []*gitea.User, output, teamName string) {
fmt.Printf("Members of team '%s':\n", teamName)
t.print(output)
}
// TeamRepositoriesList prints a list of repositories accessible by a team.
func TeamRepositoriesList(repos []*gitea.Repository, output, teamName string) {
if len(repos) == 0 {
fmt.Printf("No repositories found for team '%s'\n", teamName)
return
}
t := tableWithHeader(
"ID",
"Name",
"Full Name",
"Private",
"Fork",
"Size",
"Updated",
)
for _, repo := range repos {
t.addRow(
strconv.FormatInt(repo.ID, 10),
repo.Name,
repo.FullName,
yesNo(repo.Private),
yesNo(repo.Fork),
formatSize(int64(repo.Size)),
FormatTime(repo.Updated, output != ""),
)
}
fmt.Printf("Repositories accessible by team '%s':\n", teamName)
t.print(output)
}