mirror of
https://gitea.com/gitea/tea.git
synced 2026-09-10 07:26:33 -04:00
feat(collaborators): add repository collaborator management commands
Add a 'tea collaborators' command for managing repository collaborators,
mirroring the conventions of the recently-added 'tea deploy-keys' command:
- list: List all collaborators of a repository
- create: Add a collaborator with specified permission (read/write/admin)
- delete: Remove a collaborator from a repository
- permission: Check a collaborator's current permission level
Running 'tea collaborators <username>' without a subcommand is a shortcut
for 'tea collaborators permission <username>'.
Subcommand naming follows the codebase's create/delete convention; aliases
('add' for create, 'rm'/'remove' for delete) preserve the existing muscle
memory.
The deploy-keys functionality previously bundled here is intentionally
omitted: it is covered by the already-open upstream PR #1067 and would
conflict.
This commit is contained in:
parent
58931b5d17
commit
d1e0849df7
|
|
@ -42,6 +42,7 @@ func App() *cli.Command {
|
|||
&CmdWiki,
|
||||
&CmdWebhooks,
|
||||
&CmdComments,
|
||||
&CmdCollaborators,
|
||||
|
||||
&CmdOpen,
|
||||
&CmdNotifications,
|
||||
|
|
|
|||
73
cmd/collaborators.go
Normal file
73
cmd/collaborators.go
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
|
||||
"gitea.dev/tea/cmd/collaborators"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/tea/modules/print"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// CmdCollaborators represents the collaborators command for managing repository
|
||||
// collaborators.
|
||||
var CmdCollaborators = cli.Command{
|
||||
Name: "collaborators",
|
||||
Aliases: []string{"collaborator", "collab"},
|
||||
Category: catEntities,
|
||||
Usage: "Manage repository collaborators",
|
||||
Description: "List, create, delete, and check permissions for repository collaborators",
|
||||
ArgsUsage: "[<username>]",
|
||||
Action: runCollaboratorsDefault,
|
||||
Commands: []*cli.Command{
|
||||
&collaborators.CmdCollaboratorsList,
|
||||
&collaborators.CmdCollaboratorsCreate,
|
||||
&collaborators.CmdCollaboratorsDelete,
|
||||
&collaborators.CmdCollaboratorsPermission,
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "repo",
|
||||
Usage: "repository 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]",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runCollaboratorsDefault(ctx stdctx.Context, cmd *cli.Command) error {
|
||||
if cmd.Args().Len() == 1 {
|
||||
return runCollaboratorPermission(ctx, cmd, cmd.Args().First())
|
||||
}
|
||||
return collaborators.RunCollaboratorsList(ctx, cmd)
|
||||
}
|
||||
|
||||
func runCollaboratorPermission(ctx stdctx.Context, cmd *cli.Command, username string) error {
|
||||
c, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
client := c.Login.Client()
|
||||
|
||||
permission, _, err := client.CollaboratorPermission(ctx, c.Owner, c.Repo, username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
print.CollaboratorPermission(username, permission)
|
||||
return nil
|
||||
}
|
||||
61
cmd/collaborators/create.go
Normal file
61
cmd/collaborators/create.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package collaborators
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
gitea "gitea.dev/sdk"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// CmdCollaboratorsCreate represents a sub command of collaborators to create a collaborator
|
||||
var CmdCollaboratorsCreate = cli.Command{
|
||||
Name: "create",
|
||||
Aliases: []string{"add", "c"},
|
||||
Usage: "Create a collaborator",
|
||||
Description: "Add a collaborator to a repository. To change permissions, run 'delete' followed by 'create'.",
|
||||
ArgsUsage: "<username>",
|
||||
Action: runCollaboratorsCreate,
|
||||
Flags: append([]cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "permission",
|
||||
Usage: "permission level for the collaborator (read, write, admin)",
|
||||
Value: "write",
|
||||
},
|
||||
}, flags.AllDefaultFlags...),
|
||||
}
|
||||
|
||||
func runCollaboratorsCreate(ctx stdctx.Context, cmd *cli.Command) error {
|
||||
if cmd.Args().Len() == 0 {
|
||||
return errors.New("username is required")
|
||||
}
|
||||
|
||||
c, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
client := c.Login.Client()
|
||||
|
||||
username := cmd.Args().First()
|
||||
permission := gitea.AccessMode(cmd.String("permission"))
|
||||
|
||||
_, err = client.AddCollaborator(ctx, c.Owner, c.Repo, username, gitea.AddCollaboratorOption{
|
||||
Permission: &permission,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("User %s added as collaborator with %s permission\n", username, permission)
|
||||
return nil
|
||||
}
|
||||
65
cmd/collaborators/delete.go
Normal file
65
cmd/collaborators/delete.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package collaborators
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// CmdCollaboratorsDelete represents a sub command of collaborators to delete a collaborator
|
||||
var CmdCollaboratorsDelete = cli.Command{
|
||||
Name: "delete",
|
||||
Aliases: []string{"rm", "remove"},
|
||||
Usage: "Delete a collaborator",
|
||||
Description: "Remove a collaborator from a repository",
|
||||
ArgsUsage: "<username>",
|
||||
Action: runCollaboratorsDelete,
|
||||
Flags: append([]cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "confirm",
|
||||
Aliases: []string{"y"},
|
||||
Usage: "confirm deletion without prompting",
|
||||
},
|
||||
}, flags.AllDefaultFlags...),
|
||||
}
|
||||
|
||||
func runCollaboratorsDelete(ctx stdctx.Context, cmd *cli.Command) error {
|
||||
if cmd.Args().Len() == 0 {
|
||||
return errors.New("username is required")
|
||||
}
|
||||
|
||||
c, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
client := c.Login.Client()
|
||||
|
||||
username := cmd.Args().First()
|
||||
|
||||
if !cmd.Bool("confirm") {
|
||||
fmt.Printf("Are you sure you want to remove %s as collaborator? [y/N] ", username)
|
||||
var response string
|
||||
fmt.Scanln(&response)
|
||||
if response != "y" && response != "Y" && response != "yes" {
|
||||
fmt.Println("Deletion canceled.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = client.DeleteCollaborator(ctx, c.Owner, c.Repo, username); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Collaborator %s removed successfully\n", username)
|
||||
return nil
|
||||
}
|
||||
50
cmd/collaborators/list.go
Normal file
50
cmd/collaborators/list.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package collaborators
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
|
||||
gitea "gitea.dev/sdk"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/tea/modules/print"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// CmdCollaboratorsList represents a sub command of collaborators to list collaborators
|
||||
var CmdCollaboratorsList = cli.Command{
|
||||
Name: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Usage: "List collaborators",
|
||||
Description: "List collaborators of a repository",
|
||||
Action: RunCollaboratorsList,
|
||||
Flags: append([]cli.Flag{
|
||||
&flags.PaginationPageFlag,
|
||||
&flags.PaginationLimitFlag,
|
||||
}, flags.AllDefaultFlags...),
|
||||
}
|
||||
|
||||
// RunCollaboratorsList lists collaborators
|
||||
func RunCollaboratorsList(ctx stdctx.Context, cmd *cli.Command) error {
|
||||
c, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
client := c.Login.Client()
|
||||
|
||||
collaborators, _, err := client.ListCollaborators(ctx, c.Owner, c.Repo, gitea.ListCollaboratorsOptions{
|
||||
ListOptions: flags.GetListOptions(cmd),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
print.CollaboratorsList(collaborators, c.Output)
|
||||
return nil
|
||||
}
|
||||
50
cmd/collaborators/permission.go
Normal file
50
cmd/collaborators/permission.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package collaborators
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"errors"
|
||||
|
||||
"gitea.dev/tea/cmd/flags"
|
||||
"gitea.dev/tea/modules/context"
|
||||
"gitea.dev/tea/modules/print"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// CmdCollaboratorsPermission represents a sub command of collaborators to check permission
|
||||
var CmdCollaboratorsPermission = cli.Command{
|
||||
Name: "permission",
|
||||
Aliases: []string{"perm"},
|
||||
Usage: "Check collaborator permission",
|
||||
Description: "Check the permission level of a collaborator on a repository",
|
||||
ArgsUsage: "<username>",
|
||||
Action: runCollaboratorsPermission,
|
||||
Flags: flags.AllDefaultFlags,
|
||||
}
|
||||
|
||||
func runCollaboratorsPermission(ctx stdctx.Context, cmd *cli.Command) error {
|
||||
if cmd.Args().Len() == 0 {
|
||||
return errors.New("username is required")
|
||||
}
|
||||
|
||||
c, err := context.InitCommand(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Ensure(context.CtxRequirement{RemoteRepo: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
client := c.Login.Client()
|
||||
|
||||
username := cmd.Args().First()
|
||||
|
||||
permission, _, err := client.CollaboratorPermission(ctx, c.Owner, c.Repo, username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
print.CollaboratorPermission(username, permission)
|
||||
return nil
|
||||
}
|
||||
41
modules/print/collaborator.go
Normal file
41
modules/print/collaborator.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package print
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"gitea.dev/sdk"
|
||||
)
|
||||
|
||||
// CollaboratorsList prints a listing of collaborators
|
||||
func CollaboratorsList(collaborators []*gitea.User, output string) {
|
||||
t := tableWithHeader(
|
||||
"ID",
|
||||
"Username",
|
||||
"Full Name",
|
||||
"Email",
|
||||
)
|
||||
|
||||
for _, user := range collaborators {
|
||||
t.addRow(
|
||||
strconv.FormatInt(user.ID, 10),
|
||||
user.UserName,
|
||||
user.FullName,
|
||||
user.Email,
|
||||
)
|
||||
}
|
||||
|
||||
t.print(output)
|
||||
}
|
||||
|
||||
// CollaboratorPermission prints detailed information about a collaborator's permissions
|
||||
func CollaboratorPermission(username string, permission *gitea.CollaboratorPermissionResult) {
|
||||
fmt.Printf("# Collaborator: %s\n\n", username)
|
||||
fmt.Printf("- **Permission**: %s\n", permission.Permission)
|
||||
if permission.Role != "" {
|
||||
fmt.Printf("- **Role**: %s\n", permission.Role)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue