diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2022-06-06 16:01:49 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-06 16:01:49 +0800 |
commit | 26095115f4ae90e3fdc6ab695978efd16e317f75 (patch) | |
tree | 92ec1c7ff54e0a65f4f0662baa8c0244dd9f324b /modules | |
parent | ebeb6e7c71a0c763b52153f4eb427e7c5b89a95e (diff) | |
download | gitea-26095115f4ae90e3fdc6ab695978efd16e317f75.tar.gz gitea-26095115f4ae90e3fdc6ab695978efd16e317f75.zip |
Move some repository related code into sub package (#19711)
* Move some repository related code into sub package
* Move more repository functions out of models
* Fix lint
* Some performance optimization for webhooks and others
* some refactors
* Fix lint
* Fix
* Update modules/repository/delete.go
Co-authored-by: delvh <dev.lh@web.de>
* Fix test
* Merge
* Fix test
* Fix test
* Fix test
* Fix test
Co-authored-by: delvh <dev.lh@web.de>
Diffstat (limited to 'modules')
-rw-r--r-- | modules/context/repo.go | 5 | ||||
-rw-r--r-- | modules/indexer/issues/indexer.go | 4 | ||||
-rw-r--r-- | modules/repository/create.go | 115 | ||||
-rw-r--r-- | modules/repository/create_test.go | 21 | ||||
-rw-r--r-- | modules/repository/delete.go | 33 | ||||
-rw-r--r-- | modules/repository/fork.go | 31 | ||||
-rw-r--r-- | modules/repository/generate.go | 65 | ||||
-rw-r--r-- | modules/repository/generate_test.go | 57 | ||||
-rw-r--r-- | modules/repository/init.go | 2 | ||||
-rw-r--r-- | modules/repository/repo.go | 25 |
10 files changed, 338 insertions, 20 deletions
diff --git a/modules/context/repo.go b/modules/context/repo.go index df3fe4e74d..5f4af114ff 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -26,6 +26,7 @@ import ( code_indexer "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup/markdown" + repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -551,14 +552,14 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues) ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(unit_model.TypePullRequests) - canSignedUserFork, err := models.CanUserForkRepo(ctx.Doer, ctx.Repo.Repository) + canSignedUserFork, err := repo_module.CanUserForkRepo(ctx.Doer, ctx.Repo.Repository) if err != nil { ctx.ServerError("CanUserForkRepo", err) return } ctx.Data["CanSignedUserFork"] = canSignedUserFork - userAndOrgForks, err := models.GetForksByUserAndOrgs(ctx, ctx.Doer, ctx.Repo.Repository) + userAndOrgForks, err := repo_model.GetForksByUserAndOrgs(ctx, ctx.Doer, ctx.Repo.Repository) if err != nil { ctx.ServerError("GetForksByUserAndOrgs", err) return diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index 7adc938dcc..85de4c75b3 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -291,8 +291,8 @@ func populateIssueIndexer(ctx context.Context) { return default: } - repos, _, err := models.SearchRepositoryByName(&models.SearchRepoOptions{ - ListOptions: db.ListOptions{Page: page, PageSize: models.RepositoryListDefaultPageSize}, + repos, _, err := repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{ + ListOptions: db.ListOptions{Page: page, PageSize: repo_model.RepositoryListDefaultPageSize}, OrderBy: db.SearchOrderByID, Private: true, Collaborate: util.OptionalBoolFalse, diff --git a/modules/repository/create.go b/modules/repository/create.go index 21d45c896e..95bb825403 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -7,15 +7,20 @@ package repository import ( "context" "fmt" + "os" + "path" "strings" + "unicode/utf8" "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" + access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" ) @@ -108,7 +113,7 @@ func CreateRepository(doer, u *user_model.User, opts models.CreateRepoOptions) ( } } - if err := models.CheckDaemonExportOK(ctx, repo); err != nil { + if err := CheckDaemonExportOK(ctx, repo); err != nil { return fmt.Errorf("checkDaemonExportOK: %v", err) } @@ -133,3 +138,111 @@ func CreateRepository(doer, u *user_model.User, opts models.CreateRepoOptions) ( return repo, nil } + +// UpdateRepoSize updates the repository size, calculating it using util.GetDirectorySize +func UpdateRepoSize(ctx context.Context, repo *repo_model.Repository) error { + size, err := util.GetDirectorySize(repo.RepoPath()) + if err != nil { + return fmt.Errorf("updateSize: %v", err) + } + + lfsSize, err := models.GetRepoLFSSize(ctx, repo.ID) + if err != nil { + return fmt.Errorf("updateSize: GetLFSMetaObjects: %v", err) + } + + return repo_model.UpdateRepoSize(ctx, repo.ID, size+lfsSize) +} + +// CheckDaemonExportOK creates/removes git-daemon-export-ok for git-daemon... +func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error { + if err := repo.GetOwner(ctx); err != nil { + return err + } + + // Create/Remove git-daemon-export-ok for git-daemon... + daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`) + + isExist, err := util.IsExist(daemonExportFile) + if err != nil { + log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err) + return err + } + + isPublic := !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePublic + if !isPublic && isExist { + if err = util.Remove(daemonExportFile); err != nil { + log.Error("Failed to remove %s: %v", daemonExportFile, err) + } + } else if isPublic && !isExist { + if f, err := os.Create(daemonExportFile); err != nil { + log.Error("Failed to create %s: %v", daemonExportFile, err) + } else { + f.Close() + } + } + + return nil +} + +// UpdateRepository updates a repository with db context +func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) { + repo.LowerName = strings.ToLower(repo.Name) + + if utf8.RuneCountInString(repo.Description) > 255 { + repo.Description = string([]rune(repo.Description)[:255]) + } + if utf8.RuneCountInString(repo.Website) > 255 { + repo.Website = string([]rune(repo.Website)[:255]) + } + + e := db.GetEngine(ctx) + + if _, err = e.ID(repo.ID).AllCols().Update(repo); err != nil { + return fmt.Errorf("update: %v", err) + } + + if err = UpdateRepoSize(ctx, repo); err != nil { + log.Error("Failed to update size for repository: %v", err) + } + + if visibilityChanged { + if err = repo.GetOwner(ctx); err != nil { + return fmt.Errorf("getOwner: %v", err) + } + if repo.Owner.IsOrganization() { + // Organization repository need to recalculate access table when visibility is changed. + if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil { + return fmt.Errorf("recalculateTeamAccesses: %v", err) + } + } + + // If repo has become private, we need to set its actions to private. + if repo.IsPrivate { + _, err = e.Where("repo_id = ?", repo.ID).Cols("is_private").Update(&models.Action{ + IsPrivate: true, + }) + if err != nil { + return err + } + } + + // Create/Remove git-daemon-export-ok for git-daemon... + if err := CheckDaemonExportOK(db.WithEngine(ctx, e), repo); err != nil { + return err + } + + forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID) + if err != nil { + return fmt.Errorf("getRepositoriesByForkID: %v", err) + } + for i := range forkRepos { + forkRepos[i].IsPrivate = repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate + if err = UpdateRepository(ctx, forkRepos[i], true); err != nil { + return fmt.Errorf("updateRepository[%d]: %v", forkRepos[i].ID, err) + } + } + } + + return nil +} diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go index b6a89a7ed6..2a47e93631 100644 --- a/modules/repository/create_test.go +++ b/modules/repository/create_test.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/structs" @@ -147,3 +148,23 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { } assert.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization") } + +func TestUpdateRepositoryVisibilityChanged(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + // Get sample repo and change visibility + repo, err := repo_model.GetRepositoryByID(9) + assert.NoError(t, err) + repo.IsPrivate = true + + // Update it + err = UpdateRepository(db.DefaultContext, repo, true) + assert.NoError(t, err) + + // Check visibility of action has become private + act := models.Action{} + _, err = db.GetEngine(db.DefaultContext).ID(3).Get(&act) + + assert.NoError(t, err) + assert.True(t, act.IsPrivate) +} diff --git a/modules/repository/delete.go b/modules/repository/delete.go new file mode 100644 index 0000000000..25fb15e300 --- /dev/null +++ b/modules/repository/delete.go @@ -0,0 +1,33 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repository + +import ( + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/organization" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" +) + +// CanUserDelete returns true if user could delete the repository +func CanUserDelete(repo *repo_model.Repository, user *user_model.User) (bool, error) { + if user.IsAdmin || user.ID == repo.OwnerID { + return true, nil + } + + if err := repo.GetOwner(db.DefaultContext); err != nil { + return false, err + } + + if repo.Owner.IsOrganization() { + isOwner, err := organization.OrgFromUser(repo.Owner).IsOwnedBy(user.ID) + if err != nil { + return false, err + } + return isOwner, nil + } + + return false, nil +} diff --git a/modules/repository/fork.go b/modules/repository/fork.go new file mode 100644 index 0000000000..c967d3b741 --- /dev/null +++ b/modules/repository/fork.go @@ -0,0 +1,31 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repository + +import ( + "code.gitea.io/gitea/models/organization" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" +) + +// CanUserForkRepo returns true if specified user can fork repository. +func CanUserForkRepo(user *user_model.User, repo *repo_model.Repository) (bool, error) { + if user == nil { + return false, nil + } + if repo.OwnerID != user.ID && !repo_model.HasForkedRepo(user.ID, repo.ID) { + return true, nil + } + ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(user.ID) + if err != nil { + return false, err + } + for _, org := range ownedOrgs { + if repo.OwnerID != org.ID && !repo_model.HasForkedRepo(org.ID, repo.ID) { + return true, nil + } + } + return false, nil +} diff --git a/modules/repository/generate.go b/modules/repository/generate.go index b3ce809173..94bb6e6429 100644 --- a/modules/repository/generate.go +++ b/modules/repository/generate.go @@ -5,6 +5,8 @@ package repository import ( + "bufio" + "bytes" "context" "fmt" "os" @@ -20,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" + "github.com/gobwas/glob" "github.com/huandu/xstrings" ) @@ -78,7 +81,38 @@ func generateExpansion(src string, templateRepo, generateRepo *repo_model.Reposi }) } -func checkGiteaTemplate(tmpDir string) (*models.GiteaTemplate, error) { +// GiteaTemplate holds information about a .gitea/template file +type GiteaTemplate struct { + Path string + Content []byte + + globs []glob.Glob +} + +// Globs parses the .gitea/template globs or returns them if they were already parsed +func (gt GiteaTemplate) Globs() []glob.Glob { + if gt.globs != nil { + return gt.globs + } + + gt.globs = make([]glob.Glob, 0) + scanner := bufio.NewScanner(bytes.NewReader(gt.Content)) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + g, err := glob.Compile(line, '/') + if err != nil { + log.Info("Invalid glob expression '%s' (skipped): %v", line, err) + continue + } + gt.globs = append(gt.globs, g) + } + return gt.globs +} + +func checkGiteaTemplate(tmpDir string) (*GiteaTemplate, error) { gtPath := filepath.Join(tmpDir, ".gitea", "template") if _, err := os.Stat(gtPath); os.IsNotExist(err) { return nil, nil @@ -91,7 +125,7 @@ func checkGiteaTemplate(tmpDir string) (*models.GiteaTemplate, error) { return nil, err } - gt := &models.GiteaTemplate{ + gt := &GiteaTemplate{ Path: gtPath, Content: content, } @@ -227,7 +261,7 @@ func generateGitContent(ctx context.Context, repo, templateRepo, generateRepo *r if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { return fmt.Errorf("setDefaultBranch: %v", err) } - if err = models.UpdateRepositoryCtx(ctx, repo, false); err != nil { + if err = UpdateRepository(ctx, repo, false); err != nil { return fmt.Errorf("updateRepository: %v", err) } @@ -240,7 +274,7 @@ func GenerateGitContent(ctx context.Context, templateRepo, generateRepo *repo_mo return err } - if err := models.UpdateRepoSize(ctx, generateRepo); err != nil { + if err := UpdateRepoSize(ctx, generateRepo); err != nil { return fmt.Errorf("failed to update size for repository: %v", err) } @@ -250,8 +284,27 @@ func GenerateGitContent(ctx context.Context, templateRepo, generateRepo *repo_mo return nil } +// GenerateRepoOptions contains the template units to generate +type GenerateRepoOptions struct { + Name string + DefaultBranch string + Description string + Private bool + GitContent bool + Topics bool + GitHooks bool + Webhooks bool + Avatar bool + IssueLabels bool +} + +// IsValid checks whether at least one option is chosen for generation +func (gro GenerateRepoOptions) IsValid() bool { + return gro.GitContent || gro.Topics || gro.GitHooks || gro.Webhooks || gro.Avatar || gro.IssueLabels // or other items as they are added +} + // GenerateRepository generates a repository from a template -func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *repo_model.Repository, opts models.GenerateRepoOptions) (_ *repo_model.Repository, err error) { +func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *repo_model.Repository, opts GenerateRepoOptions) (_ *repo_model.Repository, err error) { generateRepo := &repo_model.Repository{ OwnerID: owner.ID, Owner: owner, @@ -288,7 +341,7 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ return generateRepo, err } - if err = models.CheckDaemonExportOK(ctx, generateRepo); err != nil { + if err = CheckDaemonExportOK(ctx, generateRepo); err != nil { return generateRepo, fmt.Errorf("checkDaemonExportOK: %v", err) } diff --git a/modules/repository/generate_test.go b/modules/repository/generate_test.go new file mode 100644 index 0000000000..139fa4c918 --- /dev/null +++ b/modules/repository/generate_test.go @@ -0,0 +1,57 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repository + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var giteaTemplate = []byte(` +# Header + +# All .go files +**.go + +# All text files in /text/ +text/*.txt + +# All files in modules folders +**/modules/* +`) + +func TestGiteaTemplate(t *testing.T) { + gt := GiteaTemplate{Content: giteaTemplate} + assert.Len(t, gt.Globs(), 3) + + tt := []struct { + Path string + Match bool + }{ + {Path: "main.go", Match: true}, + {Path: "a/b/c/d/e.go", Match: true}, + {Path: "main.txt", Match: false}, + {Path: "a/b.txt", Match: false}, + {Path: "text/a.txt", Match: true}, + {Path: "text/b.txt", Match: true}, + {Path: "text/c.json", Match: false}, + {Path: "a/b/c/modules/README.md", Match: true}, + {Path: "a/b/c/modules/d/README.md", Match: false}, + } + + for _, tc := range tt { + t.Run(tc.Path, func(t *testing.T) { + match := false + for _, g := range gt.Globs() { + if g.Match(tc.Path) { + match = true + break + } + } + assert.Equal(t, tc.Match, match) + }) + } +} diff --git a/modules/repository/init.go b/modules/repository/init.go index 845a61ed0a..f8c7a89552 100644 --- a/modules/repository/init.go +++ b/modules/repository/init.go @@ -444,7 +444,7 @@ func initRepository(ctx context.Context, repoPath string, u *user_model.User, re } } - if err = models.UpdateRepositoryCtx(ctx, repo, false); err != nil { + if err = UpdateRepository(ctx, repo, false); err != nil { return fmt.Errorf("updateRepository: %v", err) } diff --git a/modules/repository/repo.go b/modules/repository/repo.go index 30ca6fdff8..281999a1eb 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -116,7 +116,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, repo.Owner = u } - if err = models.CheckDaemonExportOK(ctx, repo); err != nil { + if err = CheckDaemonExportOK(ctx, repo); err != nil { return repo, fmt.Errorf("checkDaemonExportOK: %v", err) } @@ -168,9 +168,11 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, } } - if err = models.UpdateRepoSize(ctx, repo); err != nil { - log.Error("Failed to update size for repository: %v", err) + ctx, committer, err := db.TxContext() + if err != nil { + return nil, err } + defer committer.Close() if opts.Mirror { mirrorModel := repo_model.Mirror{ @@ -203,17 +205,24 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, } } - if err = repo_model.InsertMirror(&mirrorModel); err != nil { + if err = repo_model.InsertMirror(ctx, &mirrorModel); err != nil { return repo, fmt.Errorf("InsertOne: %v", err) } repo.IsMirror = true - err = models.UpdateRepository(repo, false) + if err = UpdateRepository(ctx, repo, false); err != nil { + return nil, err + } } else { - repo, err = CleanUpMigrateInfo(ctx, repo) + if err = UpdateRepoSize(ctx, repo); err != nil { + log.Error("Failed to update size for repository: %v", err) + } + if repo, err = CleanUpMigrateInfo(ctx, repo); err != nil { + return nil, err + } } - return repo, err + return repo, committer.Commit() } // cleanUpMigrateGitConfig removes mirror info which prevents "push --all". @@ -253,7 +262,7 @@ func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo } } - return repo, models.UpdateRepository(repo, false) + return repo, UpdateRepository(ctx, repo, false) } // SyncReleasesWithTags synchronizes release table with repository tags |