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>
This commit is contained in:
parent
ebeb6e7c71
commit
26095115f4
76 changed files with 1756 additions and 1674 deletions
|
@ -17,6 +17,7 @@ import (
|
||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||||
"code.gitea.io/gitea/models/auth"
|
"code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
"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)")
|
log.Trace("Synchronizing repository releases (this may take a while)")
|
||||||
for page := 1; ; page++ {
|
for page := 1; ; page++ {
|
||||||
repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
|
repos, count, err := repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
PageSize: models.RepositoryListDefaultPageSize,
|
PageSize: repo_model.RepositoryListDefaultPageSize,
|
||||||
Page: page,
|
Page: page,
|
||||||
},
|
},
|
||||||
Private: true,
|
Private: true,
|
||||||
|
|
|
@ -393,7 +393,7 @@ func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
|
||||||
|
|
||||||
// check readable repositories by doer/actor
|
// check readable repositories by doer/actor
|
||||||
if opts.Actor == nil || !opts.Actor.IsAdmin {
|
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 {
|
if opts.RequestedRepo != nil {
|
||||||
|
|
|
@ -190,6 +190,13 @@ func GetDeployKeyByRepo(ctx context.Context, keyID, repoID int64) (*DeployKey, e
|
||||||
return key, nil
|
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.
|
// UpdateDeployKeyCols updates deploy key information in the specified columns.
|
||||||
func UpdateDeployKeyCols(key *DeployKey, cols ...string) error {
|
func UpdateDeployKeyCols(key *DeployKey, cols ...string) error {
|
||||||
_, err := db.GetEngine(db.DefaultContext).ID(key.ID).Cols(cols...).Update(key)
|
_, err := db.GetEngine(db.DefaultContext).ID(key.ID).Cols(cols...).Update(key)
|
||||||
|
|
|
@ -271,11 +271,6 @@ func MaxBatchInsertSize(bean interface{}) int {
|
||||||
return 999 / len(t.ColumnsSeq())
|
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
|
// IsTableNotEmpty returns true if table has at least one record
|
||||||
func IsTableNotEmpty(tableName string) (bool, error) {
|
func IsTableNotEmpty(tableName string) (bool, error) {
|
||||||
return x.Table(tableName).Exist()
|
return x.Table(tableName).Exist()
|
||||||
|
|
|
@ -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
|
// 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 {
|
func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organization.Organization, team *organization.Team, isPull bool) builder.Cond {
|
||||||
cond := builder.NewCond()
|
cond := builder.NewCond()
|
||||||
|
@ -1356,19 +1398,19 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati
|
||||||
} else {
|
} else {
|
||||||
cond = cond.And(
|
cond = cond.And(
|
||||||
builder.Or(
|
builder.Or(
|
||||||
userOrgUnitRepoCond(repoIDstr, userID, org.ID, unitType), // team member repos
|
repo_model.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.UserOrgPublicUnitRepoCond(userID, org.ID), // user org public non-member repos, TODO: check repo has issues
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cond = cond.And(
|
cond = cond.And(
|
||||||
builder.Or(
|
builder.Or(
|
||||||
userOwnedRepoCond(userID), // owned repos
|
repo_model.UserOwnedRepoCond(userID), // owned repos
|
||||||
userCollaborationRepoCond(repoIDstr, userID), // collaboration repos
|
repo_model.UserCollaborationRepoCond(repoIDstr, userID), // collaboration repos
|
||||||
userAssignedRepoCond(repoIDstr, userID), // user has been assigned accessible public repos
|
repo_model.UserAssignedRepoCond(repoIDstr, userID), // user has been assigned accessible public repos
|
||||||
userMentionedRepoCond(repoIDstr, userID), // user has been mentioned accessible public repos
|
repo_model.UserMentionedRepoCond(repoIDstr, userID), // user has been mentioned accessible public repos
|
||||||
userCreateIssueRepoCond(repoIDstr, userID, isPull), // user has created issue/pr 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)
|
opts.setupSessionNoLimit(sess)
|
||||||
|
|
||||||
accessCond := accessibleRepositoryCondition(user)
|
accessCond := repo_model.AccessibleRepositoryCondition(user)
|
||||||
if err := sess.Where(accessCond).
|
if err := sess.Where(accessCond).
|
||||||
Distinct("issue.repo_id").
|
Distinct("issue.repo_id").
|
||||||
Table("issue").
|
Table("issue").
|
||||||
|
|
|
@ -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
|
// LoadRepositories loads issues' all repositories
|
||||||
|
|
|
@ -26,7 +26,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIssueUsers(ctx context.Context, repo *repo_model.Repository, issue *Issue) error {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("getAssignees: %v", err)
|
return fmt.Errorf("getAssignees: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}})
|
count, err := db.GetEngine(db.DefaultContext).Count(&LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}})
|
||||||
return count > 0, err
|
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}})
|
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
|
return count > 0, err
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ func LFSAutoAssociate(metas []*LFSMetaObject, user *user_model.User, repoID int6
|
||||||
newMetas := make([]*LFSMetaObject, 0, len(metas))
|
newMetas := make([]*LFSMetaObject, 0, len(metas))
|
||||||
cond := builder.In(
|
cond := builder.In(
|
||||||
"`lfs_meta_object`.repository_id",
|
"`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)
|
err = sess.Cols("oid").Where(cond).In("oid", oids...).GroupBy("oid").Find(&newMetas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -246,3 +246,12 @@ func CopyLFS(ctx context.Context, newRepo, oldRepo *repo_model.Repository) error
|
||||||
|
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -266,10 +266,10 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if issue.IsPull && !checkRepoUnitUser(ctx, issue.Repo, user, unit.TypePullRequests) {
|
if issue.IsPull && !CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypePullRequests) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !issue.IsPull && !checkRepoUnitUser(ctx, issue.Repo, user, unit.TypeIssues) {
|
if !issue.IsPull && !CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypeIssues) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,9 +510,9 @@ func (nl NotificationList) getPendingRepoIDs() []int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadRepos loads repositories from database
|
// 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 {
|
if len(nl) == 0 {
|
||||||
return RepositoryList{}, []int{}, nil
|
return repo_model.RepositoryList{}, []int{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
repoIDs := nl.getPendingRepoIDs()
|
repoIDs := nl.getPendingRepoIDs()
|
||||||
|
@ -548,7 +548,7 @@ func (nl NotificationList) LoadRepos() (RepositoryList, []int, error) {
|
||||||
|
|
||||||
failed := []int{}
|
failed := []int{}
|
||||||
|
|
||||||
reposList := make(RepositoryList, 0, len(repoIDs))
|
reposList := make(repo_model.RepositoryList, 0, len(repoIDs))
|
||||||
for i, notification := range nl {
|
for i, notification := range nl {
|
||||||
if notification.Repository == nil {
|
if notification.Repository == nil {
|
||||||
notification.Repository = repos[notification.RepoID]
|
notification.Repository = repos[notification.RepoID]
|
||||||
|
|
|
@ -54,7 +54,7 @@ func GetUserOrgsList(user *user_model.User) ([]*MinimalOrg, error) {
|
||||||
Join("LEFT", builder.
|
Join("LEFT", builder.
|
||||||
Select("id as repo_id, owner_id as repo_owner_id").
|
Select("id as repo_id, owner_id as repo_owner_id").
|
||||||
From("repository").
|
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).
|
Where("`team_user`.uid = ?", user.ID).
|
||||||
GroupBy(groupByStr)
|
GroupBy(groupByStr)
|
||||||
|
|
||||||
|
|
403
models/repo.go
403
models/repo.go
|
@ -8,11 +8,7 @@ package models
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
_ "image/jpeg" // Needed for jpeg support
|
_ "image/jpeg" // Needed for jpeg support
|
||||||
|
|
||||||
|
@ -47,11 +43,7 @@ func NewRepoContext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckRepoUnitUser check whether user could visit the unit of this repository
|
// 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 {
|
func CheckRepoUnitUser(ctx context.Context, 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 {
|
|
||||||
if user != nil && user.IsAdmin {
|
if user != nil && user.IsAdmin {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -64,241 +56,6 @@ func checkRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *u
|
||||||
return perm.CanRead(unitType)
|
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
|
// CreateRepoOptions contains the create repository options
|
||||||
type CreateRepoOptions struct {
|
type CreateRepoOptions struct {
|
||||||
Name string
|
Name string
|
||||||
|
@ -441,126 +198,6 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_
|
||||||
return nil
|
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.
|
// 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)
|
// 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 {
|
func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
|
||||||
|
@ -1052,14 +689,14 @@ func CheckRepoStats(ctx context.Context) error {
|
||||||
continue
|
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 {
|
if err != nil {
|
||||||
log.Error("Select count of forks[%d]: %v", repo.ID, err)
|
log.Error("Select count of forks[%d]: %v", repo.ID, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
repo.NumForks = int(parseCountResult(rawResult))
|
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)
|
log.Error("UpdateRepository[%d]: %v", id, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -1130,30 +767,6 @@ func DoctorUserStarNum() (err error) {
|
||||||
return
|
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
|
// DeleteDeployKey delete deploy keys
|
||||||
func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error {
|
func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error {
|
||||||
key, err := asymkey_model.GetDeployKeyByID(ctx, id)
|
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)
|
return fmt.Errorf("GetDeployKeyByID: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
// Check if user has access to delete this key.
|
// Check if user has access to delete this key.
|
||||||
if !doer.IsAdmin {
|
if !doer.IsAdmin {
|
||||||
repo, err := repo_model.GetRepositoryByIDCtx(ctx, key.RepoID)
|
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)
|
return fmt.Errorf("delete deploy key [%d]: %v", key.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is the last reference to same key content.
|
// Check if this is the last reference to same key content.
|
||||||
has, err := sess.
|
has, err := asymkey_model.IsDeployKeyExistByKeyID(ctx, key.KeyID)
|
||||||
Where("key_id = ?", key.KeyID).
|
|
||||||
Get(new(asymkey_model.DeployKey))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package repo
|
package repo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -16,7 +17,7 @@ import (
|
||||||
func TestIncreaseDownloadCount(t *testing.T) {
|
func TestIncreaseDownloadCount(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
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.NoError(t, err)
|
||||||
assert.Equal(t, int64(0), attachment.DownloadCount)
|
assert.Equal(t, int64(0), attachment.DownloadCount)
|
||||||
|
|
||||||
|
@ -24,7 +25,7 @@ func TestIncreaseDownloadCount(t *testing.T) {
|
||||||
err = attachment.IncreaseDownloadCount()
|
err = attachment.IncreaseDownloadCount()
|
||||||
assert.NoError(t, err)
|
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.NoError(t, err)
|
||||||
assert.Equal(t, int64(1), attachment.DownloadCount)
|
assert.Equal(t, int64(1), attachment.DownloadCount)
|
||||||
}
|
}
|
||||||
|
@ -33,11 +34,11 @@ func TestGetByCommentOrIssueID(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
// count of attachments from issue ID
|
// 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.NoError(t, err)
|
||||||
assert.Len(t, attachments, 1)
|
assert.Len(t, attachments, 1)
|
||||||
|
|
||||||
attachments, err = GetAttachmentsByCommentID(db.DefaultContext, 1)
|
attachments, err = repo_model.GetAttachmentsByCommentID(db.DefaultContext, 1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, attachments, 2)
|
assert.Len(t, attachments, 2)
|
||||||
}
|
}
|
||||||
|
@ -45,33 +46,33 @@ func TestGetByCommentOrIssueID(t *testing.T) {
|
||||||
func TestDeleteAttachments(t *testing.T) {
|
func TestDeleteAttachments(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
count, err := DeleteAttachmentsByIssue(4, false)
|
count, err := repo_model.DeleteAttachmentsByIssue(4, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 2, count)
|
assert.Equal(t, 2, count)
|
||||||
|
|
||||||
count, err = DeleteAttachmentsByComment(2, false)
|
count, err = repo_model.DeleteAttachmentsByComment(2, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 2, count)
|
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)
|
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.Error(t, err)
|
||||||
assert.True(t, IsErrAttachmentNotExist(err))
|
assert.True(t, repo_model.IsErrAttachmentNotExist(err))
|
||||||
assert.Nil(t, attachment)
|
assert.Nil(t, attachment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAttachmentByID(t *testing.T) {
|
func TestGetAttachmentByID(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
attach, err := GetAttachmentByID(db.DefaultContext, 1)
|
attach, err := repo_model.GetAttachmentByID(db.DefaultContext, 1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.UUID)
|
assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.UUID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAttachment_DownloadURL(t *testing.T) {
|
func TestAttachment_DownloadURL(t *testing.T) {
|
||||||
attach := &Attachment{
|
attach := &repo_model.Attachment{
|
||||||
UUID: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
|
UUID: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
|
||||||
ID: 1,
|
ID: 1,
|
||||||
}
|
}
|
||||||
|
@ -81,20 +82,20 @@ func TestAttachment_DownloadURL(t *testing.T) {
|
||||||
func TestUpdateAttachment(t *testing.T) {
|
func TestUpdateAttachment(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
attach, err := GetAttachmentByID(db.DefaultContext, 1)
|
attach, err := repo_model.GetAttachmentByID(db.DefaultContext, 1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.UUID)
|
assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.UUID)
|
||||||
|
|
||||||
attach.Name = "new_name"
|
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) {
|
func TestGetAttachmentsByUUIDs(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
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.NoError(t, err)
|
||||||
assert.Len(t, attachList, 2)
|
assert.Len(t, attachList, 2)
|
||||||
assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attachList[0].UUID)
|
assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attachList[0].UUID)
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package repo
|
package repo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -16,10 +17,10 @@ import (
|
||||||
func TestRepository_GetCollaborators(t *testing.T) {
|
func TestRepository_GetCollaborators(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
test := func(repoID int64) {
|
test := func(repoID int64) {
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository)
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
|
||||||
collaborators, err := GetCollaborators(db.DefaultContext, repo.ID, db.ListOptions{})
|
collaborators, err := repo_model.GetCollaborators(db.DefaultContext, repo.ID, db.ListOptions{})
|
||||||
assert.NoError(t, err)
|
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.NoError(t, err)
|
||||||
assert.Len(t, collaborators, int(expectedLen))
|
assert.Len(t, collaborators, int(expectedLen))
|
||||||
for _, collaborator := range collaborators {
|
for _, collaborator := range collaborators {
|
||||||
|
@ -37,8 +38,8 @@ func TestRepository_IsCollaborator(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
test := func(repoID, userID int64, expected bool) {
|
test := func(repoID, userID int64, expected bool) {
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository)
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
|
||||||
actual, err := IsCollaborator(db.DefaultContext, repo.ID, userID)
|
actual, err := repo_model.IsCollaborator(db.DefaultContext, repo.ID, userID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, expected, actual)
|
assert.Equal(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"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.
|
// 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)
|
forks := make([]*Repository, 0, listOptions.PageSize)
|
||||||
return forks, sess.Find(&forks, &Repository{ForkID: repo.ID})
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package repo
|
package repo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -17,17 +18,17 @@ func TestGetUserFork(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
// User13 has repo 11 forked from repo10
|
// User13 has repo 11 forked from repo10
|
||||||
repo, err := GetRepositoryByID(10)
|
repo, err := repo_model.GetRepositoryByID(10)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, repo)
|
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.NoError(t, err)
|
||||||
assert.NotNil(t, repo)
|
assert.NotNil(t, repo)
|
||||||
|
|
||||||
repo, err = GetRepositoryByID(9)
|
repo, err = repo_model.GetRepositoryByID(9)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, repo)
|
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.NoError(t, err)
|
||||||
assert.Nil(t, repo)
|
assert.Nil(t, repo)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,31 +2,22 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package repo
|
package repo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"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"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
unittest.MainTest(m, &unittest.TestOptions{
|
unittest.MainTest(m, &unittest.TestOptions{
|
||||||
GiteaRootPath: filepath.Join("..", ".."),
|
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",
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,8 +123,8 @@ func MirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertMirror inserts a mirror to database
|
// InsertMirror inserts a mirror to database
|
||||||
func InsertMirror(mirror *Mirror) error {
|
func InsertMirror(ctx context.Context, mirror *Mirror) error {
|
||||||
_, err := db.GetEngine(db.DefaultContext).Insert(mirror)
|
_, err := db.GetEngine(ctx).Insert(mirror)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,3 +168,12 @@ func (repos MirrorRepositoryList) loadAttributes(ctx context.Context) error {
|
||||||
func (repos MirrorRepositoryList) LoadAttributes() error {
|
func (repos MirrorRepositoryList) LoadAttributes() error {
|
||||||
return repos.loadAttributes(db.DefaultContext)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package repo
|
package repo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
|
@ -19,20 +20,20 @@ func TestPushMirrorsIterate(t *testing.T) {
|
||||||
|
|
||||||
now := timeutil.TimeStampNow()
|
now := timeutil.TimeStampNow()
|
||||||
|
|
||||||
InsertPushMirror(&PushMirror{
|
repo_model.InsertPushMirror(&repo_model.PushMirror{
|
||||||
RemoteName: "test-1",
|
RemoteName: "test-1",
|
||||||
LastUpdateUnix: now,
|
LastUpdateUnix: now,
|
||||||
Interval: 1,
|
Interval: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
long, _ := time.ParseDuration("24h")
|
long, _ := time.ParseDuration("24h")
|
||||||
InsertPushMirror(&PushMirror{
|
repo_model.InsertPushMirror(&repo_model.PushMirror{
|
||||||
RemoteName: "test-2",
|
RemoteName: "test-2",
|
||||||
LastUpdateUnix: now,
|
LastUpdateUnix: now,
|
||||||
Interval: long,
|
Interval: long,
|
||||||
})
|
})
|
||||||
|
|
||||||
InsertPushMirror(&PushMirror{
|
repo_model.InsertPushMirror(&repo_model.PushMirror{
|
||||||
RemoteName: "test-3",
|
RemoteName: "test-3",
|
||||||
LastUpdateUnix: now,
|
LastUpdateUnix: now,
|
||||||
Interval: 0,
|
Interval: 0,
|
||||||
|
@ -40,8 +41,8 @@ func TestPushMirrorsIterate(t *testing.T) {
|
||||||
|
|
||||||
time.Sleep(1 * time.Millisecond)
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
|
||||||
PushMirrorsIterate(1, func(idx int, bean interface{}) error {
|
repo_model.PushMirrorsIterate(1, func(idx int, bean interface{}) error {
|
||||||
m, ok := bean.(*PushMirror)
|
m, ok := bean.(*repo_model.PushMirror)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.Equal(t, "test-1", m.RemoteName)
|
assert.Equal(t, "test-1", m.RemoteName)
|
||||||
assert.Equal(t, m.RemoteName, m.GetRemoteName())
|
assert.Equal(t, m.RemoteName, m.GetRemoteName())
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package repo
|
package repo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -16,27 +17,27 @@ import (
|
||||||
func TestLookupRedirect(t *testing.T) {
|
func TestLookupRedirect(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
repoID, err := LookupRedirect(2, "oldrepo1")
|
repoID, err := repo_model.LookupRedirect(2, "oldrepo1")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, 1, repoID)
|
assert.EqualValues(t, 1, repoID)
|
||||||
|
|
||||||
_, err = LookupRedirect(unittest.NonexistentID, "doesnotexist")
|
_, err = repo_model.LookupRedirect(unittest.NonexistentID, "doesnotexist")
|
||||||
assert.True(t, IsErrRedirectNotExist(err))
|
assert.True(t, repo_model.IsErrRedirectNotExist(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRedirect(t *testing.T) {
|
func TestNewRedirect(t *testing.T) {
|
||||||
// redirect to a completely new name
|
// redirect to a completely new name
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
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)
|
||||||
assert.NoError(t, NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "newreponame"))
|
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,
|
OwnerID: repo.OwnerID,
|
||||||
LowerName: repo.LowerName,
|
LowerName: repo.LowerName,
|
||||||
RedirectRepoID: repo.ID,
|
RedirectRepoID: repo.ID,
|
||||||
})
|
})
|
||||||
unittest.AssertExistsAndLoadBean(t, &Redirect{
|
unittest.AssertExistsAndLoadBean(t, &repo_model.Redirect{
|
||||||
OwnerID: repo.OwnerID,
|
OwnerID: repo.OwnerID,
|
||||||
LowerName: "oldrepo1",
|
LowerName: "oldrepo1",
|
||||||
RedirectRepoID: repo.ID,
|
RedirectRepoID: repo.ID,
|
||||||
|
@ -47,15 +48,15 @@ func TestNewRedirect2(t *testing.T) {
|
||||||
// redirect to previously used name
|
// redirect to previously used name
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
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)
|
||||||
assert.NoError(t, NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "oldrepo1"))
|
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,
|
OwnerID: repo.OwnerID,
|
||||||
LowerName: repo.LowerName,
|
LowerName: repo.LowerName,
|
||||||
RedirectRepoID: repo.ID,
|
RedirectRepoID: repo.ID,
|
||||||
})
|
})
|
||||||
unittest.AssertNotExistsBean(t, &Redirect{
|
unittest.AssertNotExistsBean(t, &repo_model.Redirect{
|
||||||
OwnerID: repo.OwnerID,
|
OwnerID: repo.OwnerID,
|
||||||
LowerName: "oldrepo1",
|
LowerName: "oldrepo1",
|
||||||
RedirectRepoID: repo.ID,
|
RedirectRepoID: repo.ID,
|
||||||
|
@ -66,10 +67,10 @@ func TestNewRedirect3(t *testing.T) {
|
||||||
// redirect for a previously-unredirected repo
|
// redirect for a previously-unredirected repo
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
|
||||||
assert.NoError(t, NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "newreponame"))
|
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,
|
OwnerID: repo.OwnerID,
|
||||||
LowerName: repo.LowerName,
|
LowerName: repo.LowerName,
|
||||||
RedirectRepoID: repo.ID,
|
RedirectRepoID: repo.ID,
|
||||||
|
|
|
@ -5,18 +5,21 @@
|
||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.gitea.io/gitea/models/db"
|
"context"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"fmt"
|
||||||
)
|
"strings"
|
||||||
|
|
||||||
// GetUserMirrorRepositories returns a list of mirror repositories of given user.
|
"code.gitea.io/gitea/models/db"
|
||||||
func GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
|
"code.gitea.io/gitea/models/perm"
|
||||||
repos := make([]*Repository, 0, 10)
|
"code.gitea.io/gitea/models/unit"
|
||||||
return repos, db.GetEngine(db.DefaultContext).
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
Where("owner_id = ?", userID).
|
"code.gitea.io/gitea/modules/container"
|
||||||
And("is_mirror = ?", true).
|
"code.gitea.io/gitea/modules/setting"
|
||||||
Find(&repos)
|
"code.gitea.io/gitea/modules/structs"
|
||||||
}
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
|
"xorm.io/builder"
|
||||||
|
)
|
||||||
|
|
||||||
// IterateRepository iterate repositories
|
// IterateRepository iterate repositories
|
||||||
func IterateRepository(f func(repo *Repository) error) error {
|
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 {
|
func FindReposMapByIDs(repoIDs []int64, res map[int64]*Repository) error {
|
||||||
return db.GetEngine(db.DefaultContext).In("id", repoIDs).Find(&res)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package models
|
package repo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ func TestSearchRepository(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
// test search public repository on explore page
|
// test search public repository on explore page
|
||||||
repos, count, err := SearchRepositoryByName(&SearchRepoOptions{
|
repos, count, err := repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
Page: 1,
|
Page: 1,
|
||||||
PageSize: 10,
|
PageSize: 10,
|
||||||
|
@ -34,7 +35,7 @@ func TestSearchRepository(t *testing.T) {
|
||||||
}
|
}
|
||||||
assert.Equal(t, int64(1), count)
|
assert.Equal(t, int64(1), count)
|
||||||
|
|
||||||
repos, count, err = SearchRepositoryByName(&SearchRepoOptions{
|
repos, count, err = repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
Page: 1,
|
Page: 1,
|
||||||
PageSize: 10,
|
PageSize: 10,
|
||||||
|
@ -48,7 +49,7 @@ func TestSearchRepository(t *testing.T) {
|
||||||
assert.Len(t, repos, 2)
|
assert.Len(t, repos, 2)
|
||||||
|
|
||||||
// test search private repository on explore page
|
// test search private repository on explore page
|
||||||
repos, count, err = SearchRepositoryByName(&SearchRepoOptions{
|
repos, count, err = repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
Page: 1,
|
Page: 1,
|
||||||
PageSize: 10,
|
PageSize: 10,
|
||||||
|
@ -64,7 +65,7 @@ func TestSearchRepository(t *testing.T) {
|
||||||
}
|
}
|
||||||
assert.Equal(t, int64(1), count)
|
assert.Equal(t, int64(1), count)
|
||||||
|
|
||||||
repos, count, err = SearchRepositoryByName(&SearchRepoOptions{
|
repos, count, err = repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
Page: 1,
|
Page: 1,
|
||||||
PageSize: 10,
|
PageSize: 10,
|
||||||
|
@ -79,14 +80,14 @@ func TestSearchRepository(t *testing.T) {
|
||||||
assert.Len(t, repos, 3)
|
assert.Len(t, repos, 3)
|
||||||
|
|
||||||
// Test non existing owner
|
// 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.NoError(t, err)
|
||||||
assert.Empty(t, repos)
|
assert.Empty(t, repos)
|
||||||
assert.Equal(t, int64(0), count)
|
assert.Equal(t, int64(0), count)
|
||||||
|
|
||||||
// Test search within description
|
// Test search within description
|
||||||
repos, count, err = SearchRepository(&SearchRepoOptions{
|
repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
Page: 1,
|
Page: 1,
|
||||||
PageSize: 10,
|
PageSize: 10,
|
||||||
|
@ -103,7 +104,7 @@ func TestSearchRepository(t *testing.T) {
|
||||||
assert.Equal(t, int64(1), count)
|
assert.Equal(t, int64(1), count)
|
||||||
|
|
||||||
// Test NOT search within description
|
// Test NOT search within description
|
||||||
repos, count, err = SearchRepository(&SearchRepoOptions{
|
repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
Page: 1,
|
Page: 1,
|
||||||
PageSize: 10,
|
PageSize: 10,
|
||||||
|
@ -119,164 +120,164 @@ func TestSearchRepository(t *testing.T) {
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
opts *SearchRepoOptions
|
opts *repo_model.SearchRepoOptions
|
||||||
count int
|
count int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "PublicRepositoriesByName",
|
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,
|
count: 7,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicAndPrivateRepositoriesByName",
|
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,
|
count: 14,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage",
|
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,
|
count: 14,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage",
|
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,
|
count: 14,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage",
|
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,
|
count: 14,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage",
|
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,
|
count: 14,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicRepositoriesOfUser",
|
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,
|
count: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicRepositoriesOfUser2",
|
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,
|
count: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicRepositoriesOfUser3",
|
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,
|
count: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicAndPrivateRepositoriesOfUser",
|
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,
|
count: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicAndPrivateRepositoriesOfUser2",
|
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,
|
count: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicAndPrivateRepositoriesOfUser3",
|
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,
|
count: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicRepositoriesOfUserIncludingCollaborative",
|
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,
|
count: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicRepositoriesOfUser2IncludingCollaborative",
|
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,
|
count: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicRepositoriesOfUser3IncludingCollaborative",
|
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,
|
count: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
|
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,
|
count: 9,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicAndPrivateRepositoriesOfUser2IncludingCollaborative",
|
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,
|
count: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicAndPrivateRepositoriesOfUser3IncludingCollaborative",
|
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,
|
count: 7,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicRepositoriesOfOrganization",
|
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,
|
count: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PublicAndPrivateRepositoriesOfOrganization",
|
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,
|
count: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AllPublic/PublicRepositoriesByName",
|
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,
|
count: 7,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AllPublic/PublicAndPrivateRepositoriesByName",
|
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,
|
count: 14,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
|
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,
|
count: 28,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
|
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,
|
count: 33,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
|
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,
|
count: 15,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AllPublic/PublicAndPrivateRepositoriesOfUser2IncludingCollaborativeByName",
|
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,
|
count: 13,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AllPublic/PublicRepositoriesOfOrganization",
|
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,
|
count: 28,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AllTemplates",
|
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,
|
count: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "OwnerSlashRepoSearch",
|
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,
|
count: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "OwnerSlashSearch",
|
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,
|
count: 4,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
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.NoError(t, err)
|
||||||
assert.Equal(t, int64(testCase.count), count)
|
assert.Equal(t, int64(testCase.count), count)
|
||||||
|
@ -354,29 +355,29 @@ func TestSearchRepositoryByTopicName(t *testing.T) {
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
opts *SearchRepoOptions
|
opts *repo_model.SearchRepoOptions
|
||||||
count int
|
count int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "AllPublic/SearchPublicRepositoriesFromTopicAndName",
|
name: "AllPublic/SearchPublicRepositoriesFromTopicAndName",
|
||||||
opts: &SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql"},
|
opts: &repo_model.SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql"},
|
||||||
count: 2,
|
count: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AllPublic/OnlySearchPublicRepositoriesFromTopic",
|
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,
|
count: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AllPublic/OnlySearchMultipleKeywordPublicRepositoriesFromTopic",
|
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,
|
count: 2,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
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.NoError(t, err)
|
||||||
assert.Equal(t, int64(testCase.count), count)
|
assert.Equal(t, int64(testCase.count), count)
|
||||||
})
|
})
|
|
@ -2,12 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package repo
|
package repo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
|
@ -15,18 +16,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
countRepospts = CountRepositoryOptions{OwnerID: 10}
|
countRepospts = repo_model.CountRepositoryOptions{OwnerID: 10}
|
||||||
countReposptsPublic = CountRepositoryOptions{OwnerID: 10, Private: util.OptionalBoolFalse}
|
countReposptsPublic = repo_model.CountRepositoryOptions{OwnerID: 10, Private: util.OptionalBoolFalse}
|
||||||
countReposptsPrivate = CountRepositoryOptions{OwnerID: 10, Private: util.OptionalBoolTrue}
|
countReposptsPrivate = repo_model.CountRepositoryOptions{OwnerID: 10, Private: util.OptionalBoolTrue}
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetRepositoryCount(t *testing.T) {
|
func TestGetRepositoryCount(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
ctx := db.DefaultContext
|
ctx := db.DefaultContext
|
||||||
count, err1 := CountRepositories(ctx, countRepospts)
|
count, err1 := repo_model.CountRepositories(ctx, countRepospts)
|
||||||
privateCount, err2 := CountRepositories(ctx, countReposptsPrivate)
|
privateCount, err2 := repo_model.CountRepositories(ctx, countReposptsPrivate)
|
||||||
publicCount, err3 := CountRepositories(ctx, countReposptsPublic)
|
publicCount, err3 := repo_model.CountRepositories(ctx, countReposptsPublic)
|
||||||
assert.NoError(t, err1)
|
assert.NoError(t, err1)
|
||||||
assert.NoError(t, err2)
|
assert.NoError(t, err2)
|
||||||
assert.NoError(t, err3)
|
assert.NoError(t, err3)
|
||||||
|
@ -37,7 +38,7 @@ func TestGetRepositoryCount(t *testing.T) {
|
||||||
func TestGetPublicRepositoryCount(t *testing.T) {
|
func TestGetPublicRepositoryCount(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
count, err := CountRepositories(db.DefaultContext, countReposptsPublic)
|
count, err := repo_model.CountRepositories(db.DefaultContext, countReposptsPublic)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, int64(1), count)
|
assert.Equal(t, int64(1), count)
|
||||||
}
|
}
|
||||||
|
@ -45,14 +46,14 @@ func TestGetPublicRepositoryCount(t *testing.T) {
|
||||||
func TestGetPrivateRepositoryCount(t *testing.T) {
|
func TestGetPrivateRepositoryCount(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
count, err := CountRepositories(db.DefaultContext, countReposptsPrivate)
|
count, err := repo_model.CountRepositories(db.DefaultContext, countReposptsPrivate)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, int64(2), count)
|
assert.Equal(t, int64(2), count)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRepoAPIURL(t *testing.T) {
|
func TestRepoAPIURL(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
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())
|
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package repo
|
package repo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -17,26 +18,26 @@ func TestStarRepo(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
const userID = 2
|
const userID = 2
|
||||||
const repoID = 1
|
const repoID = 1
|
||||||
unittest.AssertNotExistsBean(t, &Star{UID: userID, RepoID: repoID})
|
unittest.AssertNotExistsBean(t, &repo_model.Star{UID: userID, RepoID: repoID})
|
||||||
assert.NoError(t, StarRepo(userID, repoID, true))
|
assert.NoError(t, repo_model.StarRepo(userID, repoID, true))
|
||||||
unittest.AssertExistsAndLoadBean(t, &Star{UID: userID, RepoID: repoID})
|
unittest.AssertExistsAndLoadBean(t, &repo_model.Star{UID: userID, RepoID: repoID})
|
||||||
assert.NoError(t, StarRepo(userID, repoID, true))
|
assert.NoError(t, repo_model.StarRepo(userID, repoID, true))
|
||||||
unittest.AssertExistsAndLoadBean(t, &Star{UID: userID, RepoID: repoID})
|
unittest.AssertExistsAndLoadBean(t, &repo_model.Star{UID: userID, RepoID: repoID})
|
||||||
assert.NoError(t, StarRepo(userID, repoID, false))
|
assert.NoError(t, repo_model.StarRepo(userID, repoID, false))
|
||||||
unittest.AssertNotExistsBean(t, &Star{UID: userID, RepoID: repoID})
|
unittest.AssertNotExistsBean(t, &repo_model.Star{UID: userID, RepoID: repoID})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsStaring(t *testing.T) {
|
func TestIsStaring(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
assert.True(t, IsStaring(db.DefaultContext, 2, 4))
|
assert.True(t, repo_model.IsStaring(db.DefaultContext, 2, 4))
|
||||||
assert.False(t, IsStaring(db.DefaultContext, 3, 4))
|
assert.False(t, repo_model.IsStaring(db.DefaultContext, 3, 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRepository_GetStargazers(t *testing.T) {
|
func TestRepository_GetStargazers(t *testing.T) {
|
||||||
// repo with stargazers
|
// repo with stargazers
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 4}).(*Repository)
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}).(*repo_model.Repository)
|
||||||
gazers, err := GetStargazers(repo, db.ListOptions{Page: 0})
|
gazers, err := repo_model.GetStargazers(repo, db.ListOptions{Page: 0})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
if assert.Len(t, gazers, 1) {
|
if assert.Len(t, gazers, 1) {
|
||||||
assert.Equal(t, int64(2), gazers[0].ID)
|
assert.Equal(t, int64(2), gazers[0].ID)
|
||||||
|
@ -46,8 +47,8 @@ func TestRepository_GetStargazers(t *testing.T) {
|
||||||
func TestRepository_GetStargazers2(t *testing.T) {
|
func TestRepository_GetStargazers2(t *testing.T) {
|
||||||
// repo with stargazers
|
// repo with stargazers
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
|
||||||
gazers, err := GetStargazers(repo, db.ListOptions{Page: 0})
|
gazers, err := repo_model.GetStargazers(repo, db.ListOptions{Page: 0})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, gazers, 0)
|
assert.Len(t, gazers, 0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package repo
|
package repo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -19,47 +20,47 @@ func TestAddTopic(t *testing.T) {
|
||||||
|
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
topics, _, err := FindTopics(&FindTopicOptions{})
|
topics, _, err := repo_model.FindTopics(&repo_model.FindTopicOptions{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, topics, totalNrOfTopics)
|
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},
|
ListOptions: db.ListOptions{Page: 1, PageSize: 2},
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, topics, 2)
|
assert.Len(t, topics, 2)
|
||||||
assert.EqualValues(t, 6, total)
|
assert.EqualValues(t, 6, total)
|
||||||
|
|
||||||
topics, _, err = FindTopics(&FindTopicOptions{
|
topics, _, err = repo_model.FindTopics(&repo_model.FindTopicOptions{
|
||||||
RepoID: 1,
|
RepoID: 1,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, topics, repo1NrOfTopics)
|
assert.Len(t, topics, repo1NrOfTopics)
|
||||||
|
|
||||||
assert.NoError(t, SaveTopics(2, "golang"))
|
assert.NoError(t, repo_model.SaveTopics(2, "golang"))
|
||||||
repo2NrOfTopics := 1
|
repo2NrOfTopics := 1
|
||||||
topics, _, err = FindTopics(&FindTopicOptions{})
|
topics, _, err = repo_model.FindTopics(&repo_model.FindTopicOptions{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, topics, totalNrOfTopics)
|
assert.Len(t, topics, totalNrOfTopics)
|
||||||
|
|
||||||
topics, _, err = FindTopics(&FindTopicOptions{
|
topics, _, err = repo_model.FindTopics(&repo_model.FindTopicOptions{
|
||||||
RepoID: 2,
|
RepoID: 2,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, topics, repo2NrOfTopics)
|
assert.Len(t, topics, repo2NrOfTopics)
|
||||||
|
|
||||||
assert.NoError(t, SaveTopics(2, "golang", "gitea"))
|
assert.NoError(t, repo_model.SaveTopics(2, "golang", "gitea"))
|
||||||
repo2NrOfTopics = 2
|
repo2NrOfTopics = 2
|
||||||
totalNrOfTopics++
|
totalNrOfTopics++
|
||||||
topic, err := GetTopicByName("gitea")
|
topic, err := repo_model.GetTopicByName("gitea")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, 1, topic.RepoCount)
|
assert.EqualValues(t, 1, topic.RepoCount)
|
||||||
|
|
||||||
topics, _, err = FindTopics(&FindTopicOptions{})
|
topics, _, err = repo_model.FindTopics(&repo_model.FindTopicOptions{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, topics, totalNrOfTopics)
|
assert.Len(t, topics, totalNrOfTopics)
|
||||||
|
|
||||||
topics, _, err = FindTopics(&FindTopicOptions{
|
topics, _, err = repo_model.FindTopics(&repo_model.FindTopicOptions{
|
||||||
RepoID: 2,
|
RepoID: 2,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -67,14 +68,14 @@ func TestAddTopic(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTopicValidator(t *testing.T) {
|
func TestTopicValidator(t *testing.T) {
|
||||||
assert.True(t, ValidateTopic("12345"))
|
assert.True(t, repo_model.ValidateTopic("12345"))
|
||||||
assert.True(t, ValidateTopic("2-test"))
|
assert.True(t, repo_model.ValidateTopic("2-test"))
|
||||||
assert.True(t, ValidateTopic("test-3"))
|
assert.True(t, repo_model.ValidateTopic("test-3"))
|
||||||
assert.True(t, ValidateTopic("first"))
|
assert.True(t, repo_model.ValidateTopic("first"))
|
||||||
assert.True(t, ValidateTopic("second-test-topic"))
|
assert.True(t, repo_model.ValidateTopic("second-test-topic"))
|
||||||
assert.True(t, ValidateTopic("third-project-topic-with-max-length"))
|
assert.True(t, repo_model.ValidateTopic("third-project-topic-with-max-length"))
|
||||||
|
|
||||||
assert.False(t, ValidateTopic("$fourth-test,topic"))
|
assert.False(t, repo_model.ValidateTopic("$fourth-test,topic"))
|
||||||
assert.False(t, ValidateTopic("-fifth-test-topic"))
|
assert.False(t, repo_model.ValidateTopic("-fifth-test-topic"))
|
||||||
assert.False(t, ValidateTopic("sixth-go-project-topic-with-excess-length"))
|
assert.False(t, repo_model.ValidateTopic("sixth-go-project-topic-with-excess-length"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,3 +172,11 @@ func ChangeRepositoryName(doer *user_model.User, repo *Repository, newRepoName s
|
||||||
|
|
||||||
return committer.Commit()
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,14 @@
|
||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"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
|
// 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)
|
total, err := sess.FindAndCount(&repos)
|
||||||
return repos, total, err
|
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)
|
||||||
|
}
|
||||||
|
|
74
models/repo/user_repo_test.go
Normal file
74
models/repo/user_repo_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
|
@ -2,12 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package repo
|
package repo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
@ -17,20 +18,20 @@ import (
|
||||||
func TestIsWatching(t *testing.T) {
|
func TestIsWatching(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
assert.True(t, IsWatching(1, 1))
|
assert.True(t, repo_model.IsWatching(1, 1))
|
||||||
assert.True(t, IsWatching(4, 1))
|
assert.True(t, repo_model.IsWatching(4, 1))
|
||||||
assert.True(t, IsWatching(11, 1))
|
assert.True(t, repo_model.IsWatching(11, 1))
|
||||||
|
|
||||||
assert.False(t, IsWatching(1, 5))
|
assert.False(t, repo_model.IsWatching(1, 5))
|
||||||
assert.False(t, IsWatching(8, 1))
|
assert.False(t, repo_model.IsWatching(8, 1))
|
||||||
assert.False(t, IsWatching(unittest.NonexistentID, unittest.NonexistentID))
|
assert.False(t, repo_model.IsWatching(unittest.NonexistentID, unittest.NonexistentID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetWatchers(t *testing.T) {
|
func TestGetWatchers(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
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)
|
||||||
watches, err := GetWatchers(db.DefaultContext, repo.ID)
|
watches, err := repo_model.GetWatchers(db.DefaultContext, repo.ID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// One watchers are inactive, thus minus 1
|
// One watchers are inactive, thus minus 1
|
||||||
assert.Len(t, watches, repo.NumWatches-1)
|
assert.Len(t, watches, repo.NumWatches-1)
|
||||||
|
@ -38,7 +39,7 @@ func TestGetWatchers(t *testing.T) {
|
||||||
assert.EqualValues(t, repo.ID, watch.RepoID)
|
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.NoError(t, err)
|
||||||
assert.Len(t, watches, 0)
|
assert.Len(t, watches, 0)
|
||||||
}
|
}
|
||||||
|
@ -46,16 +47,16 @@ func TestGetWatchers(t *testing.T) {
|
||||||
func TestRepository_GetWatchers(t *testing.T) {
|
func TestRepository_GetWatchers(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
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)
|
||||||
watchers, err := GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
watchers, err := repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, watchers, repo.NumWatches)
|
assert.Len(t, watchers, repo.NumWatches)
|
||||||
for _, watcher := range watchers {
|
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)
|
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 9}).(*repo_model.Repository)
|
||||||
watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, watchers, 0)
|
assert.Len(t, watchers, 0)
|
||||||
}
|
}
|
||||||
|
@ -63,8 +64,8 @@ func TestRepository_GetWatchers(t *testing.T) {
|
||||||
func TestWatchIfAuto(t *testing.T) {
|
func TestWatchIfAuto(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
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)
|
||||||
watchers, err := GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
watchers, err := repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, watchers, repo.NumWatches)
|
assert.Len(t, watchers, repo.NumWatches)
|
||||||
|
|
||||||
|
@ -73,46 +74,46 @@ func TestWatchIfAuto(t *testing.T) {
|
||||||
prevCount := repo.NumWatches
|
prevCount := repo.NumWatches
|
||||||
|
|
||||||
// Must not add watch
|
// Must not add watch
|
||||||
assert.NoError(t, WatchIfAuto(db.DefaultContext, 8, 1, true))
|
assert.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 8, 1, true))
|
||||||
watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, watchers, prevCount)
|
assert.Len(t, watchers, prevCount)
|
||||||
|
|
||||||
// Should not add watch
|
// Should not add watch
|
||||||
assert.NoError(t, WatchIfAuto(db.DefaultContext, 10, 1, true))
|
assert.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 10, 1, true))
|
||||||
watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, watchers, prevCount)
|
assert.Len(t, watchers, prevCount)
|
||||||
|
|
||||||
setting.Service.AutoWatchOnChanges = true
|
setting.Service.AutoWatchOnChanges = true
|
||||||
|
|
||||||
// Must not add watch
|
// Must not add watch
|
||||||
assert.NoError(t, WatchIfAuto(db.DefaultContext, 8, 1, true))
|
assert.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 8, 1, true))
|
||||||
watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, watchers, prevCount)
|
assert.Len(t, watchers, prevCount)
|
||||||
|
|
||||||
// Should not add watch
|
// Should not add watch
|
||||||
assert.NoError(t, WatchIfAuto(db.DefaultContext, 12, 1, false))
|
assert.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 12, 1, false))
|
||||||
watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, watchers, prevCount)
|
assert.Len(t, watchers, prevCount)
|
||||||
|
|
||||||
// Should add watch
|
// Should add watch
|
||||||
assert.NoError(t, WatchIfAuto(db.DefaultContext, 12, 1, true))
|
assert.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 12, 1, true))
|
||||||
watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, watchers, prevCount+1)
|
assert.Len(t, watchers, prevCount+1)
|
||||||
|
|
||||||
// Should remove watch, inhibit from adding auto
|
// Should remove watch, inhibit from adding auto
|
||||||
assert.NoError(t, WatchRepo(db.DefaultContext, 12, 1, false))
|
assert.NoError(t, repo_model.WatchRepo(db.DefaultContext, 12, 1, false))
|
||||||
watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, watchers, prevCount)
|
assert.Len(t, watchers, prevCount)
|
||||||
|
|
||||||
// Must not add watch
|
// Must not add watch
|
||||||
assert.NoError(t, WatchIfAuto(db.DefaultContext, 12, 1, true))
|
assert.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 12, 1, true))
|
||||||
watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, watchers, prevCount)
|
assert.Len(t, watchers, prevCount)
|
||||||
}
|
}
|
||||||
|
@ -120,20 +121,20 @@ func TestWatchIfAuto(t *testing.T) {
|
||||||
func TestWatchRepoMode(t *testing.T) {
|
func TestWatchRepoMode(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
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))
|
assert.NoError(t, repo_model.WatchRepoMode(12, 1, repo_model.WatchModeAuto))
|
||||||
unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 1)
|
unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1}, 1)
|
||||||
unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: WatchModeAuto}, 1)
|
unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1, Mode: repo_model.WatchModeAuto}, 1)
|
||||||
|
|
||||||
assert.NoError(t, WatchRepoMode(12, 1, WatchModeNormal))
|
assert.NoError(t, repo_model.WatchRepoMode(12, 1, repo_model.WatchModeNormal))
|
||||||
unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 1)
|
unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1}, 1)
|
||||||
unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: WatchModeNormal}, 1)
|
unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1, Mode: repo_model.WatchModeNormal}, 1)
|
||||||
|
|
||||||
assert.NoError(t, WatchRepoMode(12, 1, WatchModeDont))
|
assert.NoError(t, repo_model.WatchRepoMode(12, 1, repo_model.WatchModeDont))
|
||||||
unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 1)
|
unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1}, 1)
|
||||||
unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: WatchModeDont}, 1)
|
unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1, Mode: repo_model.WatchModeDont}, 1)
|
||||||
|
|
||||||
assert.NoError(t, WatchRepoMode(12, 1, WatchModeNone))
|
assert.NoError(t, repo_model.WatchRepoMode(12, 1, repo_model.WatchModeNone))
|
||||||
unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 0)
|
unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1}, 0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package repo
|
package repo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ import (
|
||||||
func TestRepository_WikiCloneLink(t *testing.T) {
|
func TestRepository_WikiCloneLink(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
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()
|
cloneLink := repo.WikiCloneLink()
|
||||||
assert.Equal(t, "ssh://sshuser@try.gitea.io:3000/user2/repo1.wiki.git", cloneLink.SSH)
|
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)
|
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) {
|
func TestWikiPath(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
expected := filepath.Join(setting.RepoRootPath, "user2/repo1.wiki.git")
|
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) {
|
func TestRepository_WikiPath(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
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")
|
expected := filepath.Join(setting.RepoRootPath, "user2/repo1.wiki.git")
|
||||||
assert.Equal(t, expected, repo.WikiPath())
|
assert.Equal(t, expected, repo.WikiPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRepository_HasWiki(t *testing.T) {
|
func TestRepository_HasWiki(t *testing.T) {
|
||||||
unittest.PrepareTestEnv(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())
|
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())
|
assert.False(t, repo2.HasWiki())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -84,127 +84,8 @@ func TestMetas(t *testing.T) {
|
||||||
assert.Equal(t, ",owners,team1,", metas["teams"])
|
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) {
|
func TestDoctorUserStarNum(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
assert.NoError(t, DoctorUserStarNum())
|
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))
|
|
||||||
}
|
|
||||||
|
|
|
@ -397,6 +397,14 @@ func CreateWebhook(ctx context.Context, w *Webhook) error {
|
||||||
return db.Insert(ctx, w)
|
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,
|
// getWebhook uses argument bean as query condition,
|
||||||
// ID must be specified and do not assign unnecessary fields.
|
// ID must be specified and do not assign unnecessary fields.
|
||||||
func getWebhook(bean *Webhook) (*Webhook, error) {
|
func getWebhook(bean *Webhook) (*Webhook, error) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
code_indexer "code.gitea.io/gitea/modules/indexer/code"
|
code_indexer "code.gitea.io/gitea/modules/indexer/code"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/markup/markdown"
|
"code.gitea.io/gitea/modules/markup/markdown"
|
||||||
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"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["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues)
|
||||||
ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(unit_model.TypePullRequests)
|
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 {
|
if err != nil {
|
||||||
ctx.ServerError("CanUserForkRepo", err)
|
ctx.ServerError("CanUserForkRepo", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Data["CanSignedUserFork"] = canSignedUserFork
|
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 {
|
if err != nil {
|
||||||
ctx.ServerError("GetForksByUserAndOrgs", err)
|
ctx.ServerError("GetForksByUserAndOrgs", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -291,8 +291,8 @@ func populateIssueIndexer(ctx context.Context) {
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
repos, _, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
|
repos, _, err := repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{Page: page, PageSize: models.RepositoryListDefaultPageSize},
|
ListOptions: db.ListOptions{Page: page, PageSize: repo_model.RepositoryListDefaultPageSize},
|
||||||
OrderBy: db.SearchOrderByID,
|
OrderBy: db.SearchOrderByID,
|
||||||
Private: true,
|
Private: true,
|
||||||
Collaborate: util.OptionalBoolFalse,
|
Collaborate: util.OptionalBoolFalse,
|
||||||
|
|
|
@ -7,15 +7,20 @@ package repository
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"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)
|
return fmt.Errorf("checkDaemonExportOK: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,3 +138,111 @@ func CreateRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (
|
||||||
|
|
||||||
return repo, nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/structs"
|
"code.gitea.io/gitea/modules/structs"
|
||||||
|
@ -147,3 +148,23 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
|
||||||
}
|
}
|
||||||
assert.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization")
|
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)
|
||||||
|
}
|
||||||
|
|
33
modules/repository/delete.go
Normal file
33
modules/repository/delete.go
Normal file
|
@ -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
|
||||||
|
}
|
31
modules/repository/fork.go
Normal file
31
modules/repository/fork.go
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -5,6 +5,8 @@
|
||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
@ -20,6 +22,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
|
"github.com/gobwas/glob"
|
||||||
"github.com/huandu/xstrings"
|
"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")
|
gtPath := filepath.Join(tmpDir, ".gitea", "template")
|
||||||
if _, err := os.Stat(gtPath); os.IsNotExist(err) {
|
if _, err := os.Stat(gtPath); os.IsNotExist(err) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -91,7 +125,7 @@ func checkGiteaTemplate(tmpDir string) (*models.GiteaTemplate, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
gt := &models.GiteaTemplate{
|
gt := &GiteaTemplate{
|
||||||
Path: gtPath,
|
Path: gtPath,
|
||||||
Content: content,
|
Content: content,
|
||||||
}
|
}
|
||||||
|
@ -227,7 +261,7 @@ func generateGitContent(ctx context.Context, repo, templateRepo, generateRepo *r
|
||||||
if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
|
if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
|
||||||
return fmt.Errorf("setDefaultBranch: %v", err)
|
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)
|
return fmt.Errorf("updateRepository: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +274,7 @@ func GenerateGitContent(ctx context.Context, templateRepo, generateRepo *repo_mo
|
||||||
return err
|
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)
|
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
|
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
|
// 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{
|
generateRepo := &repo_model.Repository{
|
||||||
OwnerID: owner.ID,
|
OwnerID: owner.ID,
|
||||||
Owner: owner,
|
Owner: owner,
|
||||||
|
@ -288,7 +341,7 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ
|
||||||
return generateRepo, err
|
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)
|
return generateRepo, fmt.Errorf("checkDaemonExportOK: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package models
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
|
@ -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)
|
return fmt.Errorf("updateRepository: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
||||||
repo.Owner = u
|
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)
|
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 {
|
ctx, committer, err := db.TxContext()
|
||||||
log.Error("Failed to update size for repository: %v", err)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer committer.Close()
|
||||||
|
|
||||||
if opts.Mirror {
|
if opts.Mirror {
|
||||||
mirrorModel := repo_model.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)
|
return repo, fmt.Errorf("InsertOne: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
repo.IsMirror = true
|
repo.IsMirror = true
|
||||||
err = models.UpdateRepository(repo, false)
|
if err = UpdateRepository(ctx, repo, false); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
} else {
|
} 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".
|
// 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
|
// SyncReleasesWithTags synchronizes release table with repository tags
|
||||||
|
|
|
@ -312,7 +312,7 @@ func GetReviewers(ctx *context.APIContext) {
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/UserList"
|
// "$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 {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
|
ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
|
||||||
return
|
return
|
||||||
|
@ -342,7 +342,7 @@ func GetAssignees(ctx *context.APIContext) {
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/UserList"
|
// "$ref": "#/responses/UserList"
|
||||||
|
|
||||||
assignees, err := models.GetRepoAssignees(ctx.Repo.Repository)
|
assignees, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
|
ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
@ -130,7 +131,7 @@ func SearchIssues(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// find repos user can access (for issue search)
|
// find repos user can access (for issue search)
|
||||||
opts := &models.SearchRepoOptions{
|
opts := &repo_model.SearchRepoOptions{
|
||||||
Private: false,
|
Private: false,
|
||||||
AllPublic: true,
|
AllPublic: true,
|
||||||
TopicOnly: false,
|
TopicOnly: false,
|
||||||
|
@ -176,8 +177,8 @@ func SearchIssues(ctx *context.APIContext) {
|
||||||
opts.TeamID = team.ID
|
opts.TeamID = team.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
repoCond := models.SearchRepositoryCondition(opts)
|
repoCond := repo_model.SearchRepositoryCondition(opts)
|
||||||
repoIDs, _, err := models.SearchRepositoryIDs(opts)
|
repoIDs, _, err := repo_model.SearchRepositoryIDs(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err)
|
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -123,7 +123,7 @@ func Search(ctx *context.APIContext) {
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
|
||||||
opts := &models.SearchRepoOptions{
|
opts := &repo_model.SearchRepoOptions{
|
||||||
ListOptions: utils.GetListOptions(ctx),
|
ListOptions: utils.GetListOptions(ctx),
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
Keyword: ctx.FormTrim("q"),
|
Keyword: ctx.FormTrim("q"),
|
||||||
|
@ -192,7 +192,7 @@ func Search(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
repos, count, err := models.SearchRepository(opts)
|
repos, count, err := repo_model.SearchRepository(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(http.StatusInternalServerError, api.SearchError{
|
ctx.JSON(http.StatusInternalServerError, api.SearchError{
|
||||||
OK: false,
|
OK: false,
|
||||||
|
@ -344,7 +344,7 @@ func Generate(ctx *context.APIContext) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := models.GenerateRepoOptions{
|
opts := repo_module.GenerateRepoOptions{
|
||||||
Name: form.Name,
|
Name: form.Name,
|
||||||
DefaultBranch: form.DefaultBranch,
|
DefaultBranch: form.DefaultBranch,
|
||||||
Description: form.Description,
|
Description: form.Description,
|
||||||
|
@ -717,7 +717,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
|
||||||
repo.DefaultBranch = *opts.DefaultBranch
|
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)
|
ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1036,7 +1036,7 @@ func Delete(ctx *context.APIContext) {
|
||||||
owner := ctx.Repo.Owner
|
owner := ctx.Repo.Owner
|
||||||
repo := ctx.Repo.Repository
|
repo := ctx.Repo.Repository
|
||||||
|
|
||||||
canDelete, err := models.CanUserDelete(repo, ctx.Doer)
|
canDelete, err := repo_module.CanUserDelete(repo, ctx.Doer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "CanUserDelete", err)
|
ctx.Error(http.StatusInternalServerError, "CanUserDelete", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -7,9 +7,9 @@ package user
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
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"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
|
@ -21,7 +21,7 @@ import (
|
||||||
func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) {
|
func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) {
|
||||||
opts := utils.GetListOptions(ctx)
|
opts := utils.GetListOptions(ctx)
|
||||||
|
|
||||||
repos, count, err := models.GetUserRepositories(&models.SearchRepoOptions{
|
repos, count, err := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{
|
||||||
Actor: u,
|
Actor: u,
|
||||||
Private: private,
|
Private: private,
|
||||||
ListOptions: opts,
|
ListOptions: opts,
|
||||||
|
@ -103,7 +103,7 @@ func ListMyRepos(ctx *context.APIContext) {
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/RepositoryList"
|
// "$ref": "#/responses/RepositoryList"
|
||||||
|
|
||||||
opts := &models.SearchRepoOptions{
|
opts := &repo_model.SearchRepoOptions{
|
||||||
ListOptions: utils.GetListOptions(ctx),
|
ListOptions: utils.GetListOptions(ctx),
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
OwnerID: ctx.Doer.ID,
|
OwnerID: ctx.Doer.ID,
|
||||||
|
@ -112,7 +112,7 @@ func ListMyRepos(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
repos, count, err := models.SearchRepository(opts)
|
repos, count, err := repo_model.SearchRepository(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "SearchRepository", err)
|
ctx.Error(http.StatusInternalServerError, "SearchRepository", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -55,7 +55,7 @@ func Code(ctx *context.Context) {
|
||||||
|
|
||||||
// guest user or non-admin user
|
// guest user or non-admin user
|
||||||
if ctx.Doer == nil || !isAdmin {
|
if ctx.Doer == nil || !isAdmin {
|
||||||
repoIDs, err = models.FindUserAccessibleRepoIDs(ctx.Doer)
|
repoIDs, err = repo_model.FindUserAccessibleRepoIDs(ctx.Doer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("SearchResults", err)
|
ctx.ServerError("SearchResults", err)
|
||||||
return
|
return
|
||||||
|
@ -79,7 +79,7 @@ func Code(ctx *context.Context) {
|
||||||
rightRepoMap := make(map[int64]*repo_model.Repository, len(repoMaps))
|
rightRepoMap := make(map[int64]*repo_model.Repository, len(repoMaps))
|
||||||
repoIDs = make([]int64, 0, len(repoMaps))
|
repoIDs = make([]int64, 0, len(repoMaps))
|
||||||
for id, repo := range 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
|
rightRepoMap[id] = repo
|
||||||
repoIDs = append(repoIDs, id)
|
repoIDs = append(repoIDs, id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ package explore
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
@ -81,7 +80,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
|
||||||
language := ctx.FormTrim("language")
|
language := ctx.FormTrim("language")
|
||||||
ctx.Data["Language"] = language
|
ctx.Data["Language"] = language
|
||||||
|
|
||||||
repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
|
repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
Page: page,
|
Page: page,
|
||||||
PageSize: opts.PageSize,
|
PageSize: opts.PageSize,
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
@ -105,7 +104,7 @@ func Home(ctx *context.Context) {
|
||||||
count int64
|
count int64
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
|
repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
PageSize: setting.UI.User.RepoPagingNum,
|
PageSize: setting.UI.User.RepoPagingNum,
|
||||||
Page: page,
|
Page: page,
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
user_setting "code.gitea.io/gitea/routers/web/user/setting"
|
user_setting "code.gitea.io/gitea/routers/web/user/setting"
|
||||||
"code.gitea.io/gitea/services/forms"
|
"code.gitea.io/gitea/services/forms"
|
||||||
"code.gitea.io/gitea/services/org"
|
"code.gitea.io/gitea/services/org"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
user_service "code.gitea.io/gitea/services/user"
|
user_service "code.gitea.io/gitea/services/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -117,7 +118,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
|
|
||||||
// update forks visibility
|
// update forks visibility
|
||||||
if visibilityChanged {
|
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},
|
Actor: org.AsUser(), Private: true, ListOptions: db.ListOptions{Page: 1, PageSize: org.NumRepos},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -126,7 +127,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
for _, repo := range repos {
|
for _, repo := range repos {
|
||||||
repo.OwnerName = org.Name
|
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)
|
ctx.ServerError("UpdateRepository", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
access_model "code.gitea.io/gitea/models/perm/access"
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
@ -19,6 +18,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/upload"
|
"code.gitea.io/gitea/modules/upload"
|
||||||
"code.gitea.io/gitea/routers/common"
|
"code.gitea.io/gitea/routers/common"
|
||||||
"code.gitea.io/gitea/services/attachment"
|
"code.gitea.io/gitea/services/attachment"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UploadIssueAttachment response for Issue/PR attachments
|
// UploadIssueAttachment response for Issue/PR attachments
|
||||||
|
@ -95,7 +95,7 @@ func GetAttachment(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
repository, unitType, err := models.LinkedRepository(attach)
|
repository, unitType, err := repo_service.LinkedRepository(attach)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("LinkedRepository", err)
|
ctx.ServerError("LinkedRepository", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -457,7 +457,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
|
||||||
if rootRepo != nil &&
|
if rootRepo != nil &&
|
||||||
rootRepo.ID != ci.HeadRepo.ID &&
|
rootRepo.ID != ci.HeadRepo.ID &&
|
||||||
rootRepo.ID != baseRepo.ID {
|
rootRepo.ID != baseRepo.ID {
|
||||||
canRead := models.CheckRepoUnitUser(rootRepo, ctx.Doer, unit.TypeCode)
|
canRead := models.CheckRepoUnitUser(ctx, rootRepo, ctx.Doer, unit.TypeCode)
|
||||||
if canRead {
|
if canRead {
|
||||||
ctx.Data["RootRepo"] = rootRepo
|
ctx.Data["RootRepo"] = rootRepo
|
||||||
if !fileOnly {
|
if !fileOnly {
|
||||||
|
@ -482,7 +482,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
|
||||||
ownForkRepo.ID != ci.HeadRepo.ID &&
|
ownForkRepo.ID != ci.HeadRepo.ID &&
|
||||||
ownForkRepo.ID != baseRepo.ID &&
|
ownForkRepo.ID != baseRepo.ID &&
|
||||||
(rootRepo == nil || ownForkRepo.ID != rootRepo.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 {
|
if canRead {
|
||||||
ctx.Data["OwnForkRepo"] = ownForkRepo
|
ctx.Data["OwnForkRepo"] = ownForkRepo
|
||||||
if !fileOnly {
|
if !fileOnly {
|
||||||
|
|
|
@ -49,6 +49,7 @@ import (
|
||||||
"code.gitea.io/gitea/services/forms"
|
"code.gitea.io/gitea/services/forms"
|
||||||
issue_service "code.gitea.io/gitea/services/issue"
|
issue_service "code.gitea.io/gitea/services/issue"
|
||||||
pull_service "code.gitea.io/gitea/services/pull"
|
pull_service "code.gitea.io/gitea/services/pull"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -283,7 +284,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
|
||||||
ctx.Data["CommitStatuses"] = commitStatuses
|
ctx.Data["CommitStatuses"] = commitStatuses
|
||||||
|
|
||||||
// Get assignees.
|
// Get assignees.
|
||||||
ctx.Data["Assignees"], err = models.GetRepoAssignees(repo)
|
ctx.Data["Assignees"], err = repo_model.GetRepoAssignees(ctx, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetAssignees", err)
|
ctx.ServerError("GetAssignees", err)
|
||||||
return
|
return
|
||||||
|
@ -441,7 +442,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["Assignees"], err = models.GetRepoAssignees(repo)
|
ctx.Data["Assignees"], err = repo_model.GetRepoAssignees(ctx, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetAssignees", err)
|
ctx.ServerError("GetAssignees", err)
|
||||||
return
|
return
|
||||||
|
@ -522,13 +523,13 @@ func RetrieveRepoReviewers(ctx *context.Context, repo *repo_model.Repository, is
|
||||||
posterID = 0
|
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 {
|
if err != nil {
|
||||||
ctx.ServerError("GetReviewers", err)
|
ctx.ServerError("GetReviewers", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
teamReviewers, err = models.GetReviewerTeams(repo)
|
teamReviewers, err = repo_service.GetReviewerTeams(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetReviewerTeams", err)
|
ctx.ServerError("GetReviewerTeams", err)
|
||||||
return
|
return
|
||||||
|
@ -2160,7 +2161,7 @@ func SearchIssues(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// find repos user can access (for issue search)
|
// find repos user can access (for issue search)
|
||||||
opts := &models.SearchRepoOptions{
|
opts := &repo_model.SearchRepoOptions{
|
||||||
Private: false,
|
Private: false,
|
||||||
AllPublic: true,
|
AllPublic: true,
|
||||||
TopicOnly: false,
|
TopicOnly: false,
|
||||||
|
@ -2206,8 +2207,8 @@ func SearchIssues(ctx *context.Context) {
|
||||||
opts.TeamID = team.ID
|
opts.TeamID = team.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
repoCond := models.SearchRepositoryCondition(opts)
|
repoCond := repo_model.SearchRepositoryCondition(opts)
|
||||||
repoIDs, _, err := models.SearchRepositoryIDs(opts)
|
repoIDs, _, err := repo_model.SearchRepositoryIDs(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err.Error())
|
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err.Error())
|
||||||
return
|
return
|
||||||
|
|
|
@ -758,7 +758,7 @@ func ViewPullFiles(ctx *context.Context) {
|
||||||
setCompareContext(ctx, baseCommit, commit, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
setCompareContext(ctx, baseCommit, commit, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
||||||
|
|
||||||
ctx.Data["RequireTribute"] = true
|
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)
|
ctx.ServerError("GetAssignees", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,7 +152,7 @@ func Create(ctx *context.Context) {
|
||||||
templateID := ctx.FormInt64("template_id")
|
templateID := ctx.FormInt64("template_id")
|
||||||
if templateID > 0 {
|
if templateID > 0 {
|
||||||
templateRepo, err := repo_model.GetRepositoryByID(templateID)
|
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"] = templateID
|
||||||
ctx.Data["repo_template_name"] = templateRepo.Name
|
ctx.Data["repo_template_name"] = templateRepo.Name
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,7 @@ func CreatePost(ctx *context.Context) {
|
||||||
var repo *repo_model.Repository
|
var repo *repo_model.Repository
|
||||||
var err error
|
var err error
|
||||||
if form.RepoTemplate > 0 {
|
if form.RepoTemplate > 0 {
|
||||||
opts := models.GenerateRepoOptions{
|
opts := repo_module.GenerateRepoOptions{
|
||||||
Name: form.RepoName,
|
Name: form.RepoName,
|
||||||
Description: form.Description,
|
Description: form.Description,
|
||||||
Private: form.Private,
|
Private: form.Private,
|
||||||
|
@ -304,7 +304,7 @@ func Action(ctx *context.Context) {
|
||||||
|
|
||||||
ctx.Repo.Repository.Description = ctx.FormString("desc")
|
ctx.Repo.Repository.Description = ctx.FormString("desc")
|
||||||
ctx.Repo.Repository.Website = ctx.FormString("site")
|
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 {
|
if err != nil {
|
||||||
|
@ -509,7 +509,7 @@ func InitiateDownload(ctx *context.Context) {
|
||||||
|
|
||||||
// SearchRepo repositories via options
|
// SearchRepo repositories via options
|
||||||
func SearchRepo(ctx *context.Context) {
|
func SearchRepo(ctx *context.Context) {
|
||||||
opts := &models.SearchRepoOptions{
|
opts := &repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
Page: ctx.FormInt("page"),
|
Page: ctx.FormInt("page"),
|
||||||
PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
|
PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
|
||||||
|
@ -581,7 +581,7 @@ func SearchRepo(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
repos, count, err := models.SearchRepository(opts)
|
repos, count, err := repo_model.SearchRepository(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(http.StatusInternalServerError, api.SearchError{
|
ctx.JSON(http.StatusInternalServerError, api.SearchError{
|
||||||
OK: false,
|
OK: false,
|
||||||
|
|
|
@ -168,7 +168,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
repo.IsPrivate = form.Private
|
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)
|
ctx.ServerError("UpdateRepository", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -491,7 +491,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if repoChanged {
|
if repoChanged {
|
||||||
if err := models.UpdateRepository(repo, false); err != nil {
|
if err := repo_service.UpdateRepository(repo, false); err != nil {
|
||||||
ctx.ServerError("UpdateRepository", err)
|
ctx.ServerError("UpdateRepository", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -510,7 +510,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
if err := models.UpdateRepository(repo, false); err != nil {
|
if err := repo_service.UpdateRepository(repo, false); err != nil {
|
||||||
ctx.ServerError("UpdateRepository", err)
|
ctx.ServerError("UpdateRepository", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -530,7 +530,7 @@ func SettingsPost(ctx *context.Context) {
|
||||||
repo.IsFsckEnabled = form.EnableHealthCheck
|
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)
|
ctx.ServerError("UpdateRepository", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/lfs"
|
"code.gitea.io/gitea/modules/lfs"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"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/setting"
|
||||||
"code.gitea.io/gitea/modules/structs"
|
"code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/typesniffer"
|
"code.gitea.io/gitea/modules/typesniffer"
|
||||||
|
@ -905,7 +906,7 @@ func renderCode(ctx *context.Context) {
|
||||||
ctx.ServerError("UpdateRepositoryCols", err)
|
ctx.ServerError("UpdateRepositoryCols", err)
|
||||||
return
|
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)
|
ctx.ServerError("UpdateRepoSize", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,7 +166,7 @@ func Milestones(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
repoOpts := models.SearchRepoOptions{
|
repoOpts := repo_model.SearchRepoOptions{
|
||||||
Actor: ctxUser,
|
Actor: ctxUser,
|
||||||
OwnerID: ctxUser.ID,
|
OwnerID: ctxUser.ID,
|
||||||
Private: true,
|
Private: true,
|
||||||
|
@ -181,7 +181,7 @@ func Milestones(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
userRepoCond = models.SearchRepositoryCondition(&repoOpts) // all repo condition user could visit
|
userRepoCond = repo_model.SearchRepositoryCondition(&repoOpts) // all repo condition user could visit
|
||||||
repoCond = userRepoCond
|
repoCond = userRepoCond
|
||||||
repoIDs []int64
|
repoIDs []int64
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ func Milestones(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
showRepos, _, err := models.SearchRepositoryByCondition(&repoOpts, userRepoCond, false)
|
showRepos, _, err := repo_model.SearchRepositoryByCondition(&repoOpts, userRepoCond, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("SearchRepositoryByCondition", err)
|
ctx.ServerError("SearchRepositoryByCondition", err)
|
||||||
return
|
return
|
||||||
|
@ -437,7 +437,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||||
// As team:
|
// As team:
|
||||||
// - Team org's owns the repository.
|
// - Team org's owns the repository.
|
||||||
// - Team has read permission to repository.
|
// - Team has read permission to repository.
|
||||||
repoOpts := &models.SearchRepoOptions{
|
repoOpts := &repo_model.SearchRepoOptions{
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
OwnerID: ctx.Doer.ID,
|
OwnerID: ctx.Doer.ID,
|
||||||
Private: true,
|
Private: true,
|
||||||
|
@ -559,7 +559,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// a RepositoryList
|
// a RepositoryList
|
||||||
showRepos := models.RepositoryListOfMap(showReposMap)
|
showRepos := repo_model.RepositoryListOfMap(showReposMap)
|
||||||
sort.Sort(showRepos)
|
sort.Sort(showRepos)
|
||||||
|
|
||||||
// maps pull request IDs to their CommitStatus. Will be posted to ctx.Data.
|
// maps pull request IDs to their CommitStatus. Will be posted to ctx.Data.
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/test"
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
@ -26,7 +26,7 @@ func TestArchivedIssues(t *testing.T) {
|
||||||
ctx.Req.Form.Set("state", "open")
|
ctx.Req.Form.Set("state", "open")
|
||||||
|
|
||||||
// Assume: User 30 has access to two Repos with Issues, one of the Repos being archived.
|
// 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)
|
assert.Len(t, repos, 2)
|
||||||
IsArchived := make(map[int64]bool)
|
IsArchived := make(map[int64]bool)
|
||||||
NumIssues := make(map[int64]int)
|
NumIssues := make(map[int64]int)
|
||||||
|
|
|
@ -7,7 +7,6 @@ package user
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
packages_model "code.gitea.io/gitea/models/packages"
|
packages_model "code.gitea.io/gitea/models/packages"
|
||||||
container_model "code.gitea.io/gitea/models/packages/container"
|
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["ContextUser"] = ctx.ContextUser
|
||||||
ctx.Data["PackageDescriptor"] = pd
|
ctx.Data["PackageDescriptor"] = pd
|
||||||
|
|
||||||
repos, _, _ := models.GetUserRepositories(&models.SearchRepoOptions{
|
repos, _, _ := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{
|
||||||
Actor: pd.Owner,
|
Actor: pd.Owner,
|
||||||
Private: true,
|
Private: true,
|
||||||
})
|
})
|
||||||
|
|
|
@ -195,7 +195,7 @@ func Profile(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
case "stars":
|
case "stars":
|
||||||
ctx.Data["PageIsProfileStarList"] = true
|
ctx.Data["PageIsProfileStarList"] = true
|
||||||
repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
|
repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
PageSize: setting.UI.User.RepoPagingNum,
|
PageSize: setting.UI.User.RepoPagingNum,
|
||||||
Page: page,
|
Page: page,
|
||||||
|
@ -227,7 +227,7 @@ func Profile(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case "watching":
|
case "watching":
|
||||||
repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
|
repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
PageSize: setting.UI.User.RepoPagingNum,
|
PageSize: setting.UI.User.RepoPagingNum,
|
||||||
Page: page,
|
Page: page,
|
||||||
|
@ -249,7 +249,7 @@ func Profile(ctx *context.Context) {
|
||||||
|
|
||||||
total = int(count)
|
total = int(count)
|
||||||
default:
|
default:
|
||||||
repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
|
repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
PageSize: setting.UI.User.RepoPagingNum,
|
PageSize: setting.UI.User.RepoPagingNum,
|
||||||
Page: page,
|
Page: page,
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
@ -304,7 +303,7 @@ func Repos(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userRepos, _, err := models.GetUserRepositories(&models.SearchRepoOptions{
|
userRepos, _, err := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{
|
||||||
Actor: ctxUser,
|
Actor: ctxUser,
|
||||||
Private: true,
|
Private: true,
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
|
@ -329,7 +328,7 @@ func Repos(ctx *context.Context) {
|
||||||
ctx.Data["Dirs"] = repoNames
|
ctx.Data["Dirs"] = repoNames
|
||||||
ctx.Data["ReposMap"] = repos
|
ctx.Data["ReposMap"] = repos
|
||||||
} else {
|
} 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 {
|
if err != nil {
|
||||||
ctx.ServerError("GetUserRepositories", err)
|
ctx.ServerError("GetUserRepositories", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -1220,7 +1220,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
|
||||||
oid := strings.TrimPrefix(line[1:], lfs.MetaFileOidPrefix)
|
oid := strings.TrimPrefix(line[1:], lfs.MetaFileOidPrefix)
|
||||||
if len(oid) == 64 {
|
if len(oid) == 64 {
|
||||||
m := &models.LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}}
|
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 {
|
if err == nil && count > 0 {
|
||||||
curFile.IsBin = true
|
curFile.IsBin = true
|
||||||
|
|
|
@ -145,7 +145,7 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
|
||||||
visited[user.ID] = true
|
visited[user.ID] = true
|
||||||
|
|
||||||
// test if this user is allowed to see the issue/pull
|
// 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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
admin_model "code.gitea.io/gitea/models/admin"
|
admin_model "code.gitea.io/gitea/models/admin"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
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()
|
gitRepo.Close()
|
||||||
|
|
||||||
log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo)
|
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)
|
log.Error("SyncMirrors [repo: %-v]: failed to update size for mirror repository: %v", m.Repo, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ func AdoptRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*
|
||||||
if err := adoptRepository(ctx, repoPath, doer, repo, opts); err != nil {
|
if err := adoptRepository(ctx, repoPath, doer, repo, opts); err != nil {
|
||||||
return fmt.Errorf("createDelegateHooks: %v", err)
|
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)
|
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)
|
return fmt.Errorf("updateRepository: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +246,7 @@ func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unad
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
repos, _, err := models.GetUserRepositories(&models.SearchRepoOptions{
|
repos, _, err := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{
|
||||||
Actor: ctxUser,
|
Actor: ctxUser,
|
||||||
Private: true,
|
Private: true,
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"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
|
// 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)
|
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)
|
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 {
|
if err = admin_model.CreateRepositoryNotice(desc); err != nil {
|
||||||
|
|
|
@ -96,7 +96,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
|
||||||
return err
|
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
|
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)
|
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)
|
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
|
// 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)
|
log.Error("Failed to update size for repository: %v", err)
|
||||||
}
|
}
|
||||||
if err := repo_model.CopyLanguageStat(opts.BaseRepo, repo); err != nil {
|
if err := repo_model.CopyLanguageStat(opts.BaseRepo, repo); err != nil {
|
||||||
|
@ -173,7 +173,7 @@ func ConvertForkToNormalRepository(repo *repo_model.Repository) error {
|
||||||
return nil
|
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)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ func ConvertForkToNormalRepository(repo *repo_model.Repository) error {
|
||||||
repo.IsFork = false
|
repo.IsFork = false
|
||||||
repo.ForkID = 0
|
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)
|
log.Error("Unable to update repository %-v whilst converting from fork. Error: %v", repo, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
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/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
|
@ -84,3 +85,29 @@ func GenerateGitHooks(ctx context.Context, templateRepo, generateRepo *repo_mode
|
||||||
}
|
}
|
||||||
return nil
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||||
}
|
}
|
||||||
defer gitRepo.Close()
|
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)
|
log.Error("Failed to update size for repository: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
packages_model "code.gitea.io/gitea/models/packages"
|
packages_model "code.gitea.io/gitea/models/packages"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/models/unit"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/notification"
|
"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())
|
admin_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repositories", repo_module.LocalCopyPath())
|
||||||
return initPushQueue()
|
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
|
||||||
|
}
|
||||||
|
|
43
services/repository/repository_test.go
Normal file
43
services/repository/repository_test.go
Normal file
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
24
services/repository/review.go
Normal file
24
services/repository/review.go
Normal file
|
@ -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)
|
||||||
|
}
|
28
services/repository/review_test.go
Normal file
28
services/repository/review_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
|
@ -16,8 +16,27 @@ import (
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
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
|
// 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() {
|
if !doer.IsAdmin && !owner.CanCreateRepo() {
|
||||||
return nil, repo_model.ErrReachLimitOfRepo{
|
return nil, repo_model.ErrReachLimitOfRepo{
|
||||||
Limit: owner.MaxRepoCreation,
|
Limit: owner.MaxRepoCreation,
|
||||||
|
@ -54,7 +73,7 @@ func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.R
|
||||||
|
|
||||||
// Webhooks
|
// Webhooks
|
||||||
if opts.Webhooks {
|
if opts.Webhooks {
|
||||||
if err = models.GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
|
if err = GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +87,7 @@ func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.R
|
||||||
|
|
||||||
// Issue Labels
|
// Issue Labels
|
||||||
if opts.IssueLabels {
|
if opts.IssueLabels {
|
||||||
if err = models.GenerateIssueLabels(ctx, templateRepo, generateRepo); err != nil {
|
if err = GenerateIssueLabels(ctx, templateRepo, generateRepo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue