mirror of
https://gitea.com/gitea/tea.git
synced 2026-09-10 07:26:33 -04:00
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:
parent
3346617581
commit
f877621715
|
|
@ -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
69
cmd/teams/repos/create.go
Normal 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
75
cmd/teams/repos/delete.go
Normal 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
58
cmd/teams/repos/list.go
Normal 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
26
cmd/teams/repos/repos.go
Normal 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")
|
||||
},
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue