From 572324049008ac803d3d7c17a7b3a81ef00386fc Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 12 Dec 2021 23:48:20 +0800 Subject: [PATCH] Some repository refactors (#17950) * some repository refactors * remove unnecessary code * Fix test * Remove unnecessary banner --- cmd/migrate_storage.go | 2 +- integrations/delete_user_test.go | 2 +- integrations/pull_update_test.go | 2 +- integrations/repo_watch_test.go | 6 +- models/action.go | 125 +++++++ models/action_test.go | 37 ++ models/error.go | 63 ---- models/issue_watch.go | 5 +- models/notification.go | 2 +- models/org.go | 2 +- models/org_team.go | 8 +- models/release.go | 86 +++++ models/repo.go | 303 +--------------- models/repo/archiver.go | 7 + models/repo/fork.go | 69 ++++ models/repo/fork_test.go | 32 ++ models/repo/main_test.go | 6 + models/repo/redirect.go | 82 +++++ models/repo/redirect_test.go | 77 ++++ models/repo/repo.go | 36 ++ models/repo/repo_test.go | 7 + models/repo/repo_unit.go | 26 ++ models/{ => repo}/star.go | 5 +- models/{ => repo}/star_test.go | 7 +- models/{ => repo}/topic.go | 25 +- models/{ => repo}/topic_test.go | 2 +- models/repo/update.go | 179 ++++++++++ models/repo/watch.go | 196 +++++++++++ .../watch_test.go} | 80 +---- models/repo_archiver.go | 25 -- models/repo_collaboration.go | 11 +- models/repo_generate.go | 56 --- models/repo_permission.go | 18 + models/repo_redirect.go | 62 ---- models/repo_redirect_test.go | 78 ----- models/repo_test.go | 90 +---- models/repo_transfer.go | 37 +- models/repo_watch.go | 328 ------------------ models/statistic.go | 4 +- models/update.go | 100 ------ models/user.go | 8 +- modules/context/repo.go | 10 +- modules/convert/convert.go | 2 +- modules/notification/ui/ui.go | 3 +- modules/repofiles/commit_status.go | 43 --- modules/repository/create.go | 4 +- modules/repository/generate.go | 2 +- modules/repository/init.go | 2 +- routers/api/v1/api.go | 4 +- routers/api/v1/repo/fork.go | 5 +- routers/api/v1/repo/migrate.go | 6 +- routers/api/v1/repo/pull.go | 2 +- routers/api/v1/repo/repo.go | 12 +- routers/api/v1/repo/star.go | 4 +- routers/api/v1/repo/subscriber.go | 6 +- routers/api/v1/repo/topic.go | 24 +- routers/api/v1/repo/transfer.go | 2 +- routers/api/v1/user/star.go | 7 +- routers/api/v1/user/watch.go | 6 +- routers/private/hook_post_receive.go | 2 +- routers/web/org/setting.go | 3 +- routers/web/repo/branch.go | 2 +- routers/web/repo/compare.go | 6 +- routers/web/repo/http.go | 2 +- routers/web/repo/issue.go | 2 +- routers/web/repo/migrate.go | 9 +- routers/web/repo/pull.go | 10 +- routers/web/repo/repo.go | 14 +- routers/web/repo/setting.go | 16 +- routers/web/repo/topic.go | 6 +- routers/web/repo/view.go | 8 +- routers/web/user/setting/profile.go | 4 +- services/cron/tasks_extended.go | 2 +- services/mailer/mail_issue.go | 4 +- services/mailer/mail_release.go | 4 +- services/migrations/gitea_uploader.go | 4 +- services/mirror/mirror_pull.go | 6 +- services/repository/adopt.go | 8 +- .../repository/avatar.go | 64 ++-- services/repository/avatar_test.go | 64 ++++ services/repository/fork.go | 11 +- services/repository/fork_test.go | 2 +- services/repository/hooks.go | 34 ++ services/repository/push.go | 6 +- .../repository/{generate.go => template.go} | 8 +- services/repository/transfer.go | 2 +- services/task/migrate.go | 6 +- services/wiki/wiki.go | 2 +- 88 files changed, 1363 insertions(+), 1388 deletions(-) create mode 100644 models/repo/fork.go create mode 100644 models/repo/fork_test.go create mode 100644 models/repo/redirect.go create mode 100644 models/repo/redirect_test.go rename models/{ => repo}/star.go (93%) rename models/{ => repo}/star_test.go (85%) rename models/{ => repo}/topic.go (92%) rename models/{ => repo}/topic_test.go (99%) create mode 100644 models/repo/update.go create mode 100644 models/repo/watch.go rename models/{repo_watch_test.go => repo/watch_test.go} (59%) delete mode 100644 models/repo_archiver.go delete mode 100644 models/repo_redirect.go delete mode 100644 models/repo_redirect_test.go delete mode 100644 models/repo_watch.go delete mode 100644 models/update.go delete mode 100644 modules/repofiles/commit_status.go rename models/repo_avatar.go => services/repository/avatar.go (61%) create mode 100644 services/repository/avatar_test.go rename services/repository/{generate.go => template.go} (87%) diff --git a/cmd/migrate_storage.go b/cmd/migrate_storage.go index fb4b8dcd9..f9770d50e 100644 --- a/cmd/migrate_storage.go +++ b/cmd/migrate_storage.go @@ -102,7 +102,7 @@ func migrateAvatars(dstStorage storage.ObjectStorage) error { } func migrateRepoAvatars(dstStorage storage.ObjectStorage) error { - return models.IterateRepository(func(repo *repo_model.Repository) error { + return repo_model.IterateRepository(func(repo *repo_model.Repository) error { _, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath()) return err }) diff --git a/integrations/delete_user_test.go b/integrations/delete_user_test.go index 8eb4f45fa..e44d2e7bd 100644 --- a/integrations/delete_user_test.go +++ b/integrations/delete_user_test.go @@ -24,7 +24,7 @@ func assertUserDeleted(t *testing.T, userID int64) { unittest.AssertNotExistsBean(t, &models.OrgUser{UID: userID}) unittest.AssertNotExistsBean(t, &models.IssueUser{UID: userID}) unittest.AssertNotExistsBean(t, &models.TeamUser{UID: userID}) - unittest.AssertNotExistsBean(t, &models.Star{UID: userID}) + unittest.AssertNotExistsBean(t, &repo_model.Star{UID: userID}) } func TestUserDeleteAccount(t *testing.T) { diff --git a/integrations/pull_update_test.go b/integrations/pull_update_test.go index e5e19475c..ede0cc989 100644 --- a/integrations/pull_update_test.go +++ b/integrations/pull_update_test.go @@ -89,7 +89,7 @@ func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *models.Pul assert.NoError(t, err) assert.NotEmpty(t, baseRepo) - headRepo, err := repo_service.ForkRepository(actor, forkOrg, models.ForkRepoOptions{ + headRepo, err := repo_service.ForkRepository(actor, forkOrg, repo_service.ForkRepoOptions{ BaseRepo: baseRepo, Name: "repo-pr-update", Description: "desc", diff --git a/integrations/repo_watch_test.go b/integrations/repo_watch_test.go index 6ff189975..2ff3b2636 100644 --- a/integrations/repo_watch_test.go +++ b/integrations/repo_watch_test.go @@ -8,7 +8,7 @@ import ( "net/url" "testing" - "code.gitea.io/gitea/models" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/setting" ) @@ -18,8 +18,8 @@ func TestRepoWatch(t *testing.T) { // Test round-trip auto-watch setting.Service.AutoWatchOnChanges = true session := loginUser(t, "user2") - unittest.AssertNotExistsBean(t, &models.Watch{UserID: 2, RepoID: 3}) + unittest.AssertNotExistsBean(t, &repo_model.Watch{UserID: 2, RepoID: 3}) testEditFile(t, session, "user3", "repo3", "master", "README.md", "Hello, World (Edited for watch)\n") - unittest.AssertExistsAndLoadBean(t, &models.Watch{UserID: 2, RepoID: 3, Mode: models.RepoWatchModeAuto}) + unittest.AssertExistsAndLoadBean(t, &repo_model.Watch{UserID: 2, RepoID: 3, Mode: repo_model.WatchModeAuto}) }) } diff --git a/models/action.go b/models/action.go index 16d6c42aa..da9e6776b 100644 --- a/models/action.go +++ b/models/action.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/models/db" 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/base" "code.gitea.io/gitea/modules/git" @@ -414,3 +415,127 @@ func DeleteOldActions(olderThan time.Duration) (err error) { _, err = db.GetEngine(db.DefaultContext).Where("created_unix < ?", time.Now().Add(-olderThan).Unix()).Delete(&Action{}) return } + +func notifyWatchers(ctx context.Context, actions ...*Action) error { + var watchers []*repo_model.Watch + var repo *repo_model.Repository + var err error + var permCode []bool + var permIssue []bool + var permPR []bool + + e := db.GetEngine(ctx) + + for _, act := range actions { + repoChanged := repo == nil || repo.ID != act.RepoID + + if repoChanged { + // Add feeds for user self and all watchers. + watchers, err = repo_model.GetWatchers(ctx, act.RepoID) + if err != nil { + return fmt.Errorf("get watchers: %v", err) + } + } + + // Add feed for actioner. + act.UserID = act.ActUserID + if _, err = e.Insert(act); err != nil { + return fmt.Errorf("insert new actioner: %v", err) + } + + if repoChanged { + act.loadRepo() + repo = act.Repo + + // check repo owner exist. + if err := act.Repo.GetOwner(ctx); err != nil { + return fmt.Errorf("can't get repo owner: %v", err) + } + } else if act.Repo == nil { + act.Repo = repo + } + + // Add feed for organization + if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID { + act.ID = 0 + act.UserID = act.Repo.Owner.ID + if _, err = e.InsertOne(act); err != nil { + return fmt.Errorf("insert new actioner: %v", err) + } + } + + if repoChanged { + permCode = make([]bool, len(watchers)) + permIssue = make([]bool, len(watchers)) + permPR = make([]bool, len(watchers)) + for i, watcher := range watchers { + user, err := user_model.GetUserByIDEngine(e, watcher.UserID) + if err != nil { + permCode[i] = false + permIssue[i] = false + permPR[i] = false + continue + } + perm, err := getUserRepoPermission(ctx, repo, user) + if err != nil { + permCode[i] = false + permIssue[i] = false + permPR[i] = false + continue + } + permCode[i] = perm.CanRead(unit.TypeCode) + permIssue[i] = perm.CanRead(unit.TypeIssues) + permPR[i] = perm.CanRead(unit.TypePullRequests) + } + } + + for i, watcher := range watchers { + if act.ActUserID == watcher.UserID { + continue + } + act.ID = 0 + act.UserID = watcher.UserID + act.Repo.Units = nil + + switch act.OpType { + case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionPublishRelease, ActionDeleteBranch: + if !permCode[i] { + continue + } + case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue: + if !permIssue[i] { + continue + } + case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest: + if !permPR[i] { + continue + } + } + + if _, err = e.InsertOne(act); err != nil { + return fmt.Errorf("insert new action: %v", err) + } + } + } + return nil +} + +// NotifyWatchers creates batch of actions for every watcher. +func NotifyWatchers(actions ...*Action) error { + return notifyWatchers(db.DefaultContext, actions...) +} + +// NotifyWatchersActions creates batch of actions for every watcher. +func NotifyWatchersActions(acts []*Action) error { + ctx, committer, err := db.TxContext() + if err != nil { + return err + } + defer committer.Close() + for _, act := range acts { + if err := notifyWatchers(ctx, act); err != nil { + return err + } + } + return committer.Commit() +} diff --git a/models/action_test.go b/models/action_test.go index 02edae2df..306d38236 100644 --- a/models/action_test.go +++ b/models/action_test.go @@ -92,3 +92,40 @@ func TestGetFeeds2(t *testing.T) { assert.NoError(t, err) assert.Len(t, actions, 0) } + +func TestNotifyWatchers(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + action := &Action{ + ActUserID: 8, + RepoID: 1, + OpType: ActionStarRepo, + } + assert.NoError(t, NotifyWatchers(action)) + + // One watchers are inactive, thus action is only created for user 8, 1, 4, 11 + unittest.AssertExistsAndLoadBean(t, &Action{ + ActUserID: action.ActUserID, + UserID: 8, + RepoID: action.RepoID, + OpType: action.OpType, + }) + unittest.AssertExistsAndLoadBean(t, &Action{ + ActUserID: action.ActUserID, + UserID: 1, + RepoID: action.RepoID, + OpType: action.OpType, + }) + unittest.AssertExistsAndLoadBean(t, &Action{ + ActUserID: action.ActUserID, + UserID: 4, + RepoID: action.RepoID, + OpType: action.OpType, + }) + unittest.AssertExistsAndLoadBean(t, &Action{ + ActUserID: action.ActUserID, + UserID: 11, + RepoID: action.RepoID, + OpType: action.OpType, + }) +} diff --git a/models/error.go b/models/error.go index 54556fd78..f0e8751d7 100644 --- a/models/error.go +++ b/models/error.go @@ -71,21 +71,6 @@ func (err ErrUserNotAllowedCreateOrg) Error() string { return "user is not allowed to create organizations" } -// ErrReachLimitOfRepo represents a "ReachLimitOfRepo" kind of error. -type ErrReachLimitOfRepo struct { - Limit int -} - -// IsErrReachLimitOfRepo checks if an error is a ErrReachLimitOfRepo. -func IsErrReachLimitOfRepo(err error) bool { - _, ok := err.(ErrReachLimitOfRepo) - return ok -} - -func (err ErrReachLimitOfRepo) Error() string { - return fmt.Sprintf("user has reached maximum limit of repositories [limit: %d]", err.Limit) -} - // __ __.__ __ .__ // / \ / \__| | _|__| // \ \/\/ / | |/ / | @@ -322,38 +307,6 @@ func (err ErrRepoTransferInProgress) Error() string { return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name) } -// ErrRepoAlreadyExist represents a "RepoAlreadyExist" kind of error. -type ErrRepoAlreadyExist struct { - Uname string - Name string -} - -// IsErrRepoAlreadyExist checks if an error is a ErrRepoAlreadyExist. -func IsErrRepoAlreadyExist(err error) bool { - _, ok := err.(ErrRepoAlreadyExist) - return ok -} - -func (err ErrRepoAlreadyExist) Error() string { - return fmt.Sprintf("repository already exists [uname: %s, name: %s]", err.Uname, err.Name) -} - -// ErrRepoFilesAlreadyExist represents a "RepoFilesAlreadyExist" kind of error. -type ErrRepoFilesAlreadyExist struct { - Uname string - Name string -} - -// IsErrRepoFilesAlreadyExist checks if an error is a ErrRepoAlreadyExist. -func IsErrRepoFilesAlreadyExist(err error) bool { - _, ok := err.(ErrRepoFilesAlreadyExist) - return ok -} - -func (err ErrRepoFilesAlreadyExist) Error() string { - return fmt.Sprintf("repository files already exist [uname: %s, name: %s]", err.Uname, err.Name) -} - // ErrForkAlreadyExist represents a "ForkAlreadyExist" kind of error. type ErrForkAlreadyExist struct { Uname string @@ -371,22 +324,6 @@ func (err ErrForkAlreadyExist) Error() string { return fmt.Sprintf("repository is already forked by user [uname: %s, repo path: %s, fork path: %s]", err.Uname, err.RepoName, err.ForkName) } -// ErrRepoRedirectNotExist represents a "RepoRedirectNotExist" kind of error. -type ErrRepoRedirectNotExist struct { - OwnerID int64 - RepoName string -} - -// IsErrRepoRedirectNotExist check if an error is an ErrRepoRedirectNotExist. -func IsErrRepoRedirectNotExist(err error) bool { - _, ok := err.(ErrRepoRedirectNotExist) - return ok -} - -func (err ErrRepoRedirectNotExist) Error() string { - return fmt.Sprintf("repository redirect does not exist [uid: %d, name: %s]", err.OwnerID, err.RepoName) -} - // ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error. type ErrInvalidCloneAddr struct { Host string diff --git a/models/issue_watch.go b/models/issue_watch.go index bf5c2593a..181cd2343 100644 --- a/models/issue_watch.go +++ b/models/issue_watch.go @@ -6,6 +6,7 @@ package models import ( "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" ) @@ -80,11 +81,11 @@ func CheckIssueWatch(user *user_model.User, issue *Issue) (bool, error) { if exist { return iw.IsWatching, nil } - w, err := getWatch(db.GetEngine(db.DefaultContext), user.ID, issue.RepoID) + w, err := repo_model.GetWatch(db.DefaultContext, user.ID, issue.RepoID) if err != nil { return false, err } - return isWatchMode(w.Mode) || IsUserParticipantsOfIssue(user, issue), nil + return repo_model.IsWatchMode(w.Mode) || IsUserParticipantsOfIssue(user, issue), nil } // GetIssueWatchersIDs returns IDs of subscribers or explicit unsubscribers to a given issue id diff --git a/models/notification.go b/models/notification.go index b71973823..0be014492 100644 --- a/models/notification.go +++ b/models/notification.go @@ -225,7 +225,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n toNotify[id] = struct{}{} } if !(issue.IsPull && HasWorkInProgressPrefix(issue.Title)) { - repoWatches, err := getRepoWatchersIDs(e, issue.RepoID) + repoWatches, err := repo_model.GetRepoWatchersIDs(ctx, issue.RepoID) if err != nil { return err } diff --git a/models/org.go b/models/org.go index e5cd80ab7..c135bb9d3 100644 --- a/models/org.go +++ b/models/org.go @@ -794,7 +794,7 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error { return fmt.Errorf("GetUserRepositories [%d]: %v", userID, err) } for _, repoID := range repoIDs { - if err = watchRepo(sess, userID, repoID, false); err != nil { + if err = repo_model.WatchRepoCtx(ctx, userID, repoID, false); err != nil { return err } } diff --git a/models/org_team.go b/models/org_team.go index c42312323..3d4a2882c 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -238,7 +238,7 @@ func (t *Team) addRepository(ctx context.Context, repo *repo_model.Repository) ( return fmt.Errorf("getMembers: %v", err) } for _, u := range t.Members { - if err = watchRepo(e, u.ID, repo.ID, true); err != nil { + if err = repo_model.WatchRepoCtx(ctx, u.ID, repo.ID, true); err != nil { return fmt.Errorf("watchRepo: %v", err) } } @@ -341,7 +341,7 @@ func (t *Team) removeAllRepositories(ctx context.Context) (err error) { continue } - if err = watchRepo(e, user.ID, repo.ID, false); err != nil { + if err = repo_model.WatchRepoCtx(ctx, user.ID, repo.ID, false); err != nil { return err } @@ -399,7 +399,7 @@ func (t *Team) removeRepository(ctx context.Context, repo *repo_model.Repository continue } - if err = watchRepo(e, teamUser.UID, repo.ID, false); err != nil { + if err = repo_model.WatchRepoCtx(ctx, teamUser.UID, repo.ID, false); err != nil { return err } @@ -857,7 +857,7 @@ func AddTeamMember(team *Team, userID int64) error { return err } if setting.Service.AutoWatchNewRepos { - if err = watchRepo(sess, userID, repo.ID, true); err != nil { + if err = repo_model.WatchRepoCtx(ctx, userID, repo.ID, true); err != nil { return err } } diff --git a/models/release.go b/models/release.go index a19d4f937..51ac0426a 100644 --- a/models/release.go +++ b/models/release.go @@ -370,3 +370,89 @@ func UpdateReleasesMigrationsByType(gitServiceType structs.GitServiceType, origi }) return err } + +// PushUpdateDeleteTagsContext updates a number of delete tags with context +func PushUpdateDeleteTagsContext(ctx context.Context, repo *repo_model.Repository, tags []string) error { + return pushUpdateDeleteTags(db.GetEngine(ctx), repo, tags) +} + +func pushUpdateDeleteTags(e db.Engine, repo *repo_model.Repository, tags []string) error { + if len(tags) == 0 { + return nil + } + lowerTags := make([]string, 0, len(tags)) + for _, tag := range tags { + lowerTags = append(lowerTags, strings.ToLower(tag)) + } + + if _, err := e. + Where("repo_id = ? AND is_tag = ?", repo.ID, true). + In("lower_tag_name", lowerTags). + Delete(new(Release)); err != nil { + return fmt.Errorf("Delete: %v", err) + } + + if _, err := e. + Where("repo_id = ? AND is_tag = ?", repo.ID, false). + In("lower_tag_name", lowerTags). + Cols("is_draft", "num_commits", "sha1"). + Update(&Release{ + IsDraft: true, + }); err != nil { + return fmt.Errorf("Update: %v", err) + } + + return nil +} + +// PushUpdateDeleteTag must be called for any push actions to delete tag +func PushUpdateDeleteTag(repo *repo_model.Repository, tagName string) error { + rel, err := GetRelease(repo.ID, tagName) + if err != nil { + if IsErrReleaseNotExist(err) { + return nil + } + return fmt.Errorf("GetRelease: %v", err) + } + if rel.IsTag { + if _, err = db.GetEngine(db.DefaultContext).ID(rel.ID).Delete(new(Release)); err != nil { + return fmt.Errorf("Delete: %v", err) + } + } else { + rel.IsDraft = true + rel.NumCommits = 0 + rel.Sha1 = "" + if _, err = db.GetEngine(db.DefaultContext).ID(rel.ID).AllCols().Update(rel); err != nil { + return fmt.Errorf("Update: %v", err) + } + } + + return nil +} + +// SaveOrUpdateTag must be called for any push actions to add tag +func SaveOrUpdateTag(repo *repo_model.Repository, newRel *Release) error { + rel, err := GetRelease(repo.ID, newRel.TagName) + if err != nil && !IsErrReleaseNotExist(err) { + return fmt.Errorf("GetRelease: %v", err) + } + + if rel == nil { + rel = newRel + if _, err = db.GetEngine(db.DefaultContext).Insert(rel); err != nil { + return fmt.Errorf("InsertOne: %v", err) + } + } else { + rel.Sha1 = newRel.Sha1 + rel.CreatedUnix = newRel.CreatedUnix + rel.NumCommits = newRel.NumCommits + rel.IsDraft = false + if rel.IsTag && newRel.PublisherID > 0 { + rel.PublisherID = newRel.PublisherID + } + if _, err = db.GetEngine(db.DefaultContext).ID(rel.ID).AllCols().Update(rel); err != nil { + return fmt.Errorf("Update: %v", err) + } + } + return nil +} diff --git a/models/repo.go b/models/repo.go index adc62c952..397b4380d 100644 --- a/models/repo.go +++ b/models/repo.go @@ -14,7 +14,6 @@ import ( "sort" "strconv" "strings" - "time" "unicode/utf8" _ "image/jpeg" // Needed for jpeg support @@ -218,7 +217,7 @@ func getReviewers(ctx context.Context, repo *repo_model.Repository, doerID, post "SELECT uid AS user_id FROM `org_user` WHERE org_id = ? "+ ") AND id NOT IN (?, ?) ORDER BY name", repo.ID, perm.AccessModeRead, - repo.ID, RepoWatchModeNormal, RepoWatchModeAuto, + repo.ID, repo_model.WatchModeNormal, repo_model.WatchModeAuto, repo.OwnerID, doerID, posterID). Find(&users); err != nil { @@ -280,7 +279,7 @@ func CanUserForkRepo(user *user_model.User, repo *repo_model.Repository) (bool, if user == nil { return false, nil } - if repo.OwnerID != user.ID && !HasForkedRepo(user.ID, repo.ID) { + if repo.OwnerID != user.ID && !repo_model.HasForkedRepo(user.ID, repo.ID) { return true, nil } ownedOrgs, err := GetOrgsCanCreateRepoByUserID(user.ID) @@ -288,7 +287,7 @@ func CanUserForkRepo(user *user_model.User, repo *repo_model.Repository) (bool, return false, err } for _, org := range ownedOrgs { - if repo.OwnerID != org.ID && !HasForkedRepo(org.ID, repo.ID) { + if repo.OwnerID != org.ID && !repo_model.HasForkedRepo(org.ID, repo.ID) { return true, nil } } @@ -317,24 +316,6 @@ func CanUserDelete(repo *repo_model.Repository, user *user_model.User) (bool, er return false, nil } -// GetRepoReaders returns all users that have explicit read access or higher to the repository. -func GetRepoReaders(repo *repo_model.Repository) (_ []*user_model.User, err error) { - return getUsersWithAccessMode(db.DefaultContext, repo, perm.AccessModeRead) -} - -// GetRepoWriters returns all users that have write access to the repository. -func GetRepoWriters(repo *repo_model.Repository) (_ []*user_model.User, err error) { - return getUsersWithAccessMode(db.DefaultContext, repo, perm.AccessModeWrite) -} - -// IsRepoReader returns true if user has explicit read access or higher to the repository. -func IsRepoReader(repo *repo_model.Repository, userID int64) (bool, error) { - if repo.OwnerID == userID { - return true, nil - } - return db.GetEngine(db.DefaultContext).Where("repo_id = ? AND user_id = ? AND mode >= ?", repo.ID, userID, perm.AccessModeRead).Get(&Access{}) -} - // getUsersWithAccessMode returns users that have at least given access mode to the repository. func getUsersWithAccessMode(ctx context.Context, repo *repo_model.Repository, mode perm.AccessMode) (_ []*user_model.User, err error) { if err = repo.GetOwner(ctx); err != nil { @@ -372,35 +353,6 @@ func SetRepoReadBy(repoID, userID int64) error { return setRepoNotificationStatusReadIfUnread(db.GetEngine(db.DefaultContext), userID, repoID) } -// CheckCreateRepository check if could created a repository -func CheckCreateRepository(doer, u *user_model.User, name string, overwriteOrAdopt bool) error { - if !doer.CanCreateRepo() { - return ErrReachLimitOfRepo{u.MaxRepoCreation} - } - - if err := IsUsableRepoName(name); err != nil { - return err - } - - has, err := repo_model.IsRepositoryExist(u, name) - if err != nil { - return fmt.Errorf("IsRepositoryExist: %v", err) - } else if has { - return ErrRepoAlreadyExist{u.Name, name} - } - - repoPath := repo_model.RepoPath(u.Name, name) - isExist, err := util.IsExist(repoPath) - if err != nil { - log.Error("Unable to check if %s exists. Error: %v", repoPath, err) - return err - } - if !overwriteOrAdopt && isExist { - return ErrRepoFilesAlreadyExist{u.Name, name} - } - return nil -} - // CreateRepoOptions contains the create repository options type CreateRepoOptions struct { Name string @@ -421,13 +373,6 @@ type CreateRepoOptions struct { MirrorInterval string } -// ForkRepoOptions contains the fork repository options -type ForkRepoOptions struct { - BaseRepo *repo_model.Repository - Name string - Description string -} - // GetRepoInitFile returns repository init files func GetRepoInitFile(tp, name string) ([]byte, error) { cleanedName := strings.TrimLeft(path.Clean("/"+name), "/") @@ -457,23 +402,9 @@ func GetRepoInitFile(tp, name string) ([]byte, error) { } } -var ( - reservedRepoNames = []string{".", ".."} - reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"} -) - -// IsUsableRepoName returns true when repository is usable -func IsUsableRepoName(name string) error { - if db.AlphaDashDotPattern.MatchString(name) { - // Note: usually this error is normally caught up earlier in the UI - return db.ErrNameCharsNotAllowed{Name: name} - } - return db.IsUsableName(reservedRepoNames, reservedRepoPatterns, name) -} - // CreateRepository creates a repository for the user/organization. func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt bool) (err error) { - if err = IsUsableRepoName(repo.Name); err != nil { + if err = repo_model.IsUsableRepoName(repo.Name); err != nil { return err } @@ -481,7 +412,10 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_ if err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { - return ErrRepoAlreadyExist{u.Name, repo.Name} + return repo_model.ErrRepoAlreadyExist{ + Uname: u.Name, + Name: repo.Name, + } } repoPath := repo_model.RepoPath(u.Name, repo.Name) @@ -492,7 +426,7 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_ } if !overwriteOrAdopt && isExist { log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath) - return ErrRepoFilesAlreadyExist{ + return repo_model.ErrRepoFilesAlreadyExist{ Uname: u.Name, Name: repo.Name, } @@ -501,7 +435,7 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_ if err = db.Insert(ctx, repo); err != nil { return err } - if err = deleteRepoRedirect(db.GetEngine(ctx), u.ID, repo.Name); err != nil { + if err = repo_model.DeleteRedirect(ctx, u.ID, repo.Name); err != nil { return err } @@ -578,7 +512,7 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_ } if setting.Service.AutoWatchNewRepos { - if err = watchRepo(db.GetEngine(ctx), doer.ID, repo.ID, true); err != nil { + if err = repo_model.WatchRepoCtx(ctx, doer.ID, repo.ID, true); err != nil { return fmt.Errorf("watchRepo: %v", err) } } @@ -633,67 +567,6 @@ func DecrementRepoForkNum(ctx context.Context, repoID int64) error { return err } -// ChangeRepositoryName changes all corresponding setting from old repository name to new one. -func ChangeRepositoryName(doer *user_model.User, repo *repo_model.Repository, newRepoName string) (err error) { - oldRepoName := repo.Name - newRepoName = strings.ToLower(newRepoName) - if err = IsUsableRepoName(newRepoName); err != nil { - return err - } - - if err := repo.GetOwner(db.DefaultContext); err != nil { - return err - } - - has, err := repo_model.IsRepositoryExist(repo.Owner, newRepoName) - if err != nil { - return fmt.Errorf("IsRepositoryExist: %v", err) - } else if has { - return ErrRepoAlreadyExist{repo.Owner.Name, newRepoName} - } - - newRepoPath := repo_model.RepoPath(repo.Owner.Name, newRepoName) - if err = util.Rename(repo.RepoPath(), newRepoPath); err != nil { - return fmt.Errorf("rename repository directory: %v", err) - } - - wikiPath := repo.WikiPath() - isExist, err := util.IsExist(wikiPath) - if err != nil { - log.Error("Unable to check if %s exists. Error: %v", wikiPath, err) - return err - } - if isExist { - if err = util.Rename(wikiPath, repo_model.WikiPath(repo.Owner.Name, newRepoName)); err != nil { - return fmt.Errorf("rename repository wiki: %v", err) - } - } - - ctx, committer, err := db.TxContext() - if err != nil { - return err - } - defer committer.Close() - - if err := newRepoRedirect(db.GetEngine(ctx), repo.Owner.ID, repo.ID, oldRepoName, newRepoName); err != nil { - return err - } - - return committer.Commit() -} - -func getRepositoriesByForkID(e db.Engine, forkID int64) ([]*repo_model.Repository, error) { - repos := make([]*repo_model.Repository, 0, 10) - return repos, e. - Where("fork_id=?", forkID). - Find(&repos) -} - -// GetRepositoriesByForkID returns all repositories with given fork ID. -func GetRepositoriesByForkID(forkID int64) ([]*repo_model.Repository, error) { - return getRepositoriesByForkID(db.GetEngine(db.DefaultContext), forkID) -} - func updateRepository(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) { repo.LowerName = strings.ToLower(repo.Name) @@ -740,7 +613,7 @@ func updateRepository(ctx context.Context, repo *repo_model.Repository, visibili return err } - forkRepos, err := getRepositoriesByForkID(e, repo.ID) + forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID) if err != nil { return fmt.Errorf("getRepositoriesByForkID: %v", err) } @@ -775,58 +648,6 @@ func UpdateRepository(repo *repo_model.Repository, visibilityChanged bool) (err return committer.Commit() } -// UpdateRepositoryOwnerNames updates repository owner_names (this should only be used when the ownerName has changed case) -func UpdateRepositoryOwnerNames(ownerID int64, ownerName string) error { - if ownerID == 0 { - return nil - } - ctx, committer, err := db.TxContext() - if err != nil { - return err - } - defer committer.Close() - - if _, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Cols("owner_name").Update(&repo_model.Repository{ - OwnerName: ownerName, - }); err != nil { - return err - } - - return committer.Commit() -} - -// UpdateRepositoryUpdatedTime updates a repository's updated time -func UpdateRepositoryUpdatedTime(repoID int64, updateTime time.Time) error { - _, err := db.GetEngine(db.DefaultContext).Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", updateTime.Unix(), repoID) - return err -} - -// UpdateRepositoryUnits updates a repository's units -func UpdateRepositoryUnits(repo *repo_model.Repository, units []repo_model.RepoUnit, deleteUnitTypes []unit.Type) (err error) { - ctx, committer, err := db.TxContext() - if err != nil { - return err - } - defer committer.Close() - - // Delete existing settings of units before adding again - for _, u := range units { - deleteUnitTypes = append(deleteUnitTypes, u.Type) - } - - if _, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).In("type", deleteUnitTypes).Delete(new(repo_model.RepoUnit)); err != nil { - return err - } - - if len(units) > 0 { - if err = db.Insert(ctx, units); err != nil { - return err - } - } - - return committer.Commit() -} - // DeleteRepository deletes a repository for a user or organization. // make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock) func DeleteRepository(doer *user_model.User, uid, repoID int64) error { @@ -927,11 +748,11 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error { &repo_model.PushMirror{RepoID: repoID}, &Release{RepoID: repoID}, &repo_model.RepoIndexerStatus{RepoID: repoID}, - &RepoRedirect{RedirectRepoID: repoID}, + &repo_model.Redirect{RedirectRepoID: repoID}, &repo_model.RepoUnit{RepoID: repoID}, - &Star{RepoID: repoID}, + &repo_model.Star{RepoID: repoID}, &Task{RepoID: repoID}, - &Watch{RepoID: repoID}, + &repo_model.Watch{RepoID: repoID}, &webhook.Webhook{RepoID: repoID}, ); err != nil { return fmt.Errorf("deleteBeans: %v", err) @@ -964,7 +785,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error { } if len(repo.Topics) > 0 { - if err := removeTopicsFromRepo(sess, repo.ID); err != nil { + if err := repo_model.RemoveTopicsFromRepo(ctx, repo.ID); err != nil { return err } } @@ -1261,76 +1082,6 @@ func CheckRepoStats(ctx context.Context) error { return nil } -// SetArchiveRepoState sets if a repo is archived -func SetArchiveRepoState(repo *repo_model.Repository, isArchived bool) (err error) { - repo.IsArchived = isArchived - _, err = db.GetEngine(db.DefaultContext).Where("id = ?", repo.ID).Cols("is_archived").NoAutoTime().Update(repo) - return -} - -// ___________ __ -// \_ _____/__________| | __ -// | __)/ _ \_ __ \ |/ / -// | \( <_> ) | \/ < -// \___ / \____/|__| |__|_ \ -// \/ \/ - -// GetForkedRepo checks if given user has already forked a repository with given ID. -func GetForkedRepo(ownerID, repoID int64) *repo_model.Repository { - repo := new(repo_model.Repository) - has, _ := db.GetEngine(db.DefaultContext). - Where("owner_id=? AND fork_id=?", ownerID, repoID). - Get(repo) - if has { - return repo - } - return nil -} - -// HasForkedRepo checks if given user has already forked a repository with given ID. -func HasForkedRepo(ownerID, repoID int64) bool { - has, _ := db.GetEngine(db.DefaultContext). - Table("repository"). - Where("owner_id=? AND fork_id=?", ownerID, repoID). - Exist() - return has -} - -// GetForks returns all the forks of the repository -func GetForks(repo *repo_model.Repository, listOptions db.ListOptions) ([]*repo_model.Repository, error) { - if listOptions.Page == 0 { - forks := make([]*repo_model.Repository, 0, repo.NumForks) - return forks, db.GetEngine(db.DefaultContext).Find(&forks, &repo_model.Repository{ForkID: repo.ID}) - } - - sess := db.GetPaginatedSession(&listOptions) - forks := make([]*repo_model.Repository, 0, listOptions.PageSize) - return forks, sess.Find(&forks, &repo_model.Repository{ForkID: repo.ID}) -} - -// GetUserFork return user forked repository from this repository, if not forked return nil -func GetUserFork(repoID, userID int64) (*repo_model.Repository, error) { - var forkedRepo repo_model.Repository - has, err := db.GetEngine(db.DefaultContext).Where("fork_id = ?", repoID).And("owner_id = ?", userID).Get(&forkedRepo) - if err != nil { - return nil, err - } - if !has { - return nil, nil - } - return &forkedRepo, nil -} - -func updateRepositoryCols(e db.Engine, repo *repo_model.Repository, cols ...string) error { - _, err := e.ID(repo.ID).Cols(cols...).Update(repo) - return err -} - -// UpdateRepositoryCols updates repository's columns -func UpdateRepositoryCols(repo *repo_model.Repository, cols ...string) error { - return updateRepositoryCols(db.GetEngine(db.DefaultContext), repo, cols...) -} - func updateUserStarNumbers(users []user_model.User) error { ctx, committer, err := db.TxContext() if err != nil { @@ -1370,28 +1121,6 @@ func DoctorUserStarNum() (err error) { return } -// IterateRepository iterate repositories -func IterateRepository(f func(repo *repo_model.Repository) error) error { - var start int - batchSize := setting.Database.IterateBufferSize - for { - repos := make([]*repo_model.Repository, 0, batchSize) - if err := db.GetEngine(db.DefaultContext).Limit(batchSize, start).Find(&repos); err != nil { - return err - } - if len(repos) == 0 { - return nil - } - start += len(repos) - - for _, repo := range repos { - if err := f(repo); err != nil { - return err - } - } - } -} - // LinkedRepository returns the linked repo if any func LinkedRepository(a *repo_model.Attachment) (*repo_model.Repository, unit.Type, error) { if a.IssueID != 0 { diff --git a/models/repo/archiver.go b/models/repo/archiver.go index cee6013ca..c29891397 100644 --- a/models/repo/archiver.go +++ b/models/repo/archiver.go @@ -107,3 +107,10 @@ func FindRepoArchives(opts FindRepoArchiversOption) ([]*RepoArchiver, error) { Find(&archivers) return archivers, err } + +// SetArchiveRepoState sets if a repo is archived +func SetArchiveRepoState(repo *Repository, isArchived bool) (err error) { + repo.IsArchived = isArchived + _, err = db.GetEngine(db.DefaultContext).Where("id = ?", repo.ID).Cols("is_archived").NoAutoTime().Update(repo) + return +} diff --git a/models/repo/fork.go b/models/repo/fork.go new file mode 100644 index 000000000..570a5b68a --- /dev/null +++ b/models/repo/fork.go @@ -0,0 +1,69 @@ +// Copyright 2021 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 + +import ( + "context" + + "code.gitea.io/gitea/models/db" +) + +func getRepositoriesByForkID(e db.Engine, forkID int64) ([]*Repository, error) { + repos := make([]*Repository, 0, 10) + return repos, e. + Where("fork_id=?", forkID). + Find(&repos) +} + +// GetRepositoriesByForkID returns all repositories with given fork ID. +func GetRepositoriesByForkID(ctx context.Context, forkID int64) ([]*Repository, error) { + return getRepositoriesByForkID(db.GetEngine(ctx), forkID) +} + +// GetForkedRepo checks if given user has already forked a repository with given ID. +func GetForkedRepo(ownerID, repoID int64) *Repository { + repo := new(Repository) + has, _ := db.GetEngine(db.DefaultContext). + Where("owner_id=? AND fork_id=?", ownerID, repoID). + Get(repo) + if has { + return repo + } + return nil +} + +// HasForkedRepo checks if given user has already forked a repository with given ID. +func HasForkedRepo(ownerID, repoID int64) bool { + has, _ := db.GetEngine(db.DefaultContext). + Table("repository"). + Where("owner_id=? AND fork_id=?", ownerID, repoID). + Exist() + return has +} + +// GetUserFork return user forked repository from this repository, if not forked return nil +func GetUserFork(repoID, userID int64) (*Repository, error) { + var forkedRepo Repository + has, err := db.GetEngine(db.DefaultContext).Where("fork_id = ?", repoID).And("owner_id = ?", userID).Get(&forkedRepo) + if err != nil { + return nil, err + } + if !has { + return nil, nil + } + return &forkedRepo, nil +} + +// GetForks returns all the forks of the repository +func GetForks(repo *Repository, listOptions db.ListOptions) ([]*Repository, error) { + if listOptions.Page == 0 { + forks := make([]*Repository, 0, repo.NumForks) + return forks, db.GetEngine(db.DefaultContext).Find(&forks, &Repository{ForkID: repo.ID}) + } + + sess := db.GetPaginatedSession(&listOptions) + forks := make([]*Repository, 0, listOptions.PageSize) + return forks, sess.Find(&forks, &Repository{ForkID: repo.ID}) +} diff --git a/models/repo/fork_test.go b/models/repo/fork_test.go new file mode 100644 index 000000000..bf6b90b38 --- /dev/null +++ b/models/repo/fork_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 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 + +import ( + "testing" + + "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" +) + +func TestGetUserFork(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + // User13 has repo 11 forked from repo10 + repo, err := GetRepositoryByID(10) + assert.NoError(t, err) + assert.NotNil(t, repo) + repo, err = GetUserFork(repo.ID, 13) + assert.NoError(t, err) + assert.NotNil(t, repo) + + repo, err = GetRepositoryByID(9) + assert.NoError(t, err) + assert.NotNil(t, repo) + repo, err = GetUserFork(repo.ID, 13) + assert.NoError(t, err) + assert.Nil(t, repo) +} diff --git a/models/repo/main_test.go b/models/repo/main_test.go index f40a97628..fdd6c3f4d 100644 --- a/models/repo/main_test.go +++ b/models/repo/main_test.go @@ -18,5 +18,11 @@ func TestMain(m *testing.M) { "repository.yml", "repo_unit.yml", "repo_indexer_status.yml", + "repo_redirect.yml", + "watch.yml", + "star.yml", + "topic.yml", + "repo_topic.yml", + "user.yml", ) } diff --git a/models/repo/redirect.go b/models/repo/redirect.go new file mode 100644 index 000000000..88fad6f3e --- /dev/null +++ b/models/repo/redirect.go @@ -0,0 +1,82 @@ +// 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 + +import ( + "context" + "fmt" + "strings" + + "code.gitea.io/gitea/models/db" +) + +// ErrRedirectNotExist represents a "RedirectNotExist" kind of error. +type ErrRedirectNotExist struct { + OwnerID int64 + RepoName string +} + +// IsErrRedirectNotExist check if an error is an ErrRepoRedirectNotExist. +func IsErrRedirectNotExist(err error) bool { + _, ok := err.(ErrRedirectNotExist) + return ok +} + +func (err ErrRedirectNotExist) Error() string { + return fmt.Sprintf("repository redirect does not exist [uid: %d, name: %s]", err.OwnerID, err.RepoName) +} + +// Redirect represents that a repo name should be redirected to another +type Redirect struct { + ID int64 `xorm:"pk autoincr"` + OwnerID int64 `xorm:"UNIQUE(s)"` + LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"` + RedirectRepoID int64 // repoID to redirect to +} + +// TableName represents real table name in database +func (Redirect) TableName() string { + return "repo_redirect" +} + +func init() { + db.RegisterModel(new(Redirect)) +} + +// LookupRedirect look up if a repository has a redirect name +func LookupRedirect(ownerID int64, repoName string) (int64, error) { + repoName = strings.ToLower(repoName) + redirect := &Redirect{OwnerID: ownerID, LowerName: repoName} + if has, err := db.GetEngine(db.DefaultContext).Get(redirect); err != nil { + return 0, err + } else if !has { + return 0, ErrRedirectNotExist{OwnerID: ownerID, RepoName: repoName} + } + return redirect.RedirectRepoID, nil +} + +// NewRedirect create a new repo redirect +func NewRedirect(ctx context.Context, ownerID, repoID int64, oldRepoName, newRepoName string) error { + oldRepoName = strings.ToLower(oldRepoName) + newRepoName = strings.ToLower(newRepoName) + + if err := DeleteRedirect(ctx, ownerID, newRepoName); err != nil { + return err + } + + return db.Insert(ctx, &Redirect{ + OwnerID: ownerID, + LowerName: oldRepoName, + RedirectRepoID: repoID, + }) +} + +// DeleteRedirect delete any redirect from the specified repo name to +// anything else +func DeleteRedirect(ctx context.Context, ownerID int64, repoName string) error { + repoName = strings.ToLower(repoName) + _, err := db.GetEngine(ctx).Delete(&Redirect{OwnerID: ownerID, LowerName: repoName}) + return err +} diff --git a/models/repo/redirect_test.go b/models/repo/redirect_test.go new file mode 100644 index 000000000..2dca2cbbf --- /dev/null +++ b/models/repo/redirect_test.go @@ -0,0 +1,77 @@ +// 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 + +import ( + "testing" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" +) + +func TestLookupRedirect(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repoID, err := LookupRedirect(2, "oldrepo1") + assert.NoError(t, err) + assert.EqualValues(t, 1, repoID) + + _, err = LookupRedirect(unittest.NonexistentID, "doesnotexist") + assert.True(t, IsErrRedirectNotExist(err)) +} + +func TestNewRedirect(t *testing.T) { + // redirect to a completely new name + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) + assert.NoError(t, NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "newreponame")) + + unittest.AssertExistsAndLoadBean(t, &Redirect{ + OwnerID: repo.OwnerID, + LowerName: repo.LowerName, + RedirectRepoID: repo.ID, + }) + unittest.AssertExistsAndLoadBean(t, &Redirect{ + OwnerID: repo.OwnerID, + LowerName: "oldrepo1", + RedirectRepoID: repo.ID, + }) +} + +func TestNewRedirect2(t *testing.T) { + // redirect to previously used name + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) + assert.NoError(t, NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "oldrepo1")) + + unittest.AssertExistsAndLoadBean(t, &Redirect{ + OwnerID: repo.OwnerID, + LowerName: repo.LowerName, + RedirectRepoID: repo.ID, + }) + unittest.AssertNotExistsBean(t, &Redirect{ + OwnerID: repo.OwnerID, + LowerName: "oldrepo1", + RedirectRepoID: repo.ID, + }) +} + +func TestNewRedirect3(t *testing.T) { + // redirect for a previously-unredirected repo + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository) + assert.NoError(t, NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "newreponame")) + + unittest.AssertExistsAndLoadBean(t, &Redirect{ + OwnerID: repo.OwnerID, + LowerName: repo.LowerName, + RedirectRepoID: repo.ID, + }) +} diff --git a/models/repo/repo.go b/models/repo/repo.go index 9353e813b..8907691dd 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -25,6 +25,20 @@ import ( "code.gitea.io/gitea/modules/util" ) +var ( + reservedRepoNames = []string{".", ".."} + reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"} +) + +// IsUsableRepoName returns true when repository is usable +func IsUsableRepoName(name string) error { + if db.AlphaDashDotPattern.MatchString(name) { + // Note: usually this error is normally caught up earlier in the UI + return db.ErrNameCharsNotAllowed{Name: name} + } + return db.IsUsableName(reservedRepoNames, reservedRepoPatterns, name) +} + // TrustModelType defines the types of trust model for this repository type TrustModelType int @@ -734,3 +748,25 @@ func GetPublicRepositoryCount(u *user_model.User) (int64, error) { func GetPrivateRepositoryCount(u *user_model.User) (int64, error) { return getPrivateRepositoryCount(db.GetEngine(db.DefaultContext), u) } + +// IterateRepository iterate repositories +func IterateRepository(f func(repo *Repository) error) error { + var start int + batchSize := setting.Database.IterateBufferSize + for { + repos := make([]*Repository, 0, batchSize) + if err := db.GetEngine(db.DefaultContext).Limit(batchSize, start).Find(&repos); err != nil { + return err + } + if len(repos) == 0 { + return nil + } + start += len(repos) + + for _, repo := range repos { + if err := f(repo); err != nil { + return err + } + } + } +} diff --git a/models/repo/repo_test.go b/models/repo/repo_test.go index 6f48a22e4..92b95f1d4 100644 --- a/models/repo/repo_test.go +++ b/models/repo/repo_test.go @@ -42,3 +42,10 @@ func TestGetPrivateRepositoryCount(t *testing.T) { assert.NoError(t, err) assert.Equal(t, int64(2), count) } + +func TestRepoAPIURL(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 10}).(*Repository) + + assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL()) +} diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index 5f6c43f02..1957f88ff 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -242,3 +242,29 @@ func UpdateRepoUnit(unit *RepoUnit) error { _, err := db.GetEngine(db.DefaultContext).ID(unit.ID).Update(unit) return err } + +// UpdateRepositoryUnits updates a repository's units +func UpdateRepositoryUnits(repo *Repository, units []RepoUnit, deleteUnitTypes []unit.Type) (err error) { + ctx, committer, err := db.TxContext() + if err != nil { + return err + } + defer committer.Close() + + // Delete existing settings of units before adding again + for _, u := range units { + deleteUnitTypes = append(deleteUnitTypes, u.Type) + } + + if _, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).In("type", deleteUnitTypes).Delete(new(RepoUnit)); err != nil { + return err + } + + if len(units) > 0 { + if err = db.Insert(ctx, units); err != nil { + return err + } + } + + return committer.Commit() +} diff --git a/models/star.go b/models/repo/star.go similarity index 93% rename from models/star.go rename to models/repo/star.go index de3207797..8db297e3b 100644 --- a/models/star.go +++ b/models/repo/star.go @@ -2,11 +2,10 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package models +package repo import ( "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" ) @@ -76,7 +75,7 @@ func isStaring(e db.Engine, userID, repoID int64) bool { } // GetStargazers returns the users that starred the repo. -func GetStargazers(repo *repo_model.Repository, opts db.ListOptions) ([]*user_model.User, error) { +func GetStargazers(repo *Repository, opts db.ListOptions) ([]*user_model.User, error) { sess := db.GetEngine(db.DefaultContext).Where("star.repo_id = ?", repo.ID). Join("LEFT", "star", "`user`.id = star.uid") if opts.Page > 0 { diff --git a/models/star_test.go b/models/repo/star_test.go similarity index 85% rename from models/star_test.go rename to models/repo/star_test.go index 8da83661c..20c4b6bef 100644 --- a/models/star_test.go +++ b/models/repo/star_test.go @@ -2,13 +2,12 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package models +package repo 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" @@ -36,7 +35,7 @@ func TestIsStaring(t *testing.T) { func TestRepository_GetStargazers(t *testing.T) { // repo with stargazers assert.NoError(t, unittest.PrepareTestDatabase()) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}).(*repo_model.Repository) + repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 4}).(*Repository) gazers, err := GetStargazers(repo, db.ListOptions{Page: 0}) assert.NoError(t, err) if assert.Len(t, gazers, 1) { @@ -47,7 +46,7 @@ func TestRepository_GetStargazers(t *testing.T) { func TestRepository_GetStargazers2(t *testing.T) { // repo with stargazers assert.NoError(t, unittest.PrepareTestDatabase()) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository) + repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) gazers, err := GetStargazers(repo, db.ListOptions{Page: 0}) assert.NoError(t, err) assert.Len(t, gazers, 0) diff --git a/models/topic.go b/models/repo/topic.go similarity index 92% rename from models/topic.go rename to models/repo/topic.go index 2767d6c58..121863519 100644 --- a/models/topic.go +++ b/models/repo/topic.go @@ -2,15 +2,15 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package models +package repo import ( + "context" "fmt" "regexp" "strings" "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/timeutil" "xorm.io/builder" @@ -33,7 +33,7 @@ type Topic struct { } // RepoTopic represents associated repositories and topics -type RepoTopic struct { +type RepoTopic struct { //revive:disable-line:exported RepoID int64 `xorm:"pk"` TopicID int64 `xorm:"pk"` } @@ -145,8 +145,9 @@ func removeTopicFromRepo(e db.Engine, repoID int64, topic *Topic) error { return nil } -// removeTopicsFromRepo remove all topics from the repo and decrements respective topics repo count -func removeTopicsFromRepo(e db.Engine, repoID int64) error { +// RemoveTopicsFromRepo remove all topics from the repo and decrements respective topics repo count +func RemoveTopicsFromRepo(ctx context.Context, repoID int64) error { + e := db.GetEngine(ctx) _, err := e.Where( builder.In("id", builder.Select("topic_id").From("repo_topic").Where(builder.Eq{"repo_id": repoID}), @@ -254,7 +255,7 @@ func AddTopic(repoID int64, topicName string) (*Topic, error) { return nil, err } - if _, err := sess.ID(repoID).Cols("topics").Update(&repo_model.Repository{ + if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{ Topics: topicNames, }); err != nil { return nil, err @@ -348,7 +349,7 @@ func SaveTopics(repoID int64, topicNames ...string) error { return err } - if _, err := sess.ID(repoID).Cols("topics").Update(&repo_model.Repository{ + if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{ Topics: topicNames, }); err != nil { return err @@ -356,3 +357,13 @@ func SaveTopics(repoID int64, topicNames ...string) error { return committer.Commit() } + +// GenerateTopics generates topics from a template repository +func GenerateTopics(ctx context.Context, templateRepo, generateRepo *Repository) error { + for _, topic := range templateRepo.Topics { + if _, err := addTopicByNameToRepo(db.GetEngine(ctx), generateRepo.ID, topic); err != nil { + return err + } + } + return nil +} diff --git a/models/topic_test.go b/models/repo/topic_test.go similarity index 99% rename from models/topic_test.go rename to models/repo/topic_test.go index 0219bdded..353d96ef3 100644 --- a/models/topic_test.go +++ b/models/repo/topic_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package models +package repo import ( "testing" diff --git a/models/repo/update.go b/models/repo/update.go new file mode 100644 index 000000000..efc562a40 --- /dev/null +++ b/models/repo/update.go @@ -0,0 +1,179 @@ +// Copyright 2021 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 + +import ( + "context" + "fmt" + "strings" + "time" + + "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" +) + +// UpdateRepositoryOwnerNames updates repository owner_names (this should only be used when the ownerName has changed case) +func UpdateRepositoryOwnerNames(ownerID int64, ownerName string) error { + if ownerID == 0 { + return nil + } + ctx, committer, err := db.TxContext() + if err != nil { + return err + } + defer committer.Close() + + if _, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Cols("owner_name").Update(&Repository{ + OwnerName: ownerName, + }); err != nil { + return err + } + + return committer.Commit() +} + +// UpdateRepositoryUpdatedTime updates a repository's updated time +func UpdateRepositoryUpdatedTime(repoID int64, updateTime time.Time) error { + _, err := db.GetEngine(db.DefaultContext).Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", updateTime.Unix(), repoID) + return err +} + +// UpdateRepositoryColsCtx updates repository's columns +func UpdateRepositoryColsCtx(ctx context.Context, repo *Repository, cols ...string) error { + _, err := db.GetEngine(ctx).ID(repo.ID).Cols(cols...).Update(repo) + return err +} + +// UpdateRepositoryCols updates repository's columns +func UpdateRepositoryCols(repo *Repository, cols ...string) error { + return UpdateRepositoryColsCtx(db.DefaultContext, repo, cols...) +} + +// ErrReachLimitOfRepo represents a "ReachLimitOfRepo" kind of error. +type ErrReachLimitOfRepo struct { + Limit int +} + +// IsErrReachLimitOfRepo checks if an error is a ErrReachLimitOfRepo. +func IsErrReachLimitOfRepo(err error) bool { + _, ok := err.(ErrReachLimitOfRepo) + return ok +} + +func (err ErrReachLimitOfRepo) Error() string { + return fmt.Sprintf("user has reached maximum limit of repositories [limit: %d]", err.Limit) +} + +// ErrRepoAlreadyExist represents a "RepoAlreadyExist" kind of error. +type ErrRepoAlreadyExist struct { + Uname string + Name string +} + +// IsErrRepoAlreadyExist checks if an error is a ErrRepoAlreadyExist. +func IsErrRepoAlreadyExist(err error) bool { + _, ok := err.(ErrRepoAlreadyExist) + return ok +} + +func (err ErrRepoAlreadyExist) Error() string { + return fmt.Sprintf("repository already exists [uname: %s, name: %s]", err.Uname, err.Name) +} + +// ErrRepoFilesAlreadyExist represents a "RepoFilesAlreadyExist" kind of error. +type ErrRepoFilesAlreadyExist struct { + Uname string + Name string +} + +// IsErrRepoFilesAlreadyExist checks if an error is a ErrRepoAlreadyExist. +func IsErrRepoFilesAlreadyExist(err error) bool { + _, ok := err.(ErrRepoFilesAlreadyExist) + return ok +} + +func (err ErrRepoFilesAlreadyExist) Error() string { + return fmt.Sprintf("repository files already exist [uname: %s, name: %s]", err.Uname, err.Name) +} + +// CheckCreateRepository check if could created a repository +func CheckCreateRepository(doer, u *user_model.User, name string, overwriteOrAdopt bool) error { + if !doer.CanCreateRepo() { + return ErrReachLimitOfRepo{u.MaxRepoCreation} + } + + if err := IsUsableRepoName(name); err != nil { + return err + } + + has, err := IsRepositoryExist(u, name) + if err != nil { + return fmt.Errorf("IsRepositoryExist: %v", err) + } else if has { + return ErrRepoAlreadyExist{u.Name, name} + } + + repoPath := RepoPath(u.Name, name) + isExist, err := util.IsExist(repoPath) + if err != nil { + log.Error("Unable to check if %s exists. Error: %v", repoPath, err) + return err + } + if !overwriteOrAdopt && isExist { + return ErrRepoFilesAlreadyExist{u.Name, name} + } + return nil +} + +// ChangeRepositoryName changes all corresponding setting from old repository name to new one. +func ChangeRepositoryName(doer *user_model.User, repo *Repository, newRepoName string) (err error) { + oldRepoName := repo.Name + newRepoName = strings.ToLower(newRepoName) + if err = IsUsableRepoName(newRepoName); err != nil { + return err + } + + if err := repo.GetOwner(db.DefaultContext); err != nil { + return err + } + + has, err := IsRepositoryExist(repo.Owner, newRepoName) + if err != nil { + return fmt.Errorf("IsRepositoryExist: %v", err) + } else if has { + return ErrRepoAlreadyExist{repo.Owner.Name, newRepoName} + } + + newRepoPath := RepoPath(repo.Owner.Name, newRepoName) + if err = util.Rename(repo.RepoPath(), newRepoPath); err != nil { + return fmt.Errorf("rename repository directory: %v", err) + } + + wikiPath := repo.WikiPath() + isExist, err := util.IsExist(wikiPath) + if err != nil { + log.Error("Unable to check if %s exists. Error: %v", wikiPath, err) + return err + } + if isExist { + if err = util.Rename(wikiPath, WikiPath(repo.Owner.Name, newRepoName)); err != nil { + return fmt.Errorf("rename repository wiki: %v", err) + } + } + + ctx, committer, err := db.TxContext() + if err != nil { + return err + } + defer committer.Close() + + if err := NewRedirect(ctx, repo.Owner.ID, repo.ID, oldRepoName, newRepoName); err != nil { + return err + } + + return committer.Commit() +} diff --git a/models/repo/watch.go b/models/repo/watch.go new file mode 100644 index 000000000..8e54f0970 --- /dev/null +++ b/models/repo/watch.go @@ -0,0 +1,196 @@ +// 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 + +import ( + "context" + + "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/timeutil" +) + +// WatchMode specifies what kind of watch the user has on a repository +type WatchMode int8 + +const ( + // WatchModeNone don't watch + WatchModeNone WatchMode = iota // 0 + // WatchModeNormal watch repository (from other sources) + WatchModeNormal // 1 + // WatchModeDont explicit don't auto-watch + WatchModeDont // 2 + // WatchModeAuto watch repository (from AutoWatchOnChanges) + WatchModeAuto // 3 +) + +// Watch is connection request for receiving repository notification. +type Watch struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"UNIQUE(watch)"` + RepoID int64 `xorm:"UNIQUE(watch)"` + Mode WatchMode `xorm:"SMALLINT NOT NULL DEFAULT 1"` + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` +} + +func init() { + db.RegisterModel(new(Watch)) +} + +// GetWatch gets what kind of subscription a user has on a given repository; returns dummy record if none found +func GetWatch(ctx context.Context, userID, repoID int64) (Watch, error) { + watch := Watch{UserID: userID, RepoID: repoID} + has, err := db.GetEngine(ctx).Get(&watch) + if err != nil { + return watch, err + } + if !has { + watch.Mode = WatchModeNone + } + return watch, nil +} + +// IsWatchMode Decodes watchability of WatchMode +func IsWatchMode(mode WatchMode) bool { + return mode != WatchModeNone && mode != WatchModeDont +} + +// IsWatching checks if user has watched given repository. +func IsWatching(userID, repoID int64) bool { + watch, err := GetWatch(db.DefaultContext, userID, repoID) + return err == nil && IsWatchMode(watch.Mode) +} + +func watchRepoMode(ctx context.Context, watch Watch, mode WatchMode) (err error) { + if watch.Mode == mode { + return nil + } + if mode == WatchModeAuto && (watch.Mode == WatchModeDont || IsWatchMode(watch.Mode)) { + // Don't auto watch if already watching or deliberately not watching + return nil + } + + hadrec := watch.Mode != WatchModeNone + needsrec := mode != WatchModeNone + repodiff := 0 + + if IsWatchMode(mode) && !IsWatchMode(watch.Mode) { + repodiff = 1 + } else if !IsWatchMode(mode) && IsWatchMode(watch.Mode) { + repodiff = -1 + } + + watch.Mode = mode + + e := db.GetEngine(ctx) + + if !hadrec && needsrec { + watch.Mode = mode + if _, err = e.Insert(watch); err != nil { + return err + } + } else if needsrec { + watch.Mode = mode + if _, err := e.ID(watch.ID).AllCols().Update(watch); err != nil { + return err + } + } else if _, err = e.Delete(Watch{ID: watch.ID}); err != nil { + return err + } + if repodiff != 0 { + _, err = e.Exec("UPDATE `repository` SET num_watches = num_watches + ? WHERE id = ?", repodiff, watch.RepoID) + } + return err +} + +// WatchRepoMode watch repository in specific mode. +func WatchRepoMode(userID, repoID int64, mode WatchMode) (err error) { + var watch Watch + if watch, err = GetWatch(db.DefaultContext, userID, repoID); err != nil { + return err + } + return watchRepoMode(db.DefaultContext, watch, mode) +} + +// WatchRepoCtx watch or unwatch repository. +func WatchRepoCtx(ctx context.Context, userID, repoID int64, doWatch bool) (err error) { + var watch Watch + if watch, err = GetWatch(ctx, userID, repoID); err != nil { + return err + } + if !doWatch && watch.Mode == WatchModeAuto { + err = watchRepoMode(ctx, watch, WatchModeDont) + } else if !doWatch { + err = watchRepoMode(ctx, watch, WatchModeNone) + } else { + err = watchRepoMode(ctx, watch, WatchModeNormal) + } + return err +} + +// WatchRepo watch or unwatch repository. +func WatchRepo(userID, repoID int64, watch bool) (err error) { + return WatchRepoCtx(db.DefaultContext, userID, repoID, watch) +} + +// GetWatchers returns all watchers of given repository. +func GetWatchers(ctx context.Context, repoID int64) ([]*Watch, error) { + watches := make([]*Watch, 0, 10) + return watches, db.GetEngine(ctx).Where("`watch`.repo_id=?", repoID). + And("`watch`.mode<>?", WatchModeDont). + And("`user`.is_active=?", true). + And("`user`.prohibit_login=?", false). + Join("INNER", "`user`", "`user`.id = `watch`.user_id"). + Find(&watches) +} + +// GetRepoWatchersIDs returns IDs of watchers for a given repo ID +// but avoids joining with `user` for performance reasons +// User permissions must be verified elsewhere if required +func GetRepoWatchersIDs(ctx context.Context, repoID int64) ([]int64, error) { + ids := make([]int64, 0, 64) + return ids, db.GetEngine(ctx).Table("watch"). + Where("watch.repo_id=?", repoID). + And("watch.mode<>?", WatchModeDont). + Select("user_id"). + Find(&ids) +} + +// GetRepoWatchers returns range of users watching given repository. +func GetRepoWatchers(repoID int64, opts db.ListOptions) ([]*user_model.User, error) { + sess := db.GetEngine(db.DefaultContext).Where("watch.repo_id=?", repoID). + Join("LEFT", "watch", "`user`.id=`watch`.user_id"). + And("`watch`.mode<>?", WatchModeDont) + if opts.Page > 0 { + sess = db.SetSessionPagination(sess, &opts) + users := make([]*user_model.User, 0, opts.PageSize) + + return users, sess.Find(&users) + } + + users := make([]*user_model.User, 0, 8) + return users, sess.Find(&users) +} + +func watchIfAuto(ctx context.Context, userID, repoID int64, isWrite bool) error { + if !isWrite || !setting.Service.AutoWatchOnChanges { + return nil + } + watch, err := GetWatch(ctx, userID, repoID) + if err != nil { + return err + } + if watch.Mode != WatchModeNone { + return nil + } + return watchRepoMode(ctx, watch, WatchModeAuto) +} + +// WatchIfAuto subscribes to repo if AutoWatchOnChanges is set +func WatchIfAuto(userID, repoID int64, isWrite bool) error { + return watchIfAuto(db.DefaultContext, userID, repoID, isWrite) +} diff --git a/models/repo_watch_test.go b/models/repo/watch_test.go similarity index 59% rename from models/repo_watch_test.go rename to models/repo/watch_test.go index 1a6052139..2ff3ced2d 100644 --- a/models/repo_watch_test.go +++ b/models/repo/watch_test.go @@ -2,13 +2,12 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package models +package repo import ( "testing" "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/setting" @@ -27,25 +26,11 @@ func TestIsWatching(t *testing.T) { assert.False(t, IsWatching(unittest.NonexistentID, unittest.NonexistentID)) } -func TestWatchRepo(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - const repoID = 3 - const userID = 2 - - assert.NoError(t, WatchRepo(userID, repoID, true)) - unittest.AssertExistsAndLoadBean(t, &Watch{RepoID: repoID, UserID: userID}) - unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}) - - assert.NoError(t, WatchRepo(userID, repoID, false)) - unittest.AssertNotExistsBean(t, &Watch{RepoID: repoID, UserID: userID}) - unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}) -} - func TestGetWatchers(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) - watches, err := GetWatchers(repo.ID) + repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) + watches, err := GetWatchers(db.DefaultContext, repo.ID) assert.NoError(t, err) // One watchers are inactive, thus minus 1 assert.Len(t, watches, repo.NumWatches-1) @@ -53,7 +38,7 @@ func TestGetWatchers(t *testing.T) { assert.EqualValues(t, repo.ID, watch.RepoID) } - watches, err = GetWatchers(unittest.NonexistentID) + watches, err = GetWatchers(db.DefaultContext, unittest.NonexistentID) assert.NoError(t, err) assert.Len(t, watches, 0) } @@ -61,7 +46,7 @@ func TestGetWatchers(t *testing.T) { func TestRepository_GetWatchers(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) + repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) watchers, err := GetRepoWatchers(repo.ID, db.ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, repo.NumWatches) @@ -69,53 +54,16 @@ func TestRepository_GetWatchers(t *testing.T) { unittest.AssertExistsAndLoadBean(t, &Watch{UserID: watcher.ID, RepoID: repo.ID}) } - repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 9}).(*repo_model.Repository) + repo = unittest.AssertExistsAndLoadBean(t, &Repository{ID: 9}).(*Repository) watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, 0) } -func TestNotifyWatchers(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - action := &Action{ - ActUserID: 8, - RepoID: 1, - OpType: ActionStarRepo, - } - assert.NoError(t, NotifyWatchers(action)) - - // One watchers are inactive, thus action is only created for user 8, 1, 4, 11 - unittest.AssertExistsAndLoadBean(t, &Action{ - ActUserID: action.ActUserID, - UserID: 8, - RepoID: action.RepoID, - OpType: action.OpType, - }) - unittest.AssertExistsAndLoadBean(t, &Action{ - ActUserID: action.ActUserID, - UserID: 1, - RepoID: action.RepoID, - OpType: action.OpType, - }) - unittest.AssertExistsAndLoadBean(t, &Action{ - ActUserID: action.ActUserID, - UserID: 4, - RepoID: action.RepoID, - OpType: action.OpType, - }) - unittest.AssertExistsAndLoadBean(t, &Action{ - ActUserID: action.ActUserID, - UserID: 11, - RepoID: action.RepoID, - OpType: action.OpType, - }) -} - func TestWatchIfAuto(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) + repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) watchers, err := GetRepoWatchers(repo.ID, db.ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, repo.NumWatches) @@ -174,18 +122,18 @@ func TestWatchRepoMode(t *testing.T) { unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 0) - assert.NoError(t, WatchRepoMode(12, 1, RepoWatchModeAuto)) + assert.NoError(t, WatchRepoMode(12, 1, WatchModeAuto)) unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 1) - unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: RepoWatchModeAuto}, 1) + unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: WatchModeAuto}, 1) - assert.NoError(t, WatchRepoMode(12, 1, RepoWatchModeNormal)) + assert.NoError(t, WatchRepoMode(12, 1, WatchModeNormal)) unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 1) - unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: RepoWatchModeNormal}, 1) + unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: WatchModeNormal}, 1) - assert.NoError(t, WatchRepoMode(12, 1, RepoWatchModeDont)) + assert.NoError(t, WatchRepoMode(12, 1, WatchModeDont)) unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 1) - unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: RepoWatchModeDont}, 1) + unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: WatchModeDont}, 1) - assert.NoError(t, WatchRepoMode(12, 1, RepoWatchModeNone)) + assert.NoError(t, WatchRepoMode(12, 1, WatchModeNone)) unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 0) } diff --git a/models/repo_archiver.go b/models/repo_archiver.go deleted file mode 100644 index 1ac05da04..000000000 --- a/models/repo_archiver.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2021 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 ( - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" -) - -// LoadArchiverRepo loads repository -func LoadArchiverRepo(archiver *repo_model.RepoArchiver) (*repo_model.Repository, error) { - var repo repo_model.Repository - has, err := db.GetEngine(db.DefaultContext).ID(archiver.RepoID).Get(&repo) - if err != nil { - return nil, err - } - if !has { - return nil, repo_model.ErrRepoNotExist{ - ID: archiver.RepoID, - } - } - return &repo, nil -} diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go index ab6a3bafb..3aca1023e 100644 --- a/models/repo_collaboration.go +++ b/models/repo_collaboration.go @@ -207,15 +207,13 @@ func DeleteCollaboration(repo *repo_model.Repository, uid int64) (err error) { } defer committer.Close() - sess := db.GetEngine(ctx) - - if has, err := sess.Delete(collaboration); err != nil || has == 0 { + if has, err := db.GetEngine(ctx).Delete(collaboration); err != nil || has == 0 { return err } else if err = recalculateAccesses(ctx, repo); err != nil { return err } - if err = watchRepo(sess, uid, repo.ID, false); err != nil { + if err = repo_model.WatchRepoCtx(ctx, uid, repo.ID, false); err != nil { return err } @@ -253,13 +251,12 @@ func reconsiderWatches(ctx context.Context, repo *repo_model.Repository, uid int if has, err := hasAccess(ctx, uid, repo); err != nil || has { return err } - e := db.GetEngine(ctx) - if err := watchRepo(e, uid, repo.ID, false); err != nil { + if err := repo_model.WatchRepoCtx(ctx, uid, repo.ID, false); err != nil { return err } // Remove all IssueWatches a user has subscribed to in the repository - return removeIssueWatchersByRepoID(e, uid, repo.ID) + return removeIssueWatchersByRepoID(db.GetEngine(ctx), uid, repo.ID) } func getRepoTeams(e db.Engine, repo *repo_model.Repository) (teams []*Team, err error) { diff --git a/models/repo_generate.go b/models/repo_generate.go index 6b5b8e5bc..fc749f112 100644 --- a/models/repo_generate.go +++ b/models/repo_generate.go @@ -8,15 +8,12 @@ import ( "bufio" "bytes" "context" - "strconv" "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/git" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/storage" "github.com/gobwas/glob" ) @@ -70,49 +67,6 @@ func (gt GiteaTemplate) Globs() []glob.Glob { return gt.globs } -// GenerateTopics generates topics from a template repository -func GenerateTopics(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error { - for _, topic := range templateRepo.Topics { - if _, err := addTopicByNameToRepo(db.GetEngine(ctx), generateRepo.ID, topic); err != nil { - return err - } - } - return nil -} - -// GenerateGitHooks generates git hooks from a template repository -func GenerateGitHooks(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error { - generateGitRepo, err := git.OpenRepository(generateRepo.RepoPath()) - if err != nil { - return err - } - defer generateGitRepo.Close() - - templateGitRepo, err := git.OpenRepository(templateRepo.RepoPath()) - if err != nil { - return err - } - defer templateGitRepo.Close() - - templateHooks, err := templateGitRepo.Hooks() - if err != nil { - return err - } - - for _, templateHook := range templateHooks { - generateHook, err := generateGitRepo.GetHook(templateHook.Name()) - if err != nil { - return err - } - - generateHook.Content = templateHook.Content - if err := generateHook.Update(); err != nil { - return err - } - } - return nil -} - // GenerateWebhooks generates webhooks from a template repository func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error { templateWebhooks, err := webhook.ListWebhooksByOpts(&webhook.ListWebhookOptions{RepoID: templateRepo.ID}) @@ -141,16 +95,6 @@ func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *repo_mode return nil } -// GenerateAvatar generates the avatar from a template repository -func GenerateAvatar(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error { - generateRepo.Avatar = strings.Replace(templateRepo.Avatar, strconv.FormatInt(templateRepo.ID, 10), strconv.FormatInt(generateRepo.ID, 10), 1) - if _, err := storage.Copy(storage.RepoAvatars, generateRepo.CustomAvatarRelativePath(), storage.RepoAvatars, templateRepo.CustomAvatarRelativePath()); err != nil { - return err - } - - return updateRepositoryCols(db.GetEngine(ctx), generateRepo, "avatar") -} - // GenerateIssueLabels generates issue labels from a template repository func GenerateIssueLabels(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error { templateLabels, err := getLabelsByRepoID(db.GetEngine(ctx), templateRepo.ID, "", db.ListOptions{}) diff --git a/models/repo_permission.go b/models/repo_permission.go index 3dc8db92b..45878c8ba 100644 --- a/models/repo_permission.go +++ b/models/repo_permission.go @@ -419,3 +419,21 @@ func FilterOutRepoIdsWithoutUnitAccess(u *user_model.User, repoIDs []int64, unit } return repoIDs[:i], nil } + +// GetRepoReaders returns all users that have explicit read access or higher to the repository. +func GetRepoReaders(repo *repo_model.Repository) (_ []*user_model.User, err error) { + return getUsersWithAccessMode(db.DefaultContext, repo, perm_model.AccessModeRead) +} + +// GetRepoWriters returns all users that have write access to the repository. +func GetRepoWriters(repo *repo_model.Repository) (_ []*user_model.User, err error) { + return getUsersWithAccessMode(db.DefaultContext, repo, perm_model.AccessModeWrite) +} + +// IsRepoReader returns true if user has explicit read access or higher to the repository. +func IsRepoReader(repo *repo_model.Repository, userID int64) (bool, error) { + if repo.OwnerID == userID { + return true, nil + } + return db.GetEngine(db.DefaultContext).Where("repo_id = ? AND user_id = ? AND mode >= ?", repo.ID, userID, perm_model.AccessModeRead).Get(&Access{}) +} diff --git a/models/repo_redirect.go b/models/repo_redirect.go deleted file mode 100644 index 18422f9d1..000000000 --- a/models/repo_redirect.go +++ /dev/null @@ -1,62 +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 ( - "strings" - - "code.gitea.io/gitea/models/db" -) - -// RepoRedirect represents that a repo name should be redirected to another -type RepoRedirect struct { - ID int64 `xorm:"pk autoincr"` - OwnerID int64 `xorm:"UNIQUE(s)"` - LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"` - RedirectRepoID int64 // repoID to redirect to -} - -func init() { - db.RegisterModel(new(RepoRedirect)) -} - -// LookupRepoRedirect look up if a repository has a redirect name -func LookupRepoRedirect(ownerID int64, repoName string) (int64, error) { - repoName = strings.ToLower(repoName) - redirect := &RepoRedirect{OwnerID: ownerID, LowerName: repoName} - if has, err := db.GetEngine(db.DefaultContext).Get(redirect); err != nil { - return 0, err - } else if !has { - return 0, ErrRepoRedirectNotExist{OwnerID: ownerID, RepoName: repoName} - } - return redirect.RedirectRepoID, nil -} - -// newRepoRedirect create a new repo redirect -func newRepoRedirect(e db.Engine, ownerID, repoID int64, oldRepoName, newRepoName string) error { - oldRepoName = strings.ToLower(oldRepoName) - newRepoName = strings.ToLower(newRepoName) - - if err := deleteRepoRedirect(e, ownerID, newRepoName); err != nil { - return err - } - - if _, err := e.Insert(&RepoRedirect{ - OwnerID: ownerID, - LowerName: oldRepoName, - RedirectRepoID: repoID, - }); err != nil { - return err - } - return nil -} - -// deleteRepoRedirect delete any redirect from the specified repo name to -// anything else -func deleteRepoRedirect(e db.Engine, ownerID int64, repoName string) error { - repoName = strings.ToLower(repoName) - _, err := e.Delete(&RepoRedirect{OwnerID: ownerID, LowerName: repoName}) - return err -} diff --git a/models/repo_redirect_test.go b/models/repo_redirect_test.go deleted file mode 100644 index c6d471448..000000000 --- a/models/repo_redirect_test.go +++ /dev/null @@ -1,78 +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 ( - "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 TestLookupRepoRedirect(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - repoID, err := LookupRepoRedirect(2, "oldrepo1") - assert.NoError(t, err) - assert.EqualValues(t, 1, repoID) - - _, err = LookupRepoRedirect(unittest.NonexistentID, "doesnotexist") - assert.True(t, IsErrRepoRedirectNotExist(err)) -} - -func TestNewRepoRedirect(t *testing.T) { - // redirect to a completely new name - assert.NoError(t, unittest.PrepareTestDatabase()) - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) - assert.NoError(t, newRepoRedirect(db.GetEngine(db.DefaultContext), repo.OwnerID, repo.ID, repo.Name, "newreponame")) - - unittest.AssertExistsAndLoadBean(t, &RepoRedirect{ - OwnerID: repo.OwnerID, - LowerName: repo.LowerName, - RedirectRepoID: repo.ID, - }) - unittest.AssertExistsAndLoadBean(t, &RepoRedirect{ - OwnerID: repo.OwnerID, - LowerName: "oldrepo1", - RedirectRepoID: repo.ID, - }) -} - -func TestNewRepoRedirect2(t *testing.T) { - // redirect to previously used name - assert.NoError(t, unittest.PrepareTestDatabase()) - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) - assert.NoError(t, newRepoRedirect(db.GetEngine(db.DefaultContext), repo.OwnerID, repo.ID, repo.Name, "oldrepo1")) - - unittest.AssertExistsAndLoadBean(t, &RepoRedirect{ - OwnerID: repo.OwnerID, - LowerName: repo.LowerName, - RedirectRepoID: repo.ID, - }) - unittest.AssertNotExistsBean(t, &RepoRedirect{ - OwnerID: repo.OwnerID, - LowerName: "oldrepo1", - RedirectRepoID: repo.ID, - }) -} - -func TestNewRepoRedirect3(t *testing.T) { - // redirect for a previously-unredirected repo - assert.NoError(t, unittest.PrepareTestDatabase()) - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository) - assert.NoError(t, newRepoRedirect(db.GetEngine(db.DefaultContext), repo.OwnerID, repo.ID, repo.Name, "newreponame")) - - unittest.AssertExistsAndLoadBean(t, &RepoRedirect{ - OwnerID: repo.OwnerID, - LowerName: repo.LowerName, - RedirectRepoID: repo.ID, - }) -} diff --git a/models/repo_test.go b/models/repo_test.go index 72a297734..45e016a8f 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -5,11 +5,6 @@ package models import ( - "bytes" - "crypto/md5" - "fmt" - "image" - "image/png" "testing" "code.gitea.io/gitea/models/db" @@ -22,6 +17,20 @@ import ( "github.com/stretchr/testify/assert" ) +func TestWatchRepo(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + const repoID = 3 + const userID = 2 + + assert.NoError(t, repo_model.WatchRepo(userID, repoID, true)) + unittest.AssertExistsAndLoadBean(t, &repo_model.Watch{RepoID: repoID, UserID: userID}) + unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}) + + assert.NoError(t, repo_model.WatchRepo(userID, repoID, false)) + unittest.AssertNotExistsBean(t, &repo_model.Watch{RepoID: repoID, UserID: userID}) + unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}) +} + func TestMetas(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) @@ -90,77 +99,6 @@ func TestUpdateRepositoryVisibilityChanged(t *testing.T) { assert.True(t, act.IsPrivate) } -func TestGetUserFork(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - // User13 has repo 11 forked from repo10 - repo, err := repo_model.GetRepositoryByID(10) - assert.NoError(t, err) - assert.NotNil(t, repo) - repo, err = GetUserFork(repo.ID, 13) - assert.NoError(t, err) - assert.NotNil(t, repo) - - repo, err = repo_model.GetRepositoryByID(9) - assert.NoError(t, err) - assert.NotNil(t, repo) - repo, err = GetUserFork(repo.ID, 13) - assert.NoError(t, err) - assert.Nil(t, repo) -} - -func TestRepoAPIURL(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - 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()) -} - -func TestUploadAvatar(t *testing.T) { - // Generate image - myImage := image.NewRGBA(image.Rect(0, 0, 1, 1)) - var buff bytes.Buffer - png.Encode(&buff, myImage) - - assert.NoError(t, unittest.PrepareTestDatabase()) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository) - - err := UploadRepoAvatar(repo, buff.Bytes()) - assert.NoError(t, err) - assert.Equal(t, fmt.Sprintf("%d-%x", 10, md5.Sum(buff.Bytes())), repo.Avatar) -} - -func TestUploadBigAvatar(t *testing.T) { - // Generate BIG image - myImage := image.NewRGBA(image.Rect(0, 0, 5000, 1)) - var buff bytes.Buffer - png.Encode(&buff, myImage) - - assert.NoError(t, unittest.PrepareTestDatabase()) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository) - - err := UploadRepoAvatar(repo, buff.Bytes()) - assert.Error(t, err) -} - -func TestDeleteAvatar(t *testing.T) { - // Generate image - myImage := image.NewRGBA(image.Rect(0, 0, 1, 1)) - var buff bytes.Buffer - png.Encode(&buff, myImage) - - assert.NoError(t, unittest.PrepareTestDatabase()) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository) - - err := UploadRepoAvatar(repo, buff.Bytes()) - assert.NoError(t, err) - - err = DeleteRepoAvatar(repo) - assert.NoError(t, err) - - assert.Equal(t, "", repo.Avatar) -} - func TestDoctorUserStarNum(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 398ed0755..f7d5e2099 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -5,6 +5,7 @@ package models import ( + "context" "fmt" "os" @@ -112,8 +113,8 @@ func GetPendingRepositoryTransfer(repo *repo_model.Repository) (*RepoTransfer, e return transfer, nil } -func deleteRepositoryTransfer(e db.Engine, repoID int64) error { - _, err := e.Where("repo_id = ?", repoID).Delete(&RepoTransfer{}) +func deleteRepositoryTransfer(ctx context.Context, repoID int64) error { + _, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Delete(&RepoTransfer{}) return err } @@ -125,14 +126,13 @@ func CancelRepositoryTransfer(repo *repo_model.Repository) error { return err } defer committer.Close() - sess := db.GetEngine(ctx) repo.Status = repo_model.RepositoryReady - if err := updateRepositoryCols(sess, repo, "status"); err != nil { + if err := repo_model.UpdateRepositoryColsCtx(ctx, repo, "status"); err != nil { return err } - if err := deleteRepositoryTransfer(sess, repo.ID); err != nil { + if err := deleteRepositoryTransfer(ctx, repo.ID); err != nil { return err } @@ -158,7 +158,6 @@ func CreatePendingRepositoryTransfer(doer, newOwner *user_model.User, repoID int return err } defer committer.Close() - sess := db.GetEngine(ctx) repo, err := repo_model.GetRepositoryByIDCtx(ctx, repoID) if err != nil { @@ -171,7 +170,7 @@ func CreatePendingRepositoryTransfer(doer, newOwner *user_model.User, repoID int } repo.Status = repo_model.RepositoryPendingTransfer - if err := updateRepositoryCols(sess, repo, "status"); err != nil { + if err := repo_model.UpdateRepositoryColsCtx(ctx, repo, "status"); err != nil { return err } @@ -179,7 +178,10 @@ func CreatePendingRepositoryTransfer(doer, newOwner *user_model.User, repoID int if has, err := repo_model.IsRepositoryExistCtx(ctx, newOwner, repo.Name); err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { - return ErrRepoAlreadyExist{newOwner.LowerName, repo.Name} + return repo_model.ErrRepoAlreadyExist{ + Uname: newOwner.LowerName, + Name: repo.Name, + } } transfer := &RepoTransfer{ @@ -256,7 +258,10 @@ func TransferOwnership(doer *user_model.User, newOwnerName string, repo *repo_mo if has, err := repo_model.IsRepositoryExistCtx(ctx, newOwner, repo.Name); err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { - return ErrRepoAlreadyExist{newOwnerName, repo.Name} + return repo_model.ErrRepoAlreadyExist{ + Uname: newOwnerName, + Name: repo.Name, + } } oldOwner := repo.Owner @@ -336,13 +341,13 @@ func TransferOwnership(doer *user_model.User, newOwnerName string, repo *repo_mo return fmt.Errorf("decrease old owner repository count: %v", err) } - if err := watchRepo(sess, doer.ID, repo.ID, true); err != nil { + if err := repo_model.WatchRepoCtx(ctx, doer.ID, repo.ID, true); err != nil { return fmt.Errorf("watchRepo: %v", err) } // Remove watch for organization. if oldOwner.IsOrganization() { - if err := watchRepo(sess, oldOwner.ID, repo.ID, false); err != nil { + if err := repo_model.WatchRepoCtx(ctx, oldOwner.ID, repo.ID, false); err != nil { return fmt.Errorf("watchRepo [false]: %v", err) } } @@ -399,21 +404,21 @@ func TransferOwnership(doer *user_model.User, newOwnerName string, repo *repo_mo wikiRenamed = true } - if err := deleteRepositoryTransfer(sess, repo.ID); err != nil { + if err := deleteRepositoryTransfer(ctx, repo.ID); err != nil { return fmt.Errorf("deleteRepositoryTransfer: %v", err) } repo.Status = repo_model.RepositoryReady - if err := updateRepositoryCols(sess, repo, "status"); err != nil { + if err := repo_model.UpdateRepositoryColsCtx(ctx, repo, "status"); err != nil { return err } // If there was previously a redirect at this location, remove it. - if err := deleteRepoRedirect(sess, newOwner.ID, repo.Name); err != nil { + if err := repo_model.DeleteRedirect(ctx, newOwner.ID, repo.Name); err != nil { return fmt.Errorf("delete repo redirect: %v", err) } - if err := newRepoRedirect(sess, oldOwner.ID, repo.ID, repo.Name, repo.Name); err != nil { - return fmt.Errorf("newRepoRedirect: %v", err) + if err := repo_model.NewRedirect(ctx, oldOwner.ID, repo.ID, repo.Name, repo.Name); err != nil { + return fmt.Errorf("repo_model.NewRedirect: %v", err) } return committer.Commit() diff --git a/models/repo_watch.go b/models/repo_watch.go deleted file mode 100644 index 6ae478d65..000000000 --- a/models/repo_watch.go +++ /dev/null @@ -1,328 +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" - - "code.gitea.io/gitea/models/db" - 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/setting" - "code.gitea.io/gitea/modules/timeutil" -) - -// RepoWatchMode specifies what kind of watch the user has on a repository -type RepoWatchMode int8 - -const ( - // RepoWatchModeNone don't watch - RepoWatchModeNone RepoWatchMode = iota // 0 - // RepoWatchModeNormal watch repository (from other sources) - RepoWatchModeNormal // 1 - // RepoWatchModeDont explicit don't auto-watch - RepoWatchModeDont // 2 - // RepoWatchModeAuto watch repository (from AutoWatchOnChanges) - RepoWatchModeAuto // 3 -) - -// Watch is connection request for receiving repository notification. -type Watch struct { - ID int64 `xorm:"pk autoincr"` - UserID int64 `xorm:"UNIQUE(watch)"` - RepoID int64 `xorm:"UNIQUE(watch)"` - Mode RepoWatchMode `xorm:"SMALLINT NOT NULL DEFAULT 1"` - CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` - UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` -} - -func init() { - db.RegisterModel(new(Watch)) -} - -// getWatch gets what kind of subscription a user has on a given repository; returns dummy record if none found -func getWatch(e db.Engine, userID, repoID int64) (Watch, error) { - watch := Watch{UserID: userID, RepoID: repoID} - has, err := e.Get(&watch) - if err != nil { - return watch, err - } - if !has { - watch.Mode = RepoWatchModeNone - } - return watch, nil -} - -// Decodes watchability of RepoWatchMode -func isWatchMode(mode RepoWatchMode) bool { - return mode != RepoWatchModeNone && mode != RepoWatchModeDont -} - -// IsWatching checks if user has watched given repository. -func IsWatching(userID, repoID int64) bool { - watch, err := getWatch(db.GetEngine(db.DefaultContext), userID, repoID) - return err == nil && isWatchMode(watch.Mode) -} - -func watchRepoMode(e db.Engine, watch Watch, mode RepoWatchMode) (err error) { - if watch.Mode == mode { - return nil - } - if mode == RepoWatchModeAuto && (watch.Mode == RepoWatchModeDont || isWatchMode(watch.Mode)) { - // Don't auto watch if already watching or deliberately not watching - return nil - } - - hadrec := watch.Mode != RepoWatchModeNone - needsrec := mode != RepoWatchModeNone - repodiff := 0 - - if isWatchMode(mode) && !isWatchMode(watch.Mode) { - repodiff = 1 - } else if !isWatchMode(mode) && isWatchMode(watch.Mode) { - repodiff = -1 - } - - watch.Mode = mode - - if !hadrec && needsrec { - watch.Mode = mode - if _, err = e.Insert(watch); err != nil { - return err - } - } else if needsrec { - watch.Mode = mode - if _, err := e.ID(watch.ID).AllCols().Update(watch); err != nil { - return err - } - } else if _, err = e.Delete(Watch{ID: watch.ID}); err != nil { - return err - } - if repodiff != 0 { - _, err = e.Exec("UPDATE `repository` SET num_watches = num_watches + ? WHERE id = ?", repodiff, watch.RepoID) - } - return err -} - -// WatchRepoMode watch repository in specific mode. -func WatchRepoMode(userID, repoID int64, mode RepoWatchMode) (err error) { - var watch Watch - if watch, err = getWatch(db.GetEngine(db.DefaultContext), userID, repoID); err != nil { - return err - } - return watchRepoMode(db.GetEngine(db.DefaultContext), watch, mode) -} - -func watchRepo(e db.Engine, userID, repoID int64, doWatch bool) (err error) { - var watch Watch - if watch, err = getWatch(e, userID, repoID); err != nil { - return err - } - if !doWatch && watch.Mode == RepoWatchModeAuto { - err = watchRepoMode(e, watch, RepoWatchModeDont) - } else if !doWatch { - err = watchRepoMode(e, watch, RepoWatchModeNone) - } else { - err = watchRepoMode(e, watch, RepoWatchModeNormal) - } - return err -} - -// WatchRepo watch or unwatch repository. -func WatchRepo(userID, repoID int64, watch bool) (err error) { - return watchRepo(db.GetEngine(db.DefaultContext), userID, repoID, watch) -} - -func getWatchers(e db.Engine, repoID int64) ([]*Watch, error) { - watches := make([]*Watch, 0, 10) - return watches, e.Where("`watch`.repo_id=?", repoID). - And("`watch`.mode<>?", RepoWatchModeDont). - And("`user`.is_active=?", true). - And("`user`.prohibit_login=?", false). - Join("INNER", "`user`", "`user`.id = `watch`.user_id"). - Find(&watches) -} - -// GetWatchers returns all watchers of given repository. -func GetWatchers(repoID int64) ([]*Watch, error) { - return getWatchers(db.GetEngine(db.DefaultContext), repoID) -} - -// GetRepoWatchersIDs returns IDs of watchers for a given repo ID -// but avoids joining with `user` for performance reasons -// User permissions must be verified elsewhere if required -func GetRepoWatchersIDs(repoID int64) ([]int64, error) { - return getRepoWatchersIDs(db.GetEngine(db.DefaultContext), repoID) -} - -func getRepoWatchersIDs(e db.Engine, repoID int64) ([]int64, error) { - ids := make([]int64, 0, 64) - return ids, e.Table("watch"). - Where("watch.repo_id=?", repoID). - And("watch.mode<>?", RepoWatchModeDont). - Select("user_id"). - Find(&ids) -} - -// GetRepoWatchers returns range of users watching given repository. -func GetRepoWatchers(repoID int64, opts db.ListOptions) ([]*user_model.User, error) { - sess := db.GetEngine(db.DefaultContext).Where("watch.repo_id=?", repoID). - Join("LEFT", "watch", "`user`.id=`watch`.user_id"). - And("`watch`.mode<>?", RepoWatchModeDont) - if opts.Page > 0 { - sess = db.SetSessionPagination(sess, &opts) - users := make([]*user_model.User, 0, opts.PageSize) - - return users, sess.Find(&users) - } - - users := make([]*user_model.User, 0, 8) - return users, sess.Find(&users) -} - -func notifyWatchers(ctx context.Context, actions ...*Action) error { - var watchers []*Watch - var repo *repo_model.Repository - var err error - var permCode []bool - var permIssue []bool - var permPR []bool - - e := db.GetEngine(ctx) - - for _, act := range actions { - repoChanged := repo == nil || repo.ID != act.RepoID - - if repoChanged { - // Add feeds for user self and all watchers. - watchers, err = getWatchers(e, act.RepoID) - if err != nil { - return fmt.Errorf("get watchers: %v", err) - } - } - - // Add feed for actioner. - act.UserID = act.ActUserID - if _, err = e.InsertOne(act); err != nil { - return fmt.Errorf("insert new actioner: %v", err) - } - - if repoChanged { - act.loadRepo() - repo = act.Repo - - // check repo owner exist. - if err := act.Repo.GetOwner(ctx); err != nil { - return fmt.Errorf("can't get repo owner: %v", err) - } - } else if act.Repo == nil { - act.Repo = repo - } - - // Add feed for organization - if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID { - act.ID = 0 - act.UserID = act.Repo.Owner.ID - if _, err = e.InsertOne(act); err != nil { - return fmt.Errorf("insert new actioner: %v", err) - } - } - - if repoChanged { - permCode = make([]bool, len(watchers)) - permIssue = make([]bool, len(watchers)) - permPR = make([]bool, len(watchers)) - for i, watcher := range watchers { - user, err := user_model.GetUserByIDEngine(e, watcher.UserID) - if err != nil { - permCode[i] = false - permIssue[i] = false - permPR[i] = false - continue - } - perm, err := getUserRepoPermission(ctx, repo, user) - if err != nil { - permCode[i] = false - permIssue[i] = false - permPR[i] = false - continue - } - permCode[i] = perm.CanRead(unit.TypeCode) - permIssue[i] = perm.CanRead(unit.TypeIssues) - permPR[i] = perm.CanRead(unit.TypePullRequests) - } - } - - for i, watcher := range watchers { - if act.ActUserID == watcher.UserID { - continue - } - act.ID = 0 - act.UserID = watcher.UserID - act.Repo.Units = nil - - switch act.OpType { - case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionPublishRelease, ActionDeleteBranch: - if !permCode[i] { - continue - } - case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue: - if !permIssue[i] { - continue - } - case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest: - if !permPR[i] { - continue - } - } - - if _, err = e.InsertOne(act); err != nil { - return fmt.Errorf("insert new action: %v", err) - } - } - } - return nil -} - -// NotifyWatchers creates batch of actions for every watcher. -func NotifyWatchers(actions ...*Action) error { - return notifyWatchers(db.DefaultContext, actions...) -} - -// NotifyWatchersActions creates batch of actions for every watcher. -func NotifyWatchersActions(acts []*Action) error { - ctx, committer, err := db.TxContext() - if err != nil { - return err - } - defer committer.Close() - for _, act := range acts { - if err := notifyWatchers(ctx, act); err != nil { - return err - } - } - return committer.Commit() -} - -func watchIfAuto(e db.Engine, userID, repoID int64, isWrite bool) error { - if !isWrite || !setting.Service.AutoWatchOnChanges { - return nil - } - watch, err := getWatch(e, userID, repoID) - if err != nil { - return err - } - if watch.Mode != RepoWatchModeNone { - return nil - } - return watchRepoMode(e, watch, RepoWatchModeAuto) -} - -// WatchIfAuto subscribes to repo if AutoWatchOnChanges is set -func WatchIfAuto(userID, repoID int64, isWrite bool) error { - return watchIfAuto(db.GetEngine(db.DefaultContext), userID, repoID, isWrite) -} diff --git a/models/statistic.go b/models/statistic.go index 175815081..f39cdd5eb 100644 --- a/models/statistic.go +++ b/models/statistic.go @@ -50,8 +50,8 @@ func GetStatistic() (stats Statistic) { stats.Counter.Org = CountOrganizations() stats.Counter.PublicKey, _ = e.Count(new(asymkey_model.PublicKey)) stats.Counter.Repo = repo_model.CountRepositories(true) - stats.Counter.Watch, _ = e.Count(new(Watch)) - stats.Counter.Star, _ = e.Count(new(Star)) + stats.Counter.Watch, _ = e.Count(new(repo_model.Watch)) + stats.Counter.Star, _ = e.Count(new(repo_model.Star)) stats.Counter.Action, _ = e.Count(new(Action)) stats.Counter.Access, _ = e.Count(new(Access)) diff --git a/models/update.go b/models/update.go deleted file mode 100644 index 14333ed98..000000000 --- a/models/update.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2014 The Gogs 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" - repo_model "code.gitea.io/gitea/models/repo" -) - -// PushUpdateDeleteTagsContext updates a number of delete tags with context -func PushUpdateDeleteTagsContext(ctx context.Context, repo *repo_model.Repository, tags []string) error { - return pushUpdateDeleteTags(db.GetEngine(ctx), repo, tags) -} - -func pushUpdateDeleteTags(e db.Engine, repo *repo_model.Repository, tags []string) error { - if len(tags) == 0 { - return nil - } - lowerTags := make([]string, 0, len(tags)) - for _, tag := range tags { - lowerTags = append(lowerTags, strings.ToLower(tag)) - } - - if _, err := e. - Where("repo_id = ? AND is_tag = ?", repo.ID, true). - In("lower_tag_name", lowerTags). - Delete(new(Release)); err != nil { - return fmt.Errorf("Delete: %v", err) - } - - if _, err := e. - Where("repo_id = ? AND is_tag = ?", repo.ID, false). - In("lower_tag_name", lowerTags). - Cols("is_draft", "num_commits", "sha1"). - Update(&Release{ - IsDraft: true, - }); err != nil { - return fmt.Errorf("Update: %v", err) - } - - return nil -} - -// PushUpdateDeleteTag must be called for any push actions to delete tag -func PushUpdateDeleteTag(repo *repo_model.Repository, tagName string) error { - rel, err := GetRelease(repo.ID, tagName) - if err != nil { - if IsErrReleaseNotExist(err) { - return nil - } - return fmt.Errorf("GetRelease: %v", err) - } - if rel.IsTag { - if _, err = db.GetEngine(db.DefaultContext).ID(rel.ID).Delete(new(Release)); err != nil { - return fmt.Errorf("Delete: %v", err) - } - } else { - rel.IsDraft = true - rel.NumCommits = 0 - rel.Sha1 = "" - if _, err = db.GetEngine(db.DefaultContext).ID(rel.ID).AllCols().Update(rel); err != nil { - return fmt.Errorf("Update: %v", err) - } - } - - return nil -} - -// SaveOrUpdateTag must be called for any push actions to add tag -func SaveOrUpdateTag(repo *repo_model.Repository, newRel *Release) error { - rel, err := GetRelease(repo.ID, newRel.TagName) - if err != nil && !IsErrReleaseNotExist(err) { - return fmt.Errorf("GetRelease: %v", err) - } - - if rel == nil { - rel = newRel - if _, err = db.GetEngine(db.DefaultContext).Insert(rel); err != nil { - return fmt.Errorf("InsertOne: %v", err) - } - } else { - rel.Sha1 = newRel.Sha1 - rel.CreatedUnix = newRel.CreatedUnix - rel.NumCommits = newRel.NumCommits - rel.IsDraft = false - if rel.IsTag && newRel.PublisherID > 0 { - rel.PublisherID = newRel.PublisherID - } - if _, err = db.GetEngine(db.DefaultContext).ID(rel.ID).AllCols().Update(rel); err != nil { - return fmt.Errorf("Update: %v", err) - } - } - return nil -} diff --git a/models/user.go b/models/user.go index 2a727dd12..ddd63bf5f 100644 --- a/models/user.go +++ b/models/user.go @@ -150,7 +150,7 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) { // ***** START: Watch ***** watchedRepoIDs := make([]int64, 0, 10) if err = e.Table("watch").Cols("watch.repo_id"). - Where("watch.user_id = ?", u.ID).And("watch.mode <>?", RepoWatchModeDont).Find(&watchedRepoIDs); err != nil { + Where("watch.user_id = ?", u.ID).And("watch.mode <>?", repo_model.WatchModeDont).Find(&watchedRepoIDs); err != nil { return fmt.Errorf("get all watches: %v", err) } if _, err = e.Decr("num_watches").In("id", watchedRepoIDs).NoAutoTime().Update(new(repo_model.Repository)); err != nil { @@ -190,8 +190,8 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) { &AccessToken{UID: u.ID}, &Collaboration{UserID: u.ID}, &Access{UserID: u.ID}, - &Watch{UserID: u.ID}, - &Star{UID: u.ID}, + &repo_model.Watch{UserID: u.ID}, + &repo_model.Star{UID: u.ID}, &user_model.Follow{UserID: u.ID}, &user_model.Follow{FollowID: u.ID}, &Action{UserID: u.ID}, @@ -296,7 +296,7 @@ func GetStarredRepos(userID int64, private bool, listOptions db.ListOptions) ([] // GetWatchedRepos returns the repos watched by a particular user func GetWatchedRepos(userID int64, private bool, listOptions db.ListOptions) ([]*repo_model.Repository, int64, error) { sess := db.GetEngine(db.DefaultContext).Where("watch.user_id=?", userID). - And("`watch`.mode<>?", RepoWatchModeDont). + And("`watch`.mode<>?", repo_model.WatchModeDont). Join("LEFT", "watch", "`repository`.id=`watch`.repo_id") if !private { sess = sess.And("is_private=?", false) diff --git a/modules/context/repo.go b/modules/context/repo.go index 1725cb724..b54401f34 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -443,10 +443,10 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { repo, err := repo_model.GetRepositoryByName(owner.ID, repoName) if err != nil { if repo_model.IsErrRepoNotExist(err) { - redirectRepoID, err := models.LookupRepoRedirect(owner.ID, repoName) + redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName) if err == nil { RedirectToRepo(ctx, redirectRepoID) - } else if models.IsErrRepoRedirectNotExist(err) { + } else if repo_model.IsErrRedirectNotExist(err) { if ctx.FormString("go-get") == "1" { EarlyResponseForGoGetMeta(ctx) return @@ -512,8 +512,8 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { ctx.Data["WikiCloneLink"] = repo.WikiCloneLink() if ctx.IsSigned { - ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.ID, repo.ID) - ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID) + ctx.Data["IsWatchingRepo"] = repo_model.IsWatching(ctx.User.ID, repo.ID) + ctx.Data["IsStaringRepo"] = repo_model.IsStaring(ctx.User.ID, repo.ID) } if repo.IsFork { @@ -613,7 +613,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { // People who have push access or have forked repository can propose a new pull request. canPush := ctx.Repo.CanWrite(unit_model.TypeCode) || - (ctx.IsSigned && models.HasForkedRepo(ctx.User.ID, ctx.Repo.Repository.ID)) + (ctx.IsSigned && repo_model.HasForkedRepo(ctx.User.ID, ctx.Repo.Repository.ID)) canCompare := false // Pull request is allowed if this is a fork repository diff --git a/modules/convert/convert.go b/modules/convert/convert.go index 1c78c35cd..86e1c69d3 100644 --- a/modules/convert/convert.go +++ b/modules/convert/convert.go @@ -334,7 +334,7 @@ func ToAnnotatedTagObject(repo *repo_model.Repository, commit *git.Commit) *api. } // ToTopicResponse convert from models.Topic to api.TopicResponse -func ToTopicResponse(topic *models.Topic) *api.TopicResponse { +func ToTopicResponse(topic *repo_model.Topic) *api.TopicResponse { return &api.TopicResponse{ ID: topic.ID, Name: topic.Name, diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go index 25f015d0e..fd44cd15f 100644 --- a/modules/notification/ui/ui.go +++ b/modules/notification/ui/ui.go @@ -6,6 +6,7 @@ package ui import ( "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/graceful" @@ -122,7 +123,7 @@ func (ns *notificationService) NotifyNewPullRequest(pr *models.PullRequest, ment return } toNotify := make(map[int64]struct{}, 32) - repoWatchers, err := models.GetRepoWatchersIDs(pr.Issue.RepoID) + repoWatchers, err := repo_model.GetRepoWatchersIDs(db.DefaultContext, pr.Issue.RepoID) if err != nil { log.Error("GetRepoWatchersIDs: %v", err) return diff --git a/modules/repofiles/commit_status.go b/modules/repofiles/commit_status.go deleted file mode 100644 index 21aaa9ee5..000000000 --- a/modules/repofiles/commit_status.go +++ /dev/null @@ -1,43 +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 repofiles - -import ( - "fmt" - - "code.gitea.io/gitea/models" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/git" -) - -// CreateCommitStatus creates a new CommitStatus given a bunch of parameters -// NOTE: All text-values will be trimmed from whitespaces. -// Requires: Repo, Creator, SHA -func CreateCommitStatus(repo *repo_model.Repository, creator *user_model.User, sha string, status *models.CommitStatus) error { - repoPath := repo.RepoPath() - - // confirm that commit is exist - gitRepo, err := git.OpenRepository(repoPath) - if err != nil { - return fmt.Errorf("OpenRepository[%s]: %v", repoPath, err) - } - if _, err := gitRepo.GetCommit(sha); err != nil { - gitRepo.Close() - return fmt.Errorf("GetCommit[%s]: %v", sha, err) - } - gitRepo.Close() - - if err := models.NewCommitStatus(models.NewCommitStatusOptions{ - Repo: repo, - Creator: creator, - SHA: sha, - CommitStatus: status, - }); err != nil { - return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %v", repo.ID, creator.ID, sha, err) - } - - return nil -} diff --git a/modules/repository/create.go b/modules/repository/create.go index ddc001d60..8b98a3717 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -22,7 +22,7 @@ import ( // CreateRepository creates a repository for the user/organization. func CreateRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*repo_model.Repository, error) { if !doer.IsAdmin && !u.CanCreateRepo() { - return nil, models.ErrReachLimitOfRepo{ + return nil, repo_model.ErrReachLimitOfRepo{ Limit: u.MaxRepoCreation, } } @@ -83,7 +83,7 @@ func CreateRepository(doer, u *user_model.User, opts models.CreateRepoOptions) ( // Previously Gitea would just delete and start afresh - this was naughty. // So we will now fail and delegate to other functionality to adopt or delete log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath) - return models.ErrRepoFilesAlreadyExist{ + return repo_model.ErrRepoFilesAlreadyExist{ Uname: u.Name, Name: repo.Name, } diff --git a/modules/repository/generate.go b/modules/repository/generate.go index 756cfe227..3f83f51bb 100644 --- a/modules/repository/generate.go +++ b/modules/repository/generate.go @@ -267,7 +267,7 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ return nil, err } if isExist { - return nil, models.ErrRepoFilesAlreadyExist{ + return nil, repo_model.ErrRepoFilesAlreadyExist{ Uname: generateRepo.OwnerName, Name: generateRepo.Name, } diff --git a/modules/repository/init.go b/modules/repository/init.go index 08c5aac1b..cc59b8f29 100644 --- a/modules/repository/init.go +++ b/modules/repository/init.go @@ -184,7 +184,7 @@ func checkInitRepository(owner, name string) (err error) { return err } if isExist { - return models.ErrRepoFilesAlreadyExist{ + return repo_model.ErrRepoFilesAlreadyExist{ Uname: owner, Name: name, } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 1d7d4251d..af5ab96d0 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -161,10 +161,10 @@ func repoAssignment() func(ctx *context.APIContext) { repo, err := repo_model.GetRepositoryByName(owner.ID, repoName) if err != nil { if repo_model.IsErrRepoNotExist(err) { - redirectRepoID, err := models.LookupRepoRedirect(owner.ID, repoName) + redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName) if err == nil { context.RedirectToRepo(ctx.Context, redirectRepoID) - } else if models.IsErrRepoRedirectNotExist(err) { + } else if repo_model.IsErrRedirectNotExist(err) { ctx.NotFound() } else { ctx.Error(http.StatusInternalServerError, "LookupRepoRedirect", err) diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go index aa51019eb..542af6074 100644 --- a/routers/api/v1/repo/fork.go +++ b/routers/api/v1/repo/fork.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/perm" + repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" @@ -50,7 +51,7 @@ func ListForks(ctx *context.APIContext) { // "200": // "$ref": "#/responses/RepositoryList" - forks, err := models.GetForks(ctx.Repo.Repository, utils.GetListOptions(ctx)) + forks, err := repo_model.GetForks(ctx.Repo.Repository, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetForks", err) return @@ -125,7 +126,7 @@ func CreateFork(ctx *context.APIContext) { forker = org.AsUser() } - fork, err := repo_service.ForkRepository(ctx.User, forker, models.ForkRepoOptions{ + fork, err := repo_service.ForkRepository(ctx.User, forker, repo_service.ForkRepoOptions{ BaseRepo: repo, Name: repo.Name, Description: repo.Description, diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index 108d78ef2..8a6f421c8 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -214,15 +214,15 @@ func Migrate(ctx *context.APIContext) { func handleMigrateError(ctx *context.APIContext, repoOwner *user_model.User, remoteAddr string, err error) { switch { - case models.IsErrRepoAlreadyExist(err): + case repo_model.IsErrRepoAlreadyExist(err): ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.") - case models.IsErrRepoFilesAlreadyExist(err): + case repo_model.IsErrRepoFilesAlreadyExist(err): ctx.Error(http.StatusConflict, "", "Files already exist for this repository. Adopt them or delete them.") case migrations.IsRateLimitError(err): ctx.Error(http.StatusUnprocessableEntity, "", "Remote visit addressed rate limitation.") case migrations.IsTwoFactorAuthError(err): ctx.Error(http.StatusUnprocessableEntity, "", "Remote visit required two factors authentication.") - case models.IsErrReachLimitOfRepo(err): + case repo_model.IsErrReachLimitOfRepo(err): ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("You have already reached your limit of %d repositories.", repoOwner.MaxCreationLimit())) case db.IsErrNameReserved(err): ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' is reserved.", err.(db.ErrNameReserved).Name)) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index e593819da..0683fe454 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -956,7 +956,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) } // Check if current user has fork of repository or in the same repository. - headRepo := models.GetForkedRepo(headUser.ID, baseRepo.ID) + headRepo := repo_model.GetForkedRepo(headUser.ID, baseRepo.ID) if headRepo == nil && !isSameRepo { log.Trace("parseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID) ctx.NotFound("GetForkedRepo") diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 62f9c3724..340ba0f6d 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -261,7 +261,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre IsTemplate: opt.Template, }) if err != nil { - if models.IsErrRepoAlreadyExist(err) { + if repo_model.IsErrRepoAlreadyExist(err) { ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.") } else if db.IsErrNameReserved(err) || db.IsErrNamePatternNotAllowed(err) { @@ -410,7 +410,7 @@ func Generate(ctx *context.APIContext) { repo, err := repo_service.GenerateRepository(ctx.User, ctxUser, ctx.Repo.Repository, opts) if err != nil { - if models.IsErrRepoAlreadyExist(err) { + if repo_model.IsErrRepoAlreadyExist(err) { ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.") } else if db.IsErrNameReserved(err) || db.IsErrNamePatternNotAllowed(err) { @@ -650,7 +650,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err if repo.LowerName != strings.ToLower(newRepoName) { if err := repo_service.ChangeRepositoryName(ctx.User, repo, newRepoName); err != nil { switch { - case models.IsErrRepoAlreadyExist(err): + case repo_model.IsErrRepoAlreadyExist(err): ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is already taken [name: %s]", newRepoName), err) case db.IsErrNameReserved(err): ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is reserved [name: %s]", newRepoName), err) @@ -911,7 +911,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { } } - if err := models.UpdateRepositoryUnits(repo, units, deleteUnitTypes); err != nil { + if err := repo_model.UpdateRepositoryUnits(repo, units, deleteUnitTypes); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateRepositoryUnits", err) return err } @@ -931,14 +931,14 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e return err } if *opts.Archived { - if err := models.SetArchiveRepoState(repo, *opts.Archived); err != nil { + if err := repo_model.SetArchiveRepoState(repo, *opts.Archived); err != nil { log.Error("Tried to archive a repo: %s", err) ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err) return err } log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name) } else { - if err := models.SetArchiveRepoState(repo, *opts.Archived); err != nil { + if err := repo_model.SetArchiveRepoState(repo, *opts.Archived); err != nil { log.Error("Tried to un-archive a repo: %s", err) ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err) return err diff --git a/routers/api/v1/repo/star.go b/routers/api/v1/repo/star.go index b8a54a6bd..a53ed7136 100644 --- a/routers/api/v1/repo/star.go +++ b/routers/api/v1/repo/star.go @@ -7,7 +7,7 @@ package repo import ( "net/http" - "code.gitea.io/gitea/models" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" @@ -44,7 +44,7 @@ func ListStargazers(ctx *context.APIContext) { // "200": // "$ref": "#/responses/UserList" - stargazers, err := models.GetStargazers(ctx.Repo.Repository, utils.GetListOptions(ctx)) + stargazers, err := repo_model.GetStargazers(ctx.Repo.Repository, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetStargazers", err) return diff --git a/routers/api/v1/repo/subscriber.go b/routers/api/v1/repo/subscriber.go index 64a0fc1d5..31126d569 100644 --- a/routers/api/v1/repo/subscriber.go +++ b/routers/api/v1/repo/subscriber.go @@ -7,7 +7,7 @@ package repo import ( "net/http" - "code.gitea.io/gitea/models" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" @@ -44,9 +44,9 @@ func ListSubscribers(ctx *context.APIContext) { // "200": // "$ref": "#/responses/UserList" - subscribers, err := models.GetRepoWatchers(ctx.Repo.Repository.ID, utils.GetListOptions(ctx)) + subscribers, err := repo_model.GetRepoWatchers(ctx.Repo.Repository.ID, utils.GetListOptions(ctx)) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetWatchers", err) + ctx.Error(http.StatusInternalServerError, "GetRepoWatchers", err) return } users := make([]*api.User, len(subscribers)) diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index fc277cb3f..1cc2c50dc 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -8,7 +8,7 @@ import ( "net/http" "strings" - "code.gitea.io/gitea/models" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/log" @@ -47,12 +47,12 @@ func ListTopics(ctx *context.APIContext) { // "200": // "$ref": "#/responses/TopicNames" - opts := &models.FindTopicOptions{ + opts := &repo_model.FindTopicOptions{ ListOptions: utils.GetListOptions(ctx), RepoID: ctx.Repo.Repository.ID, } - topics, total, err := models.FindTopics(opts) + topics, total, err := repo_model.FindTopics(opts) if err != nil { ctx.InternalServerError(err) return @@ -99,7 +99,7 @@ func UpdateTopics(ctx *context.APIContext) { form := web.GetForm(ctx).(*api.RepoTopicOptions) topicNames := form.Topics - validTopics, invalidTopics := models.SanitizeAndValidateTopics(topicNames) + validTopics, invalidTopics := repo_model.SanitizeAndValidateTopics(topicNames) if len(validTopics) > 25 { ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ @@ -117,7 +117,7 @@ func UpdateTopics(ctx *context.APIContext) { return } - err := models.SaveTopics(ctx.Repo.Repository.ID, validTopics...) + err := repo_model.SaveTopics(ctx.Repo.Repository.ID, validTopics...) if err != nil { log.Error("SaveTopics failed: %v", err) ctx.InternalServerError(err) @@ -158,7 +158,7 @@ func AddTopic(ctx *context.APIContext) { topicName := strings.TrimSpace(strings.ToLower(ctx.Params(":topic"))) - if !models.ValidateTopic(topicName) { + if !repo_model.ValidateTopic(topicName) { ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ "invalidTopics": topicName, "message": "Topic name is invalid", @@ -167,7 +167,7 @@ func AddTopic(ctx *context.APIContext) { } // Prevent adding more topics than allowed to repo - count, err := models.CountTopics(&models.FindTopicOptions{ + count, err := repo_model.CountTopics(&repo_model.FindTopicOptions{ RepoID: ctx.Repo.Repository.ID, }) if err != nil { @@ -182,7 +182,7 @@ func AddTopic(ctx *context.APIContext) { return } - _, err = models.AddTopic(ctx.Repo.Repository.ID, topicName) + _, err = repo_model.AddTopic(ctx.Repo.Repository.ID, topicName) if err != nil { log.Error("AddTopic failed: %v", err) ctx.InternalServerError(err) @@ -223,7 +223,7 @@ func DeleteTopic(ctx *context.APIContext) { topicName := strings.TrimSpace(strings.ToLower(ctx.Params(":topic"))) - if !models.ValidateTopic(topicName) { + if !repo_model.ValidateTopic(topicName) { ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ "invalidTopics": topicName, "message": "Topic name is invalid", @@ -231,7 +231,7 @@ func DeleteTopic(ctx *context.APIContext) { return } - topic, err := models.DeleteTopic(ctx.Repo.Repository.ID, topicName) + topic, err := repo_model.DeleteTopic(ctx.Repo.Repository.ID, topicName) if err != nil { log.Error("DeleteTopic failed: %v", err) ctx.InternalServerError(err) @@ -272,12 +272,12 @@ func TopicSearch(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - opts := &models.FindTopicOptions{ + opts := &repo_model.FindTopicOptions{ Keyword: ctx.FormString("q"), ListOptions: utils.GetListOptions(ctx), } - topics, total, err := models.FindTopics(opts) + topics, total, err := repo_model.FindTopics(opts) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index e2a83c70e..abaed4d58 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -104,7 +104,7 @@ func Transfer(ctx *context.APIContext) { return } - if models.IsErrRepoAlreadyExist(err) { + if repo_model.IsErrRepoAlreadyExist(err) { ctx.Error(http.StatusUnprocessableEntity, "CreatePendingRepositoryTransfer", err) return } diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index 946ae738e..14bc23b1c 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" @@ -121,7 +122,7 @@ func IsStarring(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - if models.IsStaring(ctx.User.ID, ctx.Repo.Repository.ID) { + if repo_model.IsStaring(ctx.User.ID, ctx.Repo.Repository.ID) { ctx.Status(http.StatusNoContent) } else { ctx.NotFound() @@ -148,7 +149,7 @@ func Star(ctx *context.APIContext) { // "204": // "$ref": "#/responses/empty" - err := models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, true) + err := repo_model.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, true) if err != nil { ctx.Error(http.StatusInternalServerError, "StarRepo", err) return @@ -176,7 +177,7 @@ func Unstar(ctx *context.APIContext) { // "204": // "$ref": "#/responses/empty" - err := models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) + err := repo_model.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) if err != nil { ctx.Error(http.StatusInternalServerError, "StarRepo", err) return diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go index 5c7a4d8d8..6c3e1c86f 100644 --- a/routers/api/v1/user/watch.go +++ b/routers/api/v1/user/watch.go @@ -123,7 +123,7 @@ func IsWatching(ctx *context.APIContext) { // "404": // description: User is not watching this repo or repo do not exist - if models.IsWatching(ctx.User.ID, ctx.Repo.Repository.ID) { + if repo_model.IsWatching(ctx.User.ID, ctx.Repo.Repository.ID) { ctx.JSON(http.StatusOK, api.WatchInfo{ Subscribed: true, Ignored: false, @@ -157,7 +157,7 @@ func Watch(ctx *context.APIContext) { // "200": // "$ref": "#/responses/WatchInfo" - err := models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, true) + err := repo_model.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, true) if err != nil { ctx.Error(http.StatusInternalServerError, "WatchRepo", err) return @@ -193,7 +193,7 @@ func Unwatch(ctx *context.APIContext) { // "204": // "$ref": "#/responses/empty" - err := models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) + err := repo_model.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) if err != nil { ctx.Error(http.StatusInternalServerError, "UnwatchRepo", err) return diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go index a71f46591..7124ae9e8 100644 --- a/routers/private/hook_post_receive.go +++ b/routers/private/hook_post_receive.go @@ -105,7 +105,7 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) { repo.IsPrivate = opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate, repo.IsPrivate) repo.IsTemplate = opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate, repo.IsTemplate) - if err := models.UpdateRepositoryCols(repo, "is_private", "is_template"); err != nil { + if err := repo_model.UpdateRepositoryCols(repo, "is_private", "is_template"); err != nil { log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err) ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{ Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err), diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 6c7f9ed62..081e103f7 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/base" @@ -125,7 +126,7 @@ func SettingsPost(ctx *context.Context) { } } } else if nameChanged { - if err := models.UpdateRepositoryOwnerNames(org.ID, org.Name); err != nil { + if err := repo_model.UpdateRepositoryOwnerNames(org.ID, org.Name); err != nil { ctx.ServerError("UpdateRepository", err) return } diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index f6a475c3c..d8f7b7ef2 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -56,7 +56,7 @@ func Branches(ctx *context.Context) { ctx.Data["IsWriter"] = ctx.Repo.CanWrite(unit.TypeCode) ctx.Data["IsMirror"] = ctx.Repo.Repository.IsMirror ctx.Data["CanPull"] = ctx.Repo.CanWrite(unit.TypeCode) || - (ctx.IsSigned && models.HasForkedRepo(ctx.User.ID, ctx.Repo.Repository.ID)) + (ctx.IsSigned && repo_model.HasForkedRepo(ctx.User.ID, ctx.Repo.Repository.ID)) ctx.Data["PageIsViewCode"] = true ctx.Data["PageIsBranches"] = true diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index c92624df8..8d08fec8f 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -340,7 +340,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo { // "OwnForkRepo" var ownForkRepo *repo_model.Repository if ctx.User != nil && baseRepo.OwnerID != ctx.User.ID { - repo := models.GetForkedRepo(ctx.User.ID, baseRepo.ID) + repo := repo_model.GetForkedRepo(ctx.User.ID, baseRepo.ID) if repo != nil { ownForkRepo = repo ctx.Data["OwnForkRepo"] = ownForkRepo @@ -364,13 +364,13 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo { // 5. If the headOwner has a fork of the baseRepo - use that if !has { - ci.HeadRepo = models.GetForkedRepo(ci.HeadUser.ID, baseRepo.ID) + ci.HeadRepo = repo_model.GetForkedRepo(ci.HeadUser.ID, baseRepo.ID) has = ci.HeadRepo != nil } // 6. If the baseRepo is a fork and the headUser has a fork of that use that if !has && baseRepo.IsFork { - ci.HeadRepo = models.GetForkedRepo(ci.HeadUser.ID, baseRepo.ForkID) + ci.HeadRepo = repo_model.GetForkedRepo(ci.HeadUser.ID, baseRepo.ForkID) has = ci.HeadRepo != nil } diff --git a/routers/web/repo/http.go b/routers/web/repo/http.go index 3e12cd326..107f1d960 100644 --- a/routers/web/repo/http.go +++ b/routers/web/repo/http.go @@ -134,7 +134,7 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { repo, err := repo_model.GetRepositoryByName(owner.ID, reponame) if err != nil { if repo_model.IsErrRepoNotExist(err) { - if redirectRepoID, err := models.LookupRepoRedirect(owner.ID, reponame); err == nil { + if redirectRepoID, err := repo_model.LookupRedirect(owner.ID, reponame); err == nil { context.RedirectToRepo(ctx, redirectRepoID) return } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index f03a8dfbc..464646a86 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -108,7 +108,7 @@ func MustAllowPulls(ctx *context.Context) { } // User can send pull request if owns a forked repository. - if ctx.IsSigned && models.HasForkedRepo(ctx.User.ID, ctx.Repo.Repository.ID) { + if ctx.IsSigned && repo_model.HasForkedRepo(ctx.User.ID, ctx.Repo.Repository.ID) { ctx.Repo.PullRequest.Allowed = true ctx.Repo.PullRequest.HeadInfoSubURL = url.PathEscape(ctx.User.Name) + ":" + util.PathEscapeSegments(ctx.Repo.BranchName) } diff --git a/routers/web/repo/migrate.go b/routers/web/repo/migrate.go index 5eba4690d..428473410 100644 --- a/routers/web/repo/migrate.go +++ b/routers/web/repo/migrate.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" @@ -79,12 +80,12 @@ func handleMigrateError(ctx *context.Context, owner *user_model.User, err error, ctx.RenderWithErr(ctx.Tr("form.visit_rate_limit"), tpl, form) case migrations.IsTwoFactorAuthError(err): ctx.RenderWithErr(ctx.Tr("form.2fa_auth_required"), tpl, form) - case models.IsErrReachLimitOfRepo(err): + case repo_model.IsErrReachLimitOfRepo(err): ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form) - case models.IsErrRepoAlreadyExist(err): + case repo_model.IsErrRepoAlreadyExist(err): ctx.Data["Err_RepoName"] = true ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form) - case models.IsErrRepoFilesAlreadyExist(err): + case repo_model.IsErrRepoFilesAlreadyExist(err): ctx.Data["Err_RepoName"] = true switch { case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories): @@ -230,7 +231,7 @@ func MigratePost(ctx *context.Context) { opts.Releases = false } - err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName, false) + err = repo_model.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName, false) if err != nil { handleMigrateError(ctx, ctxUser, err, "MigratePost", tpl, form) return diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 3882fdb5b..2fcee33a2 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -110,7 +110,7 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository { ctx.Data["repo_name"] = forkRepo.Name ctx.Data["description"] = forkRepo.Description ctx.Data["IsPrivate"] = forkRepo.IsPrivate || forkRepo.Owner.Visibility == structs.VisibleTypePrivate - canForkToUser := forkRepo.OwnerID != ctx.User.ID && !models.HasForkedRepo(ctx.User.ID, forkRepo.ID) + canForkToUser := forkRepo.OwnerID != ctx.User.ID && !repo_model.HasForkedRepo(ctx.User.ID, forkRepo.ID) ctx.Data["ForkRepo"] = forkRepo @@ -121,7 +121,7 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository { } var orgs []*models.Organization for _, org := range ownedOrgs { - if forkRepo.OwnerID != org.ID && !models.HasForkedRepo(org.ID, forkRepo.ID) { + if forkRepo.OwnerID != org.ID && !repo_model.HasForkedRepo(org.ID, forkRepo.ID) { orgs = append(orgs, org) } } @@ -202,7 +202,7 @@ func ForkPost(ctx *context.Context) { ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form) return } - repo := models.GetForkedRepo(ctxUser.ID, traverseParentRepo.ID) + repo := repo_model.GetForkedRepo(ctxUser.ID, traverseParentRepo.ID) if repo != nil { ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name)) return @@ -229,7 +229,7 @@ func ForkPost(ctx *context.Context) { } } - repo, err := repo_service.ForkRepository(ctx.User, ctxUser, models.ForkRepoOptions{ + repo, err := repo_service.ForkRepository(ctx.User, ctxUser, repo_service.ForkRepoOptions{ BaseRepo: forkRepo, Name: form.RepoName, Description: form.Description, @@ -237,7 +237,7 @@ func ForkPost(ctx *context.Context) { if err != nil { ctx.Data["Err_RepoName"] = true switch { - case models.IsErrRepoAlreadyExist(err): + case repo_model.IsErrRepoAlreadyExist(err): ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form) case db.IsErrNameReserved(err): ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplFork, &form) diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index b4bab5960..a1e652f94 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -161,12 +161,12 @@ func Create(ctx *context.Context) { func handleCreateError(ctx *context.Context, owner *user_model.User, err error, name string, tpl base.TplName, form interface{}) { switch { - case models.IsErrReachLimitOfRepo(err): + case repo_model.IsErrReachLimitOfRepo(err): ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form) - case models.IsErrRepoAlreadyExist(err): + case repo_model.IsErrRepoAlreadyExist(err): ctx.Data["Err_RepoName"] = true ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form) - case models.IsErrRepoFilesAlreadyExist(err): + case repo_model.IsErrRepoFilesAlreadyExist(err): ctx.Data["Err_RepoName"] = true switch { case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories): @@ -278,13 +278,13 @@ func Action(ctx *context.Context) { var err error switch ctx.Params(":action") { case "watch": - err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, true) + err = repo_model.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, true) case "unwatch": - err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) + err = repo_model.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) case "star": - err = models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, true) + err = repo_model.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, true) case "unstar": - err = models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) + err = repo_model.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) case "accept_transfer": err = acceptOrRejectRepoTransfer(ctx, true) case "reject_transfer": diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index bb118a903..81009cb39 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -103,11 +103,11 @@ func SettingsPost(ctx *context.Context) { if err := repo_service.ChangeRepositoryName(ctx.User, repo, newRepoName); err != nil { ctx.Data["Err_RepoName"] = true switch { - case models.IsErrRepoAlreadyExist(err): + case repo_model.IsErrRepoAlreadyExist(err): ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tplSettingsOptions, &form) case db.IsErrNameReserved(err): ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplSettingsOptions, &form) - case models.IsErrRepoFilesAlreadyExist(err): + case repo_model.IsErrRepoFilesAlreadyExist(err): ctx.Data["Err_RepoName"] = true switch { case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories): @@ -461,7 +461,7 @@ func SettingsPost(ctx *context.Context) { deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests) } - if err := models.UpdateRepositoryUnits(repo, units, deleteUnitTypes); err != nil { + if err := repo_model.UpdateRepositoryUnits(repo, units, deleteUnitTypes); err != nil { ctx.ServerError("UpdateRepositoryUnits", err) return } @@ -612,7 +612,7 @@ func SettingsPost(ctx *context.Context) { } if err := repo_service.StartRepositoryTransfer(ctx.User, newOwner, repo, nil); err != nil { - if models.IsErrRepoAlreadyExist(err) { + if repo_model.IsErrRepoAlreadyExist(err) { ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil) } else if models.IsErrRepoTransferInProgress(err) { ctx.RenderWithErr(ctx.Tr("repo.settings.transfer_in_progress"), tplSettingsOptions, nil) @@ -714,7 +714,7 @@ func SettingsPost(ctx *context.Context) { return } - if err := models.SetArchiveRepoState(repo, true); err != nil { + if err := repo_model.SetArchiveRepoState(repo, true); err != nil { log.Error("Tried to archive a repo: %s", err) ctx.Flash.Error(ctx.Tr("repo.settings.archive.error")) ctx.Redirect(ctx.Repo.RepoLink + "/settings") @@ -732,7 +732,7 @@ func SettingsPost(ctx *context.Context) { return } - if err := models.SetArchiveRepoState(repo, false); err != nil { + if err := repo_model.SetArchiveRepoState(repo, false); err != nil { log.Error("Tried to unarchive a repo: %s", err) ctx.Flash.Error(ctx.Tr("repo.settings.unarchive.error")) ctx.Redirect(ctx.Repo.RepoLink + "/settings") @@ -1145,7 +1145,7 @@ func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error { if !(st.IsImage() && !st.IsSvgImage()) { return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image")) } - if err = models.UploadRepoAvatar(ctxRepo, data); err != nil { + if err = repo_service.UploadAvatar(ctxRepo, data); err != nil { return fmt.Errorf("UploadAvatar: %v", err) } return nil @@ -1165,7 +1165,7 @@ func SettingsAvatar(ctx *context.Context) { // SettingsDeleteAvatar delete repository avatar func SettingsDeleteAvatar(ctx *context.Context) { - if err := models.DeleteRepoAvatar(ctx.Repo.Repository); err != nil { + if err := repo_service.DeleteAvatar(ctx.Repo.Repository); err != nil { ctx.Flash.Error(fmt.Sprintf("DeleteAvatar: %v", err)) } ctx.Redirect(ctx.Repo.RepoLink + "/settings") diff --git a/routers/web/repo/topic.go b/routers/web/repo/topic.go index 41e3f995b..810b241e2 100644 --- a/routers/web/repo/topic.go +++ b/routers/web/repo/topic.go @@ -8,7 +8,7 @@ import ( "net/http" "strings" - "code.gitea.io/gitea/models" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" ) @@ -28,7 +28,7 @@ func TopicsPost(ctx *context.Context) { topics = strings.Split(topicsStr, ",") } - validTopics, invalidTopics := models.SanitizeAndValidateTopics(topics) + validTopics, invalidTopics := repo_model.SanitizeAndValidateTopics(topics) if len(validTopics) > 25 { ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ @@ -46,7 +46,7 @@ func TopicsPost(ctx *context.Context) { return } - err := models.SaveTopics(ctx.Repo.Repository.ID, validTopics...) + err := repo_model.SaveTopics(ctx.Repo.Repository.ID, validTopics...) if err != nil { log.Error("SaveTopics failed: %v", err) ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 9e0f670cf..5e9003b16 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -822,7 +822,7 @@ func renderLanguageStats(ctx *context.Context) { } func renderRepoTopics(ctx *context.Context) { - topics, _, err := models.FindTopics(&models.FindTopicOptions{ + topics, _, err := repo_model.FindTopics(&repo_model.FindTopicOptions{ RepoID: ctx.Repo.Repository.ID, }) if err != nil { @@ -931,7 +931,7 @@ func Watchers(ctx *context.Context) { ctx.Data["PageIsWatchers"] = true RenderUserCards(ctx, ctx.Repo.Repository.NumWatches, func(opts db.ListOptions) ([]*user_model.User, error) { - return models.GetRepoWatchers(ctx.Repo.Repository.ID, opts) + return repo_model.GetRepoWatchers(ctx.Repo.Repository.ID, opts) }, tplWatchers) } @@ -941,7 +941,7 @@ func Stars(ctx *context.Context) { ctx.Data["CardsTitle"] = ctx.Tr("repo.stargazers") ctx.Data["PageIsStargazers"] = true RenderUserCards(ctx, ctx.Repo.Repository.NumStars, func(opts db.ListOptions) ([]*user_model.User, error) { - return models.GetStargazers(ctx.Repo.Repository, opts) + return repo_model.GetStargazers(ctx.Repo.Repository, opts) }, tplWatchers) } @@ -957,7 +957,7 @@ func Forks(ctx *context.Context) { pager := context.NewPagination(ctx.Repo.Repository.NumForks, models.ItemsPerPage, page, 5) ctx.Data["Page"] = pager - forks, err := models.GetForks(ctx.Repo.Repository, db.ListOptions{ + forks, err := repo_model.GetForks(ctx.Repo.Repository, db.ListOptions{ Page: pager.Paginater.Current(), PageSize: models.ItemsPerPage, }) diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index 089177874..ab156b43f 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -77,7 +77,7 @@ func HandleUsernameChange(ctx *context.Context, user *user_model.User, newName s return err } } else { - if err := models.UpdateRepositoryOwnerNames(user.ID, newName); err != nil { + if err := repo_model.UpdateRepositoryOwnerNames(user.ID, newName); err != nil { ctx.ServerError("UpdateRepository", err) return err } @@ -290,7 +290,7 @@ func Repos(ctx *context.Context) { return filepath.SkipDir } name = name[:len(name)-4] - if models.IsUsableRepoName(name) != nil || strings.ToLower(name) != name { + if repo_model.IsUsableRepoName(name) != nil || strings.ToLower(name) != name { return filepath.SkipDir } if count >= start && count < end { diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go index 90b391474..ded819a71 100644 --- a/services/cron/tasks_extended.go +++ b/services/cron/tasks_extended.go @@ -118,7 +118,7 @@ func registerRemoveRandomAvatars() { RunAtStart: false, Schedule: "@every 72h", }, func(ctx context.Context, _ *user_model.User, _ Config) error { - return models.RemoveRandomAvatars(ctx) + return repo_service.RemoveRandomAvatars(ctx) }) } diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go index 22ebbb1a9..c9cc2e015 100644 --- a/services/mailer/mail_issue.go +++ b/services/mailer/mail_issue.go @@ -8,6 +8,8 @@ import ( "fmt" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" @@ -78,7 +80,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo // =========== Repo watchers =========== // Make repo watchers last, since it's likely the list with the most users if !(ctx.Issue.IsPull && ctx.Issue.PullRequest.IsWorkInProgress() && ctx.ActionType != models.ActionCreatePullRequest) { - ids, err = models.GetRepoWatchersIDs(ctx.Issue.RepoID) + ids, err = repo_model.GetRepoWatchersIDs(db.DefaultContext, ctx.Issue.RepoID) if err != nil { return fmt.Errorf("GetRepoWatchersIDs(%d): %v", ctx.Issue.RepoID, err) } diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go index 72476fe8b..02aa0f312 100644 --- a/services/mailer/mail_release.go +++ b/services/mailer/mail_release.go @@ -8,6 +8,8 @@ import ( "bytes" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" @@ -29,7 +31,7 @@ func MailNewRelease(rel *models.Release) { return } - watcherIDList, err := models.GetRepoWatchersIDs(rel.RepoID) + watcherIDList, err := repo_model.GetRepoWatchersIDs(db.DefaultContext, rel.RepoID) if err != nil { log.Error("GetRepoWatchersIDs(%d): %v", rel.RepoID, err) return diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 0887aa113..33ca3127f 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -154,7 +154,7 @@ func (g *GiteaLocalUploader) CreateTopics(topics ...string) error { } } topics = topics[:c] - return models.SaveTopics(g.repo.ID, topics...) + return repo_model.SaveTopics(g.repo.ID, topics...) } // CreateMilestones creates milestones @@ -980,5 +980,5 @@ func (g *GiteaLocalUploader) Finish() error { } g.repo.Status = repo_model.RepositoryReady - return models.UpdateRepositoryCols(g.repo, "status") + return repo_model.UpdateRepositoryCols(g.repo, "status") } diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 1a1584922..da2221d91 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -60,7 +60,7 @@ func UpdateAddress(m *repo_model.Mirror, addr string) error { } m.Repo.OriginalURL = addr - return models.UpdateRepositoryCols(m.Repo, "original_url") + return repo_model.UpdateRepositoryCols(m.Repo, "original_url") } // mirrorSyncResult contains information of a updated reference. @@ -476,7 +476,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { return false } - if err = models.UpdateRepositoryUpdatedTime(m.RepoID, commitDate); err != nil { + if err = repo_model.UpdateRepositoryUpdatedTime(m.RepoID, commitDate); err != nil { log.Error("Update repository 'updated_unix' [%d]: %v", m.RepoID, err) return false } @@ -539,7 +539,7 @@ func checkAndUpdateEmptyRepository(m *repo_model.Mirror, gitRepo *git.Repository } m.Repo.IsEmpty = false // Update the is empty and default_branch columns - if err := models.UpdateRepositoryCols(m.Repo, "default_branch", "is_empty"); err != nil { + if err := repo_model.UpdateRepositoryCols(m.Repo, "default_branch", "is_empty"); err != nil { log.Error("Failed to update default branch of repository %-v. Error: %v", m.Repo, err) desc := fmt.Sprintf("Failed to uupdate default branch of repository '%s': %v", m.Repo.RepoPath(), err) if err = admin_model.CreateRepositoryNotice(desc); err != nil { diff --git a/services/repository/adopt.go b/services/repository/adopt.go index d5b851d10..2f87b0d7b 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -28,7 +28,7 @@ import ( // AdoptRepository adopts pre-existing repository files for the user/organization. func AdoptRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*repo_model.Repository, error) { if !doer.IsAdmin && !u.CanCreateRepo() { - return nil, models.ErrReachLimitOfRepo{ + return nil, repo_model.ErrReachLimitOfRepo{ Limit: u.MaxRepoCreation, } } @@ -188,7 +188,7 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r // DeleteUnadoptedRepository deletes unadopted repository files from the filesystem func DeleteUnadoptedRepository(doer, u *user_model.User, repoName string) error { - if err := models.IsUsableRepoName(repoName); err != nil { + if err := repo_model.IsUsableRepoName(repoName); err != nil { return err } @@ -208,7 +208,7 @@ func DeleteUnadoptedRepository(doer, u *user_model.User, repoName string) error if exist, err := repo_model.IsRepositoryExist(u, repoName); err != nil { return err } else if exist { - return models.ErrRepoAlreadyExist{ + return repo_model.ErrRepoAlreadyExist{ Uname: u.Name, Name: repoName, } @@ -312,7 +312,7 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in return filepath.SkipDir } name = name[:len(name)-4] - if models.IsUsableRepoName(name) != nil || strings.ToLower(name) != name || !globRepo.Match(name) { + if repo_model.IsUsableRepoName(name) != nil || strings.ToLower(name) != name || !globRepo.Match(name) { return filepath.SkipDir } if count < end { diff --git a/models/repo_avatar.go b/services/repository/avatar.go similarity index 61% rename from models/repo_avatar.go rename to services/repository/avatar.go index 27af911a7..f51a312e1 100644 --- a/models/repo_avatar.go +++ b/services/repository/avatar.go @@ -1,8 +1,8 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. +// Copyright 2021 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 +package repository import ( "context" @@ -11,38 +11,18 @@ import ( "image/png" "io" "strconv" + "strings" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/avatar" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" ) -// RemoveRandomAvatars removes the randomly generated avatars that were created for repositories -func RemoveRandomAvatars(ctx context.Context) error { - return db.GetEngine(db.DefaultContext). - Where("id > 0").BufferSize(setting.Database.IterateBufferSize). - Iterate(new(repo_model.Repository), - func(idx int, bean interface{}) error { - repository := bean.(*repo_model.Repository) - select { - case <-ctx.Done(): - return db.ErrCancelledf("before random avatars removed for %s", repository.FullName()) - default: - } - stringifiedID := strconv.FormatInt(repository.ID, 10) - if repository.Avatar == stringifiedID { - return DeleteRepoAvatar(repository) - } - return nil - }) -} - -// UploadRepoAvatar saves custom avatar for repository. +// UploadAvatar saves custom avatar for repository. // FIXME: split uploads to different subdirs in case we have massive number of repos. -func UploadRepoAvatar(repo *repo_model.Repository, data []byte) error { +func UploadAvatar(repo *repo_model.Repository, data []byte) error { m, err := avatar.Prepare(data) if err != nil { return err @@ -64,7 +44,7 @@ func UploadRepoAvatar(repo *repo_model.Repository, data []byte) error { // Users can upload the same image to other repo - prefix it with ID // Then repo will be removed - only it avatar file will be removed repo.Avatar = newAvatar - if _, err := db.GetEngine(ctx).ID(repo.ID).Cols("avatar").Update(repo); err != nil { + if err := repo_model.UpdateRepositoryColsCtx(ctx, repo, "avatar"); err != nil { return fmt.Errorf("UploadAvatar: Update repository avatar: %v", err) } @@ -86,8 +66,8 @@ func UploadRepoAvatar(repo *repo_model.Repository, data []byte) error { return committer.Commit() } -// DeleteRepoAvatar deletes the repos's custom avatar. -func DeleteRepoAvatar(repo *repo_model.Repository) error { +// DeleteAvatar deletes the repos's custom avatar. +func DeleteAvatar(repo *repo_model.Repository) error { // Avatar not exists if len(repo.Avatar) == 0 { return nil @@ -103,7 +83,7 @@ func DeleteRepoAvatar(repo *repo_model.Repository) error { defer committer.Close() repo.Avatar = "" - if _, err := db.GetEngine(ctx).ID(repo.ID).Cols("avatar").Update(repo); err != nil { + if err := repo_model.UpdateRepositoryColsCtx(ctx, repo, "avatar"); err != nil { return fmt.Errorf("DeleteAvatar: Update repository avatar: %v", err) } @@ -113,3 +93,29 @@ func DeleteRepoAvatar(repo *repo_model.Repository) error { return committer.Commit() } + +// RemoveRandomAvatars removes the randomly generated avatars that were created for repositories +func RemoveRandomAvatars(ctx context.Context) error { + return repo_model.IterateRepository(func(repository *repo_model.Repository) error { + select { + case <-ctx.Done(): + return db.ErrCancelledf("before random avatars removed for %s", repository.FullName()) + default: + } + stringifiedID := strconv.FormatInt(repository.ID, 10) + if repository.Avatar == stringifiedID { + return DeleteAvatar(repository) + } + return nil + }) +} + +// generateAvatar generates the avatar from a template repository +func generateAvatar(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error { + generateRepo.Avatar = strings.Replace(templateRepo.Avatar, strconv.FormatInt(templateRepo.ID, 10), strconv.FormatInt(generateRepo.ID, 10), 1) + if _, err := storage.Copy(storage.RepoAvatars, generateRepo.CustomAvatarRelativePath(), storage.RepoAvatars, templateRepo.CustomAvatarRelativePath()); err != nil { + return err + } + + return repo_model.UpdateRepositoryColsCtx(ctx, generateRepo, "avatar") +} diff --git a/services/repository/avatar_test.go b/services/repository/avatar_test.go new file mode 100644 index 000000000..efad392a2 --- /dev/null +++ b/services/repository/avatar_test.go @@ -0,0 +1,64 @@ +// Copyright 2021 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 ( + "bytes" + "crypto/md5" + "fmt" + "image" + "image/png" + "testing" + + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" +) + +func TestUploadAvatar(t *testing.T) { + // Generate image + myImage := image.NewRGBA(image.Rect(0, 0, 1, 1)) + var buff bytes.Buffer + png.Encode(&buff, myImage) + + assert.NoError(t, unittest.PrepareTestDatabase()) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository) + + err := UploadAvatar(repo, buff.Bytes()) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf("%d-%x", 10, md5.Sum(buff.Bytes())), repo.Avatar) +} + +func TestUploadBigAvatar(t *testing.T) { + // Generate BIG image + myImage := image.NewRGBA(image.Rect(0, 0, 5000, 1)) + var buff bytes.Buffer + png.Encode(&buff, myImage) + + assert.NoError(t, unittest.PrepareTestDatabase()) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository) + + err := UploadAvatar(repo, buff.Bytes()) + assert.Error(t, err) +} + +func TestDeleteAvatar(t *testing.T) { + // Generate image + myImage := image.NewRGBA(image.Rect(0, 0, 1, 1)) + var buff bytes.Buffer + png.Encode(&buff, myImage) + + assert.NoError(t, unittest.PrepareTestDatabase()) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository) + + err := UploadAvatar(repo, buff.Bytes()) + assert.NoError(t, err) + + err = DeleteAvatar(repo) + assert.NoError(t, err) + + assert.Equal(t, "", repo.Avatar) +} diff --git a/services/repository/fork.go b/services/repository/fork.go index 027877cfd..b091ca8fd 100644 --- a/services/repository/fork.go +++ b/services/repository/fork.go @@ -22,9 +22,16 @@ import ( "code.gitea.io/gitea/modules/util" ) +// ForkRepoOptions contains the fork repository options +type ForkRepoOptions struct { + BaseRepo *repo_model.Repository + Name string + Description string +} + // ForkRepository forks a repository -func ForkRepository(doer, owner *user_model.User, opts models.ForkRepoOptions) (_ *repo_model.Repository, err error) { - forkedRepo, err := models.GetUserFork(opts.BaseRepo.ID, owner.ID) +func ForkRepository(doer, owner *user_model.User, opts ForkRepoOptions) (_ *repo_model.Repository, err error) { + forkedRepo, err := repo_model.GetUserFork(opts.BaseRepo.ID, owner.ID) if err != nil { return nil, err } diff --git a/services/repository/fork_test.go b/services/repository/fork_test.go index 859889fc5..5d392e224 100644 --- a/services/repository/fork_test.go +++ b/services/repository/fork_test.go @@ -22,7 +22,7 @@ func TestForkRepository(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 13}).(*user_model.User) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository) - fork, err := ForkRepository(user, user, models.ForkRepoOptions{ + fork, err := ForkRepository(user, user, ForkRepoOptions{ BaseRepo: repo, Name: "test", Description: "test", diff --git a/services/repository/hooks.go b/services/repository/hooks.go index 714cd6b2e..f567702e9 100644 --- a/services/repository/hooks.go +++ b/services/repository/hooks.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" @@ -50,3 +51,36 @@ func SyncRepositoryHooks(ctx context.Context) error { log.Trace("Finished: SyncRepositoryHooks") return nil } + +// GenerateGitHooks generates git hooks from a template repository +func GenerateGitHooks(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error { + generateGitRepo, err := git.OpenRepository(generateRepo.RepoPath()) + if err != nil { + return err + } + defer generateGitRepo.Close() + + templateGitRepo, err := git.OpenRepository(templateRepo.RepoPath()) + if err != nil { + return err + } + defer templateGitRepo.Close() + + templateHooks, err := templateGitRepo.Hooks() + if err != nil { + return err + } + + for _, templateHook := range templateHooks { + generateHook, err := generateGitRepo.GetHook(templateHook.Name()) + if err != nil { + return err + } + + generateHook.Content = templateHook.Content + if err := generateHook.Update(); err != nil { + return err + } + } + return nil +} diff --git a/services/repository/push.go b/services/repository/push.go index 60d2f0c58..c8ac5b289 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -166,7 +166,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } } // Update the is empty and default_branch columns - if err := models.UpdateRepositoryCols(repo, "default_branch", "is_empty"); err != nil { + if err := repo_model.UpdateRepositoryCols(repo, "default_branch", "is_empty"); err != nil { return fmt.Errorf("UpdateRepositoryCols: %v", err) } } @@ -227,7 +227,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } // Even if user delete a branch on a repository which he didn't watch, he will be watch that. - if err = models.WatchIfAuto(opts.PusherID, repo.ID, true); err != nil { + if err = repo_model.WatchIfAuto(opts.PusherID, repo.ID, true); err != nil { log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err) } } else { @@ -239,7 +239,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } // Change repository last updated time. - if err := models.UpdateRepositoryUpdatedTime(repo.ID, time.Now()); err != nil { + if err := repo_model.UpdateRepositoryUpdatedTime(repo.ID, time.Now()); err != nil { return fmt.Errorf("UpdateRepositoryUpdatedTime: %v", err) } diff --git a/services/repository/generate.go b/services/repository/template.go similarity index 87% rename from services/repository/generate.go rename to services/repository/template.go index 2b5a750ad..28fa1523a 100644 --- a/services/repository/generate.go +++ b/services/repository/template.go @@ -19,7 +19,7 @@ import ( // 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) { if !doer.IsAdmin && !owner.CanCreateRepo() { - return nil, models.ErrReachLimitOfRepo{ + return nil, repo_model.ErrReachLimitOfRepo{ Limit: owner.MaxRepoCreation, } } @@ -40,14 +40,14 @@ func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.R // Topics if opts.Topics { - if err = models.GenerateTopics(ctx, templateRepo, generateRepo); err != nil { + if err = repo_model.GenerateTopics(ctx, templateRepo, generateRepo); err != nil { return err } } // Git Hooks if opts.GitHooks { - if err = models.GenerateGitHooks(ctx, templateRepo, generateRepo); err != nil { + if err = GenerateGitHooks(ctx, templateRepo, generateRepo); err != nil { return err } } @@ -61,7 +61,7 @@ func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.R // Avatar if opts.Avatar && len(templateRepo.Avatar) > 0 { - if err = models.GenerateAvatar(ctx, templateRepo, generateRepo); err != nil { + if err = generateAvatar(ctx, templateRepo, generateRepo); err != nil { return err } } diff --git a/services/repository/transfer.go b/services/repository/transfer.go index b60338300..83cac401d 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -64,7 +64,7 @@ func ChangeRepositoryName(doer *user_model.User, repo *repo_model.Repository, ne // local copy's origin accordingly. repoWorkingPool.CheckIn(fmt.Sprint(repo.ID)) - if err := models.ChangeRepositoryName(doer, repo, newRepoName); err != nil { + if err := repo_model.ChangeRepositoryName(doer, repo, newRepoName); err != nil { repoWorkingPool.CheckOut(fmt.Sprint(repo.ID)) return err } diff --git a/services/task/migrate.go b/services/task/migrate.go index dbe237b69..d6ff51432 100644 --- a/services/task/migrate.go +++ b/services/task/migrate.go @@ -27,9 +27,9 @@ import ( func handleCreateError(owner *user_model.User, err error) error { switch { - case models.IsErrReachLimitOfRepo(err): + case repo_model.IsErrReachLimitOfRepo(err): return fmt.Errorf("You have already reached your limit of %d repositories", owner.MaxCreationLimit()) - case models.IsErrRepoAlreadyExist(err): + case repo_model.IsErrRepoAlreadyExist(err): return errors.New("The repository name is already used") case db.IsErrNameReserved(err): return fmt.Errorf("The repository name '%s' is reserved", err.(db.ErrNameReserved).Name) @@ -123,7 +123,7 @@ func runMigrateTask(t *models.Task) (err error) { return } - if models.IsErrRepoAlreadyExist(err) { + if repo_model.IsErrRepoAlreadyExist(err) { err = errors.New("The repository name is already used") return } diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index aa683dd44..4b679a4ad 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -375,7 +375,7 @@ func DeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, wikiName // DeleteWiki removes the actual and local copy of repository wiki. func DeleteWiki(repo *repo_model.Repository) error { - if err := models.UpdateRepositoryUnits(repo, nil, []unit.Type{unit.TypeWiki}); err != nil { + if err := repo_model.UpdateRepositoryUnits(repo, nil, []unit.Type{unit.TypeWiki}); err != nil { return err }