refactor: migrate protected branch fields into one json column and support system users (#39735)

1. migrate protected branch fields into one json column
2. use the dropdown list for push/force-push user selection, and Actions
user can be added into the list now.
This commit is contained in:
wxiaoguang authored and GitHub committed 2026-10-13 00:40:21 +00:00
1 parent f270534e83
commit c6e2513570
25 files changed
+659 -334

No files matched your search

+48 -18
View File
@@ -560,13 +560,13 @@ func GetUserSalt() (string, error) {
}
type globalVarsStruct struct {
customCharsReplacement *strings.Replacer
removeCharsRE *regexp.Regexp
transformDiacritics transform.Transformer
replaceCharsHyphenRE *regexp.Regexp
emailToReplacer *strings.Replacer
systemUserNewFuncs map[int64]func() *User
systemUserNameIdMap map[string]int64
customCharsReplacement *strings.Replacer
removeCharsRE *regexp.Regexp
transformDiacritics transform.Transformer
replaceCharsHyphenRE *regexp.Regexp
emailToReplacer *strings.Replacer
systemUserNewFuncs map[int64]func() *User
systemUserLowerNameIdMap map[string]int64
}
var globalVars = sync.OnceValue(func() *globalVarsStruct {
@@ -592,11 +592,11 @@ var globalVars = sync.OnceValue(func() *globalVarsStruct {
userFuncs := []func() *User{NewGhostUser, NewActionsUser, NewDeployKeyUser, NewCliUser, NewAuthSourceUser}
ret.systemUserNewFuncs = map[int64]func() *User{}
ret.systemUserNameIdMap = map[string]int64{}
ret.systemUserLowerNameIdMap = map[string]int64{}
for _, fn := range userFuncs {
u := fn()
ret.systemUserNewFuncs[u.ID] = fn
ret.systemUserNameIdMap[u.LowerName] = u.ID
ret.systemUserLowerNameIdMap[u.LowerName] = u.ID
}
return ret
})
@@ -1148,20 +1148,50 @@ func GetMailableUsersByIDs(ctx context.Context, ids []int64, isMention bool) ([]
Find(&ous)
}
// GetUserIDsByNames returns a slice of ids corresponds to names.
func GetUserIDsByNames(ctx context.Context, names []string, ignoreNonExistent bool) ([]int64, error) {
ids := make([]int64, 0, len(names))
func getDoerUserIDsByNames(ctx context.Context, names []string, allowSystemDoer bool) ([]int64, error) {
namesLower := make([]string, 0, len(names))
namesMap := make(map[string]*User, len(names))
for _, name := range names {
u, err := GetUserByName(ctx, name)
if err != nil {
if ignoreNonExistent {
continue
nameLower := strings.ToLower(name)
namesLower = append(namesLower, nameLower)
namesMap[nameLower] = nil
if allowSystemDoer {
if id, ok := globalVars().systemUserLowerNameIdMap[nameLower]; ok && id != GhostUserID {
namesMap[nameLower] = globalVars().systemUserNewFuncs[id]()
}
return nil, err
}
}
var users []*User
err := db.GetEngine(ctx).In("lower_name", namesLower).Where("`type` IN (?,?)", UserTypeIndividual, UserTypeBot).Find(&users)
if err != nil {
return nil, err
}
for _, user := range users {
namesMap[user.LowerName] = user
}
ids := make([]int64, 0, len(names))
var errFinal error
for _, nameLower := range namesLower {
u := namesMap[nameLower]
if u == nil {
errFinal = ErrUserNotExist{Name: nameLower}
continue
}
ids = append(ids, u.ID)
}
return ids, nil
return ids, errFinal
}
func GetDoerUserIDsByNamesWithSystem(ctx context.Context, names []string) ([]int64, error) {
return getDoerUserIDsByNames(ctx, names, true)
}
// GetDoerUserIDsByNames returns a slice of ids corresponds to names.
// It always returns found user IDs even if some names are not found, but returns an error if any name is not found.
func GetDoerUserIDsByNames(ctx context.Context, names []string) ([]int64, error) {
return getDoerUserIDsByNames(ctx, names, false)
}
// GetUsersBySource returns a list of Users for a login source
+8 -4
View File
@@ -73,7 +73,7 @@ func NewActionsUserWithTaskID(id int64) *User {
}
func NewDeployKeyUser() *User {
return newSystemUser(DeployKeyUserID, "(deploy-key)", "Deploy Key")
return newSystemUser(DeployKeyUserID, "gitea:deploy-key", "Deploy Key")
}
func GetDeployKeyUserDeployKeyID(u *User) (int64, bool) {
@@ -94,17 +94,21 @@ func NewDeployKeyUserWithKeyID(id int64) *User {
func NewCliUser() *User {
// for audit log only
return newSystemUser(CliUserID, "(gitea-cli)", "Gitea CLI")
return newSystemUser(CliUserID, "gitea:cli", "Gitea CLI")
}
func NewAuthSourceUser() *User {
// for audit log only
return newSystemUser(AuthSourceUserID, "(gitea-auth-source)", "Gitea Auth Source")
return newSystemUser(AuthSourceUserID, "gitea:auth-source", "Gitea Auth Source")
}
func GetSystemUserByName(name string) *User {
lowerName := strings.ToLower(name)
uid := globalVars().systemUserNameIdMap[lowerName]
uid := globalVars().systemUserLowerNameIdMap[lowerName]
return GetSystemUserByID(uid)
}
func GetSystemUserByID(uid int64) *User {
if fn := globalVars().systemUserNewFuncs[uid]; fn != nil {
return fn()
}
+7 -9
View File
@@ -21,6 +21,7 @@ import (
"gitea.dev/modules/structs"
"gitea.dev/modules/test"
"gitea.dev/modules/timeutil"
"gitea.dev/modules/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -363,18 +364,15 @@ func TestCreateUserWithoutCustomTimestamps(t *testing.T) {
assert.LessOrEqual(t, fetched.UpdatedUnix, timestampEnd)
}
func TestGetUserIDsByNames(t *testing.T) {
func TestGetDoerUserIDsByNames(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
ids, err := user_model.GetDoerUserIDsByNames(t.Context(), []string{"user1", "user2", "none_existing_user"})
assert.ErrorIs(t, err, util.ErrNotExist)
assert.Equal(t, []int64{1, 2}, ids)
// ignore non existing
IDs, err := user_model.GetUserIDsByNames(t.Context(), []string{"user1", "user2", "none_existing_user"}, true)
ids, err = user_model.GetDoerUserIDsByNamesWithSystem(t.Context(), []string{"gitea-actions", "gitea:deploy-key"})
assert.NoError(t, err)
assert.Equal(t, []int64{1, 2}, IDs)
// ignore non existing
IDs, err = user_model.GetUserIDsByNames(t.Context(), []string{"user1", "do_not_exist"}, false)
assert.Error(t, err)
assert.Equal(t, []int64(nil), IDs)
assert.Equal(t, []int64{-2, -3}, ids)
}
func TestGetMaileableUsersByIDs(t *testing.T) {