aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2022-06-06 16:01:49 +0800
committerGitHub <noreply@github.com>2022-06-06 16:01:49 +0800
commit26095115f4ae90e3fdc6ab695978efd16e317f75 (patch)
tree92ec1c7ff54e0a65f4f0662baa8c0244dd9f324b
parentebeb6e7c71a0c763b52153f4eb427e7c5b89a95e (diff)
downloadgitea-26095115f4ae90e3fdc6ab695978efd16e317f75.tar.gz
gitea-26095115f4ae90e3fdc6ab695978efd16e317f75.zip
Move some repository related code into sub package (#19711)
* Move some repository related code into sub package * Move more repository functions out of models * Fix lint * Some performance optimization for webhooks and others * some refactors * Fix lint * Fix * Update modules/repository/delete.go Co-authored-by: delvh <dev.lh@web.de> * Fix test * Merge * Fix test * Fix test * Fix test * Fix test Co-authored-by: delvh <dev.lh@web.de>
-rw-r--r--cmd/admin.go5
-rw-r--r--models/action.go2
-rw-r--r--models/asymkey/ssh_key_deploy.go7
-rwxr-xr-xmodels/db/engine.go5
-rw-r--r--models/issue.go58
-rw-r--r--models/issue_list.go2
-rw-r--r--models/issue_user.go2
-rw-r--r--models/lfs.go13
-rw-r--r--models/notification.go10
-rw-r--r--models/org.go2
-rw-r--r--models/repo.go403
-rw-r--r--models/repo/attachment_test.go33
-rw-r--r--models/repo/collaboration_test.go13
-rw-r--r--models/repo/fork.go50
-rw-r--r--models/repo/fork_test.go11
-rw-r--r--models/repo/main_test.go21
-rw-r--r--models/repo/mirror.go13
-rw-r--r--models/repo/pushmirror_test.go13
-rw-r--r--models/repo/redirect_test.go31
-rw-r--r--models/repo/repo_list.go661
-rw-r--r--models/repo/repo_list_test.go (renamed from models/repo_list_test.go)91
-rw-r--r--models/repo/repo_test.go21
-rw-r--r--models/repo/star_test.go29
-rw-r--r--models/repo/topic_test.go43
-rw-r--r--models/repo/update.go8
-rw-r--r--models/repo/user_repo.go122
-rw-r--r--models/repo/user_repo_test.go74
-rw-r--r--models/repo/watch_test.go87
-rw-r--r--models/repo/wiki_test.go13
-rw-r--r--models/repo_generate.go118
-rw-r--r--models/repo_list.go704
-rw-r--r--models/repo_test.go119
-rw-r--r--models/webhook/webhook.go8
-rw-r--r--modules/context/repo.go5
-rw-r--r--modules/indexer/issues/indexer.go4
-rw-r--r--modules/repository/create.go115
-rw-r--r--modules/repository/create_test.go21
-rw-r--r--modules/repository/delete.go33
-rw-r--r--modules/repository/fork.go31
-rw-r--r--modules/repository/generate.go65
-rw-r--r--modules/repository/generate_test.go (renamed from models/repo_generate_test.go)2
-rw-r--r--modules/repository/init.go2
-rw-r--r--modules/repository/repo.go25
-rw-r--r--routers/api/v1/repo/collaborators.go4
-rw-r--r--routers/api/v1/repo/issue.go7
-rw-r--r--routers/api/v1/repo/repo.go10
-rw-r--r--routers/api/v1/user/repo.go8
-rw-r--r--routers/web/explore/code.go4
-rw-r--r--routers/web/explore/repo.go3
-rw-r--r--routers/web/org/home.go3
-rw-r--r--routers/web/org/setting.go5
-rw-r--r--routers/web/repo/attachment.go4
-rw-r--r--routers/web/repo/compare.go4
-rw-r--r--routers/web/repo/issue.go15
-rw-r--r--routers/web/repo/pull.go2
-rw-r--r--routers/web/repo/repo.go10
-rw-r--r--routers/web/repo/setting.go8
-rw-r--r--routers/web/repo/view.go3
-rw-r--r--routers/web/user/home.go10
-rw-r--r--routers/web/user/home_test.go4
-rw-r--r--routers/web/user/package.go3
-rw-r--r--routers/web/user/profile.go6
-rw-r--r--routers/web/user/setting/profile.go5
-rw-r--r--services/gitdiff/gitdiff.go2
-rw-r--r--services/mailer/mail_issue.go2
-rw-r--r--services/mirror/mirror_pull.go3
-rw-r--r--services/repository/adopt.go6
-rw-r--r--services/repository/check.go3
-rw-r--r--services/repository/fork.go10
-rw-r--r--services/repository/hooks.go27
-rw-r--r--services/repository/push.go2
-rw-r--r--services/repository/repository.go40
-rw-r--r--services/repository/repository_test.go43
-rw-r--r--services/repository/review.go24
-rw-r--r--services/repository/review_test.go28
-rw-r--r--services/repository/template.go25
76 files changed, 1755 insertions, 1673 deletions
diff --git a/cmd/admin.go b/cmd/admin.go
index 0629dfc2db..32f9a95a66 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -17,6 +17,7 @@ import (
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful"
@@ -722,9 +723,9 @@ func runRepoSyncReleases(_ *cli.Context) error {
log.Trace("Synchronizing repository releases (this may take a while)")
for page := 1; ; page++ {
- repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
+ repos, count, err := repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
- PageSize: models.RepositoryListDefaultPageSize,
+ PageSize: repo_model.RepositoryListDefaultPageSize,
Page: page,
},
Private: true,
diff --git a/models/action.go b/models/action.go
index 87bfcbbbad..882bc59d8f 100644
--- a/models/action.go
+++ b/models/action.go
@@ -393,7 +393,7 @@ func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
// check readable repositories by doer/actor
if opts.Actor == nil || !opts.Actor.IsAdmin {
- cond = cond.And(builder.In("repo_id", AccessibleRepoIDsQuery(opts.Actor)))
+ cond = cond.And(builder.In("repo_id", repo_model.AccessibleRepoIDsQuery(opts.Actor)))
}
if opts.RequestedRepo != nil {
diff --git a/models/asymkey/ssh_key_deploy.go b/models/asymkey/ssh_key_deploy.go
index 22fcefff69..fd8388e61d 100644
--- a/models/asymkey/ssh_key_deploy.go
+++ b/models/asymkey/ssh_key_deploy.go
@@ -190,6 +190,13 @@ func GetDeployKeyByRepo(ctx context.Context, keyID, repoID int64) (*DeployKey, e
return key, nil
}
+// IsDeployKeyExistByKeyID return true if there is at least one deploykey with the key id
+func IsDeployKeyExistByKeyID(ctx context.Context, keyID int64) (bool, error) {
+ return db.GetEngine(ctx).
+ Where("key_id = ?", keyID).
+ Get(new(DeployKey))
+}
+
// UpdateDeployKeyCols updates deploy key information in the specified columns.
func UpdateDeployKeyCols(key *DeployKey, cols ...string) error {
_, err := db.GetEngine(db.DefaultContext).ID(key.ID).Cols(cols...).Update(key)
diff --git a/models/db/engine.go b/models/db/engine.go
index 23eb59dcf5..8a3b4b206e 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -271,11 +271,6 @@ func MaxBatchInsertSize(bean interface{}) int {
return 999 / len(t.ColumnsSeq())
}
-// Count returns records number according struct's fields as database query conditions
-func Count(bean interface{}) (int64, error) {
- return x.Count(bean)
-}
-
// IsTableNotEmpty returns true if table has at least one record
func IsTableNotEmpty(tableName string) (bool, error) {
return x.Table(tableName).Exist()
diff --git a/models/issue.go b/models/issue.go
index 4150d66a65..a22c115523 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -1343,6 +1343,48 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
}
}
+// teamUnitsRepoCond returns query condition for those repo id in the special org team with special units access
+func teamUnitsRepoCond(id string, userID, orgID, teamID int64, units ...unit.Type) builder.Cond {
+ return builder.In(id,
+ builder.Select("repo_id").From("team_repo").Where(
+ builder.Eq{
+ "team_id": teamID,
+ }.And(
+ builder.Or(
+ // Check if the user is member of the team.
+ builder.In(
+ "team_id", builder.Select("team_id").From("team_user").Where(
+ builder.Eq{
+ "uid": userID,
+ },
+ ),
+ ),
+ // Check if the user is in the owner team of the organisation.
+ builder.Exists(builder.Select("team_id").From("team_user").
+ Where(builder.Eq{
+ "org_id": orgID,
+ "team_id": builder.Select("id").From("team").Where(
+ builder.Eq{
+ "org_id": orgID,
+ "lower_name": strings.ToLower(organization.OwnerTeamName),
+ }),
+ "uid": userID,
+ }),
+ ),
+ )).And(
+ builder.In(
+ "team_id", builder.Select("team_id").From("team_unit").Where(
+ builder.Eq{
+ "`team_unit`.org_id": orgID,
+ }.And(
+ builder.In("`team_unit`.type", units),
+ ),
+ ),
+ ),
+ ),
+ ))
+}
+
// issuePullAccessibleRepoCond userID must not be zero, this condition require join repository table
func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organization.Organization, team *organization.Team, isPull bool) builder.Cond {
cond := builder.NewCond()
@@ -1356,19 +1398,19 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati
} else {
cond = cond.And(
builder.Or(
- userOrgUnitRepoCond(repoIDstr, userID, org.ID, unitType), // team member repos
- userOrgPublicUnitRepoCond(userID, org.ID), // user org public non-member repos, TODO: check repo has issues
+ repo_model.UserOrgUnitRepoCond(repoIDstr, userID, org.ID, unitType), // team member repos
+ repo_model.UserOrgPublicUnitRepoCond(userID, org.ID), // user org public non-member repos, TODO: check repo has issues
),
)
}
} else {
cond = cond.And(
builder.Or(
- userOwnedRepoCond(userID), // owned repos
- userCollaborationRepoCond(repoIDstr, userID), // collaboration repos
- userAssignedRepoCond(repoIDstr, userID), // user has been assigned accessible public repos
- userMentionedRepoCond(repoIDstr, userID), // user has been mentioned accessible public repos
- userCreateIssueRepoCond(repoIDstr, userID, isPull), // user has created issue/pr accessible public repos
+ repo_model.UserOwnedRepoCond(userID), // owned repos
+ repo_model.UserCollaborationRepoCond(repoIDstr, userID), // collaboration repos
+ repo_model.UserAssignedRepoCond(repoIDstr, userID), // user has been assigned accessible public repos
+ repo_model.UserMentionedRepoCond(repoIDstr, userID), // user has been mentioned accessible public repos
+ repo_model.UserCreateIssueRepoCond(repoIDstr, userID, isPull), // user has created issue/pr accessible public repos
),
)
}
@@ -1434,7 +1476,7 @@ func GetRepoIDsForIssuesOptions(opts *IssuesOptions, user *user_model.User) ([]i
opts.setupSessionNoLimit(sess)
- accessCond := accessibleRepositoryCondition(user)
+ accessCond := repo_model.AccessibleRepositoryCondition(user)
if err := sess.Where(accessCond).
Distinct("issue.repo_id").
Table("issue").
diff --git a/models/issue_list.go b/models/issue_list.go
index 31588c02a4..a5fc095e12 100644
--- a/models/issue_list.go
+++ b/models/issue_list.go
@@ -75,7 +75,7 @@ func (issues IssueList) loadRepositories(ctx context.Context) ([]*repo_model.Rep
}
}
}
- return valuesRepository(repoMaps), nil
+ return repo_model.ValuesRepository(repoMaps), nil
}
// LoadRepositories loads issues' all repositories
diff --git a/models/issue_user.go b/models/issue_user.go
index 0b1f8204ba..19c64094a1 100644
--- a/models/issue_user.go
+++ b/models/issue_user.go
@@ -26,7 +26,7 @@ func init() {
}
func newIssueUsers(ctx context.Context, repo *repo_model.Repository, issue *Issue) error {
- assignees, err := getRepoAssignees(ctx, repo)
+ assignees, err := repo_model.GetRepoAssignees(ctx, repo)
if err != nil {
return fmt.Errorf("getAssignees: %v", err)
}
diff --git a/models/lfs.go b/models/lfs.go
index 037ed8556b..d9eea6bb89 100644
--- a/models/lfs.go
+++ b/models/lfs.go
@@ -142,7 +142,7 @@ func LFSObjectAccessible(user *user_model.User, oid string) (bool, error) {
count, err := db.GetEngine(db.DefaultContext).Count(&LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}})
return count > 0, err
}
- cond := accessibleRepositoryCondition(user)
+ cond := repo_model.AccessibleRepositoryCondition(user)
count, err := db.GetEngine(db.DefaultContext).Where(cond).Join("INNER", "repository", "`lfs_meta_object`.repository_id = `repository`.id").Count(&LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}})
return count > 0, err
}
@@ -173,7 +173,7 @@ func LFSAutoAssociate(metas []*LFSMetaObject, user *user_model.User, repoID int6
newMetas := make([]*LFSMetaObject, 0, len(metas))
cond := builder.In(
"`lfs_meta_object`.repository_id",
- builder.Select("`repository`.id").From("repository").Where(accessibleRepositoryCondition(user)),
+ builder.Select("`repository`.id").From("repository").Where(repo_model.AccessibleRepositoryCondition(user)),
)
err = sess.Cols("oid").Where(cond).In("oid", oids...).GroupBy("oid").Find(&newMetas)
if err != nil {
@@ -246,3 +246,12 @@ func CopyLFS(ctx context.Context, newRepo, oldRepo *repo_model.Repository) error
return nil
}
+
+// GetRepoLFSSize return a repository's lfs files size
+func GetRepoLFSSize(ctx context.Context, repoID int64) (int64, error) {
+ lfsSize, err := db.GetEngine(ctx).Where("repository_id = ?", repoID).SumInt(new(LFSMetaObject), "size")
+ if err != nil {
+ return 0, fmt.Errorf("updateSize: GetLFSMetaObjects: %v", err)
+ }
+ return lfsSize, nil
+}
diff --git a/models/notification.go b/models/notification.go
index 548362d190..ac5abc6f92 100644
--- a/models/notification.go
+++ b/models/notification.go
@@ -266,10 +266,10 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
return err
}
- if issue.IsPull && !checkRepoUnitUser(ctx, issue.Repo, user, unit.TypePullRequests) {
+ if issue.IsPull && !CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypePullRequests) {
continue
}
- if !issue.IsPull && !checkRepoUnitUser(ctx, issue.Repo, user, unit.TypeIssues) {
+ if !issue.IsPull && !CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypeIssues) {
continue
}
@@ -510,9 +510,9 @@ func (nl NotificationList) getPendingRepoIDs() []int64 {
}
// LoadRepos loads repositories from database
-func (nl NotificationList) LoadRepos() (RepositoryList, []int, error) {
+func (nl NotificationList) LoadRepos() (repo_model.RepositoryList, []int, error) {
if len(nl) == 0 {
- return RepositoryList{}, []int{}, nil
+ return repo_model.RepositoryList{}, []int{}, nil
}
repoIDs := nl.getPendingRepoIDs()
@@ -548,7 +548,7 @@ func (nl NotificationList) LoadRepos() (RepositoryList, []int, error) {
failed := []int{}
- reposList := make(RepositoryList, 0, len(repoIDs))
+ reposList := make(repo_model.RepositoryList, 0, len(repoIDs))
for i, notification := range nl {
if notification.Repository == nil {
notification.Repository = repos[notification.RepoID]
diff --git a/models/org.go b/models/org.go
index 681b367f45..009fe758b5 100644
--- a/models/org.go
+++ b/models/org.go
@@ -54,7 +54,7 @@ func GetUserOrgsList(user *user_model.User) ([]*MinimalOrg, error) {
Join("LEFT", builder.
Select("id as repo_id, owner_id as repo_owner_id").
From("repository").
- Where(accessibleRepositoryCondition(user)), "`repository`.repo_owner_id = `team`.org_id").
+ Where(repo_model.AccessibleRepositoryCondition(user)), "`repository`.repo_owner_id = `team`.org_id").
Where("`team_user`.uid = ?", user.ID).
GroupBy(groupByStr)
diff --git a/models/repo.go b/models/repo.go
index 6c3dca41be..fff9cc5271 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -8,11 +8,7 @@ package models
import (
"context"
"fmt"
- "os"
- "path"
"strconv"
- "strings"
- "unicode/utf8"
_ "image/jpeg" // Needed for jpeg support
@@ -47,11 +43,7 @@ func NewRepoContext() {
}
// CheckRepoUnitUser check whether user could visit the unit of this repository
-func CheckRepoUnitUser(repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
- return checkRepoUnitUser(db.DefaultContext, repo, user, unitType)
-}
-
-func checkRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
+func CheckRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
if user != nil && user.IsAdmin {
return true
}
@@ -64,241 +56,6 @@ func checkRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *u
return perm.CanRead(unitType)
}
-func getRepoAssignees(ctx context.Context, repo *repo_model.Repository) (_ []*user_model.User, err error) {
- if err = repo.GetOwner(ctx); err != nil {
- return nil, err
- }
-
- e := db.GetEngine(ctx)
- userIDs := make([]int64, 0, 10)
- if err = e.Table("access").
- Where("repo_id = ? AND mode >= ?", repo.ID, perm.AccessModeWrite).
- Select("user_id").
- Find(&userIDs); err != nil {
- return nil, err
- }
-
- additionalUserIDs := make([]int64, 0, 10)
- if err = e.Table("team_user").
- Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id").
- Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id").
- Where("`team_repo`.repo_id = ? AND `team_unit`.access_mode >= ?", repo.ID, perm.AccessModeWrite).
- Distinct("`team_user`.uid").
- Select("`team_user`.uid").
- Find(&additionalUserIDs); err != nil {
- return nil, err
- }
-
- uidMap := map[int64]bool{}
- i := 0
- for _, uid := range userIDs {
- if uidMap[uid] {
- continue
- }
- uidMap[uid] = true
- userIDs[i] = uid
- i++
- }
- userIDs = userIDs[:i]
- userIDs = append(userIDs, additionalUserIDs...)
-
- for _, uid := range additionalUserIDs {
- if uidMap[uid] {
- continue
- }
- userIDs[i] = uid
- i++
- }
- userIDs = userIDs[:i]
-
- // Leave a seat for owner itself to append later, but if owner is an organization
- // and just waste 1 unit is cheaper than re-allocate memory once.
- users := make([]*user_model.User, 0, len(userIDs)+1)
- if len(userIDs) > 0 {
- if err = e.In("id", userIDs).Find(&users); err != nil {
- return nil, err
- }
- }
- if !repo.Owner.IsOrganization() && !uidMap[repo.OwnerID] {
- users = append(users, repo.Owner)
- }
-
- return users, nil
-}
-
-// GetRepoAssignees returns all users that have write access and can be assigned to issues
-// of the repository,
-func GetRepoAssignees(repo *repo_model.Repository) (_ []*user_model.User, err error) {
- return getRepoAssignees(db.DefaultContext, repo)
-}
-
-func getReviewers(ctx context.Context, repo *repo_model.Repository, doerID, posterID int64) ([]*user_model.User, error) {
- // Get the owner of the repository - this often already pre-cached and if so saves complexity for the following queries
- if err := repo.GetOwner(ctx); err != nil {
- return nil, err
- }
-
- cond := builder.And(builder.Neq{"`user`.id": posterID})
-
- if repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate {
- // This a private repository:
- // Anyone who can read the repository is a requestable reviewer
-
- cond = cond.And(builder.In("`user`.id",
- builder.Select("user_id").From("access").Where(
- builder.Eq{"repo_id": repo.ID}.
- And(builder.Gte{"mode": perm.AccessModeRead}),
- ),
- ))
-
- if repo.Owner.Type == user_model.UserTypeIndividual && repo.Owner.ID != posterID {
- // as private *user* repos don't generate an entry in the `access` table,
- // the owner of a private repo needs to be explicitly added.
- cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID})
- }
-
- } else {
- // This is a "public" repository:
- // Any user that has read access, is a watcher or organization member can be requested to review
- cond = cond.And(builder.And(builder.In("`user`.id",
- builder.Select("user_id").From("access").
- Where(builder.Eq{"repo_id": repo.ID}.
- And(builder.Gte{"mode": perm.AccessModeRead})),
- ).Or(builder.In("`user`.id",
- builder.Select("user_id").From("watch").
- Where(builder.Eq{"repo_id": repo.ID}.
- And(builder.In("mode", repo_model.WatchModeNormal, repo_model.WatchModeAuto))),
- ).Or(builder.In("`user`.id",
- builder.Select("uid").From("org_user").
- Where(builder.Eq{"org_id": repo.OwnerID}),
- )))))
- }
-
- users := make([]*user_model.User, 0, 8)
- return users, db.GetEngine(ctx).Where(cond).OrderBy("name").Find(&users)
-}
-
-// GetReviewers get all users can be requested to review:
-// * for private repositories this returns all users that have read access or higher to the repository.
-// * for public repositories this returns all users that have read access or higher to the repository,
-// all repo watchers and all organization members.
-// TODO: may be we should have a busy choice for users to block review request to them.
-func GetReviewers(repo *repo_model.Repository, doerID, posterID int64) ([]*user_model.User, error) {
- return getReviewers(db.DefaultContext, repo, doerID, posterID)
-}
-
-// GetReviewerTeams get all teams can be requested to review
-func GetReviewerTeams(repo *repo_model.Repository) ([]*organization.Team, error) {
- if err := repo.GetOwner(db.DefaultContext); err != nil {
- return nil, err
- }
- if !repo.Owner.IsOrganization() {
- return nil, nil
- }
-
- teams, err := organization.GetTeamsWithAccessToRepo(db.DefaultContext, repo.OwnerID, repo.ID, perm.AccessModeRead)
- if err != nil {
- return nil, err
- }
-
- return teams, err
-}
-
-// UpdateRepoSize updates the repository size, calculating it using util.GetDirectorySize
-func UpdateRepoSize(ctx context.Context, repo *repo_model.Repository) error {
- size, err := util.GetDirectorySize(repo.RepoPath())
- if err != nil {
- return fmt.Errorf("updateSize: %v", err)
- }
-
- lfsSize, err := db.GetEngine(ctx).Where("repository_id = ?", repo.ID).SumInt(new(LFSMetaObject), "size")
- if err != nil {
- return fmt.Errorf("updateSize: GetLFSMetaObjects: %v", err)
- }
-
- repo.Size = size + lfsSize
- _, err = db.GetEngine(ctx).ID(repo.ID).Cols("size").NoAutoTime().Update(repo)
- return err
-}
-
-// CanUserForkRepo returns true if specified user can fork repository.
-func CanUserForkRepo(user *user_model.User, repo *repo_model.Repository) (bool, error) {
- if user == nil {
- return false, nil
- }
- if repo.OwnerID != user.ID && !repo_model.HasForkedRepo(user.ID, repo.ID) {
- return true, nil
- }
- ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(user.ID)
- if err != nil {
- return false, err
- }
- for _, org := range ownedOrgs {
- if repo.OwnerID != org.ID && !repo_model.HasForkedRepo(org.ID, repo.ID) {
- return true, nil
- }
- }
- return false, nil
-}
-
-// FindUserOrgForks returns the forked repositories for one user from a repository
-func FindUserOrgForks(ctx context.Context, repoID, userID int64) ([]*repo_model.Repository, error) {
- cond := builder.And(
- builder.Eq{"fork_id": repoID},
- builder.In("owner_id",
- builder.Select("org_id").
- From("org_user").
- Where(builder.Eq{"uid": userID}),
- ),
- )
-
- var repos []*repo_model.Repository
- return repos, db.GetEngine(ctx).Table("repository").Where(cond).Find(&repos)
-}
-
-// GetForksByUserAndOrgs return forked repos of the user and owned orgs
-func GetForksByUserAndOrgs(ctx context.Context, user *user_model.User, repo *repo_model.Repository) ([]*repo_model.Repository, error) {
- var repoList []*repo_model.Repository
- if user == nil {
- return repoList, nil
- }
- forkedRepo, err := repo_model.GetUserFork(ctx, repo.ID, user.ID)
- if err != nil {
- return repoList, err
- }
- if forkedRepo != nil {
- repoList = append(repoList, forkedRepo)
- }
- orgForks, err := FindUserOrgForks(ctx, repo.ID, user.ID)
- if err != nil {
- return nil, err
- }
- repoList = append(repoList, orgForks...)
- return repoList, nil
-}
-
-// CanUserDelete returns true if user could delete the repository
-func CanUserDelete(repo *repo_model.Repository, user *user_model.User) (bool, error) {
- if user.IsAdmin || user.ID == repo.OwnerID {
- return true, nil
- }
-
- if err := repo.GetOwner(db.DefaultContext); err != nil {
- return false, err
- }
-
- if repo.Owner.IsOrganization() {
- isOwner, err := organization.OrgFromUser(repo.Owner).IsOwnedBy(user.ID)
- if err != nil {
- return false, err
- } else if isOwner {
- return true, nil
- }
- }
-
- return false, nil
-}
-
// CreateRepoOptions contains the create repository options
type CreateRepoOptions struct {
Name string
@@ -441,126 +198,6 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_
return nil
}
-// CheckDaemonExportOK creates/removes git-daemon-export-ok for git-daemon...
-func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error {
- if err := repo.GetOwner(ctx); err != nil {
- return err
- }
-
- // Create/Remove git-daemon-export-ok for git-daemon...
- daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
-
- isExist, err := util.IsExist(daemonExportFile)
- if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err)
- return err
- }
-
- isPublic := !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePublic
- if !isPublic && isExist {
- if err = util.Remove(daemonExportFile); err != nil {
- log.Error("Failed to remove %s: %v", daemonExportFile, err)
- }
- } else if isPublic && !isExist {
- if f, err := os.Create(daemonExportFile); err != nil {
- log.Error("Failed to create %s: %v", daemonExportFile, err)
- } else {
- f.Close()
- }
- }
-
- return nil
-}
-
-// IncrementRepoForkNum increment repository fork number
-func IncrementRepoForkNum(ctx context.Context, repoID int64) error {
- _, err := db.GetEngine(ctx).Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", repoID)
- return err
-}
-
-// DecrementRepoForkNum decrement repository fork number
-func DecrementRepoForkNum(ctx context.Context, repoID int64) error {
- _, err := db.GetEngine(ctx).Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repoID)
- return err
-}
-
-// UpdateRepositoryCtx updates a repository with db context
-func UpdateRepositoryCtx(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) {
- repo.LowerName = strings.ToLower(repo.Name)
-
- if utf8.RuneCountInString(repo.Description) > 255 {
- repo.Description = string([]rune(repo.Description)[:255])
- }
- if utf8.RuneCountInString(repo.Website) > 255 {
- repo.Website = string([]rune(repo.Website)[:255])
- }
-
- e := db.GetEngine(ctx)
-
- if _, err = e.ID(repo.ID).AllCols().Update(repo); err != nil {
- return fmt.Errorf("update: %v", err)
- }
-
- if err = UpdateRepoSize(ctx, repo); err != nil {
- log.Error("Failed to update size for repository: %v", err)
- }
-
- if visibilityChanged {
- if err = repo.GetOwner(ctx); err != nil {
- return fmt.Errorf("getOwner: %v", err)
- }
- if repo.Owner.IsOrganization() {
- // Organization repository need to recalculate access table when visibility is changed.
- if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
- return fmt.Errorf("recalculateTeamAccesses: %v", err)
- }
- }
-
- // If repo has become private, we need to set its actions to private.
- if repo.IsPrivate {
- _, err = e.Where("repo_id = ?", repo.ID).Cols("is_private").Update(&Action{
- IsPrivate: true,
- })
- if err != nil {
- return err
- }
- }
-
- // Create/Remove git-daemon-export-ok for git-daemon...
- if err := CheckDaemonExportOK(ctx, repo); err != nil {
- return err
- }
-
- forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID)
- if err != nil {
- return fmt.Errorf("GetRepositoriesByForkID: %v", err)
- }
- for i := range forkRepos {
- forkRepos[i].IsPrivate = repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate
- if err = UpdateRepositoryCtx(ctx, forkRepos[i], true); err != nil {
- return fmt.Errorf("updateRepository[%d]: %v", forkRepos[i].ID, err)
- }
- }
- }
-
- return nil
-}
-
-// UpdateRepository updates a repository
-func UpdateRepository(repo *repo_model.Repository, visibilityChanged bool) (err error) {
- ctx, committer, err := db.TxContext()
- if err != nil {
- return err
- }
- defer committer.Close()
-
- if err = UpdateRepositoryCtx(ctx, repo, visibilityChanged); err != nil {
- return fmt.Errorf("updateRepository: %v", err)
- }
-
- return committer.Commit()
-}
-
// DeleteRepository deletes a repository for a user or organization.
// make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
@@ -1052,14 +689,14 @@ func CheckRepoStats(ctx context.Context) error {
continue
}
- rawResult, err := db.GetEngine(db.DefaultContext).Query("SELECT COUNT(*) FROM `repository` WHERE fork_id=?", repo.ID)
+ rawResult, err := e.Query("SELECT COUNT(*) FROM `repository` WHERE fork_id=?", repo.ID)
if err != nil {
log.Error("Select count of forks[%d]: %v", repo.ID, err)
continue
}
repo.NumForks = int(parseCountResult(rawResult))
- if err = UpdateRepository(repo, false); err != nil {
+ if _, err = e.ID(repo.ID).Cols("num_forks").Update(repo); err != nil {
log.Error("UpdateRepository[%d]: %v", id, err)
continue
}
@@ -1130,30 +767,6 @@ func DoctorUserStarNum() (err error) {
return
}
-// LinkedRepository returns the linked repo if any
-func LinkedRepository(a *repo_model.Attachment) (*repo_model.Repository, unit.Type, error) {
- if a.IssueID != 0 {
- iss, err := GetIssueByID(a.IssueID)
- if err != nil {
- return nil, unit.TypeIssues, err
- }
- repo, err := repo_model.GetRepositoryByID(iss.RepoID)
- unitType := unit.TypeIssues
- if iss.IsPull {
- unitType = unit.TypePullRequests
- }
- return repo, unitType, err
- } else if a.ReleaseID != 0 {
- rel, err := GetReleaseByID(db.DefaultContext, a.ReleaseID)
- if err != nil {
- return nil, unit.TypeReleases, err
- }
- repo, err := repo_model.GetRepositoryByID(rel.RepoID)
- return repo, unit.TypeReleases, err
- }
- return nil, -1, nil
-}
-
// DeleteDeployKey delete deploy keys
func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error {
key, err := asymkey_model.GetDeployKeyByID(ctx, id)
@@ -1164,8 +777,6 @@ func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error
return fmt.Errorf("GetDeployKeyByID: %v", err)
}
- sess := db.GetEngine(ctx)
-
// Check if user has access to delete this key.
if !doer.IsAdmin {
repo, err := repo_model.GetRepositoryByIDCtx(ctx, key.RepoID)
@@ -1184,14 +795,14 @@ func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error
}
}
- if _, err = sess.ID(key.ID).Delete(new(asymkey_model.DeployKey)); err != nil {
+ if _, err := db.DeleteByBean(ctx, &asymkey_model.DeployKey{
+ ID: key.ID,
+ }); err != nil {
return fmt.Errorf("delete deploy key [%d]: %v", key.ID, err)
}
// Check if this is the last reference to same key content.
- has, err := sess.
- Where("key_id = ?", key.KeyID).
- Get(new(asymkey_model.DeployKey))
+ has, err := asymkey_model.IsDeployKeyExistByKeyID(ctx, key.KeyID)
if err != nil {
return err
} else if !has {
diff --git a/models/repo/attachment_test.go b/models/repo/attachment_test.go
index da486fdb2b..d7c2f529db 100644
--- a/models/repo/attachment_test.go
+++ b/models/repo/attachment_test.go
@@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repo
+package repo_test
import (
"testing"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
@@ -16,7 +17,7 @@ import (
func TestIncreaseDownloadCount(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- attachment, err := GetAttachmentByUUID(db.DefaultContext, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
+ attachment, err := repo_model.GetAttachmentByUUID(db.DefaultContext, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
assert.NoError(t, err)
assert.Equal(t, int64(0), attachment.DownloadCount)
@@ -24,7 +25,7 @@ func TestIncreaseDownloadCount(t *testing.T) {
err = attachment.IncreaseDownloadCount()
assert.NoError(t, err)
- attachment, err = GetAttachmentByUUID(db.DefaultContext, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
+ attachment, err = repo_model.GetAttachmentByUUID(db.DefaultContext, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
assert.NoError(t, err)
assert.Equal(t, int64(1), attachment.DownloadCount)
}
@@ -33,11 +34,11 @@ func TestGetByCommentOrIssueID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// count of attachments from issue ID
- attachments, err := GetAttachmentsByIssueID(db.DefaultContext, 1)
+ attachments, err := repo_model.GetAttachmentsByIssueID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.Len(t, attachments, 1)
- attachments, err = GetAttachmentsByCommentID(db.DefaultContext, 1)
+ attachments, err = repo_model.GetAttachmentsByCommentID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.Len(t, attachments, 2)
}
@@ -45,33 +46,33 @@ func TestGetByCommentOrIssueID(t *testing.T) {
func TestDeleteAttachments(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- count, err := DeleteAttachmentsByIssue(4, false)
+ count, err := repo_model.DeleteAttachmentsByIssue(4, false)
assert.NoError(t, err)
assert.Equal(t, 2, count)
- count, err = DeleteAttachmentsByComment(2, false)
+ count, err = repo_model.DeleteAttachmentsByComment(2, false)
assert.NoError(t, err)
assert.Equal(t, 2, count)
- err = DeleteAttachment(&Attachment{ID: 8}, false)
+ err = repo_model.DeleteAttachment(&repo_model.Attachment{ID: 8}, false)
assert.NoError(t, err)
- attachment, err := GetAttachmentByUUID(db.DefaultContext, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18")
+ attachment, err := repo_model.GetAttachmentByUUID(db.DefaultContext, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18")
assert.Error(t, err)
- assert.True(t, IsErrAttachmentNotExist(err))
+ assert.True(t, repo_model.IsErrAttachmentNotExist(err))
assert.Nil(t, attachment)
}
func TestGetAttachmentByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- attach, err := GetAttachmentByID(db.DefaultContext, 1)
+ attach, err := repo_model.GetAttachmentByID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.UUID)
}
func TestAttachment_DownloadURL(t *testing.T) {
- attach := &Attachment{
+ attach := &repo_model.Attachment{
UUID: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
ID: 1,
}
@@ -81,20 +82,20 @@ func TestAttachment_DownloadURL(t *testing.T) {
func TestUpdateAttachment(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- attach, err := GetAttachmentByID(db.DefaultContext, 1)
+ attach, err := repo_model.GetAttachmentByID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.UUID)
attach.Name = "new_name"
- assert.NoError(t, UpdateAttachment(db.DefaultContext, attach))
+ assert.NoError(t, repo_model.UpdateAttachment(db.DefaultContext, attach))
- unittest.AssertExistsAndLoadBean(t, &Attachment{Name: "new_name"})
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{Name: "new_name"})
}
func TestGetAttachmentsByUUIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- attachList, err := GetAttachmentsByUUIDs(db.DefaultContext, []string{"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17", "not-existing-uuid"})
+ attachList, err := repo_model.GetAttachmentsByUUIDs(db.DefaultContext, []string{"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17", "not-existing-uuid"})
assert.NoError(t, err)
assert.Len(t, attachList, 2)
assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attachList[0].UUID)
diff --git a/models/repo/collaboration_test.go b/models/repo/collaboration_test.go
index a7d04498e9..8cb7980a75 100644
--- a/models/repo/collaboration_test.go
+++ b/models/repo/collaboration_test.go
@@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repo
+package repo_test
import (
"testing"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
@@ -16,10 +17,10 @@ import (
func TestRepository_GetCollaborators(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID int64) {
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository)
- collaborators, err := GetCollaborators(db.DefaultContext, repo.ID, db.ListOptions{})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+ collaborators, err := repo_model.GetCollaborators(db.DefaultContext, repo.ID, db.ListOptions{})
assert.NoError(t, err)
- expectedLen, err := db.GetEngine(db.DefaultContext).Count(&Collaboration{RepoID: repoID})
+ expectedLen, err := db.GetEngine(db.DefaultContext).Count(&repo_model.Collaboration{RepoID: repoID})
assert.NoError(t, err)
assert.Len(t, collaborators, int(expectedLen))
for _, collaborator := range collaborators {
@@ -37,8 +38,8 @@ func TestRepository_IsCollaborator(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID, userID int64, expected bool) {
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository)
- actual, err := IsCollaborator(db.DefaultContext, repo.ID, userID)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+ actual, err := repo_model.IsCollaborator(db.DefaultContext, repo.ID, userID)
assert.NoError(t, err)
assert.Equal(t, expected, actual)
}
diff --git a/models/repo/fork.go b/models/repo/fork.go
index b48126253c..938bbae17e 100644
--- a/models/repo/fork.go
+++ b/models/repo/fork.go
@@ -8,6 +8,8 @@ import (
"context"
"code.gitea.io/gitea/models/db"
+ user_model "code.gitea.io/gitea/models/user"
+ "xorm.io/builder"
)
// GetRepositoriesByForkID returns all repositories with given fork ID.
@@ -63,3 +65,51 @@ func GetForks(repo *Repository, listOptions db.ListOptions) ([]*Repository, erro
forks := make([]*Repository, 0, listOptions.PageSize)
return forks, sess.Find(&forks, &Repository{ForkID: repo.ID})
}
+
+// IncrementRepoForkNum increment repository fork number
+func IncrementRepoForkNum(ctx context.Context, repoID int64) error {
+ _, err := db.GetEngine(ctx).Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", repoID)
+ return err
+}
+
+// DecrementRepoForkNum decrement repository fork number
+func DecrementRepoForkNum(ctx context.Context, repoID int64) error {
+ _, err := db.GetEngine(ctx).Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repoID)
+ return err
+}
+
+// FindUserOrgForks returns the forked repositories for one user from a repository
+func FindUserOrgForks(ctx context.Context, repoID, userID int64) ([]*Repository, error) {
+ cond := builder.And(
+ builder.Eq{"fork_id": repoID},
+ builder.In("owner_id",
+ builder.Select("org_id").
+ From("org_user").
+ Where(builder.Eq{"uid": userID}),
+ ),
+ )
+
+ var repos []*Repository
+ return repos, db.GetEngine(ctx).Table("repository").Where(cond).Find(&repos)
+}
+
+// GetForksByUserAndOrgs return forked repos of the user and owned orgs
+func GetForksByUserAndOrgs(ctx context.Context, user *user_model.User, repo *Repository) ([]*Repository, error) {
+ var repoList []*Repository
+ if user == nil {
+ return repoList, nil
+ }
+ forkedRepo, err := GetUserFork(ctx, repo.ID, user.ID)
+ if err != nil {
+ return repoList, err
+ }
+ if forkedRepo != nil {
+ repoList = append(repoList, forkedRepo)
+ }
+ orgForks, err := FindUserOrgForks(ctx, repo.ID, user.ID)
+ if err != nil {
+ return nil, err
+ }
+ repoList = append(repoList, orgForks...)
+ return repoList, nil
+}
diff --git a/models/repo/fork_test.go b/models/repo/fork_test.go
index 263aec4e3a..9e08d8136e 100644
--- a/models/repo/fork_test.go
+++ b/models/repo/fork_test.go
@@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repo
+package repo_test
import (
"testing"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
@@ -17,17 +18,17 @@ func TestGetUserFork(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// User13 has repo 11 forked from repo10
- repo, err := GetRepositoryByID(10)
+ repo, err := repo_model.GetRepositoryByID(10)
assert.NoError(t, err)
assert.NotNil(t, repo)
- repo, err = GetUserFork(db.DefaultContext, repo.ID, 13)
+ repo, err = repo_model.GetUserFork(db.DefaultContext, repo.ID, 13)
assert.NoError(t, err)
assert.NotNil(t, repo)
- repo, err = GetRepositoryByID(9)
+ repo, err = repo_model.GetRepositoryByID(9)
assert.NoError(t, err)
assert.NotNil(t, repo)
- repo, err = GetUserFork(db.DefaultContext, repo.ID, 13)
+ repo, err = repo_model.GetUserFork(db.DefaultContext, repo.ID, 13)
assert.NoError(t, err)
assert.Nil(t, repo)
}
diff --git a/models/repo/main_test.go b/models/repo/main_test.go
index 375d0e0df1..eb04aa8227 100644
--- a/models/repo/main_test.go
+++ b/models/repo/main_test.go
@@ -2,31 +2,22 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repo
+package repo_test
import (
"path/filepath"
"testing"
+ _ "code.gitea.io/gitea/models" // register table model
+ _ "code.gitea.io/gitea/models/perm/access" // register table model
+ _ "code.gitea.io/gitea/models/repo" // register table model
+ _ "code.gitea.io/gitea/models/user" // register table model
+
"code.gitea.io/gitea/models/unittest"
)
func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: filepath.Join("..", ".."),
- FixtureFiles: []string{
- "attachment.yml",
- "repo_archiver.yml",
- "repository.yml",
- "repo_unit.yml",
- "repo_indexer_status.yml",
- "repo_redirect.yml",
- "watch.yml",
- "star.yml",
- "topic.yml",
- "repo_topic.yml",
- "user.yml",
- "collaboration.yml",
- },
})
}
diff --git a/models/repo/mirror.go b/models/repo/mirror.go
index 5d20b7f833..bd83d24424 100644
--- a/models/repo/mirror.go
+++ b/models/repo/mirror.go
@@ -123,8 +123,8 @@ func MirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
}
// InsertMirror inserts a mirror to database
-func InsertMirror(mirror *Mirror) error {
- _, err := db.GetEngine(db.DefaultContext).Insert(mirror)
+func InsertMirror(ctx context.Context, mirror *Mirror) error {
+ _, err := db.GetEngine(ctx).Insert(mirror)
return err
}
@@ -168,3 +168,12 @@ func (repos MirrorRepositoryList) loadAttributes(ctx context.Context) error {
func (repos MirrorRepositoryList) LoadAttributes() error {
return repos.loadAttributes(db.DefaultContext)
}
+
+// GetUserMirrorRepositories returns a list of mirror repositories of given user.
+func GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
+ repos := make([]*Repository, 0, 10)
+ return repos, db.GetEngine(db.DefaultContext).
+ Where("owner_id = ?", userID).
+ And("is_mirror = ?", true).
+ Find(&repos)
+}
diff --git a/models/repo/pushmirror_test.go b/models/repo/pushmirror_test.go
index 83cf86131f..d36a48547e 100644
--- a/models/repo/pushmirror_test.go
+++ b/models/repo/pushmirror_test.go
@@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repo
+package repo_test
import (
"testing"
"time"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/timeutil"
@@ -19,20 +20,20 @@ func TestPushMirrorsIterate(t *testing.T) {
now := timeutil.TimeStampNow()
- InsertPushMirror(&PushMirror{
+ repo_model.InsertPushMirror(&repo_model.PushMirror{
RemoteName: "test-1",
LastUpdateUnix: now,
Interval: 1,
})
long, _ := time.ParseDuration("24h")
- InsertPushMirror(&PushMirror{
+ repo_model.InsertPushMirror(&repo_model.PushMirror{
RemoteName: "test-2",
LastUpdateUnix: now,
Interval: long,
})
- InsertPushMirror(&PushMirror{
+ repo_model.InsertPushMirror(&repo_model.PushMirror{
RemoteName: "test-3",
LastUpdateUnix: now,
Interval: 0,
@@ -40,8 +41,8 @@ func TestPushMirrorsIterate(t *testing.T) {
time.Sleep(1 * time.Millisecond)
- PushMirrorsIterate(1, func(idx int, bean interface{}) error {
- m, ok := bean.(*PushMirror)
+ repo_model.PushMirrorsIterate(1, func(idx int, bean interface{}) error {
+ m, ok := bean.(*repo_model.PushMirror)
assert.True(t, ok)
assert.Equal(t, "test-1", m.RemoteName)
assert.Equal(t, m.RemoteName, m.GetRemoteName())
diff --git a/models/repo/redirect_test.go b/models/repo/redirect_test.go
index 2dca2cbbfd..05b105cf63 100644
--- a/models/repo/redirect_test.go
+++ b/models/repo/redirect_test.go
@@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repo
+package repo_test
import (
"testing"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
@@ -16,27 +17,27 @@ import (
func TestLookupRedirect(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repoID, err := LookupRedirect(2, "oldrepo1")
+ repoID, err := repo_model.LookupRedirect(2, "oldrepo1")
assert.NoError(t, err)
assert.EqualValues(t, 1, repoID)
- _, err = LookupRedirect(unittest.NonexistentID, "doesnotexist")
- assert.True(t, IsErrRedirectNotExist(err))
+ _, err = repo_model.LookupRedirect(unittest.NonexistentID, "doesnotexist")
+ assert.True(t, repo_model.IsErrRedirectNotExist(err))
}
func TestNewRedirect(t *testing.T) {
// redirect to a completely new name
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
- assert.NoError(t, NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "newreponame"))
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ assert.NoError(t, repo_model.NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "newreponame"))
- unittest.AssertExistsAndLoadBean(t, &Redirect{
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Redirect{
OwnerID: repo.OwnerID,
LowerName: repo.LowerName,
RedirectRepoID: repo.ID,
})
- unittest.AssertExistsAndLoadBean(t, &Redirect{
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Redirect{
OwnerID: repo.OwnerID,
LowerName: "oldrepo1",
RedirectRepoID: repo.ID,
@@ -47,15 +48,15 @@ func TestNewRedirect2(t *testing.T) {
// redirect to previously used name
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
- assert.NoError(t, NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "oldrepo1"))
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ assert.NoError(t, repo_model.NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "oldrepo1"))
- unittest.AssertExistsAndLoadBean(t, &Redirect{
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Redirect{
OwnerID: repo.OwnerID,
LowerName: repo.LowerName,
RedirectRepoID: repo.ID,
})
- unittest.AssertNotExistsBean(t, &Redirect{
+ unittest.AssertNotExistsBean(t, &repo_model.Redirect{
OwnerID: repo.OwnerID,
LowerName: "oldrepo1",
RedirectRepoID: repo.ID,
@@ -66,10 +67,10 @@ func TestNewRedirect3(t *testing.T) {
// redirect for a previously-unredirected repo
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
- assert.NoError(t, NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "newreponame"))
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ assert.NoError(t, repo_model.NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "newreponame"))
- unittest.AssertExistsAndLoadBean(t, &Redirect{
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Redirect{
OwnerID: repo.OwnerID,
LowerName: repo.LowerName,
RedirectRepoID: repo.ID,
diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go
index 23cdd6cad6..1bec35d571 100644
--- a/models/repo/repo_list.go
+++ b/models/repo/repo_list.go
@@ -5,18 +5,21 @@
package repo
import (
+ "context"
+ "fmt"
+ "strings"
+
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/perm"
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/setting"
-)
+ "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
-// GetUserMirrorRepositories returns a list of mirror repositories of given user.
-func GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
- repos := make([]*Repository, 0, 10)
- return repos, db.GetEngine(db.DefaultContext).
- Where("owner_id = ?", userID).
- And("is_mirror = ?", true).
- Find(&repos)
-}
+ "xorm.io/builder"
+)
// IterateRepository iterate repositories
func IterateRepository(f func(repo *Repository) error) error {
@@ -45,3 +48,643 @@ func IterateRepository(f func(repo *Repository) error) error {
func FindReposMapByIDs(repoIDs []int64, res map[int64]*Repository) error {
return db.GetEngine(db.DefaultContext).In("id", repoIDs).Find(&res)
}
+
+// RepositoryListDefaultPageSize is the default number of repositories
+// to load in memory when running administrative tasks on all (or almost
+// all) of them.
+// The number should be low enough to avoid filling up all RAM with
+// repository data...
+const RepositoryListDefaultPageSize = 64
+
+// RepositoryList contains a list of repositories
+type RepositoryList []*Repository
+
+func (repos RepositoryList) Len() int {
+ return len(repos)
+}
+
+func (repos RepositoryList) Less(i, j int) bool {
+ return repos[i].FullName() < repos[j].FullName()
+}
+
+func (repos RepositoryList) Swap(i, j int) {
+ repos[i], repos[j] = repos[j], repos[i]
+}
+
+// ValuesRepository converts a repository map to a list
+// FIXME: Remove in favor of maps.values when MIN_GO_VERSION >= 1.18
+func ValuesRepository(m map[int64]*Repository) []*Repository {
+ values := make([]*Repository, 0, len(m))
+ for _, v := range m {
+ values = append(values, v)
+ }
+ return values
+}
+
+// RepositoryListOfMap make list from values of map
+func RepositoryListOfMap(repoMap map[int64]*Repository) RepositoryList {
+ return RepositoryList(ValuesRepository(repoMap))
+}
+
+func (repos RepositoryList) loadAttributes(ctx context.Context) error {
+ if len(repos) == 0 {
+ return nil
+ }
+
+ set := make(map[int64]struct{})
+ repoIDs := make([]int64, len(repos))
+ for i := range repos {
+ set[repos[i].OwnerID] = struct{}{}
+ repoIDs[i] = repos[i].ID
+ }
+
+ // Load owners.
+ users := make(map[int64]*user_model.User, len(set))
+ if err := db.GetEngine(ctx).
+ Where("id > 0").
+ In("id", container.KeysInt64(set)).
+ Find(&users); err != nil {
+ return fmt.Errorf("find users: %v", err)
+ }
+ for i := range repos {
+ repos[i].Owner = users[repos[i].OwnerID]
+ }
+
+ // Load primary language.
+ stats := make(LanguageStatList, 0, len(repos))
+ if err := db.GetEngine(ctx).
+ Where("`is_primary` = ? AND `language` != ?", true, "other").
+ In("`repo_id`", repoIDs).
+ Find(&stats); err != nil {
+ return fmt.Errorf("find primary languages: %v", err)
+ }
+ stats.LoadAttributes()
+ for i := range repos {
+ for _, st := range stats {
+ if st.RepoID == repos[i].ID {
+ repos[i].PrimaryLanguage = st
+ break
+ }
+ }
+ }
+
+ return nil
+}
+
+// LoadAttributes loads the attributes for the given RepositoryList
+func (repos RepositoryList) LoadAttributes() error {
+ return repos.loadAttributes(db.DefaultContext)
+}
+
+// SearchRepoOptions holds the search options
+type SearchRepoOptions struct {
+ db.ListOptions
+ Actor *user_model.User
+ Keyword string
+ OwnerID int64
+ PriorityOwnerID int64
+ TeamID int64
+ OrderBy db.SearchOrderBy
+ Private bool // Include private repositories in results
+ StarredByID int64
+ WatchedByID int64
+ AllPublic bool // Include also all public repositories of users and public organisations
+ AllLimited bool // Include also all public repositories of limited organisations
+ // None -> include public and private
+ // True -> include just private
+ // False -> include just public
+ IsPrivate util.OptionalBool
+ // None -> include collaborative AND non-collaborative
+ // True -> include just collaborative
+ // False -> include just non-collaborative
+ Collaborate util.OptionalBool
+ // None -> include forks AND non-forks
+ // True -> include just forks
+ // False -> include just non-forks
+ Fork util.OptionalBool
+ // None -> include templates AND non-templates
+ // True -> include just templates
+ // False -> include just non-templates
+ Template util.OptionalBool
+ // None -> include mirrors AND non-mirrors
+ // True -> include just mirrors
+ // False -> include just non-mirrors
+ Mirror util.OptionalBool
+ // None -> include archived AND non-archived
+ // True -> include just archived
+ // False -> include just non-archived
+ Archived util.OptionalBool
+ // only search topic name
+ TopicOnly bool
+ // only search repositories with specified primary language
+ Language string
+ // include description in keyword search
+ IncludeDescription bool
+ // None -> include has milestones AND has no milestone
+ // True -> include just has milestones
+ // False -> include just has no milestone
+ HasMilestones util.OptionalBool
+ // LowerNames represents valid lower names to restrict to
+ LowerNames []string
+}
+
+// SearchOrderBy is used to sort the result
+type SearchOrderBy string
+
+func (s SearchOrderBy) String() string {
+ return string(s)
+}
+
+// Strings for sorting result
+const (
+ SearchOrderByAlphabetically SearchOrderBy = "name ASC"
+ SearchOrderByAlphabeticallyReverse SearchOrderBy = "name DESC"
+ SearchOrderByLeastUpdated SearchOrderBy = "updated_unix ASC"
+ SearchOrderByRecentUpdated SearchOrderBy = "updated_unix DESC"
+ SearchOrderByOldest SearchOrderBy = "created_unix ASC"
+ SearchOrderByNewest SearchOrderBy = "created_unix DESC"
+ SearchOrderBySize SearchOrderBy = "size ASC"
+ SearchOrderBySizeReverse SearchOrderBy = "size DESC"
+ SearchOrderByID SearchOrderBy = "id ASC"
+ SearchOrderByIDReverse SearchOrderBy = "id DESC"
+ SearchOrderByStars SearchOrderBy = "num_stars ASC"
+ SearchOrderByStarsReverse SearchOrderBy = "num_stars DESC"
+ SearchOrderByForks SearchOrderBy = "num_forks ASC"
+ SearchOrderByForksReverse SearchOrderBy = "num_forks DESC"
+)
+
+// UserOwnedRepoCond returns user ownered repositories
+func UserOwnedRepoCond(userID int64) builder.Cond {
+ return builder.Eq{
+ "repository.owner_id": userID,
+ }
+}
+
+// UserAssignedRepoCond return user as assignee repositories list
+func UserAssignedRepoCond(id string, userID int64) builder.Cond {
+ return builder.And(
+ builder.Eq{
+ "repository.is_private": false,
+ },
+ builder.In(id,
+ builder.Select("issue.repo_id").From("issue_assignees").
+ InnerJoin("issue", "issue.id = issue_assignees.issue_id").
+ Where(builder.Eq{
+ "issue_assignees.assignee_id": userID,
+ }),
+ ),
+ )
+}
+
+// UserCreateIssueRepoCond return user created issues repositories list
+func UserCreateIssueRepoCond(id string, userID int64, isPull bool) builder.Cond {
+ return builder.And(
+ builder.Eq{
+ "repository.is_private": false,
+ },
+ builder.In(id,
+ builder.Select("issue.repo_id").From("issue").
+ Where(builder.Eq{
+ "issue.poster_id": userID,
+ "issue.is_pull": isPull,
+ }),
+ ),
+ )
+}
+
+// UserMentionedRepoCond return user metinoed repositories list
+func UserMentionedRepoCond(id string, userID int64) builder.Cond {
+ return builder.And(
+ builder.Eq{
+ "repository.is_private": false,
+ },
+ builder.In(id,
+ builder.Select("issue.repo_id").From("issue_user").
+ InnerJoin("issue", "issue.id = issue_user.issue_id").
+ Where(builder.Eq{
+ "issue_user.is_mentioned": true,
+ "issue_user.uid": userID,
+ }),
+ ),
+ )
+}
+
+// UserCollaborationRepoCond returns user as collabrators repositories list
+func UserCollaborationRepoCond(idStr string, userID int64) builder.Cond {
+ return builder.In(idStr, builder.Select("repo_id").
+ From("`access`").
+ Where(builder.And(
+ builder.Eq{"`access`.user_id": userID},
+ builder.Gt{"`access`.mode": int(perm.AccessModeNone)},
+ )),
+ )
+}
+
+// userOrgTeamRepoCond selects repos that the given user has access to through team membership
+func userOrgTeamRepoCond(idStr string, userID int64) builder.Cond {
+ return builder.In(idStr, userOrgTeamRepoBuilder(userID))
+}
+
+// userOrgTeamRepoBuilder returns repo ids where user's teams can access.
+func userOrgTeamRepoBuilder(userID int64) *builder.Builder {
+ return builder.Select("`team_repo`.repo_id").
+ From("team_repo").
+ Join("INNER", "team_user", "`team_user`.team_id = `team_repo`.team_id").
+ Where(builder.Eq{"`team_user`.uid": userID})
+}
+
+// userOrgTeamUnitRepoBuilder returns repo ids where user's teams can access the special unit.
+func userOrgTeamUnitRepoBuilder(userID int64, unitType unit.Type) *builder.Builder {
+ return userOrgTeamRepoBuilder(userID).
+ Join("INNER", "team_unit", "`team_unit`.team_id = `team_repo`.team_id").
+ Where(builder.Eq{"`team_unit`.`type`": unitType})
+}
+
+// UserOrgUnitRepoCond selects repos that the given user has access to through org and the special unit
+func UserOrgUnitRepoCond(idStr string, userID, orgID int64, unitType unit.Type) builder.Cond {
+ return builder.In(idStr,
+ userOrgTeamUnitRepoBuilder(userID, unitType).
+ And(builder.Eq{"`team_unit`.org_id": orgID}),
+ )
+}
+
+// userOrgPublicRepoCond returns the condition that one user could access all public repositories in organizations
+func userOrgPublicRepoCond(userID int64) builder.Cond {
+ return builder.And(
+ builder.Eq{"`repository`.is_private": false},
+ builder.In("`repository`.owner_id",
+ builder.Select("`org_user`.org_id").
+ From("org_user").
+ Where(builder.Eq{"`org_user`.uid": userID}),
+ ),
+ )
+}
+
+// userOrgPublicRepoCondPrivate returns the condition that one user could access all public repositories in private organizations
+func userOrgPublicRepoCondPrivate(userID int64) builder.Cond {
+ return builder.And(
+ builder.Eq{"`repository`.is_private": false},
+ builder.In("`repository`.owner_id",
+ builder.Select("`org_user`.org_id").
+ From("org_user").
+ Join("INNER", "`user`", "`user`.id = `org_user`.org_id").
+ Where(builder.Eq{
+ "`org_user`.uid": userID,
+ "`user`.`type`": user_model.UserTypeOrganization,
+ "`user`.visibility": structs.VisibleTypePrivate,
+ }),
+ ),
+ )
+}
+
+// UserOrgPublicUnitRepoCond returns the condition that one user could access all public repositories in the special organization
+func UserOrgPublicUnitRepoCond(userID, orgID int64) builder.Cond {
+ return userOrgPublicRepoCond(userID).
+ And(builder.Eq{"`repository`.owner_id": orgID})
+}
+
+// SearchRepositoryCondition creates a query condition according search repository options
+func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
+ cond := builder.NewCond()
+
+ if opts.Private {
+ if opts.Actor != nil && !opts.Actor.IsAdmin && opts.Actor.ID != opts.OwnerID {
+ // OK we're in the context of a User
+ cond = cond.And(AccessibleRepositoryCondition(opts.Actor))
+ }
+ } else {
+ // Not looking at private organisations and users
+ // We should be able to see all non-private repositories that
+ // isn't in a private or limited organisation.
+ cond = cond.And(
+ builder.Eq{"is_private": false},
+ builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(
+ builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}),
+ )))
+ }
+
+ if opts.IsPrivate != util.OptionalBoolNone {
+ cond = cond.And(builder.Eq{"is_private": opts.IsPrivate.IsTrue()})
+ }
+
+ if opts.Template != util.OptionalBoolNone {
+ cond = cond.And(builder.Eq{"is_template": opts.Template == util.OptionalBoolTrue})
+ }
+
+ // Restrict to starred repositories
+ if opts.StarredByID > 0 {
+ cond = cond.And(builder.In("id", builder.Select("repo_id").From("star").Where(builder.Eq{"uid": opts.StarredByID})))
+ }
+
+ // Restrict to watched repositories
+ if opts.WatchedByID > 0 {
+ cond = cond.And(builder.In("id", builder.Select("repo_id").From("watch").Where(builder.Eq{"user_id": opts.WatchedByID})))
+ }
+
+ // Restrict repositories to those the OwnerID owns or contributes to as per opts.Collaborate
+ if opts.OwnerID > 0 {
+ accessCond := builder.NewCond()
+ if opts.Collaborate != util.OptionalBoolTrue {
+ accessCond = builder.Eq{"owner_id": opts.OwnerID}
+ }
+
+ if opts.Collaborate != util.OptionalBoolFalse {
+ // A Collaboration is:
+ collaborateCond := builder.And(
+ // 1. Repository we don't own
+ builder.Neq{"owner_id": opts.OwnerID},
+ // 2. But we can see because of:
+ builder.Or(
+ // A. We have access
+ UserCollaborationRepoCond("`repository`.id", opts.OwnerID),
+ // B. We are in a team for
+ userOrgTeamRepoCond("`repository`.id", opts.OwnerID),
+ // C. Public repositories in organizations that we are member of
+ userOrgPublicRepoCondPrivate(opts.OwnerID),
+ ),
+ )
+ if !opts.Private {
+ collaborateCond = collaborateCond.And(builder.Expr("owner_id NOT IN (SELECT org_id FROM org_user WHERE org_user.uid = ? AND org_user.is_public = ?)", opts.OwnerID, false))
+ }
+
+ accessCond = accessCond.Or(collaborateCond)
+ }
+
+ if opts.AllPublic {
+ accessCond = accessCond.Or(builder.Eq{"is_private": false}.And(builder.In("owner_id", builder.Select("`user`.id").From("`user`").Where(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}))))
+ }
+
+ if opts.AllLimited {
+ accessCond = accessCond.Or(builder.Eq{"is_private": false}.And(builder.In("owner_id", builder.Select("`user`.id").From("`user`").Where(builder.Eq{"`user`.visibility": structs.VisibleTypeLimited}))))
+ }
+
+ cond = cond.And(accessCond)
+ }
+
+ if opts.TeamID > 0 {
+ cond = cond.And(builder.In("`repository`.id", builder.Select("`team_repo`.repo_id").From("team_repo").Where(builder.Eq{"`team_repo`.team_id": opts.TeamID})))
+ }
+
+ if opts.Keyword != "" {
+ // separate keyword
+ subQueryCond := builder.NewCond()
+ for _, v := range strings.Split(opts.Keyword, ",") {
+ if opts.TopicOnly {
+ subQueryCond = subQueryCond.Or(builder.Eq{"topic.name": strings.ToLower(v)})
+ } else {
+ subQueryCond = subQueryCond.Or(builder.Like{"topic.name", strings.ToLower(v)})
+ }
+ }
+ subQuery := builder.Select("repo_topic.repo_id").From("repo_topic").
+ Join("INNER", "topic", "topic.id = repo_topic.topic_id").
+ Where(subQueryCond).
+ GroupBy("repo_topic.repo_id")
+
+ keywordCond := builder.In("id", subQuery)
+ if !opts.TopicOnly {
+ likes := builder.NewCond()
+ for _, v := range strings.Split(opts.Keyword, ",") {
+ likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)})
+
+ // If the string looks like "org/repo", match against that pattern too
+ if opts.TeamID == 0 && strings.Count(opts.Keyword, "/") == 1 {
+ pieces := strings.Split(opts.Keyword, "/")
+ ownerName := pieces[0]
+ repoName := pieces[1]
+ likes = likes.Or(builder.And(builder.Like{"owner_name", strings.ToLower(ownerName)}, builder.Like{"lower_name", strings.ToLower(repoName)}))
+ }
+
+ if opts.IncludeDescription {
+ likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})
+ }
+ }
+ keywordCond = keywordCond.Or(likes)
+ }
+ cond = cond.And(keywordCond)
+ }
+
+ if opts.Language != "" {
+ cond = cond.And(builder.In("id", builder.
+ Select("repo_id").
+ From("language_stat").
+ Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true})))
+ }
+
+ if opts.Fork != util.OptionalBoolNone {
+ cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})
+ }
+
+ if opts.Mirror != util.OptionalBoolNone {
+ cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue})
+ }
+
+ if opts.Actor != nil && opts.Actor.IsRestricted {
+ cond = cond.And(AccessibleRepositoryCondition(opts.Actor))
+ }
+
+ if opts.Archived != util.OptionalBoolNone {
+ cond = cond.And(builder.Eq{"is_archived": opts.Archived == util.OptionalBoolTrue})
+ }
+
+ switch opts.HasMilestones {
+ case util.OptionalBoolTrue:
+ cond = cond.And(builder.Gt{"num_milestones": 0})
+ case util.OptionalBoolFalse:
+ cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))
+ }
+
+ return cond
+}
+
+// SearchRepository returns repositories based on search options,
+// it returns results in given range and number of total results.
+func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
+ cond := SearchRepositoryCondition(opts)
+ return SearchRepositoryByCondition(opts, cond, true)
+}
+
+// SearchRepositoryByCondition search repositories by condition
+func SearchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond, loadAttributes bool) (RepositoryList, int64, error) {
+ ctx := db.DefaultContext
+ sess, count, err := searchRepositoryByCondition(ctx, opts, cond)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ defaultSize := 50
+ if opts.PageSize > 0 {
+ defaultSize = opts.PageSize
+ }
+ repos := make(RepositoryList, 0, defaultSize)
+ if err := sess.Find(&repos); err != nil {
+ return nil, 0, fmt.Errorf("Repo: %v", err)
+ }
+
+ if opts.PageSize <= 0 {
+ count = int64(len(repos))
+ }
+
+ if loadAttributes {
+ if err := repos.loadAttributes(ctx); err != nil {
+ return nil, 0, fmt.Errorf("LoadAttributes: %v", err)
+ }
+ }
+
+ return repos, count, nil
+}
+
+func searchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, cond builder.Cond) (db.Engine, int64, error) {
+ if opts.Page <= 0 {
+ opts.Page = 1
+ }
+
+ if len(opts.OrderBy) == 0 {
+ opts.OrderBy = db.SearchOrderByAlphabetically
+ }
+
+ args := make([]interface{}, 0)
+ if opts.PriorityOwnerID > 0 {
+ opts.OrderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_id = ? THEN 0 ELSE owner_id END, %s", opts.OrderBy))
+ args = append(args, opts.PriorityOwnerID)
+ } else if strings.Count(opts.Keyword, "/") == 1 {
+ // With "owner/repo" search times, prioritise results which match the owner field
+ orgName := strings.Split(opts.Keyword, "/")[0]
+ opts.OrderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_name LIKE ? THEN 0 ELSE 1 END, %s", opts.OrderBy))
+ args = append(args, orgName)
+ }
+
+ sess := db.GetEngine(ctx)
+
+ var count int64
+ if opts.PageSize > 0 {
+ var err error
+ count, err = sess.
+ Where(cond).
+ Count(new(Repository))
+ if err != nil {
+ return nil, 0, fmt.Errorf("Count: %v", err)
+ }
+ }
+
+ sess = sess.Where(cond).OrderBy(opts.OrderBy.String(), args...)
+ if opts.PageSize > 0 {
+ sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
+ }
+ return sess, count, nil
+}
+
+// AccessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
+func AccessibleRepositoryCondition(user *user_model.User) builder.Cond {
+ cond := builder.NewCond()
+
+ if user == nil || !user.IsRestricted || user.ID <= 0 {
+ orgVisibilityLimit := []structs.VisibleType{structs.VisibleTypePrivate}
+ if user == nil || user.ID <= 0 {
+ orgVisibilityLimit = append(orgVisibilityLimit, structs.VisibleTypeLimited)
+ }
+ // 1. Be able to see all non-private repositories that either:
+ cond = cond.Or(builder.And(
+ builder.Eq{"`repository`.is_private": false},
+ // 2. Aren't in an private organisation or limited organisation if we're not logged in
+ builder.NotIn("`repository`.owner_id", builder.Select("id").From("`user`").Where(
+ builder.And(
+ builder.Eq{"type": user_model.UserTypeOrganization},
+ builder.In("visibility", orgVisibilityLimit)),
+ ))))
+ }
+
+ if user != nil {
+ cond = cond.Or(
+ // 2. Be able to see all repositories that we have access to
+ UserCollaborationRepoCond("`repository`.id", user.ID),
+ // 3. Repositories that we directly own
+ builder.Eq{"`repository`.owner_id": user.ID},
+ // 4. Be able to see all repositories that we are in a team
+ userOrgTeamRepoCond("`repository`.id", user.ID),
+ // 5. Be able to see all public repos in private organizations that we are an org_user of
+ userOrgPublicRepoCond(user.ID),
+ )
+ }
+
+ return cond
+}
+
+// SearchRepositoryByName takes keyword and part of repository name to search,
+// it returns results in given range and number of total results.
+func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, error) {
+ opts.IncludeDescription = false
+ return SearchRepository(opts)
+}
+
+// SearchRepositoryIDs takes keyword and part of repository name to search,
+// it returns results in given range and number of total results.
+func SearchRepositoryIDs(opts *SearchRepoOptions) ([]int64, int64, error) {
+ opts.IncludeDescription = false
+
+ cond := SearchRepositoryCondition(opts)
+
+ sess, count, err := searchRepositoryByCondition(db.DefaultContext, opts, cond)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ defaultSize := 50
+ if opts.PageSize > 0 {
+ defaultSize = opts.PageSize
+ }
+
+ ids := make([]int64, 0, defaultSize)
+ err = sess.Select("id").Table("repository").Find(&ids)
+ if opts.PageSize <= 0 {
+ count = int64(len(ids))
+ }
+
+ return ids, count, err
+}
+
+// AccessibleRepoIDsQuery queries accessible repository ids. Usable as a subquery wherever repo ids need to be filtered.
+func AccessibleRepoIDsQuery(user *user_model.User) *builder.Builder {
+ // NB: Please note this code needs to still work if user is nil
+ return builder.Select("id").From("repository").Where(AccessibleRepositoryCondition(user))
+}
+
+// FindUserAccessibleRepoIDs find all accessible repositories' ID by user's id
+func FindUserAccessibleRepoIDs(user *user_model.User) ([]int64, error) {
+ repoIDs := make([]int64, 0, 10)
+ if err := db.GetEngine(db.DefaultContext).
+ Table("repository").
+ Cols("id").
+ Where(AccessibleRepositoryCondition(user)).
+ Find(&repoIDs); err != nil {
+ return nil, fmt.Errorf("FindUserAccesibleRepoIDs: %v", err)
+ }
+ return repoIDs, nil
+}
+
+// GetUserRepositories returns a list of repositories of given user.
+func GetUserRepositories(opts *SearchRepoOptions) (RepositoryList, int64, error) {
+ if len(opts.OrderBy) == 0 {
+ opts.OrderBy = "updated_unix DESC"
+ }
+
+ cond := builder.NewCond()
+ cond = cond.And(builder.Eq{"owner_id": opts.Actor.ID})
+ if !opts.Private {
+ cond = cond.And(builder.Eq{"is_private": false})
+ }
+
+ if opts.LowerNames != nil && len(opts.LowerNames) > 0 {
+ cond = cond.And(builder.In("lower_name", opts.LowerNames))
+ }
+
+ sess := db.GetEngine(db.DefaultContext)
+
+ count, err := sess.Where(cond).Count(new(Repository))
+ if err != nil {
+ return nil, 0, fmt.Errorf("Count: %v", err)
+ }
+
+ sess = sess.Where(cond).OrderBy(opts.OrderBy.String())
+ repos := make(RepositoryList, 0, opts.PageSize)
+ return repos, count, db.SetSessionPagination(sess, opts).Find(&repos)
+}
diff --git a/models/repo_list_test.go b/models/repo/repo_list_test.go
index d45e10fb80..f9c84a0f3f 100644
--- a/models/repo_list_test.go
+++ b/models/repo/repo_list_test.go
@@ -2,13 +2,14 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package repo_test
import (
"strings"
"testing"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/util"
@@ -19,7 +20,7 @@ func TestSearchRepository(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// test search public repository on explore page
- repos, count, err := SearchRepositoryByName(&SearchRepoOptions{
+ repos, count, err := repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
@@ -34,7 +35,7 @@ func TestSearchRepository(t *testing.T) {
}
assert.Equal(t, int64(1), count)
- repos, count, err = SearchRepositoryByName(&SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
@@ -48,7 +49,7 @@ func TestSearchRepository(t *testing.T) {
assert.Len(t, repos, 2)
// test search private repository on explore page
- repos, count, err = SearchRepositoryByName(&SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
@@ -64,7 +65,7 @@ func TestSearchRepository(t *testing.T) {
}
assert.Equal(t, int64(1), count)
- repos, count, err = SearchRepositoryByName(&SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
@@ -79,14 +80,14 @@ func TestSearchRepository(t *testing.T) {
assert.Len(t, repos, 3)
// Test non existing owner
- repos, count, err = SearchRepositoryByName(&SearchRepoOptions{OwnerID: unittest.NonexistentID})
+ repos, count, err = repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{OwnerID: unittest.NonexistentID})
assert.NoError(t, err)
assert.Empty(t, repos)
assert.Equal(t, int64(0), count)
// Test search within description
- repos, count, err = SearchRepository(&SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
@@ -103,7 +104,7 @@ func TestSearchRepository(t *testing.T) {
assert.Equal(t, int64(1), count)
// Test NOT search within description
- repos, count, err = SearchRepository(&SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
@@ -119,164 +120,164 @@ func TestSearchRepository(t *testing.T) {
testCases := []struct {
name string
- opts *SearchRepoOptions
+ opts *repo_model.SearchRepoOptions
count int
}{
{
name: "PublicRepositoriesByName",
- opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, Collaborate: util.OptionalBoolFalse},
count: 7,
},
{
name: "PublicAndPrivateRepositoriesByName",
- opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: util.OptionalBoolFalse},
count: 14,
},
{
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage",
- opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
count: 14,
},
{
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage",
- opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
count: 14,
},
{
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage",
- opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
count: 14,
},
{
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage",
- opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
count: 14,
},
{
name: "PublicRepositoriesOfUser",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: util.OptionalBoolFalse},
count: 2,
},
{
name: "PublicRepositoriesOfUser2",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: util.OptionalBoolFalse},
count: 0,
},
{
name: "PublicRepositoriesOfUser3",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: util.OptionalBoolFalse},
count: 2,
},
{
name: "PublicAndPrivateRepositoriesOfUser",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: util.OptionalBoolFalse},
count: 4,
},
{
name: "PublicAndPrivateRepositoriesOfUser2",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: util.OptionalBoolFalse},
count: 0,
},
{
name: "PublicAndPrivateRepositoriesOfUser3",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: util.OptionalBoolFalse},
count: 4,
},
{
name: "PublicRepositoriesOfUserIncludingCollaborative",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15},
count: 5,
},
{
name: "PublicRepositoriesOfUser2IncludingCollaborative",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18},
count: 1,
},
{
name: "PublicRepositoriesOfUser3IncludingCollaborative",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20},
count: 3,
},
{
name: "PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true},
count: 9,
},
{
name: "PublicAndPrivateRepositoriesOfUser2IncludingCollaborative",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true},
count: 4,
},
{
name: "PublicAndPrivateRepositoriesOfUser3IncludingCollaborative",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true},
count: 7,
},
{
name: "PublicRepositoriesOfOrganization",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: util.OptionalBoolFalse},
count: 1,
},
{
name: "PublicAndPrivateRepositoriesOfOrganization",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: util.OptionalBoolFalse},
count: 2,
},
{
name: "AllPublic/PublicRepositoriesByName",
- opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: util.OptionalBoolFalse},
count: 7,
},
{
name: "AllPublic/PublicAndPrivateRepositoriesByName",
- opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: util.OptionalBoolFalse},
count: 14,
},
{
name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse},
count: 28,
},
{
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse},
count: 33,
},
{
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
- opts: &SearchRepoOptions{Keyword: "test", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true},
+ opts: &repo_model.SearchRepoOptions{Keyword: "test", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true},
count: 15,
},
{
name: "AllPublic/PublicAndPrivateRepositoriesOfUser2IncludingCollaborativeByName",
- opts: &SearchRepoOptions{Keyword: "test", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, AllPublic: true},
+ opts: &repo_model.SearchRepoOptions{Keyword: "test", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, AllPublic: true},
count: 13,
},
{
name: "AllPublic/PublicRepositoriesOfOrganization",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse},
count: 28,
},
{
name: "AllTemplates",
- opts: &SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: util.OptionalBoolTrue},
+ opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: util.OptionalBoolTrue},
count: 2,
},
{
name: "OwnerSlashRepoSearch",
- opts: &SearchRepoOptions{Keyword: "user/repo2", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
+ opts: &repo_model.SearchRepoOptions{Keyword: "user/repo2", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
count: 3,
},
{
name: "OwnerSlashSearch",
- opts: &SearchRepoOptions{Keyword: "user20/", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
+ opts: &repo_model.SearchRepoOptions{Keyword: "user20/", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0},
count: 4,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
- repos, count, err := SearchRepositoryByName(testCase.opts)
+ repos, count, err := repo_model.SearchRepositoryByName(testCase.opts)
assert.NoError(t, err)
assert.Equal(t, int64(testCase.count), count)
@@ -354,29 +355,29 @@ func TestSearchRepositoryByTopicName(t *testing.T) {
testCases := []struct {
name string
- opts *SearchRepoOptions
+ opts *repo_model.SearchRepoOptions
count int
}{
{
name: "AllPublic/SearchPublicRepositoriesFromTopicAndName",
- opts: &SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql"},
+ opts: &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql"},
count: 2,
},
{
name: "AllPublic/OnlySearchPublicRepositoriesFromTopic",
- opts: &SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql", TopicOnly: true},
+ opts: &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql", TopicOnly: true},
count: 1,
},
{
name: "AllPublic/OnlySearchMultipleKeywordPublicRepositoriesFromTopic",
- opts: &SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql,golang", TopicOnly: true},
+ opts: &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql,golang", TopicOnly: true},
count: 2,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
- _, count, err := SearchRepositoryByName(testCase.opts)
+ _, count, err := repo_model.SearchRepositoryByName(testCase.opts)
assert.NoError(t, err)
assert.Equal(t, int64(testCase.count), count)
})
diff --git a/models/repo/repo_test.go b/models/repo/repo_test.go
index cf6ee8b67a..8ae84eab52 100644
--- a/models/repo/repo_test.go
+++ b/models/repo/repo_test.go
@@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repo
+package repo_test
import (
"testing"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/util"
@@ -15,18 +16,18 @@ import (
)
var (
- countRepospts = CountRepositoryOptions{OwnerID: 10}
- countReposptsPublic = CountRepositoryOptions{OwnerID: 10, Private: util.OptionalBoolFalse}
- countReposptsPrivate = CountRepositoryOptions{OwnerID: 10, Private: util.OptionalBoolTrue}
+ countRepospts = repo_model.CountRepositoryOptions{OwnerID: 10}
+ countReposptsPublic = repo_model.CountRepositoryOptions{OwnerID: 10, Private: util.OptionalBoolFalse}
+ countReposptsPrivate = repo_model.CountRepositoryOptions{OwnerID: 10, Private: util.OptionalBoolTrue}
)
func TestGetRepositoryCount(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
ctx := db.DefaultContext
- count, err1 := CountRepositories(ctx, countRepospts)
- privateCount, err2 := CountRepositories(ctx, countReposptsPrivate)
- publicCount, err3 := CountRepositories(ctx, countReposptsPublic)
+ count, err1 := repo_model.CountRepositories(ctx, countRepospts)
+ privateCount, err2 := repo_model.CountRepositories(ctx, countReposptsPrivate)
+ publicCount, err3 := repo_model.CountRepositories(ctx, countReposptsPublic)
assert.NoError(t, err1)
assert.NoError(t, err2)
assert.NoError(t, err3)
@@ -37,7 +38,7 @@ func TestGetRepositoryCount(t *testing.T) {
func TestGetPublicRepositoryCount(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- count, err := CountRepositories(db.DefaultContext, countReposptsPublic)
+ count, err := repo_model.CountRepositories(db.DefaultContext, countReposptsPublic)
assert.NoError(t, err)
assert.Equal(t, int64(1), count)
}
@@ -45,14 +46,14 @@ func TestGetPublicRepositoryCount(t *testing.T) {
func TestGetPrivateRepositoryCount(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- count, err := CountRepositories(db.DefaultContext, countReposptsPrivate)
+ count, err := repo_model.CountRepositories(db.DefaultContext, countReposptsPrivate)
assert.NoError(t, err)
assert.Equal(t, int64(2), count)
}
func TestRepoAPIURL(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 10}).(*Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL())
}
diff --git a/models/repo/star_test.go b/models/repo/star_test.go
index 2dde09c745..aa72b1dac8 100644
--- a/models/repo/star_test.go
+++ b/models/repo/star_test.go
@@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repo
+package repo_test
import (
"testing"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
@@ -17,26 +18,26 @@ func TestStarRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
const userID = 2
const repoID = 1
- unittest.AssertNotExistsBean(t, &Star{UID: userID, RepoID: repoID})
- assert.NoError(t, StarRepo(userID, repoID, true))
- unittest.AssertExistsAndLoadBean(t, &Star{UID: userID, RepoID: repoID})
- assert.NoError(t, StarRepo(userID, repoID, true))
- unittest.AssertExistsAndLoadBean(t, &Star{UID: userID, RepoID: repoID})
- assert.NoError(t, StarRepo(userID, repoID, false))
- unittest.AssertNotExistsBean(t, &Star{UID: userID, RepoID: repoID})
+ unittest.AssertNotExistsBean(t, &repo_model.Star{UID: userID, RepoID: repoID})
+ assert.NoError(t, repo_model.StarRepo(userID, repoID, true))
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Star{UID: userID, RepoID: repoID})
+ assert.NoError(t, repo_model.StarRepo(userID, repoID, true))
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Star{UID: userID, RepoID: repoID})
+ assert.NoError(t, repo_model.StarRepo(userID, repoID, false))
+ unittest.AssertNotExistsBean(t, &repo_model.Star{UID: userID, RepoID: repoID})
}
func TestIsStaring(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- assert.True(t, IsStaring(db.DefaultContext, 2, 4))
- assert.False(t, IsStaring(db.DefaultContext, 3, 4))
+ assert.True(t, repo_model.IsStaring(db.DefaultContext, 2, 4))
+ assert.False(t, repo_model.IsStaring(db.DefaultContext, 3, 4))
}
func TestRepository_GetStargazers(t *testing.T) {
// repo with stargazers
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 4}).(*Repository)
- gazers, err := GetStargazers(repo, db.ListOptions{Page: 0})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}).(*repo_model.Repository)
+ gazers, err := repo_model.GetStargazers(repo, db.ListOptions{Page: 0})
assert.NoError(t, err)
if assert.Len(t, gazers, 1) {
assert.Equal(t, int64(2), gazers[0].ID)
@@ -46,8 +47,8 @@ func TestRepository_GetStargazers(t *testing.T) {
func TestRepository_GetStargazers2(t *testing.T) {
// repo with stargazers
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
- gazers, err := GetStargazers(repo, db.ListOptions{Page: 0})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ gazers, err := repo_model.GetStargazers(repo, db.ListOptions{Page: 0})
assert.NoError(t, err)
assert.Len(t, gazers, 0)
}
diff --git a/models/repo/topic_test.go b/models/repo/topic_test.go
index 353d96ef3e..8187addb81 100644
--- a/models/repo/topic_test.go
+++ b/models/repo/topic_test.go
@@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repo
+package repo_test
import (
"testing"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
@@ -19,47 +20,47 @@ func TestAddTopic(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- topics, _, err := FindTopics(&FindTopicOptions{})
+ topics, _, err := repo_model.FindTopics(&repo_model.FindTopicOptions{})
assert.NoError(t, err)
assert.Len(t, topics, totalNrOfTopics)
- topics, total, err := FindTopics(&FindTopicOptions{
+ topics, total, err := repo_model.FindTopics(&repo_model.FindTopicOptions{
ListOptions: db.ListOptions{Page: 1, PageSize: 2},
})
assert.NoError(t, err)
assert.Len(t, topics, 2)
assert.EqualValues(t, 6, total)
- topics, _, err = FindTopics(&FindTopicOptions{
+ topics, _, err = repo_model.FindTopics(&repo_model.FindTopicOptions{
RepoID: 1,
})
assert.NoError(t, err)
assert.Len(t, topics, repo1NrOfTopics)
- assert.NoError(t, SaveTopics(2, "golang"))
+ assert.NoError(t, repo_model.SaveTopics(2, "golang"))
repo2NrOfTopics := 1
- topics, _, err = FindTopics(&FindTopicOptions{})
+ topics, _, err = repo_model.FindTopics(&repo_model.FindTopicOptions{})
assert.NoError(t, err)
assert.Len(t, topics, totalNrOfTopics)
- topics, _, err = FindTopics(&FindTopicOptions{
+ topics, _, err = repo_model.FindTopics(&repo_model.FindTopicOptions{
RepoID: 2,
})
assert.NoError(t, err)
assert.Len(t, topics, repo2NrOfTopics)
- assert.NoError(t, SaveTopics(2, "golang", "gitea"))
+ assert.NoError(t, repo_model.SaveTopics(2, "golang", "gitea"))
repo2NrOfTopics = 2
totalNrOfTopics++
- topic, err := GetTopicByName("gitea")
+ topic, err := repo_model.GetTopicByName("gitea")
assert.NoError(t, err)
assert.EqualValues(t, 1, topic.RepoCount)
- topics, _, err = FindTopics(&FindTopicOptions{})
+ topics, _, err = repo_model.FindTopics(&repo_model.FindTopicOptions{})
assert.NoError(t, err)
assert.Len(t, topics, totalNrOfTopics)
- topics, _, err = FindTopics(&FindTopicOptions{
+ topics, _, err = repo_model.FindTopics(&repo_model.FindTopicOptions{
RepoID: 2,
})
assert.NoError(t, err)
@@ -67,14 +68,14 @@ func TestAddTopic(t *testing.T) {
}
func TestTopicValidator(t *testing.T) {
- assert.True(t, ValidateTopic("12345"))
- assert.True(t, ValidateTopic("2-test"))
- assert.True(t, ValidateTopic("test-3"))
- assert.True(t, ValidateTopic("first"))
- assert.True(t, ValidateTopic("second-test-topic"))
- assert.True(t, ValidateTopic("third-project-topic-with-max-length"))
-
- assert.False(t, ValidateTopic("$fourth-test,topic"))
- assert.False(t, ValidateTopic("-fifth-test-topic"))
- assert.False(t, ValidateTopic("sixth-go-project-topic-with-excess-length"))
+ assert.True(t, repo_model.ValidateTopic("12345"))
+ assert.True(t, repo_model.ValidateTopic("2-test"))
+ assert.True(t, repo_model.ValidateTopic("test-3"))
+ assert.True(t, repo_model.ValidateTopic("first"))
+ assert.True(t, repo_model.ValidateTopic("second-test-topic"))
+ assert.True(t, repo_model.ValidateTopic("third-project-topic-with-max-length"))
+
+ assert.False(t, repo_model.ValidateTopic("$fourth-test,topic"))
+ assert.False(t, repo_model.ValidateTopic("-fifth-test-topic"))
+ assert.False(t, repo_model.ValidateTopic("sixth-go-project-topic-with-excess-length"))
}
diff --git a/models/repo/update.go b/models/repo/update.go
index 7fb51c9593..07776ebc01 100644
--- a/models/repo/update.go
+++ b/models/repo/update.go
@@ -172,3 +172,11 @@ func ChangeRepositoryName(doer *user_model.User, repo *Repository, newRepoName s
return committer.Commit()
}
+
+// UpdateRepoSize updates the repository size, calculating it using util.GetDirectorySize
+func UpdateRepoSize(ctx context.Context, repoID, size int64) error {
+ _, err := db.GetEngine(ctx).ID(repoID).Cols("size").NoAutoTime().Update(&Repository{
+ Size: size,
+ })
+ return err
+}
diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go
index fe96771796..e697505b81 100644
--- a/models/repo/user_repo.go
+++ b/models/repo/user_repo.go
@@ -5,7 +5,14 @@
package repo
import (
+ "context"
+
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/perm"
+ user_model "code.gitea.io/gitea/models/user"
+ api "code.gitea.io/gitea/modules/structs"
+
+ "xorm.io/builder"
)
// GetStarredRepos returns the repos starred by a particular user
@@ -48,3 +55,118 @@ func GetWatchedRepos(userID int64, private bool, listOptions db.ListOptions) ([]
total, err := sess.FindAndCount(&repos)
return repos, total, err
}
+
+// GetRepoAssignees returns all users that have write access and can be assigned to issues
+// of the repository,
+func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.User, err error) {
+ if err = repo.GetOwner(ctx); err != nil {
+ return nil, err
+ }
+
+ e := db.GetEngine(ctx)
+ userIDs := make([]int64, 0, 10)
+ if err = e.Table("access").
+ Where("repo_id = ? AND mode >= ?", repo.ID, perm.AccessModeWrite).
+ Select("user_id").
+ Find(&userIDs); err != nil {
+ return nil, err
+ }
+
+ additionalUserIDs := make([]int64, 0, 10)
+ if err = e.Table("team_user").
+ Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id").
+ Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id").
+ Where("`team_repo`.repo_id = ? AND `team_unit`.access_mode >= ?", repo.ID, perm.AccessModeWrite).
+ Distinct("`team_user`.uid").
+ Select("`team_user`.uid").
+ Find(&additionalUserIDs); err != nil {
+ return nil, err
+ }
+
+ uidMap := map[int64]bool{}
+ i := 0
+ for _, uid := range userIDs {
+ if uidMap[uid] {
+ continue
+ }
+ uidMap[uid] = true
+ userIDs[i] = uid
+ i++
+ }
+ userIDs = userIDs[:i]
+ userIDs = append(userIDs, additionalUserIDs...)
+
+ for _, uid := range additionalUserIDs {
+ if uidMap[uid] {
+ continue
+ }
+ userIDs[i] = uid
+ i++
+ }
+ userIDs = userIDs[:i]
+
+ // Leave a seat for owner itself to append later, but if owner is an organization
+ // and just waste 1 unit is cheaper than re-allocate memory once.
+ users := make([]*user_model.User, 0, len(userIDs)+1)
+ if len(userIDs) > 0 {
+ if err = e.In("id", userIDs).Find(&users); err != nil {
+ return nil, err
+ }
+ }
+ if !repo.Owner.IsOrganization() && !uidMap[repo.OwnerID] {
+ users = append(users, repo.Owner)
+ }
+
+ return users, nil
+}
+
+// GetReviewers get all users can be requested to review:
+// * for private repositories this returns all users that have read access or higher to the repository.
+// * for public repositories this returns all users that have read access or higher to the repository,
+// all repo watchers and all organization members.
+// TODO: may be we should have a busy choice for users to block review request to them.
+func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64) ([]*user_model.User, error) {
+ // Get the owner of the repository - this often already pre-cached and if so saves complexity for the following queries
+ if err := repo.GetOwner(ctx); err != nil {
+ return nil, err
+ }
+
+ cond := builder.And(builder.Neq{"`user`.id": posterID})
+
+ if repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate {
+ // This a private repository:
+ // Anyone who can read the repository is a requestable reviewer
+
+ cond = cond.And(builder.In("`user`.id",
+ builder.Select("user_id").From("access").Where(
+ builder.Eq{"repo_id": repo.ID}.
+ And(builder.Gte{"mode": perm.AccessModeRead}),
+ ),
+ ))
+
+ if repo.Owner.Type == user_model.UserTypeIndividual && repo.Owner.ID != posterID {
+ // as private *user* repos don't generate an entry in the `access` table,
+ // the owner of a private repo needs to be explicitly added.
+ cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID})
+ }
+
+ } else {
+ // This is a "public" repository:
+ // Any user that has read access, is a watcher or organization member can be requested to review
+ cond = cond.And(builder.And(builder.In("`user`.id",
+ builder.Select("user_id").From("access").
+ Where(builder.Eq{"repo_id": repo.ID}.
+ And(builder.Gte{"mode": perm.AccessModeRead})),
+ ).Or(builder.In("`user`.id",
+ builder.Select("user_id").From("watch").
+ Where(builder.Eq{"repo_id": repo.ID}.
+ And(builder.In("mode", WatchModeNormal, WatchModeAuto))),
+ ).Or(builder.In("`user`.id",
+ builder.Select("uid").From("org_user").
+ Where(builder.Eq{"org_id": repo.OwnerID}),
+ )))))
+ }
+
+ users := make([]*user_model.User, 0, 8)
+ return users, db.GetEngine(ctx).Where(cond).OrderBy("name").Find(&users)
+}
diff --git a/models/repo/user_repo_test.go b/models/repo/user_repo_test.go
new file mode 100644
index 0000000000..d024729b9c
--- /dev/null
+++ b/models/repo/user_repo_test.go
@@ -0,0 +1,74 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo_test
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRepoAssignees(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ users, err := repo_model.GetRepoAssignees(db.DefaultContext, repo2)
+ assert.NoError(t, err)
+ assert.Len(t, users, 1)
+ assert.Equal(t, users[0].ID, int64(2))
+
+ repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 21}).(*repo_model.Repository)
+ users, err = repo_model.GetRepoAssignees(db.DefaultContext, repo21)
+ assert.NoError(t, err)
+ assert.Len(t, users, 3)
+ assert.Equal(t, users[0].ID, int64(15))
+ assert.Equal(t, users[1].ID, int64(18))
+ assert.Equal(t, users[2].ID, int64(16))
+}
+
+func TestRepoGetReviewers(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ // test public repo
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+
+ ctx := db.DefaultContext
+ reviewers, err := repo_model.GetReviewers(ctx, repo1, 2, 2)
+ assert.NoError(t, err)
+ assert.Len(t, reviewers, 4)
+
+ // should include doer if doer is not PR poster.
+ reviewers, err = repo_model.GetReviewers(ctx, repo1, 11, 2)
+ assert.NoError(t, err)
+ assert.Len(t, reviewers, 4)
+
+ // should not include PR poster, if PR poster would be otherwise eligible
+ reviewers, err = repo_model.GetReviewers(ctx, repo1, 11, 4)
+ assert.NoError(t, err)
+ assert.Len(t, reviewers, 3)
+
+ // test private user repo
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+
+ reviewers, err = repo_model.GetReviewers(ctx, repo2, 2, 4)
+ assert.NoError(t, err)
+ assert.Len(t, reviewers, 1)
+ assert.EqualValues(t, reviewers[0].ID, 2)
+
+ // test private org repo
+ repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+
+ reviewers, err = repo_model.GetReviewers(ctx, repo3, 2, 1)
+ assert.NoError(t, err)
+ assert.Len(t, reviewers, 2)
+
+ reviewers, err = repo_model.GetReviewers(ctx, repo3, 2, 2)
+ assert.NoError(t, err)
+ assert.Len(t, reviewers, 1)
+}
diff --git a/models/repo/watch_test.go b/models/repo/watch_test.go
index 2f4e04ab17..3875e63fd8 100644
--- a/models/repo/watch_test.go
+++ b/models/repo/watch_test.go
@@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repo
+package repo_test
import (
"testing"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
@@ -17,20 +18,20 @@ import (
func TestIsWatching(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- assert.True(t, IsWatching(1, 1))
- assert.True(t, IsWatching(4, 1))
- assert.True(t, IsWatching(11, 1))
+ assert.True(t, repo_model.IsWatching(1, 1))
+ assert.True(t, repo_model.IsWatching(4, 1))
+ assert.True(t, repo_model.IsWatching(11, 1))
- assert.False(t, IsWatching(1, 5))
- assert.False(t, IsWatching(8, 1))
- assert.False(t, IsWatching(unittest.NonexistentID, unittest.NonexistentID))
+ assert.False(t, repo_model.IsWatching(1, 5))
+ assert.False(t, repo_model.IsWatching(8, 1))
+ assert.False(t, repo_model.IsWatching(unittest.NonexistentID, unittest.NonexistentID))
}
func TestGetWatchers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
- watches, err := GetWatchers(db.DefaultContext, repo.ID)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ watches, err := repo_model.GetWatchers(db.DefaultContext, repo.ID)
assert.NoError(t, err)
// One watchers are inactive, thus minus 1
assert.Len(t, watches, repo.NumWatches-1)
@@ -38,7 +39,7 @@ func TestGetWatchers(t *testing.T) {
assert.EqualValues(t, repo.ID, watch.RepoID)
}
- watches, err = GetWatchers(db.DefaultContext, unittest.NonexistentID)
+ watches, err = repo_model.GetWatchers(db.DefaultContext, unittest.NonexistentID)
assert.NoError(t, err)
assert.Len(t, watches, 0)
}
@@ -46,16 +47,16 @@ func TestGetWatchers(t *testing.T) {
func TestRepository_GetWatchers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
- watchers, err := GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ watchers, err := repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, repo.NumWatches)
for _, watcher := range watchers {
- unittest.AssertExistsAndLoadBean(t, &Watch{UserID: watcher.ID, RepoID: repo.ID})
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Watch{UserID: watcher.ID, RepoID: repo.ID})
}
- repo = unittest.AssertExistsAndLoadBean(t, &Repository{ID: 9}).(*Repository)
- watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
+ repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 9}).(*repo_model.Repository)
+ watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, 0)
}
@@ -63,8 +64,8 @@ func TestRepository_GetWatchers(t *testing.T) {
func TestWatchIfAuto(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
- watchers, err := GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ watchers, err := repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, repo.NumWatches)
@@ -73,46 +74,46 @@ func TestWatchIfAuto(t *testing.T) {
prevCount := repo.NumWatches
// Must not add watch
- assert.NoError(t, WatchIfAuto(db.DefaultContext, 8, 1, true))
- watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
+ assert.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 8, 1, true))
+ watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, prevCount)
// Should not add watch
- assert.NoError(t, WatchIfAuto(db.DefaultContext, 10, 1, true))
- watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
+ assert.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 10, 1, true))
+ watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, prevCount)
setting.Service.AutoWatchOnChanges = true
// Must not add watch
- assert.NoError(t, WatchIfAuto(db.DefaultContext, 8, 1, true))
- watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
+ assert.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 8, 1, true))
+ watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, prevCount)
// Should not add watch
- assert.NoError(t, WatchIfAuto(db.DefaultContext, 12, 1, false))
- watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
+ assert.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 12, 1, false))
+ watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, prevCount)
// Should add watch
- assert.NoError(t, WatchIfAuto(db.DefaultContext, 12, 1, true))
- watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
+ assert.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 12, 1, true))
+ watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, prevCount+1)
// Should remove watch, inhibit from adding auto
- assert.NoError(t, WatchRepo(db.DefaultContext, 12, 1, false))
- watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
+ assert.NoError(t, repo_model.WatchRepo(db.DefaultContext, 12, 1, false))
+ watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, prevCount)
// Must not add watch
- assert.NoError(t, WatchIfAuto(db.DefaultContext, 12, 1, true))
- watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
+ assert.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 12, 1, true))
+ watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, prevCount)
}
@@ -120,20 +121,20 @@ func TestWatchIfAuto(t *testing.T) {
func TestWatchRepoMode(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 0)
+ unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1}, 0)
- assert.NoError(t, WatchRepoMode(12, 1, WatchModeAuto))
- unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 1)
- unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: WatchModeAuto}, 1)
+ assert.NoError(t, repo_model.WatchRepoMode(12, 1, repo_model.WatchModeAuto))
+ unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1}, 1)
+ unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1, Mode: repo_model.WatchModeAuto}, 1)
- assert.NoError(t, WatchRepoMode(12, 1, WatchModeNormal))
- unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 1)
- unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: WatchModeNormal}, 1)
+ assert.NoError(t, repo_model.WatchRepoMode(12, 1, repo_model.WatchModeNormal))
+ unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1}, 1)
+ unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1, Mode: repo_model.WatchModeNormal}, 1)
- assert.NoError(t, WatchRepoMode(12, 1, WatchModeDont))
- unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 1)
- unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: WatchModeDont}, 1)
+ assert.NoError(t, repo_model.WatchRepoMode(12, 1, repo_model.WatchModeDont))
+ unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1}, 1)
+ unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1, Mode: repo_model.WatchModeDont}, 1)
- assert.NoError(t, WatchRepoMode(12, 1, WatchModeNone))
- unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 0)
+ assert.NoError(t, repo_model.WatchRepoMode(12, 1, repo_model.WatchModeNone))
+ unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1}, 0)
}
diff --git a/models/repo/wiki_test.go b/models/repo/wiki_test.go
index f5e61e5ae3..339289e05d 100644
--- a/models/repo/wiki_test.go
+++ b/models/repo/wiki_test.go
@@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repo
+package repo_test
import (
"path/filepath"
"testing"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
@@ -17,7 +18,7 @@ import (
func TestRepository_WikiCloneLink(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
cloneLink := repo.WikiCloneLink()
assert.Equal(t, "ssh://sshuser@try.gitea.io:3000/user2/repo1.wiki.git", cloneLink.SSH)
assert.Equal(t, "https://try.gitea.io/user2/repo1.wiki.git", cloneLink.HTTPS)
@@ -26,20 +27,20 @@ func TestRepository_WikiCloneLink(t *testing.T) {
func TestWikiPath(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
expected := filepath.Join(setting.RepoRootPath, "user2/repo1.wiki.git")
- assert.Equal(t, expected, WikiPath("user2", "repo1"))
+ assert.Equal(t, expected, repo_model.WikiPath("user2", "repo1"))
}
func TestRepository_WikiPath(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
expected := filepath.Join(setting.RepoRootPath, "user2/repo1.wiki.git")
assert.Equal(t, expected, repo.WikiPath())
}
func TestRepository_HasWiki(t *testing.T) {
unittest.PrepareTestEnv(t)
- repo1 := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
assert.True(t, repo1.HasWiki())
- repo2 := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
assert.False(t, repo2.HasWiki())
}
diff --git a/models/repo_generate.go b/models/repo_generate.go
deleted file mode 100644
index 6b720b4969..0000000000
--- a/models/repo_generate.go
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
- "bufio"
- "bytes"
- "context"
- "strings"
-
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/log"
-
- "github.com/gobwas/glob"
-)
-
-// GenerateRepoOptions contains the template units to generate
-type GenerateRepoOptions struct {
- Name string
- DefaultBranch string
- Description string
- Private bool
- GitContent bool
- Topics bool
- GitHooks bool
- Webhooks bool
- Avatar bool
- IssueLabels bool
-}
-
-// IsValid checks whether at least one option is chosen for generation
-func (gro GenerateRepoOptions) IsValid() bool {
- return gro.GitContent || gro.Topics || gro.GitHooks || gro.Webhooks || gro.Avatar || gro.IssueLabels // or other items as they are added
-}
-
-// GiteaTemplate holds information about a .gitea/template file
-type GiteaTemplate struct {
- Path string
- Content []byte
-
- globs []glob.Glob
-}
-
-// Globs parses the .gitea/template globs or returns them if they were already parsed
-func (gt GiteaTemplate) Globs() []glob.Glob {
- if gt.globs != nil {
- return gt.globs
- }
-
- gt.globs = make([]glob.Glob, 0)
- scanner := bufio.NewScanner(bytes.NewReader(gt.Content))
- for scanner.Scan() {
- line := strings.TrimSpace(scanner.Text())
- if line == "" || strings.HasPrefix(line, "#") {
- continue
- }
- g, err := glob.Compile(line, '/')
- if err != nil {
- log.Info("Invalid glob expression '%s' (skipped): %v", line, err)
- continue
- }
- gt.globs = append(gt.globs, g)
- }
- return gt.globs
-}
-
-// GenerateWebhooks generates webhooks from a template repository
-func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
- templateWebhooks, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{RepoID: templateRepo.ID})
- if err != nil {
- return err
- }
-
- for _, templateWebhook := range templateWebhooks {
- generateWebhook := &webhook.Webhook{
- RepoID: generateRepo.ID,
- URL: templateWebhook.URL,
- HTTPMethod: templateWebhook.HTTPMethod,
- ContentType: templateWebhook.ContentType,
- Secret: templateWebhook.Secret,
- HookEvent: templateWebhook.HookEvent,
- IsActive: templateWebhook.IsActive,
- Type: templateWebhook.Type,
- OrgID: templateWebhook.OrgID,
- Events: templateWebhook.Events,
- Meta: templateWebhook.Meta,
- }
- if err := webhook.CreateWebhook(ctx, generateWebhook); err != nil {
- return err
- }
- }
- return nil
-}
-
-// GenerateIssueLabels generates issue labels from a template repository
-func GenerateIssueLabels(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
- templateLabels, err := GetLabelsByRepoID(ctx, templateRepo.ID, "", db.ListOptions{})
- if err != nil {
- return err
- }
-
- for _, templateLabel := range templateLabels {
- generateLabel := &Label{
- RepoID: generateRepo.ID,
- Name: templateLabel.Name,
- Description: templateLabel.Description,
- Color: templateLabel.Color,
- }
- if err := db.Insert(ctx, generateLabel); err != nil {
- return err
- }
- }
- return nil
-}
diff --git a/models/repo_list.go b/models/repo_list.go
deleted file mode 100644
index 45fb10c364..0000000000
--- a/models/repo_list.go
+++ /dev/null
@@ -1,704 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
- "context"
- "fmt"
- "strings"
-
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
-
- "xorm.io/builder"
-)
-
-// RepositoryListDefaultPageSize is the default number of repositories
-// to load in memory when running administrative tasks on all (or almost
-// all) of them.
-// The number should be low enough to avoid filling up all RAM with
-// repository data...
-const RepositoryListDefaultPageSize = 64
-
-// RepositoryList contains a list of repositories
-type RepositoryList []*repo_model.Repository
-
-func (repos RepositoryList) Len() int {
- return len(repos)
-}
-
-func (repos RepositoryList) Less(i, j int) bool {
- return repos[i].FullName() < repos[j].FullName()
-}
-
-func (repos RepositoryList) Swap(i, j int) {
- repos[i], repos[j] = repos[j], repos[i]
-}
-
-// FIXME: Remove in favor of maps.values when MIN_GO_VERSION >= 1.18
-func valuesRepository(m map[int64]*repo_model.Repository) []*repo_model.Repository {
- values := make([]*repo_model.Repository, 0, len(m))
- for _, v := range m {
- values = append(values, v)
- }
- return values
-}
-
-// RepositoryListOfMap make list from values of map
-func RepositoryListOfMap(repoMap map[int64]*repo_model.Repository) RepositoryList {
- return RepositoryList(valuesRepository(repoMap))
-}
-
-func (repos RepositoryList) loadAttributes(ctx context.Context) error {
- if len(repos) == 0 {
- return nil
- }
-
- set := make(map[int64]struct{})
- repoIDs := make([]int64, len(repos))
- for i := range repos {
- set[repos[i].OwnerID] = struct{}{}
- repoIDs[i] = repos[i].ID
- }
-
- // Load owners.
- users := make(map[int64]*user_model.User, len(set))
- if err := db.GetEngine(ctx).
- Where("id > 0").
- In("id", container.KeysInt64(set)).
- Find(&users); err != nil {
- return fmt.Errorf("find users: %v", err)
- }
- for i := range repos {
- repos[i].Owner = users[repos[i].OwnerID]
- }
-
- // Load primary language.
- stats := make(repo_model.LanguageStatList, 0, len(repos))
- if err := db.GetEngine(ctx).
- Where("`is_primary` = ? AND `language` != ?", true, "other").
- In("`repo_id`", repoIDs).
- Find(&stats); err != nil {
- return fmt.Errorf("find primary languages: %v", err)
- }
- stats.LoadAttributes()
- for i := range repos {
- for _, st := range stats {
- if st.RepoID == repos[i].ID {
- repos[i].PrimaryLanguage = st
- break
- }
- }
- }
-
- return nil
-}
-
-// LoadAttributes loads the attributes for the given RepositoryList
-func (repos RepositoryList) LoadAttributes() error {
- return repos.loadAttributes(db.DefaultContext)
-}
-
-// SearchRepoOptions holds the search options
-type SearchRepoOptions struct {
- db.ListOptions
- Actor *user_model.User
- Keyword string
- OwnerID int64
- PriorityOwnerID int64
- TeamID int64
- OrderBy db.SearchOrderBy
- Private bool // Include private repositories in results
- StarredByID int64
- WatchedByID int64
- AllPublic bool // Include also all public repositories of users and public organisations
- AllLimited bool // Include also all public repositories of limited organisations
- // None -> include public and private
- // True -> include just private
- // False -> include just public
- IsPrivate util.OptionalBool
- // None -> include collaborative AND non-collaborative
- // True -> include just collaborative
- // False -> include just non-collaborative
- Collaborate util.OptionalBool
- // None -> include forks AND non-forks
- // True -> include just forks
- // False -> include just non-forks
- Fork util.OptionalBool
- // None -> include templates AND non-templates
- // True -> include just templates
- // False -> include just non-templates
- Template util.OptionalBool
- // None -> include mirrors AND non-mirrors
- // True -> include just mirrors
- // False -> include just non-mirrors
- Mirror util.OptionalBool
- // None -> include archived AND non-archived
- // True -> include just archived
- // False -> include just non-archived
- Archived util.OptionalBool
- // only search topic name
- TopicOnly bool
- // only search repositories with specified primary language
- Language string
- // include description in keyword search
- IncludeDescription bool
- // None -> include has milestones AND has no milestone
- // True -> include just has milestones
- // False -> include just has no milestone
- HasMilestones util.OptionalBool
- // LowerNames represents valid lower names to restrict to
- LowerNames []string
-}
-
-// SearchOrderBy is used to sort the result
-type SearchOrderBy string
-
-func (s SearchOrderBy) String() string {
- return string(s)
-}
-
-// Strings for sorting result
-const (
- SearchOrderByAlphabetically SearchOrderBy = "name ASC"
- SearchOrderByAlphabeticallyReverse SearchOrderBy = "name DESC"
- SearchOrderByLeastUpdated SearchOrderBy = "updated_unix ASC"
- SearchOrderByRecentUpdated SearchOrderBy = "updated_unix DESC"
- SearchOrderByOldest SearchOrderBy = "created_unix ASC"
- SearchOrderByNewest SearchOrderBy = "created_unix DESC"
- SearchOrderBySize SearchOrderBy = "size ASC"
- SearchOrderBySizeReverse SearchOrderBy = "size DESC"
- SearchOrderByID SearchOrderBy = "id ASC"
- SearchOrderByIDReverse SearchOrderBy = "id DESC"
- SearchOrderByStars SearchOrderBy = "num_stars ASC"
- SearchOrderByStarsReverse SearchOrderBy = "num_stars DESC"
- SearchOrderByForks SearchOrderBy = "num_forks ASC"
- SearchOrderByForksReverse SearchOrderBy = "num_forks DESC"
-)
-
-// userOwnedRepoCond returns user ownered repositories
-func userOwnedRepoCond(userID int64) builder.Cond {
- return builder.Eq{
- "repository.owner_id": userID,
- }
-}
-
-// userAssignedRepoCond return user as assignee repositories list
-func userAssignedRepoCond(id string, userID int64) builder.Cond {
- return builder.And(
- builder.Eq{
- "repository.is_private": false,
- },
- builder.In(id,
- builder.Select("issue.repo_id").From("issue_assignees").
- InnerJoin("issue", "issue.id = issue_assignees.issue_id").
- Where(builder.Eq{
- "issue_assignees.assignee_id": userID,
- }),
- ),
- )
-}
-
-// userCreateIssueRepoCond return user created issues repositories list
-func userCreateIssueRepoCond(id string, userID int64, isPull bool) builder.Cond {
- return builder.And(
- builder.Eq{
- "repository.is_private": false,
- },
- builder.In(id,
- builder.Select("issue.repo_id").From("issue").
- Where(builder.Eq{
- "issue.poster_id": userID,
- "issue.is_pull": isPull,
- }),
- ),
- )
-}
-
-// userMentionedRepoCond return user metinoed repositories list
-func userMentionedRepoCond(id string, userID int64) builder.Cond {
- return builder.And(
- builder.Eq{
- "repository.is_private": false,
- },
- builder.In(id,
- builder.Select("issue.repo_id").From("issue_user").
- InnerJoin("issue", "issue.id = issue_user.issue_id").
- Where(builder.Eq{
- "issue_user.is_mentioned": true,
- "issue_user.uid": userID,
- }),
- ),
- )
-}
-
-// teamUnitsRepoCond returns query condition for those repo id in the special org team with special units access
-func teamUnitsRepoCond(id string, userID, orgID, teamID int64, units ...unit.Type) builder.Cond {
- return builder.In(id,
- builder.Select("repo_id").From("team_repo").Where(
- builder.Eq{
- "team_id": teamID,
- }.And(
- builder.Or(
- // Check if the user is member of the team.
- builder.In(
- "team_id", builder.Select("team_id").From("team_user").Where(
- builder.Eq{
- "uid": userID,
- },
- ),
- ),
- // Check if the user is in the owner team of the organisation.
- builder.Exists(builder.Select("team_id").From("team_user").
- Where(builder.Eq{
- "org_id": orgID,
- "team_id": builder.Select("id").From("team").Where(
- builder.Eq{
- "org_id": orgID,
- "lower_name": strings.ToLower(organization.OwnerTeamName),
- }),
- "uid": userID,
- }),
- ),
- )).And(
- builder.In(
- "team_id", builder.Select("team_id").From("team_unit").Where(
- builder.Eq{
- "`team_unit`.org_id": orgID,
- }.And(
- builder.In("`team_unit`.type", units),
- ),
- ),
- ),
- ),
- ))
-}
-
-// userCollaborationRepoCond returns user as collabrators repositories list
-func userCollaborationRepoCond(idStr string, userID int64) builder.Cond {
- return builder.In(idStr, builder.Select("repo_id").
- From("`access`").
- Where(builder.And(
- builder.Eq{"`access`.user_id": userID},
- builder.Gt{"`access`.mode": int(perm.AccessModeNone)},
- )),
- )
-}
-
-// userOrgTeamRepoCond selects repos that the given user has access to through team membership
-func userOrgTeamRepoCond(idStr string, userID int64) builder.Cond {
- return builder.In(idStr, userOrgTeamRepoBuilder(userID))
-}
-
-// userOrgTeamRepoBuilder returns repo ids where user's teams can access.
-func userOrgTeamRepoBuilder(userID int64) *builder.Builder {
- return builder.Select("`team_repo`.repo_id").
- From("team_repo").
- Join("INNER", "team_user", "`team_user`.team_id = `team_repo`.team_id").
- Where(builder.Eq{"`team_user`.uid": userID})
-}
-
-// userOrgTeamUnitRepoBuilder returns repo ids where user's teams can access the special unit.
-func userOrgTeamUnitRepoBuilder(userID int64, unitType unit.Type) *builder.Builder {
- return userOrgTeamRepoBuilder(userID).
- Join("INNER", "team_unit", "`team_unit`.team_id = `team_repo`.team_id").
- Where(builder.Eq{"`team_unit`.`type`": unitType})
-}
-
-// userOrgUnitRepoCond selects repos that the given user has access to through org and the special unit
-func userOrgUnitRepoCond(idStr string, userID, orgID int64, unitType unit.Type) builder.Cond {
- return builder.In(idStr,
- userOrgTeamUnitRepoBuilder(userID, unitType).
- And(builder.Eq{"`team_unit`.org_id": orgID}),
- )
-}
-
-// userOrgPublicRepoCond returns the condition that one user could access all public repositories in organizations
-func userOrgPublicRepoCond(userID int64) builder.Cond {
- return builder.And(
- builder.Eq{"`repository`.is_private": false},
- builder.In("`repository`.owner_id",
- builder.Select("`org_user`.org_id").
- From("org_user").
- Where(builder.Eq{"`org_user`.uid": userID}),
- ),
- )
-}
-
-// userOrgPublicRepoCondPrivate returns the condition that one user could access all public repositories in private organizations
-func userOrgPublicRepoCondPrivate(userID int64) builder.Cond {
- return builder.And(
- builder.Eq{"`repository`.is_private": false},
- builder.In("`repository`.owner_id",
- builder.Select("`org_user`.org_id").
- From("org_user").
- Join("INNER", "`user`", "`user`.id = `org_user`.org_id").
- Where(builder.Eq{
- "`org_user`.uid": userID,
- "`user`.`type`": user_model.UserTypeOrganization,
- "`user`.visibility": structs.VisibleTypePrivate,
- }),
- ),
- )
-}
-
-// userOrgPublicUnitRepoCond returns the condition that one user could access all public repositories in the special organization
-func userOrgPublicUnitRepoCond(userID, orgID int64) builder.Cond {
- return userOrgPublicRepoCond(userID).
- And(builder.Eq{"`repository`.owner_id": orgID})
-}
-
-// SearchRepositoryCondition creates a query condition according search repository options
-func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
- cond := builder.NewCond()
-
- if opts.Private {
- if opts.Actor != nil && !opts.Actor.IsAdmin && opts.Actor.ID != opts.OwnerID {
- // OK we're in the context of a User
- cond = cond.And(accessibleRepositoryCondition(opts.Actor))
- }
- } else {
- // Not looking at private organisations and users
- // We should be able to see all non-private repositories that
- // isn't in a private or limited organisation.
- cond = cond.And(
- builder.Eq{"is_private": false},
- builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(
- builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}),
- )))
- }
-
- if opts.IsPrivate != util.OptionalBoolNone {
- cond = cond.And(builder.Eq{"is_private": opts.IsPrivate.IsTrue()})
- }
-
- if opts.Template != util.OptionalBoolNone {
- cond = cond.And(builder.Eq{"is_template": opts.Template == util.OptionalBoolTrue})
- }
-
- // Restrict to starred repositories
- if opts.StarredByID > 0 {
- cond = cond.And(builder.In("id", builder.Select("repo_id").From("star").Where(builder.Eq{"uid": opts.StarredByID})))
- }
-
- // Restrict to watched repositories
- if opts.WatchedByID > 0 {
- cond = cond.And(builder.In("id", builder.Select("repo_id").From("watch").Where(builder.Eq{"user_id": opts.WatchedByID})))
- }
-
- // Restrict repositories to those the OwnerID owns or contributes to as per opts.Collaborate
- if opts.OwnerID > 0 {
- accessCond := builder.NewCond()
- if opts.Collaborate != util.OptionalBoolTrue {
- accessCond = builder.Eq{"owner_id": opts.OwnerID}
- }
-
- if opts.Collaborate != util.OptionalBoolFalse {
- // A Collaboration is:
- collaborateCond := builder.And(
- // 1. Repository we don't own
- builder.Neq{"owner_id": opts.OwnerID},
- // 2. But we can see because of:
- builder.Or(
- // A. We have access
- userCollaborationRepoCond("`repository`.id", opts.OwnerID),
- // B. We are in a team for
- userOrgTeamRepoCond("`repository`.id", opts.OwnerID),
- // C. Public repositories in organizations that we are member of
- userOrgPublicRepoCondPrivate(opts.OwnerID),
- ),
- )
- if !opts.Private {
- collaborateCond = collaborateCond.And(builder.Expr("owner_id NOT IN (SELECT org_id FROM org_user WHERE org_user.uid = ? AND org_user.is_public = ?)", opts.OwnerID, false))
- }
-
- accessCond = accessCond.Or(collaborateCond)
- }
-
- if opts.AllPublic {
- accessCond = accessCond.Or(builder.Eq{"is_private": false}.And(builder.In("owner_id", builder.Select("`user`.id").From("`user`").Where(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}))))
- }
-
- if opts.AllLimited {
- accessCond = accessCond.Or(builder.Eq{"is_private": false}.And(builder.In("owner_id", builder.Select("`user`.id").From("`user`").Where(builder.Eq{"`user`.visibility": structs.VisibleTypeLimited}))))
- }
-
- cond = cond.And(accessCond)
- }
-
- if opts.TeamID > 0 {
- cond = cond.And(builder.In("`repository`.id", builder.Select("`team_repo`.repo_id").From("team_repo").Where(builder.Eq{"`team_repo`.team_id": opts.TeamID})))
- }
-
- if opts.Keyword != "" {
- // separate keyword
- subQueryCond := builder.NewCond()
- for _, v := range strings.Split(opts.Keyword, ",") {
- if opts.TopicOnly {
- subQueryCond = subQueryCond.Or(builder.Eq{"topic.name": strings.ToLower(v)})
- } else {
- subQueryCond = subQueryCond.Or(builder.Like{"topic.name", strings.ToLower(v)})
- }
- }
- subQuery := builder.Select("repo_topic.repo_id").From("repo_topic").
- Join("INNER", "topic", "topic.id = repo_topic.topic_id").
- Where(subQueryCond).
- GroupBy("repo_topic.repo_id")
-
- keywordCond := builder.In("id", subQuery)
- if !opts.TopicOnly {
- likes := builder.NewCond()
- for _, v := range strings.Split(opts.Keyword, ",") {
- likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)})
-
- // If the string looks like "org/repo", match against that pattern too
- if opts.TeamID == 0 && strings.Count(opts.Keyword, "/") == 1 {
- pieces := strings.Split(opts.Keyword, "/")
- ownerName := pieces[0]
- repoName := pieces[1]
- likes = likes.Or(builder.And(builder.Like{"owner_name", strings.ToLower(ownerName)}, builder.Like{"lower_name", strings.ToLower(repoName)}))
- }
-
- if opts.IncludeDescription {
- likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})
- }
- }
- keywordCond = keywordCond.Or(likes)
- }
- cond = cond.And(keywordCond)
- }
-
- if opts.Language != "" {
- cond = cond.And(builder.In("id", builder.
- Select("repo_id").
- From("language_stat").
- Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true})))
- }
-
- if opts.Fork != util.OptionalBoolNone {
- cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})
- }
-
- if opts.Mirror != util.OptionalBoolNone {
- cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue})
- }
-
- if opts.Actor != nil && opts.Actor.IsRestricted {
- cond = cond.And(accessibleRepositoryCondition(opts.Actor))
- }
-
- if opts.Archived != util.OptionalBoolNone {
- cond = cond.And(builder.Eq{"is_archived": opts.Archived == util.OptionalBoolTrue})
- }
-
- switch opts.HasMilestones {
- case util.OptionalBoolTrue:
- cond = cond.And(builder.Gt{"num_milestones": 0})
- case util.OptionalBoolFalse:
- cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))
- }
-
- return cond
-}
-
-// SearchRepository returns repositories based on search options,
-// it returns results in given range and number of total results.
-func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
- cond := SearchRepositoryCondition(opts)
- return SearchRepositoryByCondition(opts, cond, true)
-}
-
-// SearchRepositoryByCondition search repositories by condition
-func SearchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond, loadAttributes bool) (RepositoryList, int64, error) {
- ctx := db.DefaultContext
- sess, count, err := searchRepositoryByCondition(ctx, opts, cond)
- if err != nil {
- return nil, 0, err
- }
-
- defaultSize := 50
- if opts.PageSize > 0 {
- defaultSize = opts.PageSize
- }
- repos := make(RepositoryList, 0, defaultSize)
- if err := sess.Find(&repos); err != nil {
- return nil, 0, fmt.Errorf("Repo: %v", err)
- }
-
- if opts.PageSize <= 0 {
- count = int64(len(repos))
- }
-
- if loadAttributes {
- if err := repos.loadAttributes(ctx); err != nil {
- return nil, 0, fmt.Errorf("LoadAttributes: %v", err)
- }
- }
-
- return repos, count, nil
-}
-
-func searchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, cond builder.Cond) (db.Engine, int64, error) {
- if opts.Page <= 0 {
- opts.Page = 1
- }
-
- if len(opts.OrderBy) == 0 {
- opts.OrderBy = db.SearchOrderByAlphabetically
- }
-
- args := make([]interface{}, 0)
- if opts.PriorityOwnerID > 0 {
- opts.OrderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_id = ? THEN 0 ELSE owner_id END, %s", opts.OrderBy))
- args = append(args, opts.PriorityOwnerID)
- } else if strings.Count(opts.Keyword, "/") == 1 {
- // With "owner/repo" search times, prioritise results which match the owner field
- orgName := strings.Split(opts.Keyword, "/")[0]
- opts.OrderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_name LIKE ? THEN 0 ELSE 1 END, %s", opts.OrderBy))
- args = append(args, orgName)
- }
-
- sess := db.GetEngine(ctx)
-
- var count int64
- if opts.PageSize > 0 {
- var err error
- count, err = sess.
- Where(cond).
- Count(new(repo_model.Repository))
- if err != nil {
- return nil, 0, fmt.Errorf("Count: %v", err)
- }
- }
-
- sess = sess.Where(cond).OrderBy(opts.OrderBy.String(), args...)
- if opts.PageSize > 0 {
- sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
- }
- return sess, count, nil
-}
-
-// accessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
-func accessibleRepositoryCondition(user *user_model.User) builder.Cond {
- cond := builder.NewCond()
-
- if user == nil || !user.IsRestricted || user.ID <= 0 {
- orgVisibilityLimit := []structs.VisibleType{structs.VisibleTypePrivate}
- if user == nil || user.ID <= 0 {
- orgVisibilityLimit = append(orgVisibilityLimit, structs.VisibleTypeLimited)
- }
- // 1. Be able to see all non-private repositories that either:
- cond = cond.Or(builder.And(
- builder.Eq{"`repository`.is_private": false},
- // 2. Aren't in an private organisation or limited organisation if we're not logged in
- builder.NotIn("`repository`.owner_id", builder.Select("id").From("`user`").Where(
- builder.And(
- builder.Eq{"type": user_model.UserTypeOrganization},
- builder.In("visibility", orgVisibilityLimit)),
- ))))
- }
-
- if user != nil {
- cond = cond.Or(
- // 2. Be able to see all repositories that we have access to
- userCollaborationRepoCond("`repository`.id", user.ID),
- // 3. Repositories that we directly own
- builder.Eq{"`repository`.owner_id": user.ID},
- // 4. Be able to see all repositories that we are in a team
- userOrgTeamRepoCond("`repository`.id", user.ID),
- // 5. Be able to see all public repos in private organizations that we are an org_user of
- userOrgPublicRepoCond(user.ID),
- )
- }
-
- return cond
-}
-
-// SearchRepositoryByName takes keyword and part of repository name to search,
-// it returns results in given range and number of total results.
-func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, error) {
- opts.IncludeDescription = false
- return SearchRepository(opts)
-}
-
-// SearchRepositoryIDs takes keyword and part of repository name to search,
-// it returns results in given range and number of total results.
-func SearchRepositoryIDs(opts *SearchRepoOptions) ([]int64, int64, error) {
- opts.IncludeDescription = false
-
- cond := SearchRepositoryCondition(opts)
-
- sess, count, err := searchRepositoryByCondition(db.DefaultContext, opts, cond)
- if err != nil {
- return nil, 0, err
- }
-
- defaultSize := 50
- if opts.PageSize > 0 {
- defaultSize = opts.PageSize
- }
-
- ids := make([]int64, 0, defaultSize)
- err = sess.Select("id").Table("repository").Find(&ids)
- if opts.PageSize <= 0 {
- count = int64(len(ids))
- }
-
- return ids, count, err
-}
-
-// AccessibleRepoIDsQuery queries accessible repository ids. Usable as a subquery wherever repo ids need to be filtered.
-func AccessibleRepoIDsQuery(user *user_model.User) *builder.Builder {
- // NB: Please note this code needs to still work if user is nil
- return builder.Select("id").From("repository").Where(accessibleRepositoryCondition(user))
-}
-
-// FindUserAccessibleRepoIDs find all accessible repositories' ID by user's id
-func FindUserAccessibleRepoIDs(user *user_model.User) ([]int64, error) {
- repoIDs := make([]int64, 0, 10)
- if err := db.GetEngine(db.DefaultContext).
- Table("repository").
- Cols("id").
- Where(accessibleRepositoryCondition(user)).
- Find(&repoIDs); err != nil {
- return nil, fmt.Errorf("FindUserAccesibleRepoIDs: %v", err)
- }
- return repoIDs, nil
-}
-
-// GetUserRepositories returns a list of repositories of given user.
-func GetUserRepositories(opts *SearchRepoOptions) (RepositoryList, int64, error) {
- if len(opts.OrderBy) == 0 {
- opts.OrderBy = "updated_unix DESC"
- }
-
- cond := builder.NewCond()
- cond = cond.And(builder.Eq{"owner_id": opts.Actor.ID})
- if !opts.Private {
- cond = cond.And(builder.Eq{"is_private": false})
- }
-
- if opts.LowerNames != nil && len(opts.LowerNames) > 0 {
- cond = cond.And(builder.In("lower_name", opts.LowerNames))
- }
-
- sess := db.GetEngine(db.DefaultContext)
-
- count, err := sess.Where(cond).Count(new(repo_model.Repository))
- if err != nil {
- return nil, 0, fmt.Errorf("Count: %v", err)
- }
-
- sess = sess.Where(cond).OrderBy(opts.OrderBy.String())
- repos := make(RepositoryList, 0, opts.PageSize)
- return repos, count, db.SetSessionPagination(sess, opts).Find(&repos)
-}
diff --git a/models/repo_test.go b/models/repo_test.go
index dd1673f6bf..c9e66398d1 100644
--- a/models/repo_test.go
+++ b/models/repo_test.go
@@ -84,127 +84,8 @@ func TestMetas(t *testing.T) {
assert.Equal(t, ",owners,team1,", metas["teams"])
}
-func TestUpdateRepositoryVisibilityChanged(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- // Get sample repo and change visibility
- repo, err := repo_model.GetRepositoryByID(9)
- assert.NoError(t, err)
- repo.IsPrivate = true
-
- // Update it
- err = UpdateRepository(repo, true)
- assert.NoError(t, err)
-
- // Check visibility of action has become private
- act := Action{}
- _, err = db.GetEngine(db.DefaultContext).ID(3).Get(&act)
-
- assert.NoError(t, err)
- assert.True(t, act.IsPrivate)
-}
-
func TestDoctorUserStarNum(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
assert.NoError(t, DoctorUserStarNum())
}
-
-func TestRepoGetReviewers(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- // test public repo
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
-
- reviewers, err := GetReviewers(repo1, 2, 2)
- assert.NoError(t, err)
- assert.Len(t, reviewers, 4)
-
- // should include doer if doer is not PR poster.
- reviewers, err = GetReviewers(repo1, 11, 2)
- assert.NoError(t, err)
- assert.Len(t, reviewers, 4)
-
- // should not include PR poster, if PR poster would be otherwise eligible
- reviewers, err = GetReviewers(repo1, 11, 4)
- assert.NoError(t, err)
- assert.Len(t, reviewers, 3)
-
- // test private user repo
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
-
- reviewers, err = GetReviewers(repo2, 2, 4)
- assert.NoError(t, err)
- assert.Len(t, reviewers, 1)
- assert.EqualValues(t, reviewers[0].ID, 2)
-
- // test private org repo
- repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
-
- reviewers, err = GetReviewers(repo3, 2, 1)
- assert.NoError(t, err)
- assert.Len(t, reviewers, 2)
-
- reviewers, err = GetReviewers(repo3, 2, 2)
- assert.NoError(t, err)
- assert.Len(t, reviewers, 1)
-}
-
-func TestRepoGetReviewerTeams(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
- teams, err := GetReviewerTeams(repo2)
- assert.NoError(t, err)
- assert.Empty(t, teams)
-
- repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
- teams, err = GetReviewerTeams(repo3)
- assert.NoError(t, err)
- assert.Len(t, teams, 2)
-}
-
-func TestLinkedRepository(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
- testCases := []struct {
- name string
- attachID int64
- expectedRepo *repo_model.Repository
- expectedUnitType unit.Type
- }{
- {"LinkedIssue", 1, &repo_model.Repository{ID: 1}, unit.TypeIssues},
- {"LinkedComment", 3, &repo_model.Repository{ID: 1}, unit.TypePullRequests},
- {"LinkedRelease", 9, &repo_model.Repository{ID: 1}, unit.TypeReleases},
- {"Notlinked", 10, nil, -1},
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- attach, err := repo_model.GetAttachmentByID(db.DefaultContext, tc.attachID)
- assert.NoError(t, err)
- repo, unitType, err := LinkedRepository(attach)
- assert.NoError(t, err)
- if tc.expectedRepo != nil {
- assert.Equal(t, tc.expectedRepo.ID, repo.ID)
- }
- assert.Equal(t, tc.expectedUnitType, unitType)
- })
- }
-}
-
-func TestRepoAssignees(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
- users, err := GetRepoAssignees(repo2)
- assert.NoError(t, err)
- assert.Len(t, users, 1)
- assert.Equal(t, users[0].ID, int64(2))
-
- repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 21}).(*repo_model.Repository)
- users, err = GetRepoAssignees(repo21)
- assert.NoError(t, err)
- assert.Len(t, users, 3)
- assert.Equal(t, users[0].ID, int64(15))
- assert.Equal(t, users[1].ID, int64(18))
- assert.Equal(t, users[2].ID, int64(16))
-}
diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go
index 5eea977725..1b79a414ad 100644
--- a/models/webhook/webhook.go
+++ b/models/webhook/webhook.go
@@ -397,6 +397,14 @@ func CreateWebhook(ctx context.Context, w *Webhook) error {
return db.Insert(ctx, w)
}
+// CreateWebhooks creates multiple web hooks
+func CreateWebhooks(ctx context.Context, ws []*Webhook) error {
+ for i := 0; i < len(ws); i++ {
+ ws[i].Type = strings.TrimSpace(ws[i].Type)
+ }
+ return db.Insert(ctx, ws)
+}
+
// getWebhook uses argument bean as query condition,
// ID must be specified and do not assign unnecessary fields.
func getWebhook(bean *Webhook) (*Webhook, error) {
diff --git a/modules/context/repo.go b/modules/context/repo.go
index df3fe4e74d..5f4af114ff 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -26,6 +26,7 @@ import (
code_indexer "code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup/markdown"
+ repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
@@ -551,14 +552,14 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues)
ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(unit_model.TypePullRequests)
- canSignedUserFork, err := models.CanUserForkRepo(ctx.Doer, ctx.Repo.Repository)
+ canSignedUserFork, err := repo_module.CanUserForkRepo(ctx.Doer, ctx.Repo.Repository)
if err != nil {
ctx.ServerError("CanUserForkRepo", err)
return
}
ctx.Data["CanSignedUserFork"] = canSignedUserFork
- userAndOrgForks, err := models.GetForksByUserAndOrgs(ctx, ctx.Doer, ctx.Repo.Repository)
+ userAndOrgForks, err := repo_model.GetForksByUserAndOrgs(ctx, ctx.Doer, ctx.Repo.Repository)
if err != nil {
ctx.ServerError("GetForksByUserAndOrgs", err)
return
diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go
index 7adc938dcc..85de4c75b3 100644
--- a/modules/indexer/issues/indexer.go
+++ b/modules/indexer/issues/indexer.go
@@ -291,8 +291,8 @@ func populateIssueIndexer(ctx context.Context) {
return
default:
}
- repos, _, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
- ListOptions: db.ListOptions{Page: page, PageSize: models.RepositoryListDefaultPageSize},
+ repos, _, err := repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
+ ListOptions: db.ListOptions{Page: page, PageSize: repo_model.RepositoryListDefaultPageSize},
OrderBy: db.SearchOrderByID,
Private: true,
Collaborate: util.OptionalBoolFalse,
diff --git a/modules/repository/create.go b/modules/repository/create.go
index 21d45c896e..95bb825403 100644
--- a/modules/repository/create.go
+++ b/modules/repository/create.go
@@ -7,15 +7,20 @@ package repository
import (
"context"
"fmt"
+ "os"
+ "path"
"strings"
+ "unicode/utf8"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
+ access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
)
@@ -108,7 +113,7 @@ func CreateRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (
}
}
- if err := models.CheckDaemonExportOK(ctx, repo); err != nil {
+ if err := CheckDaemonExportOK(ctx, repo); err != nil {
return fmt.Errorf("checkDaemonExportOK: %v", err)
}
@@ -133,3 +138,111 @@ func CreateRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (
return repo, nil
}
+
+// UpdateRepoSize updates the repository size, calculating it using util.GetDirectorySize
+func UpdateRepoSize(ctx context.Context, repo *repo_model.Repository) error {
+ size, err := util.GetDirectorySize(repo.RepoPath())
+ if err != nil {
+ return fmt.Errorf("updateSize: %v", err)
+ }
+
+ lfsSize, err := models.GetRepoLFSSize(ctx, repo.ID)
+ if err != nil {
+ return fmt.Errorf("updateSize: GetLFSMetaObjects: %v", err)
+ }
+
+ return repo_model.UpdateRepoSize(ctx, repo.ID, size+lfsSize)
+}
+
+// CheckDaemonExportOK creates/removes git-daemon-export-ok for git-daemon...
+func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error {
+ if err := repo.GetOwner(ctx); err != nil {
+ return err
+ }
+
+ // Create/Remove git-daemon-export-ok for git-daemon...
+ daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
+
+ isExist, err := util.IsExist(daemonExportFile)
+ if err != nil {
+ log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err)
+ return err
+ }
+
+ isPublic := !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePublic
+ if !isPublic && isExist {
+ if err = util.Remove(daemonExportFile); err != nil {
+ log.Error("Failed to remove %s: %v", daemonExportFile, err)
+ }
+ } else if isPublic && !isExist {
+ if f, err := os.Create(daemonExportFile); err != nil {
+ log.Error("Failed to create %s: %v", daemonExportFile, err)
+ } else {
+ f.Close()
+ }
+ }
+
+ return nil
+}
+
+// UpdateRepository updates a repository with db context
+func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) {
+ repo.LowerName = strings.ToLower(repo.Name)
+
+ if utf8.RuneCountInString(repo.Description) > 255 {
+ repo.Description = string([]rune(repo.Description)[:255])
+ }
+ if utf8.RuneCountInString(repo.Website) > 255 {
+ repo.Website = string([]rune(repo.Website)[:255])
+ }
+
+ e := db.GetEngine(ctx)
+
+ if _, err = e.ID(repo.ID).AllCols().Update(repo); err != nil {
+ return fmt.Errorf("update: %v", err)
+ }
+
+ if err = UpdateRepoSize(ctx, repo); err != nil {
+ log.Error("Failed to update size for repository: %v", err)
+ }
+
+ if visibilityChanged {
+ if err = repo.GetOwner(ctx); err != nil {
+ return fmt.Errorf("getOwner: %v", err)
+ }
+ if repo.Owner.IsOrganization() {
+ // Organization repository need to recalculate access table when visibility is changed.
+ if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
+ return fmt.Errorf("recalculateTeamAccesses: %v", err)
+ }
+ }
+
+ // If repo has become private, we need to set its actions to private.
+ if repo.IsPrivate {
+ _, err = e.Where("repo_id = ?", repo.ID).Cols("is_private").Update(&models.Action{
+ IsPrivate: true,
+ })
+ if err != nil {
+ return err
+ }
+ }
+
+ // Create/Remove git-daemon-export-ok for git-daemon...
+ if err := CheckDaemonExportOK(db.WithEngine(ctx, e), repo); err != nil {
+ return err
+ }
+
+ forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID)
+ if err != nil {
+ return fmt.Errorf("getRepositoriesByForkID: %v", err)
+ }
+ for i := range forkRepos {
+ forkRepos[i].IsPrivate = repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate
+ if err = UpdateRepository(ctx, forkRepos[i], true); err != nil {
+ return fmt.Errorf("updateRepository[%d]: %v", forkRepos[i].ID, err)
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go
index b6a89a7ed6..2a47e93631 100644
--- a/modules/repository/create_test.go
+++ b/modules/repository/create_test.go
@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/structs"
@@ -147,3 +148,23 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
}
assert.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization")
}
+
+func TestUpdateRepositoryVisibilityChanged(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ // Get sample repo and change visibility
+ repo, err := repo_model.GetRepositoryByID(9)
+ assert.NoError(t, err)
+ repo.IsPrivate = true
+
+ // Update it
+ err = UpdateRepository(db.DefaultContext, repo, true)
+ assert.NoError(t, err)
+
+ // Check visibility of action has become private
+ act := models.Action{}
+ _, err = db.GetEngine(db.DefaultContext).ID(3).Get(&act)
+
+ assert.NoError(t, err)
+ assert.True(t, act.IsPrivate)
+}
diff --git a/modules/repository/delete.go b/modules/repository/delete.go
new file mode 100644
index 0000000000..25fb15e300
--- /dev/null
+++ b/modules/repository/delete.go
@@ -0,0 +1,33 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repository
+
+import (
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/organization"
+ repo_model "code.gitea.io/gitea/models/repo"
+ user_model "code.gitea.io/gitea/models/user"
+)
+
+// CanUserDelete returns true if user could delete the repository
+func CanUserDelete(repo *repo_model.Repository, user *user_model.User) (bool, error) {
+ if user.IsAdmin || user.ID == repo.OwnerID {
+ return true, nil
+ }
+
+ if err := repo.GetOwner(db.DefaultContext); err != nil {
+ return false, err
+ }
+
+ if repo.Owner.IsOrganization() {
+ isOwner, err := organization.OrgFromUser(repo.Owner).IsOwnedBy(user.ID)
+ if err != nil {
+ return false, err
+ }
+ return isOwner, nil
+ }
+
+ return false, nil
+}
diff --git a/modules/repository/fork.go b/modules/repository/fork.go
new file mode 100644
index 0000000000..c967d3b741
--- /dev/null
+++ b/modules/repository/fork.go
@@ -0,0 +1,31 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repository
+
+import (
+ "code.gitea.io/gitea/models/organization"
+ repo_model "code.gitea.io/gitea/models/repo"
+ user_model "code.gitea.io/gitea/models/user"
+)
+
+// CanUserForkRepo returns true if specified user can fork repository.
+func CanUserForkRepo(user *user_model.User, repo *repo_model.Repository) (bool, error) {
+ if user == nil {
+ return false, nil
+ }
+ if repo.OwnerID != user.ID && !repo_model.HasForkedRepo(user.ID, repo.ID) {
+ return true, nil
+ }
+ ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(user.ID)
+ if err != nil {
+ return false, err
+ }
+ for _, org := range ownedOrgs {
+ if repo.OwnerID != org.ID && !repo_model.HasForkedRepo(org.ID, repo.ID) {
+ return true, nil
+ }
+ }
+ return false, nil
+}
diff --git a/modules/repository/generate.go b/modules/repository/generate.go
index b3ce809173..94bb6e6429 100644
--- a/modules/repository/generate.go
+++ b/modules/repository/generate.go
@@ -5,6 +5,8 @@
package repository
import (
+ "bufio"
+ "bytes"
"context"
"fmt"
"os"
@@ -20,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
+ "github.com/gobwas/glob"
"github.com/huandu/xstrings"
)
@@ -78,7 +81,38 @@ func generateExpansion(src string, templateRepo, generateRepo *repo_model.Reposi
})
}
-func checkGiteaTemplate(tmpDir string) (*models.GiteaTemplate, error) {
+// GiteaTemplate holds information about a .gitea/template file
+type GiteaTemplate struct {
+ Path string
+ Content []byte
+
+ globs []glob.Glob
+}
+
+// Globs parses the .gitea/template globs or returns them if they were already parsed
+func (gt GiteaTemplate) Globs() []glob.Glob {
+ if gt.globs != nil {
+ return gt.globs
+ }
+
+ gt.globs = make([]glob.Glob, 0)
+ scanner := bufio.NewScanner(bytes.NewReader(gt.Content))
+ for scanner.Scan() {
+ line := strings.TrimSpace(scanner.Text())
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+ g, err := glob.Compile(line, '/')
+ if err != nil {
+ log.Info("Invalid glob expression '%s' (skipped): %v", line, err)
+ continue
+ }
+ gt.globs = append(gt.globs, g)
+ }
+ return gt.globs
+}
+
+func checkGiteaTemplate(tmpDir string) (*GiteaTemplate, error) {
gtPath := filepath.Join(tmpDir, ".gitea", "template")
if _, err := os.Stat(gtPath); os.IsNotExist(err) {
return nil, nil
@@ -91,7 +125,7 @@ func checkGiteaTemplate(tmpDir string) (*models.GiteaTemplate, error) {
return nil, err
}
- gt := &models.GiteaTemplate{
+ gt := &GiteaTemplate{
Path: gtPath,
Content: content,
}
@@ -227,7 +261,7 @@ func generateGitContent(ctx context.Context, repo, templateRepo, generateRepo *r
if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
return fmt.Errorf("setDefaultBranch: %v", err)
}
- if err = models.UpdateRepositoryCtx(ctx, repo, false); err != nil {
+ if err = UpdateRepository(ctx, repo, false); err != nil {
return fmt.Errorf("updateRepository: %v", err)
}
@@ -240,7 +274,7 @@ func GenerateGitContent(ctx context.Context, templateRepo, generateRepo *repo_mo
return err
}
- if err := models.UpdateRepoSize(ctx, generateRepo); err != nil {
+ if err := UpdateRepoSize(ctx, generateRepo); err != nil {
return fmt.Errorf("failed to update size for repository: %v", err)
}
@@ -250,8 +284,27 @@ func GenerateGitContent(ctx context.Context, templateRepo, generateRepo *repo_mo
return nil
}
+// GenerateRepoOptions contains the template units to generate
+type GenerateRepoOptions struct {
+ Name string
+ DefaultBranch string
+ Description string
+ Private bool
+ GitContent bool
+ Topics bool
+ GitHooks bool
+ Webhooks bool
+ Avatar bool
+ IssueLabels bool
+}
+
+// IsValid checks whether at least one option is chosen for generation
+func (gro GenerateRepoOptions) IsValid() bool {
+ return gro.GitContent || gro.Topics || gro.GitHooks || gro.Webhooks || gro.Avatar || gro.IssueLabels // or other items as they are added
+}
+
// GenerateRepository generates a repository from a template
-func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *repo_model.Repository, opts models.GenerateRepoOptions) (_ *repo_model.Repository, err error) {
+func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *repo_model.Repository, opts GenerateRepoOptions) (_ *repo_model.Repository, err error) {
generateRepo := &repo_model.Repository{
OwnerID: owner.ID,
Owner: owner,
@@ -288,7 +341,7 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ
return generateRepo, err
}
- if err = models.CheckDaemonExportOK(ctx, generateRepo); err != nil {
+ if err = CheckDaemonExportOK(ctx, generateRepo); err != nil {
return generateRepo, fmt.Errorf("checkDaemonExportOK: %v", err)
}
diff --git a/models/repo_generate_test.go b/modules/repository/generate_test.go
index e7a93433a7..139fa4c918 100644
--- a/models/repo_generate_test.go
+++ b/modules/repository/generate_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package repository
import (
"testing"
diff --git a/modules/repository/init.go b/modules/repository/init.go
index 845a61ed0a..f8c7a89552 100644
--- a/modules/repository/init.go
+++ b/modules/repository/init.go
@@ -444,7 +444,7 @@ func initRepository(ctx context.Context, repoPath string, u *user_model.User, re
}
}
- if err = models.UpdateRepositoryCtx(ctx, repo, false); err != nil {
+ if err = UpdateRepository(ctx, repo, false); err != nil {
return fmt.Errorf("updateRepository: %v", err)
}
diff --git a/modules/repository/repo.go b/modules/repository/repo.go
index 30ca6fdff8..281999a1eb 100644
--- a/modules/repository/repo.go
+++ b/modules/repository/repo.go
@@ -116,7 +116,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
repo.Owner = u
}
- if err = models.CheckDaemonExportOK(ctx, repo); err != nil {
+ if err = CheckDaemonExportOK(ctx, repo); err != nil {
return repo, fmt.Errorf("checkDaemonExportOK: %v", err)
}
@@ -168,9 +168,11 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
}
}
- if err = models.UpdateRepoSize(ctx, repo); err != nil {
- log.Error("Failed to update size for repository: %v", err)
+ ctx, committer, err := db.TxContext()
+ if err != nil {
+ return nil, err
}
+ defer committer.Close()
if opts.Mirror {
mirrorModel := repo_model.Mirror{
@@ -203,17 +205,24 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
}
}
- if err = repo_model.InsertMirror(&mirrorModel); err != nil {
+ if err = repo_model.InsertMirror(ctx, &mirrorModel); err != nil {
return repo, fmt.Errorf("InsertOne: %v", err)
}
repo.IsMirror = true
- err = models.UpdateRepository(repo, false)
+ if err = UpdateRepository(ctx, repo, false); err != nil {
+ return nil, err
+ }
} else {
- repo, err = CleanUpMigrateInfo(ctx, repo)
+ if err = UpdateRepoSize(ctx, repo); err != nil {
+ log.Error("Failed to update size for repository: %v", err)
+ }
+ if repo, err = CleanUpMigrateInfo(ctx, repo); err != nil {
+ return nil, err
+ }
}
- return repo, err
+ return repo, committer.Commit()
}
// cleanUpMigrateGitConfig removes mirror info which prevents "push --all".
@@ -253,7 +262,7 @@ func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo
}
}
- return repo, models.UpdateRepository(repo, false)
+ return repo, UpdateRepository(ctx, repo, false)
}
// SyncReleasesWithTags synchronizes release table with repository tags
diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go
index 248497a561..aa425e5828 100644
--- a/routers/api/v1/repo/collaborators.go
+++ b/routers/api/v1/repo/collaborators.go
@@ -312,7 +312,7 @@ func GetReviewers(ctx *context.APIContext) {
// "200":
// "$ref": "#/responses/UserList"
- reviewers, err := models.GetReviewers(ctx.Repo.Repository, ctx.Doer.ID, 0)
+ reviewers, err := repo_model.GetReviewers(ctx, ctx.Repo.Repository, ctx.Doer.ID, 0)
if err != nil {
ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
return
@@ -342,7 +342,7 @@ func GetAssignees(ctx *context.APIContext) {
// "200":
// "$ref": "#/responses/UserList"
- assignees, err := models.GetRepoAssignees(ctx.Repo.Repository)
+ assignees, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
if err != nil {
ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
return
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 62959c3a76..c394ad1756 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -17,6 +17,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
@@ -130,7 +131,7 @@ func SearchIssues(ctx *context.APIContext) {
}
// find repos user can access (for issue search)
- opts := &models.SearchRepoOptions{
+ opts := &repo_model.SearchRepoOptions{
Private: false,
AllPublic: true,
TopicOnly: false,
@@ -176,8 +177,8 @@ func SearchIssues(ctx *context.APIContext) {
opts.TeamID = team.ID
}
- repoCond := models.SearchRepositoryCondition(opts)
- repoIDs, _, err := models.SearchRepositoryIDs(opts)
+ repoCond := repo_model.SearchRepositoryCondition(opts)
+ repoIDs, _, err := repo_model.SearchRepositoryIDs(opts)
if err != nil {
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err)
return
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 8485ffbac2..cdd1f7d5c4 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -123,7 +123,7 @@ func Search(ctx *context.APIContext) {
// "422":
// "$ref": "#/responses/validationError"
- opts := &models.SearchRepoOptions{
+ opts := &repo_model.SearchRepoOptions{
ListOptions: utils.GetListOptions(ctx),
Actor: ctx.Doer,
Keyword: ctx.FormTrim("q"),
@@ -192,7 +192,7 @@ func Search(ctx *context.APIContext) {
}
var err error
- repos, count, err := models.SearchRepository(opts)
+ repos, count, err := repo_model.SearchRepository(opts)
if err != nil {
ctx.JSON(http.StatusInternalServerError, api.SearchError{
OK: false,
@@ -344,7 +344,7 @@ func Generate(ctx *context.APIContext) {
return
}
- opts := models.GenerateRepoOptions{
+ opts := repo_module.GenerateRepoOptions{
Name: form.Name,
DefaultBranch: form.DefaultBranch,
Description: form.Description,
@@ -717,7 +717,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
repo.DefaultBranch = *opts.DefaultBranch
}
- if err := models.UpdateRepository(repo, visibilityChanged); err != nil {
+ if err := repo_service.UpdateRepository(repo, visibilityChanged); err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
return err
}
@@ -1036,7 +1036,7 @@ func Delete(ctx *context.APIContext) {
owner := ctx.Repo.Owner
repo := ctx.Repo.Repository
- canDelete, err := models.CanUserDelete(repo, ctx.Doer)
+ canDelete, err := repo_module.CanUserDelete(repo, ctx.Doer)
if err != nil {
ctx.Error(http.StatusInternalServerError, "CanUserDelete", err)
return
diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go
index 05cecf508b..709e3a6c54 100644
--- a/routers/api/v1/user/repo.go
+++ b/routers/api/v1/user/repo.go
@@ -7,9 +7,9 @@ package user
import (
"net/http"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
+ repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
@@ -21,7 +21,7 @@ import (
func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) {
opts := utils.GetListOptions(ctx)
- repos, count, err := models.GetUserRepositories(&models.SearchRepoOptions{
+ repos, count, err := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{
Actor: u,
Private: private,
ListOptions: opts,
@@ -103,7 +103,7 @@ func ListMyRepos(ctx *context.APIContext) {
// "200":
// "$ref": "#/responses/RepositoryList"
- opts := &models.SearchRepoOptions{
+ opts := &repo_model.SearchRepoOptions{
ListOptions: utils.GetListOptions(ctx),
Actor: ctx.Doer,
OwnerID: ctx.Doer.ID,
@@ -112,7 +112,7 @@ func ListMyRepos(ctx *context.APIContext) {
}
var err error
- repos, count, err := models.SearchRepository(opts)
+ repos, count, err := repo_model.SearchRepository(opts)
if err != nil {
ctx.Error(http.StatusInternalServerError, "SearchRepository", err)
return
diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go
index 41ca27782f..3fba2be37d 100644
--- a/routers/web/explore/code.go
+++ b/routers/web/explore/code.go
@@ -55,7 +55,7 @@ func Code(ctx *context.Context) {
// guest user or non-admin user
if ctx.Doer == nil || !isAdmin {
- repoIDs, err = models.FindUserAccessibleRepoIDs(ctx.Doer)
+ repoIDs, err = repo_model.FindUserAccessibleRepoIDs(ctx.Doer)
if err != nil {
ctx.ServerError("SearchResults", err)
return
@@ -79,7 +79,7 @@ func Code(ctx *context.Context) {
rightRepoMap := make(map[int64]*repo_model.Repository, len(repoMaps))
repoIDs = make([]int64, 0, len(repoMaps))
for id, repo := range repoMaps {
- if models.CheckRepoUnitUser(repo, ctx.Doer, unit.TypeCode) {
+ if models.CheckRepoUnitUser(ctx, repo, ctx.Doer, unit.TypeCode) {
rightRepoMap[id] = repo
repoIDs = append(repoIDs, id)
}
diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go
index 3e8aa2bb0f..f64642bc95 100644
--- a/routers/web/explore/repo.go
+++ b/routers/web/explore/repo.go
@@ -7,7 +7,6 @@ package explore
import (
"net/http"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
@@ -81,7 +80,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
language := ctx.FormTrim("language")
ctx.Data["Language"] = language
- repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: page,
PageSize: opts.PageSize,
diff --git a/routers/web/org/home.go b/routers/web/org/home.go
index 24a0f13b54..d565a0c242 100644
--- a/routers/web/org/home.go
+++ b/routers/web/org/home.go
@@ -8,7 +8,6 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
@@ -105,7 +104,7 @@ func Home(ctx *context.Context) {
count int64
err error
)
- repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: setting.UI.User.RepoPagingNum,
Page: page,
diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go
index 758ca47af6..c22a124e74 100644
--- a/routers/web/org/setting.go
+++ b/routers/web/org/setting.go
@@ -24,6 +24,7 @@ import (
user_setting "code.gitea.io/gitea/routers/web/user/setting"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/org"
+ repo_service "code.gitea.io/gitea/services/repository"
user_service "code.gitea.io/gitea/services/user"
)
@@ -117,7 +118,7 @@ func SettingsPost(ctx *context.Context) {
// update forks visibility
if visibilityChanged {
- repos, _, err := models.GetUserRepositories(&models.SearchRepoOptions{
+ repos, _, err := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{
Actor: org.AsUser(), Private: true, ListOptions: db.ListOptions{Page: 1, PageSize: org.NumRepos},
})
if err != nil {
@@ -126,7 +127,7 @@ func SettingsPost(ctx *context.Context) {
}
for _, repo := range repos {
repo.OwnerName = org.Name
- if err := models.UpdateRepository(repo, true); err != nil {
+ if err := repo_service.UpdateRepository(repo, true); err != nil {
ctx.ServerError("UpdateRepository", err)
return
}
diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go
index 701236f838..190dc6c2c7 100644
--- a/routers/web/repo/attachment.go
+++ b/routers/web/repo/attachment.go
@@ -8,7 +8,6 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/context"
@@ -19,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/attachment"
+ repo_service "code.gitea.io/gitea/services/repository"
)
// UploadIssueAttachment response for Issue/PR attachments
@@ -95,7 +95,7 @@ func GetAttachment(ctx *context.Context) {
return
}
- repository, unitType, err := models.LinkedRepository(attach)
+ repository, unitType, err := repo_service.LinkedRepository(attach)
if err != nil {
ctx.ServerError("LinkedRepository", err)
return
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index 9f94489235..d3653f04e9 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -457,7 +457,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
if rootRepo != nil &&
rootRepo.ID != ci.HeadRepo.ID &&
rootRepo.ID != baseRepo.ID {
- canRead := models.CheckRepoUnitUser(rootRepo, ctx.Doer, unit.TypeCode)
+ canRead := models.CheckRepoUnitUser(ctx, rootRepo, ctx.Doer, unit.TypeCode)
if canRead {
ctx.Data["RootRepo"] = rootRepo
if !fileOnly {
@@ -482,7 +482,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
ownForkRepo.ID != ci.HeadRepo.ID &&
ownForkRepo.ID != baseRepo.ID &&
(rootRepo == nil || ownForkRepo.ID != rootRepo.ID) {
- canRead := models.CheckRepoUnitUser(ownForkRepo, ctx.Doer, unit.TypeCode)
+ canRead := models.CheckRepoUnitUser(ctx, ownForkRepo, ctx.Doer, unit.TypeCode)
if canRead {
ctx.Data["OwnForkRepo"] = ownForkRepo
if !fileOnly {
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index d418907a1f..4a732ba454 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -49,6 +49,7 @@ import (
"code.gitea.io/gitea/services/forms"
issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"
+ repo_service "code.gitea.io/gitea/services/repository"
)
const (
@@ -283,7 +284,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
ctx.Data["CommitStatuses"] = commitStatuses
// Get assignees.
- ctx.Data["Assignees"], err = models.GetRepoAssignees(repo)
+ ctx.Data["Assignees"], err = repo_model.GetRepoAssignees(ctx, repo)
if err != nil {
ctx.ServerError("GetAssignees", err)
return
@@ -441,7 +442,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R
return
}
- ctx.Data["Assignees"], err = models.GetRepoAssignees(repo)
+ ctx.Data["Assignees"], err = repo_model.GetRepoAssignees(ctx, repo)
if err != nil {
ctx.ServerError("GetAssignees", err)
return
@@ -522,13 +523,13 @@ func RetrieveRepoReviewers(ctx *context.Context, repo *repo_model.Repository, is
posterID = 0
}
- reviewers, err = models.GetReviewers(repo, ctx.Doer.ID, posterID)
+ reviewers, err = repo_model.GetReviewers(ctx, repo, ctx.Doer.ID, posterID)
if err != nil {
ctx.ServerError("GetReviewers", err)
return
}
- teamReviewers, err = models.GetReviewerTeams(repo)
+ teamReviewers, err = repo_service.GetReviewerTeams(repo)
if err != nil {
ctx.ServerError("GetReviewerTeams", err)
return
@@ -2160,7 +2161,7 @@ func SearchIssues(ctx *context.Context) {
}
// find repos user can access (for issue search)
- opts := &models.SearchRepoOptions{
+ opts := &repo_model.SearchRepoOptions{
Private: false,
AllPublic: true,
TopicOnly: false,
@@ -2206,8 +2207,8 @@ func SearchIssues(ctx *context.Context) {
opts.TeamID = team.ID
}
- repoCond := models.SearchRepositoryCondition(opts)
- repoIDs, _, err := models.SearchRepositoryIDs(opts)
+ repoCond := repo_model.SearchRepositoryCondition(opts)
+ repoIDs, _, err := repo_model.SearchRepositoryIDs(opts)
if err != nil {
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err.Error())
return
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 3f24be33d6..8df4ccc607 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -758,7 +758,7 @@ func ViewPullFiles(ctx *context.Context) {
setCompareContext(ctx, baseCommit, commit, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
ctx.Data["RequireTribute"] = true
- if ctx.Data["Assignees"], err = models.GetRepoAssignees(ctx.Repo.Repository); err != nil {
+ if ctx.Data["Assignees"], err = repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository); err != nil {
ctx.ServerError("GetAssignees", err)
return
}
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index 30cb888dce..c2c79e4a0d 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -152,7 +152,7 @@ func Create(ctx *context.Context) {
templateID := ctx.FormInt64("template_id")
if templateID > 0 {
templateRepo, err := repo_model.GetRepositoryByID(templateID)
- if err == nil && models.CheckRepoUnitUser(templateRepo, ctxUser, unit.TypeCode) {
+ if err == nil && models.CheckRepoUnitUser(ctx, templateRepo, ctxUser, unit.TypeCode) {
ctx.Data["repo_template"] = templateID
ctx.Data["repo_template_name"] = templateRepo.Name
}
@@ -223,7 +223,7 @@ func CreatePost(ctx *context.Context) {
var repo *repo_model.Repository
var err error
if form.RepoTemplate > 0 {
- opts := models.GenerateRepoOptions{
+ opts := repo_module.GenerateRepoOptions{
Name: form.RepoName,
Description: form.Description,
Private: form.Private,
@@ -304,7 +304,7 @@ func Action(ctx *context.Context) {
ctx.Repo.Repository.Description = ctx.FormString("desc")
ctx.Repo.Repository.Website = ctx.FormString("site")
- err = models.UpdateRepository(ctx.Repo.Repository, false)
+ err = repo_service.UpdateRepository(ctx.Repo.Repository, false)
}
if err != nil {
@@ -509,7 +509,7 @@ func InitiateDownload(ctx *context.Context) {
// SearchRepo repositories via options
func SearchRepo(ctx *context.Context) {
- opts := &models.SearchRepoOptions{
+ opts := &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: ctx.FormInt("page"),
PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
@@ -581,7 +581,7 @@ func SearchRepo(ctx *context.Context) {
}
var err error
- repos, count, err := models.SearchRepository(opts)
+ repos, count, err := repo_model.SearchRepository(opts)
if err != nil {
ctx.JSON(http.StatusInternalServerError, api.SearchError{
OK: false,
diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go
index a60bf52622..b7be0aa3f5 100644
--- a/routers/web/repo/setting.go
+++ b/routers/web/repo/setting.go
@@ -168,7 +168,7 @@ func SettingsPost(ctx *context.Context) {
}
repo.IsPrivate = form.Private
- if err := models.UpdateRepository(repo, visibilityChanged); err != nil {
+ if err := repo_service.UpdateRepository(repo, visibilityChanged); err != nil {
ctx.ServerError("UpdateRepository", err)
return
}
@@ -491,7 +491,7 @@ func SettingsPost(ctx *context.Context) {
return
}
if repoChanged {
- if err := models.UpdateRepository(repo, false); err != nil {
+ if err := repo_service.UpdateRepository(repo, false); err != nil {
ctx.ServerError("UpdateRepository", err)
return
}
@@ -510,7 +510,7 @@ func SettingsPost(ctx *context.Context) {
}
if changed {
- if err := models.UpdateRepository(repo, false); err != nil {
+ if err := repo_service.UpdateRepository(repo, false); err != nil {
ctx.ServerError("UpdateRepository", err)
return
}
@@ -530,7 +530,7 @@ func SettingsPost(ctx *context.Context) {
repo.IsFsckEnabled = form.EnableHealthCheck
}
- if err := models.UpdateRepository(repo, false); err != nil {
+ if err := repo_service.UpdateRepository(repo, false); err != nil {
ctx.ServerError("UpdateRepository", err)
return
}
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 95ca81c442..2c9e16de5b 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -34,6 +34,7 @@ import (
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
+ repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/typesniffer"
@@ -905,7 +906,7 @@ func renderCode(ctx *context.Context) {
ctx.ServerError("UpdateRepositoryCols", err)
return
}
- if err = models.UpdateRepoSize(ctx, ctx.Repo.Repository); err != nil {
+ if err = repo_module.UpdateRepoSize(ctx, ctx.Repo.Repository); err != nil {
ctx.ServerError("UpdateRepoSize", err)
return
}
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index 1412f6cfef..9b4fc652f1 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -166,7 +166,7 @@ func Milestones(ctx *context.Context) {
return
}
- repoOpts := models.SearchRepoOptions{
+ repoOpts := repo_model.SearchRepoOptions{
Actor: ctxUser,
OwnerID: ctxUser.ID,
Private: true,
@@ -181,7 +181,7 @@ func Milestones(ctx *context.Context) {
}
var (
- userRepoCond = models.SearchRepositoryCondition(&repoOpts) // all repo condition user could visit
+ userRepoCond = repo_model.SearchRepositoryCondition(&repoOpts) // all repo condition user could visit
repoCond = userRepoCond
repoIDs []int64
@@ -234,7 +234,7 @@ func Milestones(ctx *context.Context) {
return
}
- showRepos, _, err := models.SearchRepositoryByCondition(&repoOpts, userRepoCond, false)
+ showRepos, _, err := repo_model.SearchRepositoryByCondition(&repoOpts, userRepoCond, false)
if err != nil {
ctx.ServerError("SearchRepositoryByCondition", err)
return
@@ -437,7 +437,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// As team:
// - Team org's owns the repository.
// - Team has read permission to repository.
- repoOpts := &models.SearchRepoOptions{
+ repoOpts := &repo_model.SearchRepoOptions{
Actor: ctx.Doer,
OwnerID: ctx.Doer.ID,
Private: true,
@@ -559,7 +559,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
}
// a RepositoryList
- showRepos := models.RepositoryListOfMap(showReposMap)
+ showRepos := repo_model.RepositoryListOfMap(showReposMap)
sort.Sort(showRepos)
// maps pull request IDs to their CommitStatus. Will be posted to ctx.Data.
diff --git a/routers/web/user/home_test.go b/routers/web/user/home_test.go
index bf78e00ada..9ad0711dc0 100644
--- a/routers/web/user/home_test.go
+++ b/routers/web/user/home_test.go
@@ -8,7 +8,7 @@ import (
"net/http"
"testing"
- "code.gitea.io/gitea/models"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
@@ -26,7 +26,7 @@ func TestArchivedIssues(t *testing.T) {
ctx.Req.Form.Set("state", "open")
// Assume: User 30 has access to two Repos with Issues, one of the Repos being archived.
- repos, _, _ := models.GetUserRepositories(&models.SearchRepoOptions{Actor: ctx.Doer})
+ repos, _, _ := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{Actor: ctx.Doer})
assert.Len(t, repos, 2)
IsArchived := make(map[int64]bool)
NumIssues := make(map[int64]int)
diff --git a/routers/web/user/package.go b/routers/web/user/package.go
index c9aa2471ef..b2b550cb73 100644
--- a/routers/web/user/package.go
+++ b/routers/web/user/package.go
@@ -7,7 +7,6 @@ package user
import (
"net/http"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
container_model "code.gitea.io/gitea/models/packages/container"
@@ -288,7 +287,7 @@ func PackageSettings(ctx *context.Context) {
ctx.Data["ContextUser"] = ctx.ContextUser
ctx.Data["PackageDescriptor"] = pd
- repos, _, _ := models.GetUserRepositories(&models.SearchRepoOptions{
+ repos, _, _ := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{
Actor: pd.Owner,
Private: true,
})
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index 8bce5460cc..44501fc245 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -195,7 +195,7 @@ func Profile(ctx *context.Context) {
}
case "stars":
ctx.Data["PageIsProfileStarList"] = true
- repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: setting.UI.User.RepoPagingNum,
Page: page,
@@ -227,7 +227,7 @@ func Profile(ctx *context.Context) {
return
}
case "watching":
- repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: setting.UI.User.RepoPagingNum,
Page: page,
@@ -249,7 +249,7 @@ func Profile(ctx *context.Context) {
total = int(count)
default:
- repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
+ repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: setting.UI.User.RepoPagingNum,
Page: page,
diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go
index c2a406b184..972271269f 100644
--- a/routers/web/user/setting/profile.go
+++ b/routers/web/user/setting/profile.go
@@ -15,7 +15,6 @@ import (
"path/filepath"
"strings"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
@@ -304,7 +303,7 @@ func Repos(ctx *context.Context) {
return
}
- userRepos, _, err := models.GetUserRepositories(&models.SearchRepoOptions{
+ userRepos, _, err := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{
Actor: ctxUser,
Private: true,
ListOptions: db.ListOptions{
@@ -329,7 +328,7 @@ func Repos(ctx *context.Context) {
ctx.Data["Dirs"] = repoNames
ctx.Data["ReposMap"] = repos
} else {
- repos, count64, err := models.GetUserRepositories(&models.SearchRepoOptions{Actor: ctxUser, Private: true, ListOptions: opts})
+ repos, count64, err := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{Actor: ctxUser, Private: true, ListOptions: opts})
if err != nil {
ctx.ServerError("GetUserRepositories", err)
return
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index 92ff92a6c0..d611ff513e 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -1220,7 +1220,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
oid := strings.TrimPrefix(line[1:], lfs.MetaFileOidPrefix)
if len(oid) == 64 {
m := &models.LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}}
- count, err := db.Count(m)
+ count, err := db.CountByBean(db.DefaultContext, m)
if err == nil && count > 0 {
curFile.IsBin = true
diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go
index 4abf7eefda..d479dd0d44 100644
--- a/services/mailer/mail_issue.go
+++ b/services/mailer/mail_issue.go
@@ -145,7 +145,7 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
visited[user.ID] = true
// test if this user is allowed to see the issue/pull
- if !models.CheckRepoUnitUser(ctx.Issue.Repo, user, checkUnit) {
+ if !models.CheckRepoUnitUser(ctx, ctx.Issue.Repo, user, checkUnit) {
continue
}
diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go
index a93aee76cf..caa81f0fe9 100644
--- a/services/mirror/mirror_pull.go
+++ b/services/mirror/mirror_pull.go
@@ -10,7 +10,6 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
@@ -301,7 +300,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
gitRepo.Close()
log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo)
- if err := models.UpdateRepoSize(ctx, m.Repo); err != nil {
+ if err := repo_module.UpdateRepoSize(ctx, m.Repo); err != nil {
log.Error("SyncMirrors [repo: %-v]: failed to update size for mirror repository: %v", m.Repo, err)
}
diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index 1e8c22a479..48f049cd28 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -73,7 +73,7 @@ func AdoptRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*
if err := adoptRepository(ctx, repoPath, doer, repo, opts); err != nil {
return fmt.Errorf("createDelegateHooks: %v", err)
}
- if err := models.CheckDaemonExportOK(ctx, repo); err != nil {
+ if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil {
return fmt.Errorf("checkDaemonExportOK: %v", err)
}
@@ -182,7 +182,7 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r
}
}
- if err = models.UpdateRepositoryCtx(ctx, repo, false); err != nil {
+ if err = repo_module.UpdateRepository(ctx, repo, false); err != nil {
return fmt.Errorf("updateRepository: %v", err)
}
@@ -246,7 +246,7 @@ func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unad
}
return err
}
- repos, _, err := models.GetUserRepositories(&models.SearchRepoOptions{
+ repos, _, err := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{
Actor: ctxUser,
Private: true,
ListOptions: db.ListOptions{
diff --git a/services/repository/check.go b/services/repository/check.go
index 6fb86d0dc3..17bdf2fac1 100644
--- a/services/repository/check.go
+++ b/services/repository/check.go
@@ -17,6 +17,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
+ repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
@@ -89,7 +90,7 @@ func GitGcRepos(ctx context.Context, timeout time.Duration, args ...string) erro
}
// Now update the size of the repository
- if err := models.UpdateRepoSize(ctx, repo); err != nil {
+ if err := repo_module.UpdateRepoSize(ctx, repo); err != nil {
log.Error("Updating size as part of garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err)
desc := fmt.Sprintf("Updating size as part of garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err)
if err = admin_model.CreateRepositoryNotice(desc); err != nil {
diff --git a/services/repository/fork.go b/services/repository/fork.go
index a2ef75bbd0..f4888cba1d 100644
--- a/services/repository/fork.go
+++ b/services/repository/fork.go
@@ -96,7 +96,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
return err
}
- if err = models.IncrementRepoForkNum(txCtx, opts.BaseRepo.ID); err != nil {
+ if err = repo_model.IncrementRepoForkNum(txCtx, opts.BaseRepo.ID); err != nil {
return err
}
@@ -116,7 +116,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
return fmt.Errorf("git clone: %v", err)
}
- if err := models.CheckDaemonExportOK(txCtx, repo); err != nil {
+ if err := repo_module.CheckDaemonExportOK(txCtx, repo); err != nil {
return fmt.Errorf("checkDaemonExportOK: %v", err)
}
@@ -139,7 +139,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
}
// even if below operations failed, it could be ignored. And they will be retried
- if err := models.UpdateRepoSize(ctx, repo); err != nil {
+ if err := repo_module.UpdateRepoSize(ctx, repo); err != nil {
log.Error("Failed to update size for repository: %v", err)
}
if err := repo_model.CopyLanguageStat(opts.BaseRepo, repo); err != nil {
@@ -173,7 +173,7 @@ func ConvertForkToNormalRepository(repo *repo_model.Repository) error {
return nil
}
- if err := models.DecrementRepoForkNum(ctx, repo.ForkID); err != nil {
+ if err := repo_model.DecrementRepoForkNum(ctx, repo.ForkID); err != nil {
log.Error("Unable to decrement repo fork num for old root repo %d of repository %-v whilst converting from fork. Error: %v", repo.ForkID, repo, err)
return err
}
@@ -181,7 +181,7 @@ func ConvertForkToNormalRepository(repo *repo_model.Repository) error {
repo.IsFork = false
repo.ForkID = 0
- if err := models.UpdateRepositoryCtx(ctx, repo, false); err != nil {
+ if err := repo_module.UpdateRepository(ctx, repo, false); err != nil {
log.Error("Unable to update repository %-v whilst converting from fork. Error: %v", repo, err)
return err
}
diff --git a/services/repository/hooks.go b/services/repository/hooks.go
index 67931ffcb6..45a031f38e 100644
--- a/services/repository/hooks.go
+++ b/services/repository/hooks.go
@@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
@@ -84,3 +85,29 @@ func GenerateGitHooks(ctx context.Context, templateRepo, generateRepo *repo_mode
}
return nil
}
+
+// GenerateWebhooks generates webhooks from a template repository
+func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
+ templateWebhooks, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{RepoID: templateRepo.ID})
+ if err != nil {
+ return err
+ }
+
+ ws := make([]*webhook.Webhook, 0, len(templateWebhooks))
+ for _, templateWebhook := range templateWebhooks {
+ ws = append(ws, &webhook.Webhook{
+ RepoID: generateRepo.ID,
+ URL: templateWebhook.URL,
+ HTTPMethod: templateWebhook.HTTPMethod,
+ ContentType: templateWebhook.ContentType,
+ Secret: templateWebhook.Secret,
+ HookEvent: templateWebhook.HookEvent,
+ IsActive: templateWebhook.IsActive,
+ Type: templateWebhook.Type,
+ OrgID: templateWebhook.OrgID,
+ Events: templateWebhook.Events,
+ Meta: templateWebhook.Meta,
+ })
+ }
+ return webhook.CreateWebhooks(ctx, ws)
+}
diff --git a/services/repository/push.go b/services/repository/push.go
index 5e48a19ba8..b6741c5ab4 100644
--- a/services/repository/push.go
+++ b/services/repository/push.go
@@ -95,7 +95,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
}
defer gitRepo.Close()
- if err = models.UpdateRepoSize(ctx, repo); err != nil {
+ if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
log.Error("Failed to update size for repository: %v", err)
}
diff --git a/services/repository/repository.go b/services/repository/repository.go
index 6799ca586e..6848eda101 100644
--- a/services/repository/repository.go
+++ b/services/repository/repository.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/organization"
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
@@ -85,3 +86,42 @@ func Init() error {
admin_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repositories", repo_module.LocalCopyPath())
return initPushQueue()
}
+
+// UpdateRepository updates a repository
+func UpdateRepository(repo *repo_model.Repository, visibilityChanged bool) (err error) {
+ ctx, committer, err := db.TxContext()
+ if err != nil {
+ return err
+ }
+ defer committer.Close()
+
+ if err = repo_module.UpdateRepository(ctx, repo, visibilityChanged); err != nil {
+ return fmt.Errorf("updateRepository: %v", err)
+ }
+
+ return committer.Commit()
+}
+
+// LinkedRepository returns the linked repo if any
+func LinkedRepository(a *repo_model.Attachment) (*repo_model.Repository, unit.Type, error) {
+ if a.IssueID != 0 {
+ iss, err := models.GetIssueByID(a.IssueID)
+ if err != nil {
+ return nil, unit.TypeIssues, err
+ }
+ repo, err := repo_model.GetRepositoryByID(iss.RepoID)
+ unitType := unit.TypeIssues
+ if iss.IsPull {
+ unitType = unit.TypePullRequests
+ }
+ return repo, unitType, err
+ } else if a.ReleaseID != 0 {
+ rel, err := models.GetReleaseByID(db.DefaultContext, a.ReleaseID)
+ if err != nil {
+ return nil, unit.TypeReleases, err
+ }
+ repo, err := repo_model.GetRepositoryByID(rel.RepoID)
+ return repo, unit.TypeReleases, err
+ }
+ return nil, -1, nil
+}
diff --git a/services/repository/repository_test.go b/services/repository/repository_test.go
new file mode 100644
index 0000000000..e0ffcac3cc
--- /dev/null
+++ b/services/repository/repository_test.go
@@ -0,0 +1,43 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repository
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLinkedRepository(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ testCases := []struct {
+ name string
+ attachID int64
+ expectedRepo *repo_model.Repository
+ expectedUnitType unit.Type
+ }{
+ {"LinkedIssue", 1, &repo_model.Repository{ID: 1}, unit.TypeIssues},
+ {"LinkedComment", 3, &repo_model.Repository{ID: 1}, unit.TypePullRequests},
+ {"LinkedRelease", 9, &repo_model.Repository{ID: 1}, unit.TypeReleases},
+ {"Notlinked", 10, nil, -1},
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ attach, err := repo_model.GetAttachmentByID(db.DefaultContext, tc.attachID)
+ assert.NoError(t, err)
+ repo, unitType, err := LinkedRepository(attach)
+ assert.NoError(t, err)
+ if tc.expectedRepo != nil {
+ assert.Equal(t, tc.expectedRepo.ID, repo.ID)
+ }
+ assert.Equal(t, tc.expectedUnitType, unitType)
+ })
+ }
+}
diff --git a/services/repository/review.go b/services/repository/review.go
new file mode 100644
index 0000000000..9e8012978e
--- /dev/null
+++ b/services/repository/review.go
@@ -0,0 +1,24 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repository
+
+import (
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/organization"
+ "code.gitea.io/gitea/models/perm"
+ repo_model "code.gitea.io/gitea/models/repo"
+)
+
+// GetReviewerTeams get all teams can be requested to review
+func GetReviewerTeams(repo *repo_model.Repository) ([]*organization.Team, error) {
+ if err := repo.GetOwner(db.DefaultContext); err != nil {
+ return nil, err
+ }
+ if !repo.Owner.IsOrganization() {
+ return nil, nil
+ }
+
+ return organization.GetTeamsWithAccessToRepo(db.DefaultContext, repo.OwnerID, repo.ID, perm.AccessModeRead)
+}
diff --git a/services/repository/review_test.go b/services/repository/review_test.go
new file mode 100644
index 0000000000..640657d1dd
--- /dev/null
+++ b/services/repository/review_test.go
@@ -0,0 +1,28 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repository
+
+import (
+ "testing"
+
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRepoGetReviewerTeams(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ teams, err := GetReviewerTeams(repo2)
+ assert.NoError(t, err)
+ assert.Empty(t, teams)
+
+ repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ teams, err = GetReviewerTeams(repo3)
+ assert.NoError(t, err)
+ assert.Len(t, teams, 2)
+}
diff --git a/services/repository/template.go b/services/repository/template.go
index 28fa1523a5..6a1bfaff5b 100644
--- a/services/repository/template.go
+++ b/services/repository/template.go
@@ -16,8 +16,27 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
)
+// GenerateIssueLabels generates issue labels from a template repository
+func GenerateIssueLabels(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
+ templateLabels, err := models.GetLabelsByRepoID(ctx, templateRepo.ID, "", db.ListOptions{})
+ if err != nil {
+ return err
+ }
+
+ newLabels := make([]*models.Label, 0, len(templateLabels))
+ for _, templateLabel := range templateLabels {
+ newLabels = append(newLabels, &models.Label{
+ RepoID: generateRepo.ID,
+ Name: templateLabel.Name,
+ Description: templateLabel.Description,
+ Color: templateLabel.Color,
+ })
+ }
+ return db.Insert(ctx, newLabels)
+}
+
// GenerateRepository generates a repository from a template
-func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.Repository, opts models.GenerateRepoOptions) (_ *repo_model.Repository, err error) {
+func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.Repository, opts repo_module.GenerateRepoOptions) (_ *repo_model.Repository, err error) {
if !doer.IsAdmin && !owner.CanCreateRepo() {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: owner.MaxRepoCreation,
@@ -54,7 +73,7 @@ func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.R
// Webhooks
if opts.Webhooks {
- if err = models.GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
+ if err = GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
return err
}
}
@@ -68,7 +87,7 @@ func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.R
// Issue Labels
if opts.IssueLabels {
- if err = models.GenerateIssueLabels(ctx, templateRepo, generateRepo); err != nil {
+ if err = GenerateIssueLabels(ctx, templateRepo, generateRepo); err != nil {
return err
}
}