diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2022-06-12 23:51:54 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-12 23:51:54 +0800 |
commit | 110fc57cbcf293c19ed7017d8ea528b4bbbd7396 (patch) | |
tree | b36eb2ee0e3f8417a35ad095e25880b778ded0ba /models | |
parent | a9dc9b06e4a4106ec8315fe7b2922efa440ca199 (diff) | |
download | gitea-110fc57cbcf293c19ed7017d8ea528b4bbbd7396.tar.gz gitea-110fc57cbcf293c19ed7017d8ea528b4bbbd7396.zip |
Move some code into models/git (#19879)
* Move access and repo permission to models/perm/access
* fix test
* Move some git related files into sub package models/git
* Fix build
* fix git test
* move lfs to sub package
* move more git related functions to models/git
* Move functions sequence
* Some improvements per @KN4CK3R and @delvh
Diffstat (limited to 'models')
-rw-r--r-- | models/branch.go | 80 | ||||
-rw-r--r-- | models/commit.go | 26 | ||||
-rw-r--r-- | models/error.go | 78 | ||||
-rw-r--r-- | models/git/branches.go (renamed from models/branches.go) | 80 | ||||
-rw-r--r-- | models/git/branches_test.go (renamed from models/branches_test.go) | 50 | ||||
-rw-r--r-- | models/git/commit_status.go (renamed from models/commit_status.go) | 19 | ||||
-rw-r--r-- | models/git/commit_status_test.go (renamed from models/commit_status_test.go) | 5 | ||||
-rw-r--r-- | models/git/lfs.go (renamed from models/lfs.go) | 75 | ||||
-rw-r--r-- | models/git/lfs_lock.go (renamed from models/lfs_lock.go) | 2 | ||||
-rw-r--r-- | models/git/main_test.go | 18 | ||||
-rw-r--r-- | models/git/protected_tag.go (renamed from models/protected_tag.go) | 2 | ||||
-rw-r--r-- | models/git/protected_tag_test.go (renamed from models/protected_tag_test.go) | 33 | ||||
-rw-r--r-- | models/issue_comment.go | 13 | ||||
-rw-r--r-- | models/org_team.go | 3 | ||||
-rw-r--r-- | models/pull.go | 9 | ||||
-rw-r--r-- | models/repo.go | 20 | ||||
-rw-r--r-- | models/repo/collaboration.go | 21 | ||||
-rw-r--r-- | models/repo_collaboration.go | 22 | ||||
-rw-r--r-- | models/review.go | 3 | ||||
-rw-r--r-- | models/user.go | 3 |
20 files changed, 291 insertions, 271 deletions
diff --git a/models/branch.go b/models/branch.go new file mode 100644 index 0000000000..3d6e7d82e2 --- /dev/null +++ b/models/branch.go @@ -0,0 +1,80 @@ +// 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 models + +import ( + "context" + + "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" + "code.gitea.io/gitea/modules/log" +) + +// HasEnoughApprovals returns true if pr has enough granted approvals. +func HasEnoughApprovals(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool { + if protectBranch.RequiredApprovals == 0 { + return true + } + return GetGrantedApprovalsCount(ctx, protectBranch, pr) >= protectBranch.RequiredApprovals +} + +// GetGrantedApprovalsCount returns the number of granted approvals for pr. A granted approval must be authored by a user in an approval whitelist. +func GetGrantedApprovalsCount(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) int64 { + sess := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). + And("type = ?", ReviewTypeApprove). + And("official = ?", true). + And("dismissed = ?", false) + if protectBranch.DismissStaleApprovals { + sess = sess.And("stale = ?", false) + } + approvals, err := sess.Count(new(Review)) + if err != nil { + log.Error("GetGrantedApprovalsCount: %v", err) + return 0 + } + + return approvals +} + +// MergeBlockedByRejectedReview returns true if merge is blocked by rejected reviews +func MergeBlockedByRejectedReview(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool { + if !protectBranch.BlockOnRejectedReviews { + return false + } + rejectExist, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). + And("type = ?", ReviewTypeReject). + And("official = ?", true). + And("dismissed = ?", false). + Exist(new(Review)) + if err != nil { + log.Error("MergeBlockedByRejectedReview: %v", err) + return true + } + + return rejectExist +} + +// MergeBlockedByOfficialReviewRequests block merge because of some review request to official reviewer +// of from official review +func MergeBlockedByOfficialReviewRequests(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool { + if !protectBranch.BlockOnOfficialReviewRequests { + return false + } + has, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). + And("type = ?", ReviewTypeRequest). + And("official = ?", true). + Exist(new(Review)) + if err != nil { + log.Error("MergeBlockedByOfficialReviewRequests: %v", err) + return true + } + + return has +} + +// MergeBlockedByOutdatedBranch returns true if merge is blocked by an outdated head branch +func MergeBlockedByOutdatedBranch(protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool { + return protectBranch.BlockOnOutdatedBranch && pr.CommitsBehind > 0 +} diff --git a/models/commit.go b/models/commit.go deleted file mode 100644 index 92a839b780..0000000000 --- a/models/commit.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2021 Gitea. 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 ( - asymkey_model "code.gitea.io/gitea/models/asymkey" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/git" -) - -// ConvertFromGitCommit converts git commits into SignCommitWithStatuses -func ConvertFromGitCommit(commits []*git.Commit, repo *repo_model.Repository) []*SignCommitWithStatuses { - return ParseCommitsWithStatus( - asymkey_model.ParseCommitsWithSignature( - user_model.ValidateCommitsWithEmails(commits), - repo.GetTrustModel(), - func(user *user_model.User) (bool, error) { - return IsOwnerMemberCollaborator(repo, user.ID) - }, - ), - repo, - ) -} diff --git a/models/error.go b/models/error.go index 0dc14c3e31..16ae52fc43 100644 --- a/models/error.go +++ b/models/error.go @@ -8,7 +8,6 @@ package models import ( "fmt" - "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" ) @@ -145,83 +144,6 @@ func (err ErrAccessTokenEmpty) Error() string { return "access token is empty" } -//.____ ____________________ -//| | \_ _____/ _____/ -//| | | __) \_____ \ -//| |___| \ / \ -//|_______ \___ / /_______ / -// \/ \/ \/ - -// ErrLFSLockNotExist represents a "LFSLockNotExist" kind of error. -type ErrLFSLockNotExist struct { - ID int64 - RepoID int64 - Path string -} - -// IsErrLFSLockNotExist checks if an error is a ErrLFSLockNotExist. -func IsErrLFSLockNotExist(err error) bool { - _, ok := err.(ErrLFSLockNotExist) - return ok -} - -func (err ErrLFSLockNotExist) Error() string { - return fmt.Sprintf("lfs lock does not exist [id: %d, rid: %d, path: %s]", err.ID, err.RepoID, err.Path) -} - -// ErrLFSUnauthorizedAction represents a "LFSUnauthorizedAction" kind of error. -type ErrLFSUnauthorizedAction struct { - RepoID int64 - UserName string - Mode perm.AccessMode -} - -// IsErrLFSUnauthorizedAction checks if an error is a ErrLFSUnauthorizedAction. -func IsErrLFSUnauthorizedAction(err error) bool { - _, ok := err.(ErrLFSUnauthorizedAction) - return ok -} - -func (err ErrLFSUnauthorizedAction) Error() string { - if err.Mode == perm.AccessModeWrite { - return fmt.Sprintf("User %s doesn't have write access for lfs lock [rid: %d]", err.UserName, err.RepoID) - } - return fmt.Sprintf("User %s doesn't have read access for lfs lock [rid: %d]", err.UserName, err.RepoID) -} - -// ErrLFSLockAlreadyExist represents a "LFSLockAlreadyExist" kind of error. -type ErrLFSLockAlreadyExist struct { - RepoID int64 - Path string -} - -// IsErrLFSLockAlreadyExist checks if an error is a ErrLFSLockAlreadyExist. -func IsErrLFSLockAlreadyExist(err error) bool { - _, ok := err.(ErrLFSLockAlreadyExist) - return ok -} - -func (err ErrLFSLockAlreadyExist) Error() string { - return fmt.Sprintf("lfs lock already exists [rid: %d, path: %s]", err.RepoID, err.Path) -} - -// ErrLFSFileLocked represents a "LFSFileLocked" kind of error. -type ErrLFSFileLocked struct { - RepoID int64 - Path string - UserName string -} - -// IsErrLFSFileLocked checks if an error is a ErrLFSFileLocked. -func IsErrLFSFileLocked(err error) bool { - _, ok := err.(ErrLFSFileLocked) - return ok -} - -func (err ErrLFSFileLocked) Error() string { - return fmt.Sprintf("File is lfs locked [repo: %d, locked by: %s, path: %s]", err.RepoID, err.UserName, err.Path) -} - // ErrNoPendingRepoTransfer is an error type for repositories without a pending // transfer request type ErrNoPendingRepoTransfer struct { diff --git a/models/branches.go b/models/git/branches.go index 98d2c3a993..0a44c0a68d 100644 --- a/models/branches.go +++ b/models/git/branches.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 git import ( "context" @@ -129,10 +129,11 @@ func IsUserMergeWhitelisted(ctx context.Context, protectBranch *ProtectedBranch, // IsUserOfficialReviewer check if user is official reviewer for the branch (counts towards required approvals) func IsUserOfficialReviewer(protectBranch *ProtectedBranch, user *user_model.User) (bool, error) { - return isUserOfficialReviewer(db.DefaultContext, protectBranch, user) + return IsUserOfficialReviewerCtx(db.DefaultContext, protectBranch, user) } -func isUserOfficialReviewer(ctx context.Context, protectBranch *ProtectedBranch, user *user_model.User) (bool, error) { +// IsUserOfficialReviewerCtx check if user is official reviewer for the branch (counts towards required approvals) +func IsUserOfficialReviewerCtx(ctx context.Context, protectBranch *ProtectedBranch, user *user_model.User) (bool, error) { repo, err := repo_model.GetRepositoryByIDCtx(ctx, protectBranch.RepoID) if err != nil { return false, err @@ -159,73 +160,6 @@ func isUserOfficialReviewer(ctx context.Context, protectBranch *ProtectedBranch, return inTeam, nil } -// HasEnoughApprovals returns true if pr has enough granted approvals. -func (protectBranch *ProtectedBranch) HasEnoughApprovals(ctx context.Context, pr *PullRequest) bool { - if protectBranch.RequiredApprovals == 0 { - return true - } - return protectBranch.GetGrantedApprovalsCount(ctx, pr) >= protectBranch.RequiredApprovals -} - -// GetGrantedApprovalsCount returns the number of granted approvals for pr. A granted approval must be authored by a user in an approval whitelist. -func (protectBranch *ProtectedBranch) GetGrantedApprovalsCount(ctx context.Context, pr *PullRequest) int64 { - sess := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). - And("type = ?", ReviewTypeApprove). - And("official = ?", true). - And("dismissed = ?", false) - if protectBranch.DismissStaleApprovals { - sess = sess.And("stale = ?", false) - } - approvals, err := sess.Count(new(Review)) - if err != nil { - log.Error("GetGrantedApprovalsCount: %v", err) - return 0 - } - - return approvals -} - -// MergeBlockedByRejectedReview returns true if merge is blocked by rejected reviews -func (protectBranch *ProtectedBranch) MergeBlockedByRejectedReview(ctx context.Context, pr *PullRequest) bool { - if !protectBranch.BlockOnRejectedReviews { - return false - } - rejectExist, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). - And("type = ?", ReviewTypeReject). - And("official = ?", true). - And("dismissed = ?", false). - Exist(new(Review)) - if err != nil { - log.Error("MergeBlockedByRejectedReview: %v", err) - return true - } - - return rejectExist -} - -// MergeBlockedByOfficialReviewRequests block merge because of some review request to official reviewer -// of from official review -func (protectBranch *ProtectedBranch) MergeBlockedByOfficialReviewRequests(ctx context.Context, pr *PullRequest) bool { - if !protectBranch.BlockOnOfficialReviewRequests { - return false - } - has, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). - And("type = ?", ReviewTypeRequest). - And("official = ?", true). - Exist(new(Review)) - if err != nil { - log.Error("MergeBlockedByOfficialReviewRequests: %v", err) - return true - } - - return has -} - -// MergeBlockedByOutdatedBranch returns true if merge is blocked by an outdated head branch -func (protectBranch *ProtectedBranch) MergeBlockedByOutdatedBranch(pr *PullRequest) bool { - return protectBranch.BlockOnOutdatedBranch && pr.CommitsBehind > 0 -} - // GetProtectedFilePatterns parses a semicolon separated list of protected file patterns and returns a glob.Glob slice func (protectBranch *ProtectedBranch) GetProtectedFilePatterns() []glob.Glob { return getFilePatterns(protectBranch.ProtectedFilePatterns) @@ -252,13 +186,13 @@ func getFilePatterns(filePatterns string) []glob.Glob { } // MergeBlockedByProtectedFiles returns true if merge is blocked by protected files change -func (protectBranch *ProtectedBranch) MergeBlockedByProtectedFiles(pr *PullRequest) bool { +func (protectBranch *ProtectedBranch) MergeBlockedByProtectedFiles(changedProtectedFiles []string) bool { glob := protectBranch.GetProtectedFilePatterns() if len(glob) == 0 { return false } - return len(pr.ChangedProtectedFiles) > 0 + return len(changedProtectedFiles) > 0 } // IsProtectedFile return if path is protected @@ -642,7 +576,7 @@ func RenameBranch(repo *repo_model.Repository, from, to string, gitAction func(i } // 3. Update all not merged pull request base branch name - _, err = sess.Table(new(PullRequest)).Where("base_repo_id=? AND base_branch=? AND has_merged=?", + _, err = sess.Table("pull_request").Where("base_repo_id=? AND base_branch=? AND has_merged=?", repo.ID, from, false). Update(map[string]interface{}{"base_branch": to}) if err != nil { diff --git a/models/branches_test.go b/models/git/branches_test.go index 0a0f125cc6..1e0b1a98b6 100644 --- a/models/branches_test.go +++ b/models/git/branches_test.go @@ -2,12 +2,14 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package models +package git_test import ( "testing" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -17,24 +19,24 @@ import ( func TestAddDeletedBranch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) - firstBranch := unittest.AssertExistsAndLoadBean(t, &DeletedBranch{ID: 1}).(*DeletedBranch) + firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1}).(*git_model.DeletedBranch) - assert.Error(t, AddDeletedBranch(repo.ID, firstBranch.Name, firstBranch.Commit, firstBranch.DeletedByID)) - assert.NoError(t, AddDeletedBranch(repo.ID, "test", "5655464564554545466464656", int64(1))) + assert.Error(t, git_model.AddDeletedBranch(repo.ID, firstBranch.Name, firstBranch.Commit, firstBranch.DeletedByID)) + assert.NoError(t, git_model.AddDeletedBranch(repo.ID, "test", "5655464564554545466464656", int64(1))) } func TestGetDeletedBranches(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) - branches, err := GetDeletedBranches(repo.ID) + branches, err := git_model.GetDeletedBranches(repo.ID) assert.NoError(t, err) assert.Len(t, branches, 2) } func TestGetDeletedBranch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - firstBranch := unittest.AssertExistsAndLoadBean(t, &DeletedBranch{ID: 1}).(*DeletedBranch) + firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1}).(*git_model.DeletedBranch) assert.NotNil(t, getDeletedBranch(t, firstBranch)) } @@ -42,8 +44,8 @@ func TestGetDeletedBranch(t *testing.T) { func TestDeletedBranchLoadUser(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - firstBranch := unittest.AssertExistsAndLoadBean(t, &DeletedBranch{ID: 1}).(*DeletedBranch) - secondBranch := unittest.AssertExistsAndLoadBean(t, &DeletedBranch{ID: 2}).(*DeletedBranch) + firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1}).(*git_model.DeletedBranch) + secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 2}).(*git_model.DeletedBranch) branch := getDeletedBranch(t, firstBranch) assert.Nil(t, branch.DeletedBy) @@ -62,18 +64,18 @@ func TestRemoveDeletedBranch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) - firstBranch := unittest.AssertExistsAndLoadBean(t, &DeletedBranch{ID: 1}).(*DeletedBranch) + firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1}).(*git_model.DeletedBranch) - err := RemoveDeletedBranchByID(repo.ID, 1) + err := git_model.RemoveDeletedBranchByID(repo.ID, 1) assert.NoError(t, err) unittest.AssertNotExistsBean(t, firstBranch) - unittest.AssertExistsAndLoadBean(t, &DeletedBranch{ID: 2}) + unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 2}) } -func getDeletedBranch(t *testing.T, branch *DeletedBranch) *DeletedBranch { +func getDeletedBranch(t *testing.T, branch *git_model.DeletedBranch) *git_model.DeletedBranch { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) - deletedBranch, err := GetDeletedBranchByID(repo.ID, branch.ID) + deletedBranch, err := git_model.GetDeletedBranchByID(repo.ID, branch.ID) assert.NoError(t, err) assert.Equal(t, branch.ID, deletedBranch.ID) assert.Equal(t, branch.Name, deletedBranch.Name) @@ -85,12 +87,12 @@ func getDeletedBranch(t *testing.T, branch *DeletedBranch) *DeletedBranch { func TestFindRenamedBranch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - branch, exist, err := FindRenamedBranch(1, "dev") + branch, exist, err := git_model.FindRenamedBranch(1, "dev") assert.NoError(t, err) assert.Equal(t, true, exist) assert.Equal(t, "master", branch.To) - _, exist, err = FindRenamedBranch(1, "unknow") + _, exist, err = git_model.FindRenamedBranch(1, "unknow") assert.NoError(t, err) assert.Equal(t, false, exist) } @@ -103,13 +105,13 @@ func TestRenameBranch(t *testing.T) { ctx, committer, err := db.TxContext() defer committer.Close() assert.NoError(t, err) - assert.NoError(t, UpdateProtectBranch(ctx, repo1, &ProtectedBranch{ + assert.NoError(t, git_model.UpdateProtectBranch(ctx, repo1, &git_model.ProtectedBranch{ RepoID: repo1.ID, BranchName: "master", - }, WhitelistOptions{})) + }, git_model.WhitelistOptions{})) assert.NoError(t, committer.Commit()) - assert.NoError(t, RenameBranch(repo1, "master", "main", func(isDefault bool) error { + assert.NoError(t, git_model.RenameBranch(repo1, "master", "main", func(isDefault bool) error { _isDefault = isDefault return nil })) @@ -118,18 +120,18 @@ func TestRenameBranch(t *testing.T) { repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) assert.Equal(t, "main", repo1.DefaultBranch) - pull := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) // merged + pull := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest) // merged assert.Equal(t, "master", pull.BaseBranch) - pull = unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest) // open + pull = unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 2}).(*models.PullRequest) // open assert.Equal(t, "main", pull.BaseBranch) - renamedBranch := unittest.AssertExistsAndLoadBean(t, &RenamedBranch{ID: 2}).(*RenamedBranch) + renamedBranch := unittest.AssertExistsAndLoadBean(t, &git_model.RenamedBranch{ID: 2}).(*git_model.RenamedBranch) assert.Equal(t, "master", renamedBranch.From) assert.Equal(t, "main", renamedBranch.To) assert.Equal(t, int64(1), renamedBranch.RepoID) - unittest.AssertExistsAndLoadBean(t, &ProtectedBranch{ + unittest.AssertExistsAndLoadBean(t, &git_model.ProtectedBranch{ RepoID: repo1.ID, BranchName: "main", }) @@ -143,7 +145,7 @@ func TestOnlyGetDeletedBranchOnCorrectRepo(t *testing.T) { // is actually on repo with ID 1. repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository) - deletedBranch, err := GetDeletedBranchByID(repo2.ID, 1) + deletedBranch, err := git_model.GetDeletedBranchByID(repo2.ID, 1) // Expect no error, and the returned branch is nil. assert.NoError(t, err) @@ -153,7 +155,7 @@ func TestOnlyGetDeletedBranchOnCorrectRepo(t *testing.T) { // This should return the deletedBranch. repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) - deletedBranch, err = GetDeletedBranchByID(repo1.ID, 1) + deletedBranch, err = git_model.GetDeletedBranchByID(repo1.ID, 1) // Expect no error, and the returned branch to be not nil. assert.NoError(t, err) diff --git a/models/commit_status.go b/models/git/commit_status.go index ef92c5847a..54a7b43199 100644 --- a/models/commit_status.go +++ b/models/git/commit_status.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 git import ( "context" @@ -16,6 +16,7 @@ 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/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -178,7 +179,7 @@ func GetCommitStatuses(repo *repo_model.Repository, sha string, opts *CommitStat opts.Page = 1 } if opts.PageSize <= 0 { - opts.Page = ItemsPerPage + opts.Page = setting.ItemsPerPage } countSession := listCommitStatusesStatement(repo, sha, opts) @@ -353,3 +354,17 @@ func ParseCommitsWithStatus(oldCommits []*asymkey_model.SignCommit, repo *repo_m func hashCommitStatusContext(context string) string { return fmt.Sprintf("%x", sha1.Sum([]byte(context))) } + +// ConvertFromGitCommit converts git commits into SignCommitWithStatuses +func ConvertFromGitCommit(commits []*git.Commit, repo *repo_model.Repository) []*SignCommitWithStatuses { + return ParseCommitsWithStatus( + asymkey_model.ParseCommitsWithSignature( + user_model.ValidateCommitsWithEmails(commits), + repo.GetTrustModel(), + func(user *user_model.User) (bool, error) { + return repo_model.IsOwnerMemberCollaborator(repo, user.ID) + }, + ), + repo, + ) +} diff --git a/models/commit_status_test.go b/models/git/commit_status_test.go index bb9e375072..9919297430 100644 --- a/models/commit_status_test.go +++ b/models/git/commit_status_test.go @@ -2,12 +2,13 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package models +package git_test import ( "testing" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/structs" @@ -22,7 +23,7 @@ func TestGetCommitStatuses(t *testing.T) { sha1 := "1234123412341234123412341234123412341234" - statuses, maxResults, err := GetCommitStatuses(repo1, sha1, &CommitStatusOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 50}}) + statuses, maxResults, err := git_model.GetCommitStatuses(repo1, sha1, &git_model.CommitStatusOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 50}}) assert.NoError(t, err) assert.Equal(t, int(maxResults), 5) assert.Len(t, statuses, 5) diff --git a/models/lfs.go b/models/git/lfs.go index d9eea6bb89..13b8b234f9 100644 --- a/models/lfs.go +++ b/models/git/lfs.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 git import ( "context" @@ -10,6 +10,7 @@ import ( "fmt" "code.gitea.io/gitea/models/db" + "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/lfs" @@ -19,6 +20,76 @@ import ( "xorm.io/builder" ) +// ErrLFSLockNotExist represents a "LFSLockNotExist" kind of error. +type ErrLFSLockNotExist struct { + ID int64 + RepoID int64 + Path string +} + +// IsErrLFSLockNotExist checks if an error is a ErrLFSLockNotExist. +func IsErrLFSLockNotExist(err error) bool { + _, ok := err.(ErrLFSLockNotExist) + return ok +} + +func (err ErrLFSLockNotExist) Error() string { + return fmt.Sprintf("lfs lock does not exist [id: %d, rid: %d, path: %s]", err.ID, err.RepoID, err.Path) +} + +// ErrLFSUnauthorizedAction represents a "LFSUnauthorizedAction" kind of error. +type ErrLFSUnauthorizedAction struct { + RepoID int64 + UserName string + Mode perm.AccessMode +} + +// IsErrLFSUnauthorizedAction checks if an error is a ErrLFSUnauthorizedAction. +func IsErrLFSUnauthorizedAction(err error) bool { + _, ok := err.(ErrLFSUnauthorizedAction) + return ok +} + +func (err ErrLFSUnauthorizedAction) Error() string { + if err.Mode == perm.AccessModeWrite { + return fmt.Sprintf("User %s doesn't have write access for lfs lock [rid: %d]", err.UserName, err.RepoID) + } + return fmt.Sprintf("User %s doesn't have read access for lfs lock [rid: %d]", err.UserName, err.RepoID) +} + +// ErrLFSLockAlreadyExist represents a "LFSLockAlreadyExist" kind of error. +type ErrLFSLockAlreadyExist struct { + RepoID int64 + Path string +} + +// IsErrLFSLockAlreadyExist checks if an error is a ErrLFSLockAlreadyExist. +func IsErrLFSLockAlreadyExist(err error) bool { + _, ok := err.(ErrLFSLockAlreadyExist) + return ok +} + +func (err ErrLFSLockAlreadyExist) Error() string { + return fmt.Sprintf("lfs lock already exists [rid: %d, path: %s]", err.RepoID, err.Path) +} + +// ErrLFSFileLocked represents a "LFSFileLocked" kind of error. +type ErrLFSFileLocked struct { + RepoID int64 + Path string + UserName string +} + +// IsErrLFSFileLocked checks if an error is a ErrLFSFileLocked. +func IsErrLFSFileLocked(err error) bool { + _, ok := err.(ErrLFSFileLocked) + return ok +} + +func (err ErrLFSFileLocked) Error() string { + return fmt.Sprintf("File is lfs locked [repo: %d, locked by: %s, path: %s]", err.RepoID, err.UserName, err.Path) +} + // LFSMetaObject stores metadata for LFS tracked files. type LFSMetaObject struct { ID int64 `xorm:"pk autoincr"` @@ -239,7 +310,7 @@ func CopyLFS(ctx context.Context, newRepo, oldRepo *repo_model.Repository) error for _, v := range lfsObjects { v.ID = 0 v.RepositoryID = newRepo.ID - if _, err := db.GetEngine(ctx).Insert(v); err != nil { + if err := db.Insert(ctx, v); err != nil { return err } } diff --git a/models/lfs_lock.go b/models/git/lfs_lock.go index 4995305393..016db899a9 100644 --- a/models/lfs_lock.go +++ b/models/git/lfs_lock.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 git import ( "context" diff --git a/models/git/main_test.go b/models/git/main_test.go new file mode 100644 index 0000000000..02401e5204 --- /dev/null +++ b/models/git/main_test.go @@ -0,0 +1,18 @@ +// Copyright 2020 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 git_test + +import ( + "path/filepath" + "testing" + + "code.gitea.io/gitea/models/unittest" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m, &unittest.TestOptions{ + GiteaRootPath: filepath.Join("..", ".."), + }) +} diff --git a/models/protected_tag.go b/models/git/protected_tag.go index db6ff50462..7c3881643d 100644 --- a/models/protected_tag.go +++ b/models/git/protected_tag.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 git import ( "regexp" diff --git a/models/protected_tag_test.go b/models/git/protected_tag_test.go index bbd5086092..b496688b25 100644 --- a/models/protected_tag_test.go +++ b/models/git/protected_tag_test.go @@ -2,11 +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 git_test import ( "testing" + git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/unittest" "github.com/stretchr/testify/assert" @@ -15,42 +16,42 @@ import ( func TestIsUserAllowed(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - pt := &ProtectedTag{} - allowed, err := IsUserAllowedModifyTag(pt, 1) + pt := &git_model.ProtectedTag{} + allowed, err := git_model.IsUserAllowedModifyTag(pt, 1) assert.NoError(t, err) assert.False(t, allowed) - pt = &ProtectedTag{ + pt = &git_model.ProtectedTag{ AllowlistUserIDs: []int64{1}, } - allowed, err = IsUserAllowedModifyTag(pt, 1) + allowed, err = git_model.IsUserAllowedModifyTag(pt, 1) assert.NoError(t, err) assert.True(t, allowed) - allowed, err = IsUserAllowedModifyTag(pt, 2) + allowed, err = git_model.IsUserAllowedModifyTag(pt, 2) assert.NoError(t, err) assert.False(t, allowed) - pt = &ProtectedTag{ + pt = &git_model.ProtectedTag{ AllowlistTeamIDs: []int64{1}, } - allowed, err = IsUserAllowedModifyTag(pt, 1) + allowed, err = git_model.IsUserAllowedModifyTag(pt, 1) assert.NoError(t, err) assert.False(t, allowed) - allowed, err = IsUserAllowedModifyTag(pt, 2) + allowed, err = git_model.IsUserAllowedModifyTag(pt, 2) assert.NoError(t, err) assert.True(t, allowed) - pt = &ProtectedTag{ + pt = &git_model.ProtectedTag{ AllowlistUserIDs: []int64{1}, AllowlistTeamIDs: []int64{1}, } - allowed, err = IsUserAllowedModifyTag(pt, 1) + allowed, err = git_model.IsUserAllowedModifyTag(pt, 1) assert.NoError(t, err) assert.True(t, allowed) - allowed, err = IsUserAllowedModifyTag(pt, 2) + allowed, err = git_model.IsUserAllowedModifyTag(pt, 2) assert.NoError(t, err) assert.True(t, allowed) } @@ -119,7 +120,7 @@ func TestIsUserAllowedToControlTag(t *testing.T) { } t.Run("Glob", func(t *testing.T) { - protectedTags := []*ProtectedTag{ + protectedTags := []*git_model.ProtectedTag{ { NamePattern: `*gitea`, AllowlistUserIDs: []int64{1}, @@ -134,14 +135,14 @@ func TestIsUserAllowedToControlTag(t *testing.T) { } for n, c := range cases { - isAllowed, err := IsUserAllowedToControlTag(protectedTags, c.name, c.userid) + isAllowed, err := git_model.IsUserAllowedToControlTag(protectedTags, c.name, c.userid) assert.NoError(t, err) assert.Equal(t, c.allowed, isAllowed, "case %d: error should match", n) } }) t.Run("Regex", func(t *testing.T) { - protectedTags := []*ProtectedTag{ + protectedTags := []*git_model.ProtectedTag{ { NamePattern: `/gitea\z/`, AllowlistUserIDs: []int64{1}, @@ -156,7 +157,7 @@ func TestIsUserAllowedToControlTag(t *testing.T) { } for n, c := range cases { - isAllowed, err := IsUserAllowedToControlTag(protectedTags, c.name, c.userid) + isAllowed, err := git_model.IsUserAllowedToControlTag(protectedTags, c.name, c.userid) assert.NoError(t, err) assert.Equal(t, c.allowed, isAllowed, "case %d: error should match", n) } diff --git a/models/issue_comment.go b/models/issue_comment.go index 90c95afa4e..21cd87108d 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -15,6 +15,7 @@ import ( "unicode/utf8" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" project_model "code.gitea.io/gitea/models/project" @@ -271,11 +272,11 @@ type Comment struct { RefIssue *Issue `xorm:"-"` RefComment *Comment `xorm:"-"` - Commits []*SignCommitWithStatuses `xorm:"-"` - OldCommit string `xorm:"-"` - NewCommit string `xorm:"-"` - CommitsNum int64 `xorm:"-"` - IsForcePush bool `xorm:"-"` + Commits []*git_model.SignCommitWithStatuses `xorm:"-"` + OldCommit string `xorm:"-"` + NewCommit string `xorm:"-"` + CommitsNum int64 `xorm:"-"` + IsForcePush bool `xorm:"-"` } func init() { @@ -761,7 +762,7 @@ func (c *Comment) LoadPushCommits(ctx context.Context) (err error) { } defer closer.Close() - c.Commits = ConvertFromGitCommit(gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo) + c.Commits = git_model.ConvertFromGitCommit(gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo) c.CommitsNum = int64(len(c.Commits)) } diff --git a/models/org_team.go b/models/org_team.go index f1d35ee189..7ff3095273 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -12,6 +12,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/organization" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" @@ -412,7 +413,7 @@ func DeleteTeam(t *organization.Team) error { // update branch protections { - protections := make([]*ProtectedBranch, 0, 10) + protections := make([]*git_model.ProtectedBranch, 0, 10) err := sess.In("repo_id", builder.Select("id").From("repository").Where(builder.Eq{"owner_id": t.OrgID})). Find(&protections) diff --git a/models/pull.go b/models/pull.go index df96e2dc74..238eb16636 100644 --- a/models/pull.go +++ b/models/pull.go @@ -12,6 +12,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" pull_model "code.gitea.io/gitea/models/pull" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" @@ -78,9 +79,9 @@ type PullRequest struct { HeadBranch string HeadCommitID string `xorm:"-"` BaseBranch string - ProtectedBranch *ProtectedBranch `xorm:"-"` - MergeBase string `xorm:"VARCHAR(40)"` - AllowMaintainerEdit bool `xorm:"NOT NULL DEFAULT false"` + ProtectedBranch *git_model.ProtectedBranch `xorm:"-"` + MergeBase string `xorm:"VARCHAR(40)"` + AllowMaintainerEdit bool `xorm:"NOT NULL DEFAULT false"` HasMerged bool `xorm:"INDEX"` MergedCommitID string `xorm:"VARCHAR(40)"` @@ -242,7 +243,7 @@ func (pr *PullRequest) LoadProtectedBranchCtx(ctx context.Context) (err error) { return } } - pr.ProtectedBranch, err = GetProtectedBranchBy(ctx, pr.BaseRepo.ID, pr.BaseBranch) + pr.ProtectedBranch, err = git_model.GetProtectedBranchBy(ctx, pr.BaseRepo.ID, pr.BaseBranch) } return } diff --git a/models/repo.go b/models/repo.go index fff9cc5271..a8aa18381d 100644 --- a/models/repo.go +++ b/models/repo.go @@ -15,6 +15,7 @@ import ( admin_model "code.gitea.io/gitea/models/admin" asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" @@ -34,9 +35,6 @@ import ( "xorm.io/builder" ) -// ItemsPerPage maximum items per page in forks, watchers and stars of a repo -var ItemsPerPage = 40 - // NewRepoContext creates a new repository context func NewRepoContext() { unit.LoadUnitConfig() @@ -284,16 +282,16 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error { &Action{RepoID: repo.ID}, &repo_model.Collaboration{RepoID: repoID}, &Comment{RefRepoID: repoID}, - &CommitStatus{RepoID: repoID}, - &DeletedBranch{RepoID: repoID}, + &git_model.CommitStatus{RepoID: repoID}, + &git_model.DeletedBranch{RepoID: repoID}, &webhook.HookTask{RepoID: repoID}, - &LFSLock{RepoID: repoID}, + &git_model.LFSLock{RepoID: repoID}, &repo_model.LanguageStat{RepoID: repoID}, &issues_model.Milestone{RepoID: repoID}, &repo_model.Mirror{RepoID: repoID}, &Notification{RepoID: repoID}, - &ProtectedBranch{RepoID: repoID}, - &ProtectedTag{RepoID: repoID}, + &git_model.ProtectedBranch{RepoID: repoID}, + &git_model.ProtectedTag{RepoID: repoID}, &repo_model.PushMirror{RepoID: repoID}, &Release{RepoID: repoID}, &repo_model.RepoIndexerStatus{RepoID: repoID}, @@ -357,14 +355,14 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error { } // Remove LFS objects - var lfsObjects []*LFSMetaObject + var lfsObjects []*git_model.LFSMetaObject if err = sess.Where("repository_id=?", repoID).Find(&lfsObjects); err != nil { return err } lfsPaths := make([]string, 0, len(lfsObjects)) for _, v := range lfsObjects { - count, err := db.CountByBean(ctx, &LFSMetaObject{Pointer: lfs.Pointer{Oid: v.Oid}}) + count, err := db.CountByBean(ctx, &git_model.LFSMetaObject{Pointer: lfs.Pointer{Oid: v.Oid}}) if err != nil { return err } @@ -375,7 +373,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error { lfsPaths = append(lfsPaths, v.RelativePath()) } - if _, err := db.DeleteByBean(ctx, &LFSMetaObject{RepositoryID: repoID}); err != nil { + if _, err := db.DeleteByBean(ctx, &git_model.LFSMetaObject{RepositoryID: repoID}); err != nil { return err } diff --git a/models/repo/collaboration.go b/models/repo/collaboration.go index 09397dd172..be05eba74c 100644 --- a/models/repo/collaboration.go +++ b/models/repo/collaboration.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" @@ -149,3 +150,23 @@ func ChangeCollaborationAccessMode(repo *Repository, uid int64, mode perm.Access return committer.Commit() } + +// IsOwnerMemberCollaborator checks if a provided user is the owner, a collaborator or a member of a team in a repository +func IsOwnerMemberCollaborator(repo *Repository, userID int64) (bool, error) { + if repo.OwnerID == userID { + return true, nil + } + teamMember, err := db.GetEngine(db.DefaultContext).Join("INNER", "team_repo", "team_repo.team_id = team_user.team_id"). + Join("INNER", "team_unit", "team_unit.team_id = team_user.team_id"). + Where("team_repo.repo_id = ?", repo.ID). + And("team_unit.`type` = ?", unit.TypeCode). + And("team_user.uid = ?", userID).Table("team_user").Exist() + if err != nil { + return false, err + } + if teamMember { + return true, nil + } + + return db.GetEngine(db.DefaultContext).Get(&Collaboration{RepoID: repo.ID, UserID: userID}) +} diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go index 8cbd836a42..7d43115b23 100644 --- a/models/repo_collaboration.go +++ b/models/repo_collaboration.go @@ -10,11 +10,9 @@ import ( "fmt" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "xorm.io/builder" @@ -120,23 +118,3 @@ func reconsiderWatches(ctx context.Context, repo *repo_model.Repository, uid int // Remove all IssueWatches a user has subscribed to in the repository return removeIssueWatchersByRepoID(ctx, uid, repo.ID) } - -// IsOwnerMemberCollaborator checks if a provided user is the owner, a collaborator or a member of a team in a repository -func IsOwnerMemberCollaborator(repo *repo_model.Repository, userID int64) (bool, error) { - if repo.OwnerID == userID { - return true, nil - } - teamMember, err := db.GetEngine(db.DefaultContext).Join("INNER", "team_repo", "team_repo.team_id = team_user.team_id"). - Join("INNER", "team_unit", "team_unit.team_id = team_user.team_id"). - Where("team_repo.repo_id = ?", repo.ID). - And("team_unit.`type` = ?", unit.TypeCode). - And("team_user.uid = ?", userID).Table("team_user").Exist(&organization.TeamUser{}) - if err != nil { - return false, err - } - if teamMember { - return true, nil - } - - return db.GetEngine(db.DefaultContext).Get(&repo_model.Collaboration{RepoID: repo.ID, UserID: userID}) -} diff --git a/models/review.go b/models/review.go index 296c9ce040..e92caba938 100644 --- a/models/review.go +++ b/models/review.go @@ -10,6 +10,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" @@ -234,7 +235,7 @@ func IsOfficialReviewer(ctx context.Context, issue *Issue, reviewers ...*user_mo } for _, reviewer := range reviewers { - official, err := isUserOfficialReviewer(ctx, pr.ProtectedBranch, reviewer) + official, err := git_model.IsUserOfficialReviewerCtx(ctx, pr.ProtectedBranch, reviewer) if official || err != nil { return official, err } diff --git a/models/user.go b/models/user.go index e8a412ca29..59ec643d55 100644 --- a/models/user.go +++ b/models/user.go @@ -15,6 +15,7 @@ import ( asymkey_model "code.gitea.io/gitea/models/asymkey" auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" access_model "code.gitea.io/gitea/models/perm/access" @@ -125,7 +126,7 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) { { const batchSize = 50 for start := 0; ; start += batchSize { - protections := make([]*ProtectedBranch, 0, batchSize) + protections := make([]*git_model.ProtectedBranch, 0, batchSize) // @perf: We can't filter on DB side by u.ID, as those IDs are serialized as JSON strings. // We could filter down with `WHERE repo_id IN (reposWithPushPermission(u))`, // though that query will be quite complex and tricky to maintain (compare `getRepoAssignees()`). |