fix(team): merge team edit --unit into the current unit policy

Gitea replaces a team's whole unit policy on edit, so
`tea team edit 5 --unit repo.wiki=write` revoked code, issue and pull
request access. team edit now merges --unit into the current policy;
--unit-replace keeps the old replacing behavior.

Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni committed 2026-10-11 21:56:32 +02:00
1 parent a3f5468628
commit 784423bd9d
4 files changed
+83 -8

No files matched your search

+11 -2
View File
@@ -35,7 +35,7 @@ func (a *application) teamCommand() *cobra.Command {
group.AddCommand(list)
for _, verb := range []string{"view", "create", "edit", "delete"} {
var name, description, permission string
var createRepo, allRepos, force bool
var createRepo, allRepos, force, replaceUnits bool
var units map[string]string
command := &cobra.Command{Use: verb + " <id>", Short: titleCase(verb) + " an organization team", Args: argsExact(1)}
switch verb {
@@ -49,7 +49,12 @@ func (a *application) teamCommand() *cobra.Command {
command.Flags().StringVar(&name, "name", "", "Team name")
command.Flags().StringVar(&description, "description", "", "Team description")
command.Flags().StringVar(&permission, "permission", "read", "Default access: read, write, admin")
command.Flags().StringToStringVar(&units, "unit", nil, "Unit permissions, for example repo.code=write,repo.issues=read (replaces unit policy)")
unitUsage := "Unit permissions, for example repo.code=write,repo.issues=read"
if verb == "edit" {
unitUsage = "Unit permissions to change, for example repo.wiki=write; merged into the current policy"
command.Flags().BoolVar(&replaceUnits, "unit-replace", false, "Replace the whole unit policy with --unit; omitted units lose access")
}
command.Flags().StringToStringVar(&units, "unit", nil, unitUsage)
command.Flags().BoolVar(&createRepo, "can-create-repo", false, "Allow creating organization repositories")
command.Flags().BoolVar(&allRepos, "all-repositories", false, "Access every organization repository")
}
@@ -68,6 +73,9 @@ func (a *application) teamCommand() *cobra.Command {
if err := oneOf("permission", permission, "read", "write", "admin"); err != nil {
return err
}
if replaceUnits && !cmd.Flags().Changed("unit") {
return clierr.Wrap(clierr.Usage, errors.New("--unit-replace requires --unit"))
}
if cmd.Flags().Changed("unit") && (len(units) == 0 || permission == "admin") {
return clierr.Wrap(clierr.Usage, errors.New("--unit needs a nonempty policy and cannot restrict administrator access"))
}
@@ -106,6 +114,7 @@ func (a *application) teamCommand() *cobra.Command {
}
if cmd.Flags().Changed("unit") {
opts.Units = units
opts.ReplaceUnits = replaceUnits
}
if cmd.Flags().Changed("can-create-repo") {
opts.CanCreateRepo = &createRepo
+26
View File
@@ -5,6 +5,7 @@ package app
import (
"context"
"encoding/json"
"net/http"
"testing"
@@ -52,3 +53,28 @@ func TestTeamDeleteNamesTheTeamAndProtectsTheOwnerTeam(t *testing.T) {
require.Equal(t, []string{`Delete team "devs" (ID 5) from acme?`}, prompt.confirmations)
require.Equal(t, []string{"/api/v1/teams/3", "/api/v1/teams/5"}, deleted)
}
func TestTeamEditMergesUnitsIntoTheCurrentPolicy(t *testing.T) {
var sent []map[string]any
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/api/v1/teams/5", r.URL.Path)
if r.Method == http.MethodPatch {
var body map[string]any
require.NoError(t, json.NewDecoder(r.Body).Decode(&body))
sent = append(sent, body)
}
_, _ = w.Write([]byte(`{"id":5,"name":"devs","permission":"write","units_map":{"repo.code":"write","repo.issues":"write","repo.pulls":"write"}}`))
}))
defer server.Close()
deps, _, _ := serverDeps(t, server.URL)
require.NoError(t, Execute(context.Background(), deps, []string{"team", "edit", "5", "--unit", "repo.wiki=write", "--json"}))
require.NoError(t, Execute(context.Background(), deps, []string{"team", "edit", "5", "--unit", "repo.wiki=write", "--unit-replace", "--json"}))
require.Equal(t, []map[string]any{
{"units_map": map[string]any{"repo.code": "write", "repo.issues": "write", "repo.pulls": "write", "repo.wiki": "write"}},
{"units_map": map[string]any{"repo.wiki": "write"}},
}, sent)
err := Execute(context.Background(), deps, []string{"team", "edit", "5", "--unit-replace", "--name", "devs"})
require.Equal(t, clierr.Usage, clierr.ExitCode(err), "%v", err)
require.ErrorContains(t, err, "--unit-replace requires --unit")
require.Len(t, sent, 2)
}