diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2021-12-12 23:48:20 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-12 23:48:20 +0800 |
commit | 572324049008ac803d3d7c17a7b3a81ef00386fc (patch) | |
tree | a9c3709643a23165d27aa67e76f10f26a89936d5 /services | |
parent | 0a7e8327a017c5dd43e552bbcd0d0f056bc1671b (diff) | |
download | gitea-572324049008ac803d3d7c17a7b3a81ef00386fc.tar.gz gitea-572324049008ac803d3d7c17a7b3a81ef00386fc.zip |
Some repository refactors (#17950)
* some repository refactors
* remove unnecessary code
* Fix test
* Remove unnecessary banner
Diffstat (limited to 'services')
-rw-r--r-- | services/cron/tasks_extended.go | 2 | ||||
-rw-r--r-- | services/mailer/mail_issue.go | 4 | ||||
-rw-r--r-- | services/mailer/mail_release.go | 4 | ||||
-rw-r--r-- | services/migrations/gitea_uploader.go | 4 | ||||
-rw-r--r-- | services/mirror/mirror_pull.go | 6 | ||||
-rw-r--r-- | services/repository/adopt.go | 8 | ||||
-rw-r--r-- | services/repository/avatar.go | 121 | ||||
-rw-r--r-- | services/repository/avatar_test.go | 64 | ||||
-rw-r--r-- | services/repository/fork.go | 11 | ||||
-rw-r--r-- | services/repository/fork_test.go | 2 | ||||
-rw-r--r-- | services/repository/hooks.go | 34 | ||||
-rw-r--r-- | services/repository/push.go | 6 | ||||
-rw-r--r-- | services/repository/template.go (renamed from services/repository/generate.go) | 8 | ||||
-rw-r--r-- | services/repository/transfer.go | 2 | ||||
-rw-r--r-- | services/task/migrate.go | 6 | ||||
-rw-r--r-- | services/wiki/wiki.go | 2 |
16 files changed, 257 insertions, 27 deletions
diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go index 90b391474f..ded819a71e 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 22ebbb1a9c..c9cc2e015a 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 72476fe8be..02aa0f312e 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 0887aa1132..33ca3127fb 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 1a15849226..da2221d915 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 d5b851d108..2f87b0d7bd 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/services/repository/avatar.go b/services/repository/avatar.go new file mode 100644 index 0000000000..f51a312e17 --- /dev/null +++ b/services/repository/avatar.go @@ -0,0 +1,121 @@ +// 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 ( + "context" + "crypto/md5" + "fmt" + "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/storage" +) + +// UploadAvatar saves custom avatar for repository. +// FIXME: split uploads to different subdirs in case we have massive number of repos. +func UploadAvatar(repo *repo_model.Repository, data []byte) error { + m, err := avatar.Prepare(data) + if err != nil { + return err + } + + newAvatar := fmt.Sprintf("%d-%x", repo.ID, md5.Sum(data)) + if repo.Avatar == newAvatar { // upload the same picture + return nil + } + + ctx, committer, err := db.TxContext() + if err != nil { + return err + } + defer committer.Close() + + oldAvatarPath := repo.CustomAvatarRelativePath() + + // 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 := repo_model.UpdateRepositoryColsCtx(ctx, repo, "avatar"); err != nil { + return fmt.Errorf("UploadAvatar: Update repository avatar: %v", err) + } + + if err := storage.SaveFrom(storage.RepoAvatars, repo.CustomAvatarRelativePath(), func(w io.Writer) error { + if err := png.Encode(w, *m); err != nil { + log.Error("Encode: %v", err) + } + return err + }); err != nil { + return fmt.Errorf("UploadAvatar %s failed: Failed to remove old repo avatar %s: %v", repo.RepoPath(), newAvatar, err) + } + + if len(oldAvatarPath) > 0 { + if err := storage.RepoAvatars.Delete(oldAvatarPath); err != nil { + return fmt.Errorf("UploadAvatar: Failed to remove old repo avatar %s: %v", oldAvatarPath, err) + } + } + + return committer.Commit() +} + +// DeleteAvatar deletes the repos's custom avatar. +func DeleteAvatar(repo *repo_model.Repository) error { + // Avatar not exists + if len(repo.Avatar) == 0 { + return nil + } + + avatarPath := repo.CustomAvatarRelativePath() + log.Trace("DeleteAvatar[%d]: %s", repo.ID, avatarPath) + + ctx, committer, err := db.TxContext() + if err != nil { + return err + } + defer committer.Close() + + repo.Avatar = "" + if err := repo_model.UpdateRepositoryColsCtx(ctx, repo, "avatar"); err != nil { + return fmt.Errorf("DeleteAvatar: Update repository avatar: %v", err) + } + + if err := storage.RepoAvatars.Delete(avatarPath); err != nil { + return fmt.Errorf("DeleteAvatar: Failed to remove %s: %v", avatarPath, err) + } + + 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 0000000000..efad392a2d --- /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 027877cfd5..b091ca8fdc 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 859889fc59..5d392e224f 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 714cd6b2eb..f567702e9d 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 60d2f0c58d..c8ac5b2894 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 index 2b5a750ad1..28fa1523a5 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 b60338300b..83cac401d6 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 dbe237b691..d6ff514320 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 aa683dd44f..4b679a4ad2 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 } |