From 751fe8b714f8ebd5fa915e893402776c4856d6c2 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 20 Dec 2024 10:05:29 -0800 Subject: [PATCH] Move some errors to their own sub packages (#32880) --- models/error.go | 510 --------------------- models/organization/org_user.go | 15 + models/packages/package.go | 15 + models/repo/repo.go | 17 +- models/user/user.go | 15 + modules/git/remote.go | 62 +++ routers/api/v1/admin/user.go | 14 +- routers/api/v1/repo/branch.go | 4 +- routers/api/v1/repo/file.go | 20 +- routers/api/v1/repo/migrate.go | 11 +- routers/api/v1/repo/mirror.go | 9 +- routers/api/v1/repo/patch.go | 8 +- routers/api/v1/repo/pull.go | 27 +- routers/api/v1/repo/release.go | 5 +- routers/api/v1/repo/release_tags.go | 7 +- routers/api/v1/repo/tag.go | 13 +- routers/private/hook_pre_receive.go | 7 +- routers/web/admin/users.go | 12 +- routers/web/org/setting.go | 6 +- routers/web/repo/branch.go | 10 +- routers/web/repo/cherry_pick.go | 5 +- routers/web/repo/editor.go | 29 +- routers/web/repo/migrate.go | 8 +- routers/web/repo/patch.go | 3 +- routers/web/repo/pull.go | 29 +- routers/web/repo/release.go | 29 +- routers/web/repo/setting/setting.go | 9 +- routers/web/user/setting/account.go | 12 +- services/forms/repo_form.go | 23 - services/migrations/migrate.go | 18 +- services/org/org.go | 5 +- services/org/org_test.go | 4 +- services/pull/check.go | 5 +- services/pull/merge.go | 119 ++++- services/pull/merge_prepare.go | 45 +- services/pull/patch.go | 28 +- services/pull/pull.go | 27 +- services/release/release.go | 68 ++- services/repository/branch.go | 4 +- services/repository/files/cherry_pick.go | 19 +- services/repository/files/content.go | 5 +- services/repository/files/file.go | 19 + services/repository/files/patch.go | 27 +- services/repository/files/temp_repo.go | 3 +- services/repository/files/tree.go | 23 +- services/repository/files/update.go | 108 ++++- services/user/update.go | 3 +- services/user/user.go | 11 +- services/user/user_test.go | 3 +- tests/integration/api_repo_file_helpers.go | 3 +- tests/integration/pull_merge_test.go | 25 +- tests/integration/repo_tag_test.go | 3 +- 52 files changed, 728 insertions(+), 781 deletions(-) delete mode 100644 models/error.go diff --git a/models/error.go b/models/error.go deleted file mode 100644 index 865d5a442f6..00000000000 --- a/models/error.go +++ /dev/null @@ -1,510 +0,0 @@ -// Copyright 2015 The Gogs Authors. All rights reserved. -// Copyright 2019 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package models - -import ( - "fmt" - - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/util" -) - -// ErrUserOwnRepos represents a "UserOwnRepos" kind of error. -type ErrUserOwnRepos struct { - UID int64 -} - -// IsErrUserOwnRepos checks if an error is a ErrUserOwnRepos. -func IsErrUserOwnRepos(err error) bool { - _, ok := err.(ErrUserOwnRepos) - return ok -} - -func (err ErrUserOwnRepos) Error() string { - return fmt.Sprintf("user still has ownership of repositories [uid: %d]", err.UID) -} - -// ErrUserHasOrgs represents a "UserHasOrgs" kind of error. -type ErrUserHasOrgs struct { - UID int64 -} - -// IsErrUserHasOrgs checks if an error is a ErrUserHasOrgs. -func IsErrUserHasOrgs(err error) bool { - _, ok := err.(ErrUserHasOrgs) - return ok -} - -func (err ErrUserHasOrgs) Error() string { - return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID) -} - -// ErrUserOwnPackages notifies that the user (still) owns the packages. -type ErrUserOwnPackages struct { - UID int64 -} - -// IsErrUserOwnPackages checks if an error is an ErrUserOwnPackages. -func IsErrUserOwnPackages(err error) bool { - _, ok := err.(ErrUserOwnPackages) - return ok -} - -func (err ErrUserOwnPackages) Error() string { - return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID) -} - -// ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error. -type ErrDeleteLastAdminUser struct { - UID int64 -} - -// IsErrDeleteLastAdminUser checks if an error is a ErrDeleteLastAdminUser. -func IsErrDeleteLastAdminUser(err error) bool { - _, ok := err.(ErrDeleteLastAdminUser) - return ok -} - -func (err ErrDeleteLastAdminUser) Error() string { - return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID) -} - -// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error. -type ErrInvalidCloneAddr struct { - Host string - IsURLError bool - IsInvalidPath bool - IsProtocolInvalid bool - IsPermissionDenied bool - LocalPath bool -} - -// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr. -func IsErrInvalidCloneAddr(err error) bool { - _, ok := err.(*ErrInvalidCloneAddr) - return ok -} - -func (err *ErrInvalidCloneAddr) Error() string { - if err.IsInvalidPath { - return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided path is invalid", err.Host) - } - if err.IsProtocolInvalid { - return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url protocol is not allowed", err.Host) - } - if err.IsPermissionDenied { - return fmt.Sprintf("migration/cloning from '%s' is not allowed.", err.Host) - } - if err.IsURLError { - return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url is invalid", err.Host) - } - - return fmt.Sprintf("migration/cloning from '%s' is not allowed", err.Host) -} - -func (err *ErrInvalidCloneAddr) Unwrap() error { - return util.ErrInvalidArgument -} - -// ErrUpdateTaskNotExist represents a "UpdateTaskNotExist" kind of error. -type ErrUpdateTaskNotExist struct { - UUID string -} - -// IsErrUpdateTaskNotExist checks if an error is a ErrUpdateTaskNotExist. -func IsErrUpdateTaskNotExist(err error) bool { - _, ok := err.(ErrUpdateTaskNotExist) - return ok -} - -func (err ErrUpdateTaskNotExist) Error() string { - return fmt.Sprintf("update task does not exist [uuid: %s]", err.UUID) -} - -func (err ErrUpdateTaskNotExist) Unwrap() error { - return util.ErrNotExist -} - -// ErrInvalidTagName represents a "InvalidTagName" kind of error. -type ErrInvalidTagName struct { - TagName string -} - -// IsErrInvalidTagName checks if an error is a ErrInvalidTagName. -func IsErrInvalidTagName(err error) bool { - _, ok := err.(ErrInvalidTagName) - return ok -} - -func (err ErrInvalidTagName) Error() string { - return fmt.Sprintf("release tag name is not valid [tag_name: %s]", err.TagName) -} - -func (err ErrInvalidTagName) Unwrap() error { - return util.ErrInvalidArgument -} - -// ErrProtectedTagName represents a "ProtectedTagName" kind of error. -type ErrProtectedTagName struct { - TagName string -} - -// IsErrProtectedTagName checks if an error is a ErrProtectedTagName. -func IsErrProtectedTagName(err error) bool { - _, ok := err.(ErrProtectedTagName) - return ok -} - -func (err ErrProtectedTagName) Error() string { - return fmt.Sprintf("release tag name is protected [tag_name: %s]", err.TagName) -} - -func (err ErrProtectedTagName) Unwrap() error { - return util.ErrPermissionDenied -} - -// ErrRepoFileAlreadyExists represents a "RepoFileAlreadyExist" kind of error. -type ErrRepoFileAlreadyExists struct { - Path string -} - -// IsErrRepoFileAlreadyExists checks if an error is a ErrRepoFileAlreadyExists. -func IsErrRepoFileAlreadyExists(err error) bool { - _, ok := err.(ErrRepoFileAlreadyExists) - return ok -} - -func (err ErrRepoFileAlreadyExists) Error() string { - return fmt.Sprintf("repository file already exists [path: %s]", err.Path) -} - -func (err ErrRepoFileAlreadyExists) Unwrap() error { - return util.ErrAlreadyExist -} - -// ErrRepoFileDoesNotExist represents a "RepoFileDoesNotExist" kind of error. -type ErrRepoFileDoesNotExist struct { - Path string - Name string -} - -// IsErrRepoFileDoesNotExist checks if an error is a ErrRepoDoesNotExist. -func IsErrRepoFileDoesNotExist(err error) bool { - _, ok := err.(ErrRepoFileDoesNotExist) - return ok -} - -func (err ErrRepoFileDoesNotExist) Error() string { - return fmt.Sprintf("repository file does not exist [path: %s]", err.Path) -} - -func (err ErrRepoFileDoesNotExist) Unwrap() error { - return util.ErrNotExist -} - -// ErrFilenameInvalid represents a "FilenameInvalid" kind of error. -type ErrFilenameInvalid struct { - Path string -} - -// IsErrFilenameInvalid checks if an error is an ErrFilenameInvalid. -func IsErrFilenameInvalid(err error) bool { - _, ok := err.(ErrFilenameInvalid) - return ok -} - -func (err ErrFilenameInvalid) Error() string { - return fmt.Sprintf("path contains a malformed path component [path: %s]", err.Path) -} - -func (err ErrFilenameInvalid) Unwrap() error { - return util.ErrInvalidArgument -} - -// ErrUserCannotCommit represents "UserCannotCommit" kind of error. -type ErrUserCannotCommit struct { - UserName string -} - -// IsErrUserCannotCommit checks if an error is an ErrUserCannotCommit. -func IsErrUserCannotCommit(err error) bool { - _, ok := err.(ErrUserCannotCommit) - return ok -} - -func (err ErrUserCannotCommit) Error() string { - return fmt.Sprintf("user cannot commit to repo [user: %s]", err.UserName) -} - -func (err ErrUserCannotCommit) Unwrap() error { - return util.ErrPermissionDenied -} - -// ErrFilePathInvalid represents a "FilePathInvalid" kind of error. -type ErrFilePathInvalid struct { - Message string - Path string - Name string - Type git.EntryMode -} - -// IsErrFilePathInvalid checks if an error is an ErrFilePathInvalid. -func IsErrFilePathInvalid(err error) bool { - _, ok := err.(ErrFilePathInvalid) - return ok -} - -func (err ErrFilePathInvalid) Error() string { - if err.Message != "" { - return err.Message - } - return fmt.Sprintf("path is invalid [path: %s]", err.Path) -} - -func (err ErrFilePathInvalid) Unwrap() error { - return util.ErrInvalidArgument -} - -// ErrFilePathProtected represents a "FilePathProtected" kind of error. -type ErrFilePathProtected struct { - Message string - Path string -} - -// IsErrFilePathProtected checks if an error is an ErrFilePathProtected. -func IsErrFilePathProtected(err error) bool { - _, ok := err.(ErrFilePathProtected) - return ok -} - -func (err ErrFilePathProtected) Error() string { - if err.Message != "" { - return err.Message - } - return fmt.Sprintf("path is protected and can not be changed [path: %s]", err.Path) -} - -func (err ErrFilePathProtected) Unwrap() error { - return util.ErrPermissionDenied -} - -// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it. -type ErrDisallowedToMerge struct { - Reason string -} - -// IsErrDisallowedToMerge checks if an error is an ErrDisallowedToMerge. -func IsErrDisallowedToMerge(err error) bool { - _, ok := err.(ErrDisallowedToMerge) - return ok -} - -func (err ErrDisallowedToMerge) Error() string { - return fmt.Sprintf("not allowed to merge [reason: %s]", err.Reason) -} - -func (err ErrDisallowedToMerge) Unwrap() error { - return util.ErrPermissionDenied -} - -// ErrTagAlreadyExists represents an error that tag with such name already exists. -type ErrTagAlreadyExists struct { - TagName string -} - -// IsErrTagAlreadyExists checks if an error is an ErrTagAlreadyExists. -func IsErrTagAlreadyExists(err error) bool { - _, ok := err.(ErrTagAlreadyExists) - return ok -} - -func (err ErrTagAlreadyExists) Error() string { - return fmt.Sprintf("tag already exists [name: %s]", err.TagName) -} - -func (err ErrTagAlreadyExists) Unwrap() error { - return util.ErrAlreadyExist -} - -// ErrSHADoesNotMatch represents a "SHADoesNotMatch" kind of error. -type ErrSHADoesNotMatch struct { - Path string - GivenSHA string - CurrentSHA string -} - -// IsErrSHADoesNotMatch checks if an error is a ErrSHADoesNotMatch. -func IsErrSHADoesNotMatch(err error) bool { - _, ok := err.(ErrSHADoesNotMatch) - return ok -} - -func (err ErrSHADoesNotMatch) Error() string { - return fmt.Sprintf("sha does not match [given: %s, expected: %s]", err.GivenSHA, err.CurrentSHA) -} - -// ErrSHANotFound represents a "SHADoesNotMatch" kind of error. -type ErrSHANotFound struct { - SHA string -} - -// IsErrSHANotFound checks if an error is a ErrSHANotFound. -func IsErrSHANotFound(err error) bool { - _, ok := err.(ErrSHANotFound) - return ok -} - -func (err ErrSHANotFound) Error() string { - return fmt.Sprintf("sha not found [%s]", err.SHA) -} - -func (err ErrSHANotFound) Unwrap() error { - return util.ErrNotExist -} - -// ErrCommitIDDoesNotMatch represents a "CommitIDDoesNotMatch" kind of error. -type ErrCommitIDDoesNotMatch struct { - GivenCommitID string - CurrentCommitID string -} - -// IsErrCommitIDDoesNotMatch checks if an error is a ErrCommitIDDoesNotMatch. -func IsErrCommitIDDoesNotMatch(err error) bool { - _, ok := err.(ErrCommitIDDoesNotMatch) - return ok -} - -func (err ErrCommitIDDoesNotMatch) Error() string { - return fmt.Sprintf("file CommitID does not match [given: %s, expected: %s]", err.GivenCommitID, err.CurrentCommitID) -} - -// ErrSHAOrCommitIDNotProvided represents a "SHAOrCommitIDNotProvided" kind of error. -type ErrSHAOrCommitIDNotProvided struct{} - -// IsErrSHAOrCommitIDNotProvided checks if an error is a ErrSHAOrCommitIDNotProvided. -func IsErrSHAOrCommitIDNotProvided(err error) bool { - _, ok := err.(ErrSHAOrCommitIDNotProvided) - return ok -} - -func (err ErrSHAOrCommitIDNotProvided) Error() string { - return "a SHA or commit ID must be proved when updating a file" -} - -// ErrInvalidMergeStyle represents an error if merging with disabled merge strategy -type ErrInvalidMergeStyle struct { - ID int64 - Style repo_model.MergeStyle -} - -// IsErrInvalidMergeStyle checks if an error is a ErrInvalidMergeStyle. -func IsErrInvalidMergeStyle(err error) bool { - _, ok := err.(ErrInvalidMergeStyle) - return ok -} - -func (err ErrInvalidMergeStyle) Error() string { - return fmt.Sprintf("merge strategy is not allowed or is invalid [repo_id: %d, strategy: %s]", - err.ID, err.Style) -} - -func (err ErrInvalidMergeStyle) Unwrap() error { - return util.ErrInvalidArgument -} - -// ErrMergeConflicts represents an error if merging fails with a conflict -type ErrMergeConflicts struct { - Style repo_model.MergeStyle - StdOut string - StdErr string - Err error -} - -// IsErrMergeConflicts checks if an error is a ErrMergeConflicts. -func IsErrMergeConflicts(err error) bool { - _, ok := err.(ErrMergeConflicts) - return ok -} - -func (err ErrMergeConflicts) Error() string { - return fmt.Sprintf("Merge Conflict Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut) -} - -// ErrMergeUnrelatedHistories represents an error if merging fails due to unrelated histories -type ErrMergeUnrelatedHistories struct { - Style repo_model.MergeStyle - StdOut string - StdErr string - Err error -} - -// IsErrMergeUnrelatedHistories checks if an error is a ErrMergeUnrelatedHistories. -func IsErrMergeUnrelatedHistories(err error) bool { - _, ok := err.(ErrMergeUnrelatedHistories) - return ok -} - -func (err ErrMergeUnrelatedHistories) Error() string { - return fmt.Sprintf("Merge UnrelatedHistories Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut) -} - -// ErrMergeDivergingFastForwardOnly represents an error if a fast-forward-only merge fails because the branches diverge -type ErrMergeDivergingFastForwardOnly struct { - StdOut string - StdErr string - Err error -} - -// IsErrMergeDivergingFastForwardOnly checks if an error is a ErrMergeDivergingFastForwardOnly. -func IsErrMergeDivergingFastForwardOnly(err error) bool { - _, ok := err.(ErrMergeDivergingFastForwardOnly) - return ok -} - -func (err ErrMergeDivergingFastForwardOnly) Error() string { - return fmt.Sprintf("Merge DivergingFastForwardOnly Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut) -} - -// ErrRebaseConflicts represents an error if rebase fails with a conflict -type ErrRebaseConflicts struct { - Style repo_model.MergeStyle - CommitSHA string - StdOut string - StdErr string - Err error -} - -// IsErrRebaseConflicts checks if an error is a ErrRebaseConflicts. -func IsErrRebaseConflicts(err error) bool { - _, ok := err.(ErrRebaseConflicts) - return ok -} - -func (err ErrRebaseConflicts) Error() string { - return fmt.Sprintf("Rebase Error: %v: Whilst Rebasing: %s\n%s\n%s", err.Err, err.CommitSHA, err.StdErr, err.StdOut) -} - -// ErrPullRequestHasMerged represents a "PullRequestHasMerged"-error -type ErrPullRequestHasMerged struct { - ID int64 - IssueID int64 - HeadRepoID int64 - BaseRepoID int64 - HeadBranch string - BaseBranch string -} - -// IsErrPullRequestHasMerged checks if an error is a ErrPullRequestHasMerged. -func IsErrPullRequestHasMerged(err error) bool { - _, ok := err.(ErrPullRequestHasMerged) - return ok -} - -// Error does pretty-printing :D -func (err ErrPullRequestHasMerged) Error() string { - return fmt.Sprintf("pull request has merged [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]", - err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch) -} diff --git a/models/organization/org_user.go b/models/organization/org_user.go index 1d3b2fab44d..08d936d922f 100644 --- a/models/organization/org_user.go +++ b/models/organization/org_user.go @@ -36,6 +36,21 @@ func init() { db.RegisterModel(new(OrgUser)) } +// ErrUserHasOrgs represents a "UserHasOrgs" kind of error. +type ErrUserHasOrgs struct { + UID int64 +} + +// IsErrUserHasOrgs checks if an error is a ErrUserHasOrgs. +func IsErrUserHasOrgs(err error) bool { + _, ok := err.(ErrUserHasOrgs) + return ok +} + +func (err ErrUserHasOrgs) Error() string { + return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID) +} + // GetOrganizationCount returns count of membership of organization of the user. func GetOrganizationCount(ctx context.Context, u *user_model.User) (int64, error) { return db.GetEngine(ctx). diff --git a/models/packages/package.go b/models/packages/package.go index 417d62d1993..c12f345f0e2 100644 --- a/models/packages/package.go +++ b/models/packages/package.go @@ -301,6 +301,21 @@ func FindUnreferencedPackages(ctx context.Context) ([]*Package, error) { Find(&ps) } +// ErrUserOwnPackages notifies that the user (still) owns the packages. +type ErrUserOwnPackages struct { + UID int64 +} + +// IsErrUserOwnPackages checks if an error is an ErrUserOwnPackages. +func IsErrUserOwnPackages(err error) bool { + _, ok := err.(ErrUserOwnPackages) + return ok +} + +func (err ErrUserOwnPackages) Error() string { + return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID) +} + // HasOwnerPackages tests if a user/org has accessible packages func HasOwnerPackages(ctx context.Context, ownerID int64) (bool, error) { return db.GetEngine(ctx). diff --git a/models/repo/repo.go b/models/repo/repo.go index 8d211caf40b..2d9b9de88d4 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -37,7 +37,7 @@ type ErrUserDoesNotHaveAccessToRepo struct { RepoName string } -// IsErrUserDoesNotHaveAccessToRepo checks if an error is a ErrRepoFileAlreadyExists. +// IsErrUserDoesNotHaveAccessToRepo checks if an error is a ErrUserDoesNotHaveAccessToRepo. func IsErrUserDoesNotHaveAccessToRepo(err error) bool { _, ok := err.(ErrUserDoesNotHaveAccessToRepo) return ok @@ -866,6 +866,21 @@ func (repo *Repository) TemplateRepo(ctx context.Context) *Repository { return repo } +// ErrUserOwnRepos represents a "UserOwnRepos" kind of error. +type ErrUserOwnRepos struct { + UID int64 +} + +// IsErrUserOwnRepos checks if an error is a ErrUserOwnRepos. +func IsErrUserOwnRepos(err error) bool { + _, ok := err.(ErrUserOwnRepos) + return ok +} + +func (err ErrUserOwnRepos) Error() string { + return fmt.Sprintf("user still has ownership of repositories [uid: %d]", err.UID) +} + type CountRepositoryOptions struct { OwnerID int64 Private optional.Option[bool] diff --git a/models/user/user.go b/models/user/user.go index bd92693b6e0..72caafc3baa 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -788,6 +788,21 @@ func createUser(ctx context.Context, u *User, meta *Meta, createdByAdmin bool, o return committer.Commit() } +// ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error. +type ErrDeleteLastAdminUser struct { + UID int64 +} + +// IsErrDeleteLastAdminUser checks if an error is a ErrDeleteLastAdminUser. +func IsErrDeleteLastAdminUser(err error) bool { + _, ok := err.(ErrDeleteLastAdminUser) + return ok +} + +func (err ErrDeleteLastAdminUser) Error() string { + return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID) +} + // IsLastAdminUser check whether user is the last admin func IsLastAdminUser(ctx context.Context, user *User) bool { if user.IsAdmin && CountUsers(ctx, &CountUserFilter{IsAdmin: optional.Some(true)}) <= 1 { diff --git a/modules/git/remote.go b/modules/git/remote.go index 7b10e6b6631..de8d74eded7 100644 --- a/modules/git/remote.go +++ b/modules/git/remote.go @@ -5,8 +5,12 @@ package git import ( "context" + "fmt" + "net/url" + "strings" giturl "code.gitea.io/gitea/modules/git/url" + "code.gitea.io/gitea/modules/util" ) // GetRemoteAddress returns remote url of git repository in the repoPath with special remote name @@ -37,3 +41,61 @@ func GetRemoteURL(ctx context.Context, repoPath, remoteName string) (*giturl.Git } return giturl.Parse(addr) } + +// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error. +type ErrInvalidCloneAddr struct { + Host string + IsURLError bool + IsInvalidPath bool + IsProtocolInvalid bool + IsPermissionDenied bool + LocalPath bool +} + +// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr. +func IsErrInvalidCloneAddr(err error) bool { + _, ok := err.(*ErrInvalidCloneAddr) + return ok +} + +func (err *ErrInvalidCloneAddr) Error() string { + if err.IsInvalidPath { + return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided path is invalid", err.Host) + } + if err.IsProtocolInvalid { + return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url protocol is not allowed", err.Host) + } + if err.IsPermissionDenied { + return fmt.Sprintf("migration/cloning from '%s' is not allowed.", err.Host) + } + if err.IsURLError { + return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url is invalid", err.Host) + } + + return fmt.Sprintf("migration/cloning from '%s' is not allowed", err.Host) +} + +func (err *ErrInvalidCloneAddr) Unwrap() error { + return util.ErrInvalidArgument +} + +// ParseRemoteAddr checks if given remote address is valid, +// and returns composed URL with needed username and password. +func ParseRemoteAddr(remoteAddr, authUsername, authPassword string) (string, error) { + remoteAddr = strings.TrimSpace(remoteAddr) + // Remote address can be HTTP/HTTPS/Git URL or local path. + if strings.HasPrefix(remoteAddr, "http://") || + strings.HasPrefix(remoteAddr, "https://") || + strings.HasPrefix(remoteAddr, "git://") { + u, err := url.Parse(remoteAddr) + if err != nil { + return "", &ErrInvalidCloneAddr{IsURLError: true, Host: remoteAddr} + } + if len(authUsername)+len(authPassword) > 0 { + u.User = url.UserPassword(authUsername, authPassword) + } + remoteAddr = u.String() + } + + return remoteAddr, nil +} diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index b0f40084da3..3f4a73dcad5 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -9,10 +9,12 @@ import ( "fmt" "net/http" - "code.gitea.io/gitea/models" asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" + org_model "code.gitea.io/gitea/models/organization" + packages_model "code.gitea.io/gitea/models/packages" + repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/log" @@ -247,7 +249,7 @@ func EditUser(ctx *context.APIContext) { } if err := user_service.UpdateUser(ctx, ctx.ContextUser, opts); err != nil { - if models.IsErrDeleteLastAdminUser(err) { + if user_model.IsErrDeleteLastAdminUser(err) { ctx.Error(http.StatusBadRequest, "LastAdmin", err) } else { ctx.Error(http.StatusInternalServerError, "UpdateUser", err) @@ -299,10 +301,10 @@ func DeleteUser(ctx *context.APIContext) { } if err := user_service.DeleteUser(ctx, ctx.ContextUser, ctx.FormBool("purge")); err != nil { - if models.IsErrUserOwnRepos(err) || - models.IsErrUserHasOrgs(err) || - models.IsErrUserOwnPackages(err) || - models.IsErrDeleteLastAdminUser(err) { + if repo_model.IsErrUserOwnRepos(err) || + org_model.IsErrUserHasOrgs(err) || + packages_model.IsErrUserOwnPackages(err) || + user_model.IsErrDeleteLastAdminUser(err) { ctx.Error(http.StatusUnprocessableEntity, "", err) } else { ctx.Error(http.StatusInternalServerError, "DeleteUser", err) diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 946203e97ec..9a31aec3144 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -9,7 +9,6 @@ import ( "fmt" "net/http" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/organization" @@ -24,6 +23,7 @@ import ( "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" pull_service "code.gitea.io/gitea/services/pull" + release_service "code.gitea.io/gitea/services/release" repo_service "code.gitea.io/gitea/services/repository" ) @@ -247,7 +247,7 @@ func CreateBranch(ctx *context.APIContext) { if err != nil { if git_model.IsErrBranchNotExist(err) { ctx.Error(http.StatusNotFound, "", "The old branch does not exist") - } else if models.IsErrTagAlreadyExists(err) { + } else if release_service.IsErrTagAlreadyExists(err) { ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.") } else if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) { ctx.Error(http.StatusConflict, "", "The branch already exists.") diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 959a4b952a1..83848b7add5 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -15,7 +15,6 @@ import ( "strings" "time" - "code.gitea.io/gitea/models" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" @@ -30,6 +29,7 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/context" + pull_service "code.gitea.io/gitea/services/pull" archiver_service "code.gitea.io/gitea/services/repository/archiver" files_service "code.gitea.io/gitea/services/repository/files" ) @@ -736,12 +736,12 @@ func UpdateFile(ctx *context.APIContext) { } func handleCreateOrUpdateFileError(ctx *context.APIContext, err error) { - if models.IsErrUserCannotCommit(err) || models.IsErrFilePathProtected(err) { + if files_service.IsErrUserCannotCommit(err) || pull_service.IsErrFilePathProtected(err) { ctx.Error(http.StatusForbidden, "Access", err) return } - if git_model.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) || - models.IsErrFilePathInvalid(err) || models.IsErrRepoFileAlreadyExists(err) { + if git_model.IsErrBranchAlreadyExists(err) || files_service.IsErrFilenameInvalid(err) || pull_service.IsErrSHADoesNotMatch(err) || + files_service.IsErrFilePathInvalid(err) || files_service.IsErrRepoFileAlreadyExists(err) { ctx.Error(http.StatusUnprocessableEntity, "Invalid", err) return } @@ -887,17 +887,17 @@ func DeleteFile(ctx *context.APIContext) { } if filesResponse, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, opts); err != nil { - if git.IsErrBranchNotExist(err) || models.IsErrRepoFileDoesNotExist(err) || git.IsErrNotExist(err) { + if git.IsErrBranchNotExist(err) || files_service.IsErrRepoFileDoesNotExist(err) || git.IsErrNotExist(err) { ctx.Error(http.StatusNotFound, "DeleteFile", err) return } else if git_model.IsErrBranchAlreadyExists(err) || - models.IsErrFilenameInvalid(err) || - models.IsErrSHADoesNotMatch(err) || - models.IsErrCommitIDDoesNotMatch(err) || - models.IsErrSHAOrCommitIDNotProvided(err) { + files_service.IsErrFilenameInvalid(err) || + pull_service.IsErrSHADoesNotMatch(err) || + files_service.IsErrCommitIDDoesNotMatch(err) || + files_service.IsErrSHAOrCommitIDNotProvided(err) { ctx.Error(http.StatusBadRequest, "DeleteFile", err) return - } else if models.IsErrUserCannotCommit(err) { + } else if files_service.IsErrUserCannotCommit(err) { ctx.Error(http.StatusForbidden, "DeleteFile", err) return } diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index dcbd8f3dd50..452825c0a3e 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -10,13 +10,13 @@ import ( "net/http" "strings" - "code.gitea.io/gitea/models" "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" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" @@ -27,7 +27,6 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" - "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/migrations" notify_service "code.gitea.io/gitea/services/notify" repo_service "code.gitea.io/gitea/services/repository" @@ -104,7 +103,7 @@ func Migrate(ctx *context.APIContext) { } } - remoteAddr, err := forms.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword) + remoteAddr, err := git.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword) if err == nil { err = migrations.IsMigrateURLAllowed(remoteAddr, ctx.Doer) } @@ -237,7 +236,7 @@ func handleMigrateError(ctx *context.APIContext, repoOwner *user_model.User, err ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' contains invalid characters.", err.(db.ErrNameCharsNotAllowed).Name)) case db.IsErrNamePatternNotAllowed(err): ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(db.ErrNamePatternNotAllowed).Pattern)) - case models.IsErrInvalidCloneAddr(err): + case git.IsErrInvalidCloneAddr(err): ctx.Error(http.StatusUnprocessableEntity, "", err) case base.IsErrNotSupported(err): ctx.Error(http.StatusUnprocessableEntity, "", err) @@ -256,8 +255,8 @@ func handleMigrateError(ctx *context.APIContext, repoOwner *user_model.User, err } func handleRemoteAddrError(ctx *context.APIContext, err error) { - if models.IsErrInvalidCloneAddr(err) { - addrErr := err.(*models.ErrInvalidCloneAddr) + if git.IsErrInvalidCloneAddr(err) { + addrErr := err.(*git.ErrInvalidCloneAddr) switch { case addrErr.IsURLError: ctx.Error(http.StatusUnprocessableEntity, "", err) diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go index 310b82881ea..047203501e7 100644 --- a/routers/api/v1/repo/mirror.go +++ b/routers/api/v1/repo/mirror.go @@ -9,10 +9,10 @@ import ( "net/http" "time" - "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" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -20,7 +20,6 @@ import ( "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" - "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/migrations" mirror_service "code.gitea.io/gitea/services/mirror" ) @@ -344,7 +343,7 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro return } - address, err := forms.ParseRemoteAddr(mirrorOption.RemoteAddress, mirrorOption.RemoteUsername, mirrorOption.RemotePassword) + address, err := git.ParseRemoteAddr(mirrorOption.RemoteAddress, mirrorOption.RemoteUsername, mirrorOption.RemotePassword) if err == nil { err = migrations.IsMigrateURLAllowed(address, ctx.ContextUser) } @@ -397,8 +396,8 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro } func HandleRemoteAddressError(ctx *context.APIContext, err error) { - if models.IsErrInvalidCloneAddr(err) { - addrErr := err.(*models.ErrInvalidCloneAddr) + if git.IsErrInvalidCloneAddr(err) { + addrErr := err.(*git.ErrInvalidCloneAddr) switch { case addrErr.IsProtocolInvalid: ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid mirror protocol") diff --git a/routers/api/v1/repo/patch.go b/routers/api/v1/repo/patch.go index 0e0601b7d93..5e24dcf8912 100644 --- a/routers/api/v1/repo/patch.go +++ b/routers/api/v1/repo/patch.go @@ -7,13 +7,13 @@ import ( "net/http" "time" - "code.gitea.io/gitea/models" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/context" + pull_service "code.gitea.io/gitea/services/pull" "code.gitea.io/gitea/services/repository/files" ) @@ -92,12 +92,12 @@ func ApplyDiffPatch(ctx *context.APIContext) { fileResponse, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, opts) if err != nil { - if models.IsErrUserCannotCommit(err) || models.IsErrFilePathProtected(err) { + if files.IsErrUserCannotCommit(err) || pull_service.IsErrFilePathProtected(err) { ctx.Error(http.StatusForbidden, "Access", err) return } - if git_model.IsErrBranchAlreadyExists(err) || models.IsErrFilenameInvalid(err) || models.IsErrSHADoesNotMatch(err) || - models.IsErrFilePathInvalid(err) || models.IsErrRepoFileAlreadyExists(err) { + if git_model.IsErrBranchAlreadyExists(err) || files.IsErrFilenameInvalid(err) || pull_service.IsErrSHADoesNotMatch(err) || + files.IsErrFilePathInvalid(err) || files.IsErrRepoFileAlreadyExists(err) { ctx.Error(http.StatusUnprocessableEntity, "Invalid", err) return } diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 6f4f3efaa10..71c4c81b677 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -12,7 +12,6 @@ import ( "strings" "time" - "code.gitea.io/gitea/models" activities_model "code.gitea.io/gitea/models/activities" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" @@ -765,7 +764,7 @@ func EditPullRequest(ctx *context.APIContext) { } else if issues_model.IsErrIssueIsClosed(err) { ctx.Error(http.StatusUnprocessableEntity, "IsErrIssueIsClosed", err) return - } else if models.IsErrPullRequestHasMerged(err) { + } else if pull_service.IsErrPullRequestHasMerged(err) { ctx.Error(http.StatusConflict, "IsErrPullRequestHasMerged", err) return } @@ -941,7 +940,7 @@ func MergePullRequest(ctx *context.APIContext) { ctx.Error(http.StatusMethodNotAllowed, "PR is a work in progress", "Work in progress PRs cannot be merged") } else if errors.Is(err, pull_service.ErrNotMergeableState) { ctx.Error(http.StatusMethodNotAllowed, "PR not in mergeable state", "Please try again later") - } else if models.IsErrDisallowedToMerge(err) { + } else if pull_service.IsErrDisallowedToMerge(err) { ctx.Error(http.StatusMethodNotAllowed, "PR is not ready to be merged", err) } else if asymkey_service.IsErrWontSign(err) { ctx.Error(http.StatusMethodNotAllowed, fmt.Sprintf("Protected branch %s requires signed commits but this merge would not be signed", pr.BaseBranch), err) @@ -954,7 +953,7 @@ func MergePullRequest(ctx *context.APIContext) { // handle manually-merged mark if manuallyMerged { if err := pull_service.MergedManually(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, form.MergeCommitID); err != nil { - if models.IsErrInvalidMergeStyle(err) { + if pull_service.IsErrInvalidMergeStyle(err) { ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do))) return } @@ -1004,20 +1003,20 @@ func MergePullRequest(ctx *context.APIContext) { } if err := pull_service.Merge(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message, false); err != nil { - if models.IsErrInvalidMergeStyle(err) { + if pull_service.IsErrInvalidMergeStyle(err) { ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do))) - } else if models.IsErrMergeConflicts(err) { - conflictError := err.(models.ErrMergeConflicts) + } else if pull_service.IsErrMergeConflicts(err) { + conflictError := err.(pull_service.ErrMergeConflicts) ctx.JSON(http.StatusConflict, conflictError) - } else if models.IsErrRebaseConflicts(err) { - conflictError := err.(models.ErrRebaseConflicts) + } else if pull_service.IsErrRebaseConflicts(err) { + conflictError := err.(pull_service.ErrRebaseConflicts) ctx.JSON(http.StatusConflict, conflictError) - } else if models.IsErrMergeUnrelatedHistories(err) { - conflictError := err.(models.ErrMergeUnrelatedHistories) + } else if pull_service.IsErrMergeUnrelatedHistories(err) { + conflictError := err.(pull_service.ErrMergeUnrelatedHistories) ctx.JSON(http.StatusConflict, conflictError) } else if git.IsErrPushOutOfDate(err) { ctx.Error(http.StatusConflict, "Merge", "merge push out of date") - } else if models.IsErrSHADoesNotMatch(err) { + } else if pull_service.IsErrSHADoesNotMatch(err) { ctx.Error(http.StatusConflict, "Merge", "head out of date") } else if git.IsErrPushRejected(err) { errPushRej := err.(*git.ErrPushRejected) @@ -1308,10 +1307,10 @@ func UpdatePullRequest(ctx *context.APIContext) { message := fmt.Sprintf("Merge branch '%s' into %s", pr.BaseBranch, pr.HeadBranch) if err = pull_service.Update(ctx, pr, ctx.Doer, message, rebase); err != nil { - if models.IsErrMergeConflicts(err) { + if pull_service.IsErrMergeConflicts(err) { ctx.Error(http.StatusConflict, "Update", "merge failed because of conflict") return - } else if models.IsErrRebaseConflicts(err) { + } else if pull_service.IsErrRebaseConflicts(err) { ctx.Error(http.StatusConflict, "Update", "rebase failed because of conflict") return } diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 478bdbd797a..141f8121729 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -7,7 +7,6 @@ import ( "fmt" "net/http" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" @@ -250,7 +249,7 @@ func CreateRelease(ctx *context.APIContext) { if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, ""); err != nil { if repo_model.IsErrReleaseAlreadyExist(err) { ctx.Error(http.StatusConflict, "ReleaseAlreadyExist", err) - } else if models.IsErrProtectedTagName(err) { + } else if release_service.IsErrProtectedTagName(err) { ctx.Error(http.StatusUnprocessableEntity, "ProtectedTagName", err) } else if git.IsErrNotExist(err) { ctx.Error(http.StatusNotFound, "ErrNotExist", fmt.Errorf("target \"%v\" not found: %w", rel.Target, err)) @@ -408,7 +407,7 @@ func DeleteRelease(ctx *context.APIContext) { return } if err := release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, false); err != nil { - if models.IsErrProtectedTagName(err) { + if release_service.IsErrProtectedTagName(err) { ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag") return } diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go index 6df47af8d98..99f7a8cbf21 100644 --- a/routers/api/v1/repo/release_tags.go +++ b/routers/api/v1/repo/release_tags.go @@ -6,11 +6,10 @@ package repo import ( "net/http" - "code.gitea.io/gitea/models" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" - releaseservice "code.gitea.io/gitea/services/release" + release_service "code.gitea.io/gitea/services/release" ) // GetReleaseByTag get a single release of a repository by tag name @@ -112,8 +111,8 @@ func DeleteReleaseByTag(ctx *context.APIContext) { return } - if err = releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, release, ctx.Doer, false); err != nil { - if models.IsErrProtectedTagName(err) { + if err = release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, release, ctx.Doer, false); err != nil { + if release_service.IsErrProtectedTagName(err) { ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag") return } diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index a72df78666e..fe0910c735a 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -9,7 +9,6 @@ import ( "net/http" "strings" - "code.gitea.io/gitea/models" git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/organization" repo_model "code.gitea.io/gitea/models/repo" @@ -19,7 +18,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" - releaseservice "code.gitea.io/gitea/services/release" + release_service "code.gitea.io/gitea/services/release" ) // ListTags list all the tags of a repository @@ -205,12 +204,12 @@ func CreateTag(ctx *context.APIContext) { return } - if err := releaseservice.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, commit.ID.String(), form.TagName, form.Message); err != nil { - if models.IsErrTagAlreadyExists(err) { + if err := release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, commit.ID.String(), form.TagName, form.Message); err != nil { + if release_service.IsErrTagAlreadyExists(err) { ctx.Error(http.StatusConflict, "tag exist", err) return } - if models.IsErrProtectedTagName(err) { + if release_service.IsErrProtectedTagName(err) { ctx.Error(http.StatusUnprocessableEntity, "CreateNewTag", "user not allowed to create protected tag") return } @@ -280,8 +279,8 @@ func DeleteTag(ctx *context.APIContext) { return } - if err = releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, tag, ctx.Doer, true); err != nil { - if models.IsErrProtectedTagName(err) { + if err = release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, tag, ctx.Doer, true); err != nil { + if release_service.IsErrProtectedTagName(err) { ctx.Error(http.StatusUnprocessableEntity, "delTag", "user not allowed to delete protected tag") return } diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 73fe9b886cf..eb7bb2b480a 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -8,7 +8,6 @@ import ( "net/http" "os" - "code.gitea.io/gitea/models" asymkey_model "code.gitea.io/gitea/models/asymkey" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" @@ -237,7 +236,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r if len(globs) > 0 { _, err := pull_service.CheckFileProtection(gitRepo, branchName, oldCommitID, newCommitID, globs, 1, ctx.env) if err != nil { - if !models.IsErrFilePathProtected(err) { + if !pull_service.IsErrFilePathProtected(err) { log.Error("Unable to check file protection for commits from %s to %s in %-v: %v", oldCommitID, newCommitID, repo, err) ctx.JSON(http.StatusInternalServerError, private.Response{ Err: fmt.Sprintf("Unable to check file protection for commits from %s to %s: %v", oldCommitID, newCommitID, err), @@ -246,7 +245,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r } changedProtectedfiles = true - protectedFilePath = err.(models.ErrFilePathProtected).Path + protectedFilePath = err.(pull_service.ErrFilePathProtected).Path } } @@ -374,7 +373,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r // Check all status checks and reviews are ok if err := pull_service.CheckPullBranchProtections(ctx, pr, true); err != nil { - if models.IsErrDisallowedToMerge(err) { + if pull_service.IsErrDisallowedToMerge(err) { log.Warn("Forbidden: User %d is not allowed push to protected branch %s in %-v and pr #%d is not ready to be merged: %s", ctx.opts.UserID, branchName, repo, pr.Index, err.Error()) ctx.JSON(http.StatusForbidden, private.Response{ UserMsg: fmt.Sprintf("Not allowed to push to protected branch %s and pr #%d is not ready to be merged: %s", branchName, ctx.opts.PullRequestID, err.Error()), diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index a6b0b5c78bb..1eaa37bfbde 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -11,10 +11,10 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" org_model "code.gitea.io/gitea/models/organization" + packages_model "code.gitea.io/gitea/models/packages" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" @@ -446,7 +446,7 @@ func EditUserPost(ctx *context.Context) { } if err := user_service.UpdateUser(ctx, u, opts); err != nil { - if models.IsErrDeleteLastAdminUser(err) { + if user_model.IsErrDeleteLastAdminUser(err) { ctx.RenderWithErr(ctx.Tr("auth.last_admin"), tplUserEdit, &form) } else { ctx.ServerError("UpdateUser", err) @@ -501,16 +501,16 @@ func DeleteUser(ctx *context.Context) { if err = user_service.DeleteUser(ctx, u, ctx.FormBool("purge")); err != nil { switch { - case models.IsErrUserOwnRepos(err): + case repo_model.IsErrUserOwnRepos(err): ctx.Flash.Error(ctx.Tr("admin.users.still_own_repo")) ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid"))) - case models.IsErrUserHasOrgs(err): + case org_model.IsErrUserHasOrgs(err): ctx.Flash.Error(ctx.Tr("admin.users.still_has_org")) ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid"))) - case models.IsErrUserOwnPackages(err): + case packages_model.IsErrUserOwnPackages(err): ctx.Flash.Error(ctx.Tr("admin.users.still_own_packages")) ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid"))) - case models.IsErrDeleteLastAdminUser(err): + case user_model.IsErrDeleteLastAdminUser(err): ctx.Flash.Error(ctx.Tr("auth.last_admin")) ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid"))) default: diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 494ada4323a..551019c7170 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -8,8 +8,8 @@ import ( "net/http" "net/url" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" + packages_model "code.gitea.io/gitea/models/packages" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" @@ -178,10 +178,10 @@ func SettingsDelete(ctx *context.Context) { } if err := org_service.DeleteOrganization(ctx, ctx.Org.Organization, false); err != nil { - if models.IsErrUserOwnRepos(err) { + if repo_model.IsErrUserOwnRepos(err) { ctx.Flash.Error(ctx.Tr("form.org_still_own_repo")) ctx.Redirect(ctx.Org.OrgLink + "/settings/delete") - } else if models.IsErrUserOwnPackages(err) { + } else if packages_model.IsErrUserOwnPackages(err) { ctx.Flash.Error(ctx.Tr("form.org_still_own_packages")) ctx.Redirect(ctx.Org.OrgLink + "/settings/delete") } else { diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index c918cd7a72f..3e1c6fc61cb 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -11,7 +11,6 @@ import ( "net/url" "strings" - "code.gitea.io/gitea/models" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" @@ -26,6 +25,7 @@ import ( "code.gitea.io/gitea/routers/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" + pull_service "code.gitea.io/gitea/services/pull" release_service "code.gitea.io/gitea/services/release" repo_service "code.gitea.io/gitea/services/repository" ) @@ -203,14 +203,14 @@ func CreateBranch(ctx *context.Context) { err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.CommitID, form.NewBranchName) } if err != nil { - if models.IsErrProtectedTagName(err) { + if release_service.IsErrProtectedTagName(err) { ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected")) ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) return } - if models.IsErrTagAlreadyExists(err) { - e := err.(models.ErrTagAlreadyExists) + if release_service.IsErrTagAlreadyExists(err) { + e := err.(release_service.ErrTagAlreadyExists) ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName)) ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) return @@ -267,7 +267,7 @@ func MergeUpstream(ctx *context.Context) { if errors.Is(err, util.ErrNotExist) { ctx.JSONError(ctx.Tr("error.not_found")) return - } else if models.IsErrMergeConflicts(err) { + } else if pull_service.IsErrMergeConflicts(err) { ctx.JSONError(ctx.Tr("repo.pulls.merge_conflict")) return } diff --git a/routers/web/repo/cherry_pick.go b/routers/web/repo/cherry_pick.go index 61aff78d498..71671ec628d 100644 --- a/routers/web/repo/cherry_pick.go +++ b/routers/web/repo/cherry_pick.go @@ -8,7 +8,6 @@ import ( "errors" "strings" - "code.gitea.io/gitea/models" git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" @@ -131,7 +130,7 @@ func CherryPickPost(ctx *context.Context) { ctx.Data["Err_NewBranchName"] = true ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form) return - } else if models.IsErrCommitIDDoesNotMatch(err) { + } else if files.IsErrCommitIDDoesNotMatch(err) { ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form) return } @@ -168,7 +167,7 @@ func CherryPickPost(ctx *context.Context) { ctx.Data["Err_NewBranchName"] = true ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form) return - } else if models.IsErrCommitIDDoesNotMatch(err) { + } else if files.IsErrCommitIDDoesNotMatch(err) { ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form) return } diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 9396115b0d2..afc69bffdfd 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -10,7 +10,6 @@ import ( "path" "strings" - "code.gitea.io/gitea/models" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" @@ -303,12 +302,12 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b } else if git_model.IsErrLFSFileLocked(err) { ctx.Data["Err_TreePath"] = true ctx.RenderWithErr(ctx.Tr("repo.editor.upload_file_is_locked", err.(git_model.ErrLFSFileLocked).Path, err.(git_model.ErrLFSFileLocked).UserName), tplEditFile, &form) - } else if models.IsErrFilenameInvalid(err) { + } else if files_service.IsErrFilenameInvalid(err) { ctx.Data["Err_TreePath"] = true ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", form.TreePath), tplEditFile, &form) - } else if models.IsErrFilePathInvalid(err) { + } else if files_service.IsErrFilePathInvalid(err) { ctx.Data["Err_TreePath"] = true - if fileErr, ok := err.(models.ErrFilePathInvalid); ok { + if fileErr, ok := err.(files_service.ErrFilePathInvalid); ok { switch fileErr.Type { case git.EntryModeSymlink: ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplEditFile, &form) @@ -322,7 +321,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b } else { ctx.Error(http.StatusInternalServerError, err.Error()) } - } else if models.IsErrRepoFileAlreadyExists(err) { + } else if files_service.IsErrRepoFileAlreadyExists(err) { ctx.Data["Err_TreePath"] = true ctx.RenderWithErr(ctx.Tr("repo.editor.file_already_exists", form.TreePath), tplEditFile, &form) } else if git.IsErrBranchNotExist(err) { @@ -340,7 +339,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b } else { ctx.Error(http.StatusInternalServerError, err.Error()) } - } else if models.IsErrCommitIDDoesNotMatch(err) { + } else if files_service.IsErrCommitIDDoesNotMatch(err) { ctx.RenderWithErr(ctx.Tr("repo.editor.commit_id_not_matching"), tplEditFile, &form) } else if git.IsErrPushOutOfDate(err) { ctx.RenderWithErr(ctx.Tr("repo.editor.push_out_of_date"), tplEditFile, &form) @@ -506,14 +505,14 @@ func DeleteFilePost(ctx *context.Context) { Signoff: form.Signoff, }); err != nil { // This is where we handle all the errors thrown by repofiles.DeleteRepoFile - if git.IsErrNotExist(err) || models.IsErrRepoFileDoesNotExist(err) { + if git.IsErrNotExist(err) || files_service.IsErrRepoFileDoesNotExist(err) { ctx.RenderWithErr(ctx.Tr("repo.editor.file_deleting_no_longer_exists", ctx.Repo.TreePath), tplDeleteFile, &form) - } else if models.IsErrFilenameInvalid(err) { + } else if files_service.IsErrFilenameInvalid(err) { ctx.Data["Err_TreePath"] = true ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", ctx.Repo.TreePath), tplDeleteFile, &form) - } else if models.IsErrFilePathInvalid(err) { + } else if files_service.IsErrFilePathInvalid(err) { ctx.Data["Err_TreePath"] = true - if fileErr, ok := err.(models.ErrFilePathInvalid); ok { + if fileErr, ok := err.(files_service.ErrFilePathInvalid); ok { switch fileErr.Type { case git.EntryModeSymlink: ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplDeleteFile, &form) @@ -541,7 +540,7 @@ func DeleteFilePost(ctx *context.Context) { } else { ctx.Error(http.StatusInternalServerError, err.Error()) } - } else if models.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) { + } else if files_service.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) { ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form) } else if git.IsErrPushRejected(err) { errPushRej := err.(*git.ErrPushRejected) @@ -715,12 +714,12 @@ func UploadFilePost(ctx *context.Context) { if git_model.IsErrLFSFileLocked(err) { ctx.Data["Err_TreePath"] = true ctx.RenderWithErr(ctx.Tr("repo.editor.upload_file_is_locked", err.(git_model.ErrLFSFileLocked).Path, err.(git_model.ErrLFSFileLocked).UserName), tplUploadFile, &form) - } else if models.IsErrFilenameInvalid(err) { + } else if files_service.IsErrFilenameInvalid(err) { ctx.Data["Err_TreePath"] = true ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", form.TreePath), tplUploadFile, &form) - } else if models.IsErrFilePathInvalid(err) { + } else if files_service.IsErrFilePathInvalid(err) { ctx.Data["Err_TreePath"] = true - fileErr := err.(models.ErrFilePathInvalid) + fileErr := err.(files_service.ErrFilePathInvalid) switch fileErr.Type { case git.EntryModeSymlink: ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplUploadFile, &form) @@ -731,7 +730,7 @@ func UploadFilePost(ctx *context.Context) { default: ctx.Error(http.StatusInternalServerError, err.Error()) } - } else if models.IsErrRepoFileAlreadyExists(err) { + } else if files_service.IsErrRepoFileAlreadyExists(err) { ctx.Data["Err_TreePath"] = true ctx.RenderWithErr(ctx.Tr("repo.editor.file_already_exists", form.TreePath), tplUploadFile, &form) } else if git.IsErrBranchNotExist(err) { diff --git a/routers/web/repo/migrate.go b/routers/web/repo/migrate.go index 3eaf05f3833..a2c257940e1 100644 --- a/routers/web/repo/migrate.go +++ b/routers/web/repo/migrate.go @@ -9,12 +9,12 @@ import ( "net/url" "strings" - "code.gitea.io/gitea/models" admin_model "code.gitea.io/gitea/models/admin" "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/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" @@ -123,8 +123,8 @@ func handleMigrateError(ctx *context.Context, owner *user_model.User, err error, } func handleMigrateRemoteAddrError(ctx *context.Context, err error, tpl base.TplName, form *forms.MigrateRepoForm) { - if models.IsErrInvalidCloneAddr(err) { - addrErr := err.(*models.ErrInvalidCloneAddr) + if git.IsErrInvalidCloneAddr(err) { + addrErr := err.(*git.ErrInvalidCloneAddr) switch { case addrErr.IsProtocolInvalid: ctx.RenderWithErr(ctx.Tr("repo.mirror_address_protocol_invalid"), tpl, form) @@ -176,7 +176,7 @@ func MigratePost(ctx *context.Context) { return } - remoteAddr, err := forms.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword) + remoteAddr, err := git.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword) if err == nil { err = migrations.IsMigrateURLAllowed(remoteAddr, ctx.Doer) } diff --git a/routers/web/repo/patch.go b/routers/web/repo/patch.go index 0dee02dd9ca..5906ec6f3db 100644 --- a/routers/web/repo/patch.go +++ b/routers/web/repo/patch.go @@ -6,7 +6,6 @@ package repo import ( "strings" - "code.gitea.io/gitea/models" git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" @@ -101,7 +100,7 @@ func NewDiffPatchPost(ctx *context.Context) { ctx.Data["Err_NewBranchName"] = true ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplEditFile, &form) return - } else if models.IsErrCommitIDDoesNotMatch(err) { + } else if files.IsErrCommitIDDoesNotMatch(err) { ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form) return } diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 8fd23f835ad..7cc7bf58d2e 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -14,7 +14,6 @@ import ( "strings" "time" - "code.gitea.io/gitea/models" activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" @@ -968,8 +967,8 @@ func UpdatePullRequest(ctx *context.Context) { message := fmt.Sprintf("Merge branch '%s' into %s", issue.PullRequest.BaseBranch, issue.PullRequest.HeadBranch) if err = pull_service.Update(ctx, issue.PullRequest, ctx.Doer, message, rebase); err != nil { - if models.IsErrMergeConflicts(err) { - conflictError := err.(models.ErrMergeConflicts) + if pull_service.IsErrMergeConflicts(err) { + conflictError := err.(pull_service.ErrMergeConflicts) flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.merge_conflict"), "Summary": ctx.Tr("repo.pulls.merge_conflict_summary"), @@ -982,8 +981,8 @@ func UpdatePullRequest(ctx *context.Context) { ctx.Flash.Error(flashError) ctx.Redirect(issue.Link()) return - } else if models.IsErrRebaseConflicts(err) { - conflictError := err.(models.ErrRebaseConflicts) + } else if pull_service.IsErrRebaseConflicts(err) { + conflictError := err.(pull_service.ErrRebaseConflicts) flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)), "Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"), @@ -1047,7 +1046,7 @@ func MergePullRequest(ctx *context.Context) { ctx.JSONError(ctx.Tr("repo.pulls.no_merge_wip")) case errors.Is(err, pull_service.ErrNotMergeableState): ctx.JSONError(ctx.Tr("repo.pulls.no_merge_not_ready")) - case models.IsErrDisallowedToMerge(err): + case pull_service.IsErrDisallowedToMerge(err): ctx.JSONError(ctx.Tr("repo.pulls.no_merge_not_ready")) case asymkey_service.IsErrWontSign(err): ctx.JSONError(err.Error()) // has no translation ... @@ -1064,7 +1063,7 @@ func MergePullRequest(ctx *context.Context) { if manuallyMerged { if err := pull_service.MergedManually(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, form.MergeCommitID); err != nil { switch { - case models.IsErrInvalidMergeStyle(err): + case pull_service.IsErrInvalidMergeStyle(err): ctx.JSONError(ctx.Tr("repo.pulls.invalid_merge_option")) case strings.Contains(err.Error(), "Wrong commit ID"): ctx.JSONError(ctx.Tr("repo.pulls.wrong_commit_id")) @@ -1111,10 +1110,10 @@ func MergePullRequest(ctx *context.Context) { } if err := pull_service.Merge(ctx, pr, ctx.Doer, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message, false); err != nil { - if models.IsErrInvalidMergeStyle(err) { + if pull_service.IsErrInvalidMergeStyle(err) { ctx.JSONError(ctx.Tr("repo.pulls.invalid_merge_option")) - } else if models.IsErrMergeConflicts(err) { - conflictError := err.(models.ErrMergeConflicts) + } else if pull_service.IsErrMergeConflicts(err) { + conflictError := err.(pull_service.ErrMergeConflicts) flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.merge_conflict"), "Summary": ctx.Tr("repo.editor.merge_conflict_summary"), @@ -1126,8 +1125,8 @@ func MergePullRequest(ctx *context.Context) { } ctx.Flash.Error(flashError) ctx.JSONRedirect(issue.Link()) - } else if models.IsErrRebaseConflicts(err) { - conflictError := err.(models.ErrRebaseConflicts) + } else if pull_service.IsErrRebaseConflicts(err) { + conflictError := err.(pull_service.ErrRebaseConflicts) flashError, err := ctx.RenderToHTML(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)), "Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"), @@ -1139,7 +1138,7 @@ func MergePullRequest(ctx *context.Context) { } ctx.Flash.Error(flashError) ctx.JSONRedirect(issue.Link()) - } else if models.IsErrMergeUnrelatedHistories(err) { + } else if pull_service.IsErrMergeUnrelatedHistories(err) { log.Debug("MergeUnrelatedHistories error: %v", err) ctx.Flash.Error(ctx.Tr("repo.pulls.unrelated_histories")) ctx.JSONRedirect(issue.Link()) @@ -1147,7 +1146,7 @@ func MergePullRequest(ctx *context.Context) { log.Debug("MergePushOutOfDate error: %v", err) ctx.Flash.Error(ctx.Tr("repo.pulls.merge_out_of_date")) ctx.JSONRedirect(issue.Link()) - } else if models.IsErrSHADoesNotMatch(err) { + } else if pull_service.IsErrSHADoesNotMatch(err) { log.Debug("MergeHeadOutOfDate error: %v", err) ctx.Flash.Error(ctx.Tr("repo.pulls.head_out_of_date")) ctx.JSONRedirect(issue.Link()) @@ -1606,7 +1605,7 @@ func UpdatePullRequestTarget(ctx *context.Context) { "error": err.Error(), "user_error": errorMessage, }) - } else if models.IsErrPullRequestHasMerged(err) { + } else if pull_service.IsErrPullRequestHasMerged(err) { errorMessage := ctx.Tr("repo.pulls.has_merged") ctx.Flash.Error(errorMessage) diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index b3a91a60707..5c5191fc3b9 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -10,7 +10,6 @@ import ( "net/http" "strings" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/renderhelper" @@ -30,7 +29,7 @@ import ( "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/forms" - releaseservice "code.gitea.io/gitea/services/release" + release_service "code.gitea.io/gitea/services/release" ) const ( @@ -432,27 +431,27 @@ func NewReleasePost(ctx *context.Context) { } if len(form.TagOnly) > 0 { - if err = releaseservice.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, form.Target, form.TagName, msg); err != nil { - if models.IsErrTagAlreadyExists(err) { - e := err.(models.ErrTagAlreadyExists) + if err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, form.Target, form.TagName, msg); err != nil { + if release_service.IsErrTagAlreadyExists(err) { + e := err.(release_service.ErrTagAlreadyExists) ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName)) ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) return } - if models.IsErrInvalidTagName(err) { + if release_service.IsErrInvalidTagName(err) { ctx.Flash.Error(ctx.Tr("repo.release.tag_name_invalid")) ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) return } - if models.IsErrProtectedTagName(err) { + if release_service.IsErrProtectedTagName(err) { ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected")) ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) return } - ctx.ServerError("releaseservice.CreateNewTag", err) + ctx.ServerError("release_service.CreateNewTag", err) return } @@ -475,14 +474,14 @@ func NewReleasePost(ctx *context.Context) { IsTag: false, } - if err = releaseservice.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, msg); err != nil { + if err = release_service.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, msg); err != nil { ctx.Data["Err_TagName"] = true switch { case repo_model.IsErrReleaseAlreadyExist(err): ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form) - case models.IsErrInvalidTagName(err): + case release_service.IsErrInvalidTagName(err): ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_invalid"), tplReleaseNew, &form) - case models.IsErrProtectedTagName(err): + case release_service.IsErrProtectedTagName(err): ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_protected"), tplReleaseNew, &form) default: ctx.ServerError("CreateRelease", err) @@ -504,7 +503,7 @@ func NewReleasePost(ctx *context.Context) { rel.PublisherID = ctx.Doer.ID rel.IsTag = false - if err = releaseservice.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, attachmentUUIDs, nil, nil); err != nil { + if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, attachmentUUIDs, nil, nil); err != nil { ctx.Data["Err_TagName"] = true ctx.ServerError("UpdateRelease", err) return @@ -610,7 +609,7 @@ func EditReleasePost(ctx *context.Context) { rel.Note = form.Content rel.IsDraft = len(form.Draft) > 0 rel.IsPrerelease = form.Prerelease - if err = releaseservice.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, + if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, addAttachmentUUIDs, delAttachmentUUIDs, editAttachments); err != nil { ctx.ServerError("UpdateRelease", err) return @@ -649,8 +648,8 @@ func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) { return } - if err := releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, isDelTag); err != nil { - if models.IsErrProtectedTagName(err) { + if err := release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, isDelTag); err != nil { + if release_service.IsErrProtectedTagName(err) { ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected")) } else { ctx.Flash.Error("DeleteReleaseByID: " + err.Error()) diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index f2169d8a79f..1c7f9441f80 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -11,7 +11,6 @@ import ( "strings" "time" - "code.gitea.io/gitea/models" actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" @@ -223,7 +222,7 @@ func SettingsPost(ctx *context.Context) { form.MirrorPassword, _ = u.User.Password() } - address, err := forms.ParseRemoteAddr(form.MirrorAddress, form.MirrorUsername, form.MirrorPassword) + address, err := git.ParseRemoteAddr(form.MirrorAddress, form.MirrorUsername, form.MirrorPassword) if err == nil { err = migrations.IsMigrateURLAllowed(address, ctx.Doer) } @@ -385,7 +384,7 @@ func SettingsPost(ctx *context.Context) { return } - address, err := forms.ParseRemoteAddr(form.PushMirrorAddress, form.PushMirrorUsername, form.PushMirrorPassword) + address, err := git.ParseRemoteAddr(form.PushMirrorAddress, form.PushMirrorUsername, form.PushMirrorPassword) if err == nil { err = migrations.IsMigrateURLAllowed(address, ctx.Doer) } @@ -980,8 +979,8 @@ func SettingsPost(ctx *context.Context) { } func handleSettingRemoteAddrError(ctx *context.Context, err error, form *forms.RepoSettingForm) { - if models.IsErrInvalidCloneAddr(err) { - addrErr := err.(*models.ErrInvalidCloneAddr) + if git.IsErrInvalidCloneAddr(err) { + addrErr := err.(*git.ErrInvalidCloneAddr) switch { case addrErr.IsProtocolInvalid: ctx.RenderWithErr(ctx.Tr("repo.mirror_address_protocol_invalid"), tplSettingsOptions, form) diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 7f2dece4169..00fe02c886d 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -10,7 +10,9 @@ import ( "net/http" "time" - "code.gitea.io/gitea/models" + org_model "code.gitea.io/gitea/models/organization" + packages_model "code.gitea.io/gitea/models/packages" + repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/base" @@ -301,16 +303,16 @@ func DeleteAccount(ctx *context.Context) { if err := user.DeleteUser(ctx, ctx.Doer, false); err != nil { switch { - case models.IsErrUserOwnRepos(err): + case repo_model.IsErrUserOwnRepos(err): ctx.Flash.Error(ctx.Tr("form.still_own_repo")) ctx.Redirect(setting.AppSubURL + "/user/settings/account") - case models.IsErrUserHasOrgs(err): + case org_model.IsErrUserHasOrgs(err): ctx.Flash.Error(ctx.Tr("form.still_has_org")) ctx.Redirect(setting.AppSubURL + "/user/settings/account") - case models.IsErrUserOwnPackages(err): + case packages_model.IsErrUserOwnPackages(err): ctx.Flash.Error(ctx.Tr("form.still_own_packages")) ctx.Redirect(setting.AppSubURL + "/user/settings/account") - case models.IsErrDeleteLastAdminUser(err): + case user_model.IsErrDeleteLastAdminUser(err): ctx.Flash.Error(ctx.Tr("auth.last_admin")) ctx.Redirect(setting.AppSubURL + "/user/settings/account") default: diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 7647c74e465..b14171787e4 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -6,10 +6,8 @@ package forms import ( "net/http" - "net/url" "strings" - "code.gitea.io/gitea/models" issues_model "code.gitea.io/gitea/models/issues" project_model "code.gitea.io/gitea/models/project" "code.gitea.io/gitea/modules/setting" @@ -90,27 +88,6 @@ func (f *MigrateRepoForm) Validate(req *http.Request, errs binding.Errors) bindi return middleware.Validate(errs, ctx.Data, f, ctx.Locale) } -// ParseRemoteAddr checks if given remote address is valid, -// and returns composed URL with needed username and password. -func ParseRemoteAddr(remoteAddr, authUsername, authPassword string) (string, error) { - remoteAddr = strings.TrimSpace(remoteAddr) - // Remote address can be HTTP/HTTPS/Git URL or local path. - if strings.HasPrefix(remoteAddr, "http://") || - strings.HasPrefix(remoteAddr, "https://") || - strings.HasPrefix(remoteAddr, "git://") { - u, err := url.Parse(remoteAddr) - if err != nil { - return "", &models.ErrInvalidCloneAddr{IsURLError: true, Host: remoteAddr} - } - if len(authUsername)+len(authPassword) > 0 { - u.User = url.UserPassword(authUsername, authPassword) - } - remoteAddr = u.String() - } - - return remoteAddr, nil -} - // RepoSettingForm form for changing repository settings type RepoSettingForm struct { RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` diff --git a/services/migrations/migrate.go b/services/migrations/migrate.go index d0ad6d0139a..51b22d6111b 100644 --- a/services/migrations/migrate.go +++ b/services/migrations/migrate.go @@ -12,10 +12,10 @@ import ( "path/filepath" "strings" - "code.gitea.io/gitea/models" repo_model "code.gitea.io/gitea/models/repo" system_model "code.gitea.io/gitea/models/system" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/hostmatcher" "code.gitea.io/gitea/modules/log" base "code.gitea.io/gitea/modules/migration" @@ -43,16 +43,16 @@ func IsMigrateURLAllowed(remoteURL string, doer *user_model.User) error { // Remote address can be HTTP/HTTPS/Git URL or local path. u, err := url.Parse(remoteURL) if err != nil { - return &models.ErrInvalidCloneAddr{IsURLError: true, Host: remoteURL} + return &git.ErrInvalidCloneAddr{IsURLError: true, Host: remoteURL} } if u.Scheme == "file" || u.Scheme == "" { if !doer.CanImportLocal() { - return &models.ErrInvalidCloneAddr{Host: "", IsPermissionDenied: true, LocalPath: true} + return &git.ErrInvalidCloneAddr{Host: "", IsPermissionDenied: true, LocalPath: true} } isAbs := filepath.IsAbs(u.Host + u.Path) if !isAbs { - return &models.ErrInvalidCloneAddr{Host: "", IsInvalidPath: true, LocalPath: true} + return &git.ErrInvalidCloneAddr{Host: "", IsInvalidPath: true, LocalPath: true} } isDir, err := util.IsDir(u.Host + u.Path) if err != nil { @@ -60,18 +60,18 @@ func IsMigrateURLAllowed(remoteURL string, doer *user_model.User) error { return err } if !isDir { - return &models.ErrInvalidCloneAddr{Host: "", IsInvalidPath: true, LocalPath: true} + return &git.ErrInvalidCloneAddr{Host: "", IsInvalidPath: true, LocalPath: true} } return nil } if u.Scheme == "git" && u.Port() != "" && (strings.Contains(remoteURL, "%0d") || strings.Contains(remoteURL, "%0a")) { - return &models.ErrInvalidCloneAddr{Host: u.Host, IsURLError: true} + return &git.ErrInvalidCloneAddr{Host: u.Host, IsURLError: true} } if u.Opaque != "" || u.Scheme != "" && u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "git" { - return &models.ErrInvalidCloneAddr{Host: u.Host, IsProtocolInvalid: true, IsPermissionDenied: true, IsURLError: true} + return &git.ErrInvalidCloneAddr{Host: u.Host, IsProtocolInvalid: true, IsPermissionDenied: true, IsURLError: true} } hostName, _, err := net.SplitHostPort(u.Host) @@ -95,12 +95,12 @@ func checkByAllowBlockList(hostName string, addrList []net.IP) error { } var blockedError error if blockList.MatchHostName(hostName) || ipBlocked { - blockedError = &models.ErrInvalidCloneAddr{Host: hostName, IsPermissionDenied: true} + blockedError = &git.ErrInvalidCloneAddr{Host: hostName, IsPermissionDenied: true} } // if we have an allow-list, check the allow-list before return to get the more accurate error if !allowList.IsEmpty() { if !allowList.MatchHostName(hostName) && !ipAllowed { - return &models.ErrInvalidCloneAddr{Host: hostName, IsPermissionDenied: true} + return &git.ErrInvalidCloneAddr{Host: hostName, IsPermissionDenied: true} } } // otherwise, we always follow the blocked list diff --git a/services/org/org.go b/services/org/org.go index 471e6fcaf16..3d30ae21a39 100644 --- a/services/org/org.go +++ b/services/org/org.go @@ -7,7 +7,6 @@ import ( "context" "fmt" - "code.gitea.io/gitea/models" actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" org_model "code.gitea.io/gitea/models/organization" @@ -67,14 +66,14 @@ func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge if err != nil { return fmt.Errorf("GetRepositoryCount: %w", err) } else if count > 0 { - return models.ErrUserOwnRepos{UID: org.ID} + return repo_model.ErrUserOwnRepos{UID: org.ID} } // Check ownership of packages. if ownsPackages, err := packages_model.HasOwnerPackages(ctx, org.ID); err != nil { return fmt.Errorf("HasOwnerPackages: %w", err) } else if ownsPackages { - return models.ErrUserOwnPackages{UID: org.ID} + return packages_model.ErrUserOwnPackages{UID: org.ID} } if err := deleteOrganization(ctx, org); err != nil { diff --git a/services/org/org_test.go b/services/org/org_test.go index e7d2a18ea96..791404c5c89 100644 --- a/services/org/org_test.go +++ b/services/org/org_test.go @@ -6,9 +6,9 @@ package org import ( "testing" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -30,7 +30,7 @@ func TestDeleteOrganization(t *testing.T) { org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}) err := DeleteOrganization(db.DefaultContext, org, false) assert.Error(t, err) - assert.True(t, models.IsErrUserOwnRepos(err)) + assert.True(t, repo_model.IsErrUserOwnRepos(err)) user := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 5}) assert.Error(t, DeleteOrganization(db.DefaultContext, user, false)) diff --git a/services/pull/check.go b/services/pull/check.go index 736be4611be..bffca394a8b 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -11,7 +11,6 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" @@ -36,7 +35,7 @@ var prPatchCheckerQueue *queue.WorkerPoolQueue[string] var ( ErrIsClosed = errors.New("pull is closed") - ErrUserNotAllowedToMerge = models.ErrDisallowedToMerge{} + ErrUserNotAllowedToMerge = ErrDisallowedToMerge{} ErrHasMerged = errors.New("has already been merged") ErrIsWorkInProgress = errors.New("work in progress PRs cannot be merged") ErrIsChecking = errors.New("cannot merge while conflict checking is in progress") @@ -106,7 +105,7 @@ func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *acc } if err := CheckPullBranchProtections(ctx, pr, false); err != nil { - if !models.IsErrDisallowedToMerge(err) { + if !IsErrDisallowedToMerge(err) { log.Error("Error whilst checking pull branch protection for %-v: %v", pr, err) return err } diff --git a/services/pull/merge.go b/services/pull/merge.go index a3fbe4f627b..fba85f1e519 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -13,7 +13,6 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" @@ -30,6 +29,7 @@ import ( repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" issue_service "code.gitea.io/gitea/services/issue" notify_service "code.gitea.io/gitea/services/notify" ) @@ -159,6 +159,27 @@ func GetDefaultMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr return getMergeMessage(ctx, baseGitRepo, pr, mergeStyle, nil) } +// ErrInvalidMergeStyle represents an error if merging with disabled merge strategy +type ErrInvalidMergeStyle struct { + ID int64 + Style repo_model.MergeStyle +} + +// IsErrInvalidMergeStyle checks if an error is a ErrInvalidMergeStyle. +func IsErrInvalidMergeStyle(err error) bool { + _, ok := err.(ErrInvalidMergeStyle) + return ok +} + +func (err ErrInvalidMergeStyle) Error() string { + return fmt.Sprintf("merge strategy is not allowed or is invalid [repo_id: %d, strategy: %s]", + err.ID, err.Style) +} + +func (err ErrInvalidMergeStyle) Unwrap() error { + return util.ErrInvalidArgument +} + // Merge merges pull request to base repository. // Caller should check PR is ready to be merged (review and status checks) func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string, wasAutoMerged bool) error { @@ -179,7 +200,7 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U // Check if merge style is correct and allowed if !prConfig.IsMergeStyleAllowed(mergeStyle) { - return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle} + return ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle} } releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID)) @@ -283,7 +304,7 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use return "", err } default: - return "", models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle} + return "", ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle} } // OK we should cache our current head and origin/headbranch @@ -374,13 +395,66 @@ func commitAndSignNoAuthor(ctx *mergeContext, message string) error { return nil } +// ErrMergeConflicts represents an error if merging fails with a conflict +type ErrMergeConflicts struct { + Style repo_model.MergeStyle + StdOut string + StdErr string + Err error +} + +// IsErrMergeConflicts checks if an error is a ErrMergeConflicts. +func IsErrMergeConflicts(err error) bool { + _, ok := err.(ErrMergeConflicts) + return ok +} + +func (err ErrMergeConflicts) Error() string { + return fmt.Sprintf("Merge Conflict Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut) +} + +// ErrMergeUnrelatedHistories represents an error if merging fails due to unrelated histories +type ErrMergeUnrelatedHistories struct { + Style repo_model.MergeStyle + StdOut string + StdErr string + Err error +} + +// IsErrMergeUnrelatedHistories checks if an error is a ErrMergeUnrelatedHistories. +func IsErrMergeUnrelatedHistories(err error) bool { + _, ok := err.(ErrMergeUnrelatedHistories) + return ok +} + +func (err ErrMergeUnrelatedHistories) Error() string { + return fmt.Sprintf("Merge UnrelatedHistories Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut) +} + +// ErrMergeDivergingFastForwardOnly represents an error if a fast-forward-only merge fails because the branches diverge +type ErrMergeDivergingFastForwardOnly struct { + StdOut string + StdErr string + Err error +} + +// IsErrMergeDivergingFastForwardOnly checks if an error is a ErrMergeDivergingFastForwardOnly. +func IsErrMergeDivergingFastForwardOnly(err error) bool { + _, ok := err.(ErrMergeDivergingFastForwardOnly) + return ok +} + +func (err ErrMergeDivergingFastForwardOnly) Error() string { + return fmt.Sprintf("Merge DivergingFastForwardOnly Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut) +} + func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *git.Command) error { if err := cmd.Run(ctx.RunOpts()); err != nil { // Merge will leave a MERGE_HEAD file in the .git folder if there is a conflict if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "MERGE_HEAD")); statErr == nil { // We have a merge conflict error log.Debug("MergeConflict %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) - return models.ErrMergeConflicts{ + return ErrMergeConflicts{ Style: mergeStyle, StdOut: ctx.outbuf.String(), StdErr: ctx.errbuf.String(), @@ -388,7 +462,7 @@ func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *g } } else if strings.Contains(ctx.errbuf.String(), "refusing to merge unrelated histories") { log.Debug("MergeUnrelatedHistories %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) - return models.ErrMergeUnrelatedHistories{ + return ErrMergeUnrelatedHistories{ Style: mergeStyle, StdOut: ctx.outbuf.String(), StdErr: ctx.errbuf.String(), @@ -396,7 +470,7 @@ func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *g } } else if mergeStyle == repo_model.MergeStyleFastForwardOnly && strings.Contains(ctx.errbuf.String(), "Not possible to fast-forward, aborting") { log.Debug("MergeDivergingFastForwardOnly %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) - return models.ErrMergeDivergingFastForwardOnly{ + return ErrMergeDivergingFastForwardOnly{ StdOut: ctx.outbuf.String(), StdErr: ctx.errbuf.String(), Err: err, @@ -431,6 +505,25 @@ func IsUserAllowedToMerge(ctx context.Context, pr *issues_model.PullRequest, p a return false, nil } +// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it. +type ErrDisallowedToMerge struct { + Reason string +} + +// IsErrDisallowedToMerge checks if an error is an ErrDisallowedToMerge. +func IsErrDisallowedToMerge(err error) bool { + _, ok := err.(ErrDisallowedToMerge) + return ok +} + +func (err ErrDisallowedToMerge) Error() string { + return fmt.Sprintf("not allowed to merge [reason: %s]", err.Reason) +} + +func (err ErrDisallowedToMerge) Unwrap() error { + return util.ErrPermissionDenied +} + // CheckPullBranchProtections checks whether the PR is ready to be merged (reviews and status checks) func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullRequest, skipProtectedFilesCheck bool) (err error) { if err = pr.LoadBaseRepo(ctx); err != nil { @@ -450,29 +543,29 @@ func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullReques return err } if !isPass { - return models.ErrDisallowedToMerge{ + return ErrDisallowedToMerge{ Reason: "Not all required status checks successful", } } if !issues_model.HasEnoughApprovals(ctx, pb, pr) { - return models.ErrDisallowedToMerge{ + return ErrDisallowedToMerge{ Reason: "Does not have enough approvals", } } if issues_model.MergeBlockedByRejectedReview(ctx, pb, pr) { - return models.ErrDisallowedToMerge{ + return ErrDisallowedToMerge{ Reason: "There are requested changes", } } if issues_model.MergeBlockedByOfficialReviewRequests(ctx, pb, pr) { - return models.ErrDisallowedToMerge{ + return ErrDisallowedToMerge{ Reason: "There are official review requests", } } if issues_model.MergeBlockedByOutdatedBranch(pb, pr) { - return models.ErrDisallowedToMerge{ + return ErrDisallowedToMerge{ Reason: "The head branch is behind the base branch", } } @@ -482,7 +575,7 @@ func CheckPullBranchProtections(ctx context.Context, pr *issues_model.PullReques } if pb.MergeBlockedByProtectedFiles(pr.ChangedProtectedFiles) { - return models.ErrDisallowedToMerge{ + return ErrDisallowedToMerge{ Reason: "Changed protected files", } } @@ -511,7 +604,7 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use // Check if merge style is correct and allowed if !prConfig.IsMergeStyleAllowed(repo_model.MergeStyleManuallyMerged) { - return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: repo_model.MergeStyleManuallyMerged} + return ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: repo_model.MergeStyleManuallyMerged} } objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) diff --git a/services/pull/merge_prepare.go b/services/pull/merge_prepare.go index 88f6c037ebc..2e1cc8cf85c 100644 --- a/services/pull/merge_prepare.go +++ b/services/pull/merge_prepare.go @@ -14,7 +14,6 @@ import ( "strings" "time" - "code.gitea.io/gitea/models" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" @@ -43,6 +42,23 @@ func (ctx *mergeContext) RunOpts() *git.RunOpts { } } +// ErrSHADoesNotMatch represents a "SHADoesNotMatch" kind of error. +type ErrSHADoesNotMatch struct { + Path string + GivenSHA string + CurrentSHA string +} + +// IsErrSHADoesNotMatch checks if an error is a ErrSHADoesNotMatch. +func IsErrSHADoesNotMatch(err error) bool { + _, ok := err.(ErrSHADoesNotMatch) + return ok +} + +func (err ErrSHADoesNotMatch) Error() string { + return fmt.Sprintf("sha does not match [given: %s, expected: %s]", err.GivenSHA, err.CurrentSHA) +} + func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, expectedHeadCommitID string) (mergeCtx *mergeContext, cancel context.CancelFunc, err error) { // Clone base repo. prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr) @@ -65,7 +81,7 @@ func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullReque } if strings.TrimSpace(trackingCommitID) != expectedHeadCommitID { defer cancel() - return nil, nil, models.ErrSHADoesNotMatch{ + return nil, nil, ErrSHADoesNotMatch{ GivenSHA: expectedHeadCommitID, CurrentSHA: trackingCommitID, } @@ -233,8 +249,27 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string, o return err } +// ErrRebaseConflicts represents an error if rebase fails with a conflict +type ErrRebaseConflicts struct { + Style repo_model.MergeStyle + CommitSHA string + StdOut string + StdErr string + Err error +} + +// IsErrRebaseConflicts checks if an error is a ErrRebaseConflicts. +func IsErrRebaseConflicts(err error) bool { + _, ok := err.(ErrRebaseConflicts) + return ok +} + +func (err ErrRebaseConflicts) Error() string { + return fmt.Sprintf("Rebase Error: %v: Whilst Rebasing: %s\n%s\n%s", err.Err, err.CommitSHA, err.StdErr, err.StdOut) +} + // rebaseTrackingOnToBase checks out the tracking branch as staging and rebases it on to the base branch -// if there is a conflict it will return a models.ErrRebaseConflicts +// if there is a conflict it will return an ErrRebaseConflicts func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) error { // Checkout head branch if err := git.NewCommand(ctx, "checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch). @@ -268,11 +303,11 @@ func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) } } if !ok { - log.Error("Unable to determine failing commit sha for failing rebase in temp repo for %-v. Cannot cast as models.ErrRebaseConflicts.", ctx.pr) + log.Error("Unable to determine failing commit sha for failing rebase in temp repo for %-v. Cannot cast as ErrRebaseConflicts.", ctx.pr) return fmt.Errorf("unable to git rebase staging on to base in temp repo for %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) } log.Debug("Conflict when rebasing staging on to base in %-v at %s: %v\n%s\n%s", ctx.pr, commitSha, err, ctx.outbuf.String(), ctx.errbuf.String()) - return models.ErrRebaseConflicts{ + return ErrRebaseConflicts{ CommitSHA: commitSha, Style: mergeStyle, StdOut: ctx.outbuf.String(), diff --git a/services/pull/patch.go b/services/pull/patch.go index 0934a86c89a..36ca9dbdb67 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -13,7 +13,6 @@ import ( "path/filepath" "strings" - "code.gitea.io/gitea/models" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unit" @@ -502,6 +501,29 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * return false, nil } +// ErrFilePathProtected represents a "FilePathProtected" kind of error. +type ErrFilePathProtected struct { + Message string + Path string +} + +// IsErrFilePathProtected checks if an error is an ErrFilePathProtected. +func IsErrFilePathProtected(err error) bool { + _, ok := err.(ErrFilePathProtected) + return ok +} + +func (err ErrFilePathProtected) Error() string { + if err.Message != "" { + return err.Message + } + return fmt.Sprintf("path is protected and can not be changed [path: %s]", err.Path) +} + +func (err ErrFilePathProtected) Unwrap() error { + return util.ErrPermissionDenied +} + // CheckFileProtection check file Protection func CheckFileProtection(repo *git.Repository, branchName, oldCommitID, newCommitID string, patterns []glob.Glob, limit int, env []string) ([]string, error) { if len(patterns) == 0 { @@ -525,7 +547,7 @@ func CheckFileProtection(repo *git.Repository, branchName, oldCommitID, newCommi } } if len(changedProtectedFiles) > 0 { - err = models.ErrFilePathProtected{ + err = ErrFilePathProtected{ Path: changedProtectedFiles[0], } } @@ -575,7 +597,7 @@ func checkPullFilesProtection(ctx context.Context, pr *issues_model.PullRequest, } pr.ChangedProtectedFiles, err = CheckFileProtection(gitRepo, pr.HeadBranch, pr.MergeBase, "tracking", pb.GetProtectedFilePatterns(), 10, os.Environ()) - if err != nil && !models.IsErrFilePathProtected(err) { + if err != nil && !IsErrFilePathProtected(err) { return err } return nil diff --git a/services/pull/pull.go b/services/pull/pull.go index 3362cb97ff7..0256c2c3f63 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -13,7 +13,6 @@ import ( "strings" "time" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" @@ -224,6 +223,28 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error { return nil } +// ErrPullRequestHasMerged represents a "PullRequestHasMerged"-error +type ErrPullRequestHasMerged struct { + ID int64 + IssueID int64 + HeadRepoID int64 + BaseRepoID int64 + HeadBranch string + BaseBranch string +} + +// IsErrPullRequestHasMerged checks if an error is a ErrPullRequestHasMerged. +func IsErrPullRequestHasMerged(err error) bool { + _, ok := err.(ErrPullRequestHasMerged) + return ok +} + +// Error does pretty-printing :D +func (err ErrPullRequestHasMerged) Error() string { + return fmt.Sprintf("pull request has merged [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]", + err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch) +} + // ChangeTargetBranch changes the target branch of this pull request, as the given user. func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, targetBranch string) (err error) { releaser, err := globallock.Lock(ctx, getPullWorkingLockKey(pr.ID)) @@ -247,7 +268,7 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer } if pr.HasMerged { - return models.ErrPullRequestHasMerged{ + return ErrPullRequestHasMerged{ ID: pr.ID, IssueID: pr.Index, HeadRepoID: pr.HeadRepoID, @@ -654,7 +675,7 @@ func RetargetBranchPulls(ctx context.Context, doer *user_model.User, repoID int6 if err = pr.Issue.LoadRepo(ctx); err != nil { errs = append(errs, err) } else if err = ChangeTargetBranch(ctx, pr, doer, targetBranch); err != nil && - !issues_model.IsErrIssueIsClosed(err) && !models.IsErrPullRequestHasMerged(err) && + !issues_model.IsErrIssueIsClosed(err) && !IsErrPullRequestHasMerged(err) && !issues_model.IsErrPullRequestAlreadyExists(err) { errs = append(errs, err) } diff --git a/services/release/release.go b/services/release/release.go index 123e9ab7c7f..84c60a105a4 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -9,7 +9,6 @@ import ( "fmt" "strings" - "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" @@ -26,6 +25,44 @@ import ( notify_service "code.gitea.io/gitea/services/notify" ) +// ErrInvalidTagName represents a "InvalidTagName" kind of error. +type ErrInvalidTagName struct { + TagName string +} + +// IsErrInvalidTagName checks if an error is a ErrInvalidTagName. +func IsErrInvalidTagName(err error) bool { + _, ok := err.(ErrInvalidTagName) + return ok +} + +func (err ErrInvalidTagName) Error() string { + return fmt.Sprintf("release tag name is not valid [tag_name: %s]", err.TagName) +} + +func (err ErrInvalidTagName) Unwrap() error { + return util.ErrInvalidArgument +} + +// ErrProtectedTagName represents a "ProtectedTagName" kind of error. +type ErrProtectedTagName struct { + TagName string +} + +// IsErrProtectedTagName checks if an error is a ErrProtectedTagName. +func IsErrProtectedTagName(err error) bool { + _, ok := err.(ErrProtectedTagName) + return ok +} + +func (err ErrProtectedTagName) Error() string { + return fmt.Sprintf("release tag name is protected [tag_name: %s]", err.TagName) +} + +func (err ErrProtectedTagName) Unwrap() error { + return util.ErrPermissionDenied +} + func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Release, msg string) (bool, error) { err := rel.LoadAttributes(ctx) if err != nil { @@ -58,7 +95,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel return false, err } if !isAllowed { - return false, models.ErrProtectedTagName{ + return false, ErrProtectedTagName{ TagName: rel.TagName, } } @@ -71,7 +108,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel if len(msg) > 0 { if err = gitRepo.CreateAnnotatedTag(rel.TagName, msg, commit.ID.String()); err != nil { if strings.Contains(err.Error(), "is not a valid tag name") { - return false, models.ErrInvalidTagName{ + return false, ErrInvalidTagName{ TagName: rel.TagName, } } @@ -79,7 +116,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel } } else if err = gitRepo.CreateTag(rel.TagName, commit.ID.String()); err != nil { if strings.Contains(err.Error(), "is not a valid tag name") { - return false, models.ErrInvalidTagName{ + return false, ErrInvalidTagName{ TagName: rel.TagName, } } @@ -159,13 +196,32 @@ func CreateRelease(gitRepo *git.Repository, rel *repo_model.Release, attachmentU return nil } +// ErrTagAlreadyExists represents an error that tag with such name already exists. +type ErrTagAlreadyExists struct { + TagName string +} + +// IsErrTagAlreadyExists checks if an error is an ErrTagAlreadyExists. +func IsErrTagAlreadyExists(err error) bool { + _, ok := err.(ErrTagAlreadyExists) + return ok +} + +func (err ErrTagAlreadyExists) Error() string { + return fmt.Sprintf("tag already exists [name: %s]", err.TagName) +} + +func (err ErrTagAlreadyExists) Unwrap() error { + return util.ErrAlreadyExist +} + // CreateNewTag creates a new repository tag func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commit, tagName, msg string) error { has, err := repo_model.IsReleaseExist(ctx, repo.ID, tagName) if err != nil { return err } else if has { - return models.ErrTagAlreadyExists{ + return ErrTagAlreadyExists{ TagName: tagName, } } @@ -320,7 +376,7 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re return err } if !isAllowed { - return models.ErrProtectedTagName{ + return ErrProtectedTagName{ TagName: rel.TagName, } } diff --git a/services/repository/branch.go b/services/repository/branch.go index 3a95aab2649..fc476298ca5 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -9,7 +9,6 @@ import ( "fmt" "strings" - "code.gitea.io/gitea/models" actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" @@ -31,6 +30,7 @@ import ( "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" notify_service "code.gitea.io/gitea/services/notify" + release_service "code.gitea.io/gitea/services/release" files_service "code.gitea.io/gitea/services/repository/files" "xorm.io/builder" @@ -274,7 +274,7 @@ func checkBranchName(ctx context.Context, repo *repo_model.Repository, name stri BranchName: branchRefName, } case refName == git.TagPrefix+name: - return models.ErrTagAlreadyExists{ + return release_service.ErrTagAlreadyExists{ TagName: name, } } diff --git a/services/repository/files/cherry_pick.go b/services/repository/files/cherry_pick.go index 451a1821557..10545e9e038 100644 --- a/services/repository/files/cherry_pick.go +++ b/services/repository/files/cherry_pick.go @@ -8,7 +8,6 @@ import ( "fmt" "strings" - "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" @@ -17,6 +16,22 @@ import ( "code.gitea.io/gitea/services/pull" ) +// ErrCommitIDDoesNotMatch represents a "CommitIDDoesNotMatch" kind of error. +type ErrCommitIDDoesNotMatch struct { + GivenCommitID string + CurrentCommitID string +} + +// IsErrCommitIDDoesNotMatch checks if an error is a ErrCommitIDDoesNotMatch. +func IsErrCommitIDDoesNotMatch(err error) bool { + _, ok := err.(ErrCommitIDDoesNotMatch) + return ok +} + +func (err ErrCommitIDDoesNotMatch) Error() string { + return fmt.Sprintf("file CommitID does not match [given: %s, expected: %s]", err.GivenCommitID, err.CurrentCommitID) +} + // CherryPick cherrypicks or reverts a commit to the given repository func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, revert bool, opts *ApplyDiffPatchOptions) (*structs.FileResponse, error) { if err := opts.Validate(ctx, repo, doer); err != nil { @@ -57,7 +72,7 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod } opts.LastCommitID = lastCommitID.String() if commit.ID.String() != opts.LastCommitID { - return nil, models.ErrCommitIDDoesNotMatch{ + return nil, ErrCommitIDDoesNotMatch{ GivenCommitID: opts.LastCommitID, CurrentCommitID: opts.LastCommitID, } diff --git a/services/repository/files/content.go b/services/repository/files/content.go index 95e7c7087c0..0ab7422ce2e 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -10,7 +10,6 @@ import ( "path" "strings" - "code.gitea.io/gitea/models" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" @@ -53,7 +52,7 @@ func GetContentsOrList(ctx context.Context, repo *repo_model.Repository, treePat // Check that the path given in opts.treePath is valid (not a git path) cleanTreePath := CleanUploadFileName(treePath) if cleanTreePath == "" && treePath != "" { - return nil, models.ErrFilenameInvalid{ + return nil, ErrFilenameInvalid{ Path: treePath, } } @@ -128,7 +127,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref // Check that the path given in opts.treePath is valid (not a git path) cleanTreePath := CleanUploadFileName(treePath) if cleanTreePath == "" && treePath != "" { - return nil, models.ErrFilenameInvalid{ + return nil, ErrFilenameInvalid{ Path: treePath, } } diff --git a/services/repository/files/file.go b/services/repository/files/file.go index 16783f5b5f8..d7ca8e79e5b 100644 --- a/services/repository/files/file.go +++ b/services/repository/files/file.go @@ -156,6 +156,25 @@ func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *user_m return authorUser, committerUser } +// ErrFilenameInvalid represents a "FilenameInvalid" kind of error. +type ErrFilenameInvalid struct { + Path string +} + +// IsErrFilenameInvalid checks if an error is an ErrFilenameInvalid. +func IsErrFilenameInvalid(err error) bool { + _, ok := err.(ErrFilenameInvalid) + return ok +} + +func (err ErrFilenameInvalid) Error() string { + return fmt.Sprintf("path contains a malformed path component [path: %s]", err.Path) +} + +func (err ErrFilenameInvalid) Unwrap() error { + return util.ErrInvalidArgument +} + // CleanUploadFileName Trims a filename and returns empty string if it is a .git directory func CleanUploadFileName(name string) string { // Rebase the filename diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go index ab0e7ffd36f..38c17b4073d 100644 --- a/services/repository/files/patch.go +++ b/services/repository/files/patch.go @@ -8,7 +8,6 @@ import ( "fmt" "strings" - "code.gitea.io/gitea/models" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" @@ -16,9 +15,29 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" asymkey_service "code.gitea.io/gitea/services/asymkey" ) +// ErrUserCannotCommit represents "UserCannotCommit" kind of error. +type ErrUserCannotCommit struct { + UserName string +} + +// IsErrUserCannotCommit checks if an error is an ErrUserCannotCommit. +func IsErrUserCannotCommit(err error) bool { + _, ok := err.(ErrUserCannotCommit) + return ok +} + +func (err ErrUserCannotCommit) Error() string { + return fmt.Sprintf("user cannot commit to repo [user: %s]", err.UserName) +} + +func (err ErrUserCannotCommit) Unwrap() error { + return util.ErrPermissionDenied +} + // ApplyDiffPatchOptions holds the repository diff patch update options type ApplyDiffPatchOptions struct { LastCommitID string @@ -74,7 +93,7 @@ func (opts *ApplyDiffPatchOptions) Validate(ctx context.Context, repo *repo_mode if protectedBranch != nil { protectedBranch.Repo = repo if !protectedBranch.CanUserPush(ctx, doer) { - return models.ErrUserCannotCommit{ + return ErrUserCannotCommit{ UserName: doer.LowerName, } } @@ -85,7 +104,7 @@ func (opts *ApplyDiffPatchOptions) Validate(ctx context.Context, repo *repo_mode if !asymkey_service.IsErrWontSign(err) { return err } - return models.ErrUserCannotCommit{ + return ErrUserCannotCommit{ UserName: doer.LowerName, } } @@ -137,7 +156,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user } opts.LastCommitID = lastCommitID.String() if commit.ID.String() != opts.LastCommitID { - return nil, models.ErrCommitIDDoesNotMatch{ + return nil, ErrCommitIDDoesNotMatch{ GivenCommitID: opts.LastCommitID, CurrentCommitID: opts.LastCommitID, } diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index 30ab22db1e4..138af991f9b 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -13,7 +13,6 @@ import ( "strings" "time" - "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" @@ -187,7 +186,7 @@ func (t *TemporaryUploadRepository) AddObjectToIndex(mode, objectHash, objectPat if _, _, err := git.NewCommand(t.ctx, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, objectHash, objectPath).RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil { stderr := err.Error() if matched, _ := regexp.MatchString(".*Invalid path '.*", stderr); matched { - return models.ErrFilePathInvalid{ + return ErrFilePathInvalid{ Message: objectPath, Path: objectPath, } diff --git a/services/repository/files/tree.go b/services/repository/files/tree.go index e3a7f3b8b0e..6775186afdd 100644 --- a/services/repository/files/tree.go +++ b/services/repository/files/tree.go @@ -8,18 +8,37 @@ import ( "fmt" "net/url" - "code.gitea.io/gitea/models" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" ) +// ErrSHANotFound represents a "SHADoesNotMatch" kind of error. +type ErrSHANotFound struct { + SHA string +} + +// IsErrSHANotFound checks if an error is a ErrSHANotFound. +func IsErrSHANotFound(err error) bool { + _, ok := err.(ErrSHANotFound) + return ok +} + +func (err ErrSHANotFound) Error() string { + return fmt.Sprintf("sha not found [%s]", err.SHA) +} + +func (err ErrSHANotFound) Unwrap() error { + return util.ErrNotExist +} + // GetTreeBySHA get the GitTreeResponse of a repository using a sha hash. func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, sha string, page, perPage int, recursive bool) (*api.GitTreeResponse, error) { gitTree, err := gitRepo.GetTree(sha) if err != nil || gitTree == nil { - return nil, models.ErrSHANotFound{ + return nil, ErrSHANotFound{ // TODO: this error has never been catch outside of this function SHA: sha, } } diff --git a/services/repository/files/update.go b/services/repository/files/update.go index b1b64bacd99..a2763105b0c 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -11,7 +11,6 @@ import ( "strings" "time" - "code.gitea.io/gitea/models" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" @@ -21,7 +20,9 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" asymkey_service "code.gitea.io/gitea/services/asymkey" + pull_service "code.gitea.io/gitea/services/pull" ) // IdentityOptions for a person's identity like an author or committer @@ -64,6 +65,26 @@ type RepoFileOptions struct { executable bool } +// ErrRepoFileDoesNotExist represents a "RepoFileDoesNotExist" kind of error. +type ErrRepoFileDoesNotExist struct { + Path string + Name string +} + +// IsErrRepoFileDoesNotExist checks if an error is a ErrRepoDoesNotExist. +func IsErrRepoFileDoesNotExist(err error) bool { + _, ok := err.(ErrRepoFileDoesNotExist) + return ok +} + +func (err ErrRepoFileDoesNotExist) Error() string { + return fmt.Sprintf("repository file does not exist [path: %s]", err.Path) +} + +func (err ErrRepoFileDoesNotExist) Unwrap() error { + return util.ErrNotExist +} + // ChangeRepoFiles adds, updates or removes multiple files in the given repository func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, opts *ChangeRepoFilesOptions) (*structs.FilesResponse, error) { err := repo.MustNotBeArchived() @@ -100,14 +121,14 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use // Check that the path given in opts.treePath is valid (not a git path) treePath := CleanUploadFileName(file.TreePath) if treePath == "" { - return nil, models.ErrFilenameInvalid{ + return nil, ErrFilenameInvalid{ Path: file.TreePath, } } // If there is a fromTreePath (we are copying it), also clean it up fromTreePath := CleanUploadFileName(file.FromTreePath) if fromTreePath == "" && file.FromTreePath != "" { - return nil, models.ErrFilenameInvalid{ + return nil, ErrFilenameInvalid{ Path: file.FromTreePath, } } @@ -185,7 +206,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use } } if !inFilelist { - return nil, models.ErrRepoFileDoesNotExist{ + return nil, ErrRepoFileDoesNotExist{ Path: file.TreePath, } } @@ -276,6 +297,63 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use return filesResponse, nil } +// ErrRepoFileAlreadyExists represents a "RepoFileAlreadyExist" kind of error. +type ErrRepoFileAlreadyExists struct { + Path string +} + +// IsErrRepoFileAlreadyExists checks if an error is a ErrRepoFileAlreadyExists. +func IsErrRepoFileAlreadyExists(err error) bool { + _, ok := err.(ErrRepoFileAlreadyExists) + return ok +} + +func (err ErrRepoFileAlreadyExists) Error() string { + return fmt.Sprintf("repository file already exists [path: %s]", err.Path) +} + +func (err ErrRepoFileAlreadyExists) Unwrap() error { + return util.ErrAlreadyExist +} + +// ErrFilePathInvalid represents a "FilePathInvalid" kind of error. +type ErrFilePathInvalid struct { + Message string + Path string + Name string + Type git.EntryMode +} + +// IsErrFilePathInvalid checks if an error is an ErrFilePathInvalid. +func IsErrFilePathInvalid(err error) bool { + _, ok := err.(ErrFilePathInvalid) + return ok +} + +func (err ErrFilePathInvalid) Error() string { + if err.Message != "" { + return err.Message + } + return fmt.Sprintf("path is invalid [path: %s]", err.Path) +} + +func (err ErrFilePathInvalid) Unwrap() error { + return util.ErrInvalidArgument +} + +// ErrSHAOrCommitIDNotProvided represents a "SHAOrCommitIDNotProvided" kind of error. +type ErrSHAOrCommitIDNotProvided struct{} + +// IsErrSHAOrCommitIDNotProvided checks if an error is a ErrSHAOrCommitIDNotProvided. +func IsErrSHAOrCommitIDNotProvided(err error) bool { + _, ok := err.(ErrSHAOrCommitIDNotProvided) + return ok +} + +func (err ErrSHAOrCommitIDNotProvided) Error() string { + return "a SHA or commit ID must be proved when updating a file" +} + // handles the check for various issues for ChangeRepoFiles func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRepoFilesOptions) error { if file.Operation == "update" || file.Operation == "delete" { @@ -286,7 +364,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep if file.SHA != "" { // If a SHA was given and the SHA given doesn't match the SHA of the fromTreePath, throw error if file.SHA != fromEntry.ID.String() { - return models.ErrSHADoesNotMatch{ + return pull_service.ErrSHADoesNotMatch{ Path: file.Options.treePath, GivenSHA: file.SHA, CurrentSHA: fromEntry.ID.String(), @@ -299,7 +377,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep if changed, err := commit.FileChangedSinceCommit(file.Options.treePath, opts.LastCommitID); err != nil { return err } else if changed { - return models.ErrCommitIDDoesNotMatch{ + return ErrCommitIDDoesNotMatch{ GivenCommitID: opts.LastCommitID, CurrentCommitID: opts.LastCommitID, } @@ -309,7 +387,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep } else { // When updating a file, a lastCommitID or SHA needs to be given to make sure other commits // haven't been made. We throw an error if one wasn't provided. - return models.ErrSHAOrCommitIDNotProvided{} + return ErrSHAOrCommitIDNotProvided{} } file.Options.executable = fromEntry.IsExecutable() } @@ -332,7 +410,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep } if index < len(treePathParts)-1 { if !entry.IsDir() { - return models.ErrFilePathInvalid{ + return ErrFilePathInvalid{ Message: fmt.Sprintf("a file exists where you’re trying to create a subdirectory [path: %s]", subTreePath), Path: subTreePath, Name: part, @@ -340,14 +418,14 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep } } } else if entry.IsLink() { - return models.ErrFilePathInvalid{ + return ErrFilePathInvalid{ Message: fmt.Sprintf("a symbolic link exists where you’re trying to create a subdirectory [path: %s]", subTreePath), Path: subTreePath, Name: part, Type: git.EntryModeSymlink, } } else if entry.IsDir() { - return models.ErrFilePathInvalid{ + return ErrFilePathInvalid{ Message: fmt.Sprintf("a directory exists where you’re trying to create a file [path: %s]", subTreePath), Path: subTreePath, Name: part, @@ -355,7 +433,7 @@ func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRep } } else if file.Options.fromTreePath != file.Options.treePath || file.Operation == "create" { // The entry shouldn't exist if we are creating new file or moving to a new path - return models.ErrRepoFileAlreadyExists{ + return ErrRepoFileAlreadyExists{ Path: file.Options.treePath, } } @@ -376,7 +454,7 @@ func CreateOrUpdateFile(ctx context.Context, t *TemporaryUploadRepository, file if file.Operation == "create" { for _, indexFile := range filesInIndex { if indexFile == file.TreePath { - return models.ErrRepoFileAlreadyExists{ + return ErrRepoFileAlreadyExists{ Path: file.TreePath, } } @@ -479,12 +557,12 @@ func VerifyBranchProtection(ctx context.Context, repo *repo_model.Repository, do isUnprotectedFile = protectedBranch.IsUnprotectedFile(globUnprotected, treePath) } if !canUserPush && !isUnprotectedFile { - return models.ErrUserCannotCommit{ + return ErrUserCannotCommit{ UserName: doer.LowerName, } } if protectedBranch.IsProtectedFile(globProtected, treePath) { - return models.ErrFilePathProtected{ + return pull_service.ErrFilePathProtected{ Path: treePath, } } @@ -495,7 +573,7 @@ func VerifyBranchProtection(ctx context.Context, repo *repo_model.Repository, do if !asymkey_service.IsErrWontSign(err) { return err } - return models.ErrUserCannotCommit{ + return ErrUserCannotCommit{ UserName: doer.LowerName, } } diff --git a/services/user/update.go b/services/user/update.go index cbaf90053a2..4a39f4f7836 100644 --- a/services/user/update.go +++ b/services/user/update.go @@ -7,7 +7,6 @@ import ( "context" "fmt" - "code.gitea.io/gitea/models" auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" password_module "code.gitea.io/gitea/modules/auth/password" @@ -113,7 +112,7 @@ func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) er } if opts.IsAdmin.Has() { if !opts.IsAdmin.Value() && user_model.IsLastAdminUser(ctx, u) { - return models.ErrDeleteLastAdminUser{UID: u.ID} + return user_model.ErrDeleteLastAdminUser{UID: u.ID} } u.IsAdmin = opts.IsAdmin.Value() diff --git a/services/user/user.go b/services/user/user.go index 7bde642412b..1aeebff142d 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -10,7 +10,6 @@ import ( "strings" "time" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" packages_model "code.gitea.io/gitea/models/packages" @@ -127,7 +126,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error { } if u.IsActive && user_model.IsLastAdminUser(ctx, u) { - return models.ErrDeleteLastAdminUser{UID: u.ID} + return user_model.ErrDeleteLastAdminUser{UID: u.ID} } if purge { @@ -225,7 +224,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error { if err != nil { return fmt.Errorf("GetRepositoryCount: %w", err) } else if count > 0 { - return models.ErrUserOwnRepos{UID: u.ID} + return repo_model.ErrUserOwnRepos{UID: u.ID} } // Check membership of organization. @@ -233,14 +232,14 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error { if err != nil { return fmt.Errorf("GetOrganizationCount: %w", err) } else if count > 0 { - return models.ErrUserHasOrgs{UID: u.ID} + return organization.ErrUserHasOrgs{UID: u.ID} } // Check ownership of packages. if ownsPackages, err := packages_model.HasOwnerPackages(ctx, u.ID); err != nil { return fmt.Errorf("HasOwnerPackages: %w", err) } else if ownsPackages { - return models.ErrUserOwnPackages{UID: u.ID} + return packages_model.ErrUserOwnPackages{UID: u.ID} } if err := deleteUser(ctx, u, purge); err != nil { @@ -288,7 +287,7 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) error { for _, u := range inactiveUsers { if err = DeleteUser(ctx, u, false); err != nil { // Ignore inactive users that were ever active but then were set inactive by admin - if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) || models.IsErrUserOwnPackages(err) { + if repo_model.IsErrUserOwnRepos(err) || organization.IsErrUserHasOrgs(err) || packages_model.IsErrUserOwnPackages(err) { log.Warn("Inactive user %q has repositories, organizations or packages, skipping deletion: %v", u.Name, err) continue } diff --git a/services/user/user_test.go b/services/user/user_test.go index c668b005c57..162a735cd43 100644 --- a/services/user/user_test.go +++ b/services/user/user_test.go @@ -9,7 +9,6 @@ import ( "testing" "time" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" @@ -37,7 +36,7 @@ func TestDeleteUser(t *testing.T) { if len(ownedRepos) > 0 { err := DeleteUser(db.DefaultContext, user, false) assert.Error(t, err) - assert.True(t, models.IsErrUserOwnRepos(err)) + assert.True(t, repo_model.IsErrUserOwnRepos(err)) return } diff --git a/tests/integration/api_repo_file_helpers.go b/tests/integration/api_repo_file_helpers.go index 4350092b8b1..d60cd1502ce 100644 --- a/tests/integration/api_repo_file_helpers.go +++ b/tests/integration/api_repo_file_helpers.go @@ -6,7 +6,6 @@ package integration import ( "strings" - "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" @@ -48,7 +47,7 @@ func deleteFileInBranch(user *user_model.User, repo *repo_model.Repository, tree func createOrReplaceFileInBranch(user *user_model.User, repo *repo_model.Repository, treePath, branchName, content string) error { _, err := deleteFileInBranch(user, repo, treePath, branchName) - if err != nil && !models.IsErrRepoFileDoesNotExist(err) { + if err != nil && !files_service.IsErrRepoFileDoesNotExist(err) { return err } diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index 1521fcfe8a8..2edc95d4c8d 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -18,7 +18,6 @@ import ( "testing" "time" - "code.gitea.io/gitea/models" auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" @@ -36,7 +35,7 @@ import ( "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/services/automerge" - "code.gitea.io/gitea/services/pull" + pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus" files_service "code.gitea.io/gitea/services/repository/files" @@ -267,13 +266,13 @@ func TestCantMergeConflict(t *testing.T) { gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo1) assert.NoError(t, err) - err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "CONFLICT", false) + err = pull_service.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "CONFLICT", false) assert.Error(t, err, "Merge should return an error due to conflict") - assert.True(t, models.IsErrMergeConflicts(err), "Merge error is not a conflict error") + assert.True(t, pull_service.IsErrMergeConflicts(err), "Merge error is not a conflict error") - err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleRebase, "", "CONFLICT", false) + err = pull_service.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleRebase, "", "CONFLICT", false) assert.Error(t, err, "Merge should return an error due to conflict") - assert.True(t, models.IsErrRebaseConflicts(err), "Merge error is not a conflict error") + assert.True(t, pull_service.IsErrRebaseConflicts(err), "Merge error is not a conflict error") gitRepo.Close() }) } @@ -366,9 +365,9 @@ func TestCantMergeUnrelated(t *testing.T) { BaseBranch: "base", }) - err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "UNRELATED", false) + err = pull_service.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "UNRELATED", false) assert.Error(t, err, "Merge should return an error due to unrelated") - assert.True(t, models.IsErrMergeUnrelatedHistories(err), "Merge error is not a unrelated histories error") + assert.True(t, pull_service.IsErrMergeUnrelatedHistories(err), "Merge error is not a unrelated histories error") gitRepo.Close() }) } @@ -406,7 +405,7 @@ func TestFastForwardOnlyMerge(t *testing.T) { gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name)) assert.NoError(t, err) - err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "FAST-FORWARD-ONLY", false) + err = pull_service.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "FAST-FORWARD-ONLY", false) assert.NoError(t, err) @@ -448,10 +447,10 @@ func TestCantFastForwardOnlyMergeDiverging(t *testing.T) { gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name)) assert.NoError(t, err) - err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "DIVERGING", false) + err = pull_service.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "DIVERGING", false) assert.Error(t, err, "Merge should return an error due to being for a diverging branch") - assert.True(t, models.IsErrMergeDivergingFastForwardOnly(err), "Merge error is not a diverging fast-forward-only error") + assert.True(t, pull_service.IsErrMergeDivergingFastForwardOnly(err), "Merge error is not a diverging fast-forward-only error") gitRepo.Close() }) @@ -520,8 +519,8 @@ func TestConflictChecking(t *testing.T) { BaseRepo: baseRepo, Type: issues_model.PullRequestGitea, } - prOpts := &pull.NewPullRequestOptions{Repo: baseRepo, Issue: pullIssue, PullRequest: pullRequest} - err = pull.NewPullRequest(git.DefaultContext, prOpts) + prOpts := &pull_service.NewPullRequestOptions{Repo: baseRepo, Issue: pullIssue, PullRequest: pullRequest} + err = pull_service.NewPullRequest(git.DefaultContext, prOpts) assert.NoError(t, err) issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{Title: "PR with conflict!"}) diff --git a/tests/integration/repo_tag_test.go b/tests/integration/repo_tag_test.go index 0cd49ee4cd3..5638826ea0e 100644 --- a/tests/integration/repo_tag_test.go +++ b/tests/integration/repo_tag_test.go @@ -9,7 +9,6 @@ import ( "net/url" "testing" - "code.gitea.io/gitea/models" auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" @@ -39,7 +38,7 @@ func TestCreateNewTagProtected(t *testing.T) { err = release.CreateNewTag(git.DefaultContext, owner, repo, "master", "v-2", "second tag") assert.Error(t, err) - assert.True(t, models.IsErrProtectedTagName(err)) + assert.True(t, release.IsErrProtectedTagName(err)) err = release.CreateNewTag(git.DefaultContext, owner, repo, "master", "v-1.1", "third tag") assert.NoError(t, err)