Skip to content

Delete related PullAutoMerge and ReviewState on User/Repo Deletion #19649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 8, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions models/db/error.go
Original file line number Diff line number Diff line change
@@ -42,3 +42,18 @@ func IsErrSSHDisabled(err error) bool {
func (err ErrSSHDisabled) Error() string {
return "SSH is disabled"
}

// ErrNotExist represents a non-exist error.
type ErrNotExist struct {
ID int64
}

// IsErrNotExist checks if an error is an ErrNotExist
func IsErrNotExist(err error) bool {
_, ok := err.(ErrNotExist)
return ok
}

func (err ErrNotExist) Error() string {
return fmt.Sprintf("record does not exist [id: %d]", err.ID)
}
15 changes: 0 additions & 15 deletions models/error.go
Original file line number Diff line number Diff line change
@@ -13,21 +13,6 @@ import (
"code.gitea.io/gitea/modules/git"
)

// ErrNotExist represents a non-exist error.
type ErrNotExist struct {
ID int64
}

// IsErrNotExist checks if an error is an ErrNotExist
func IsErrNotExist(err error) bool {
_, ok := err.(ErrNotExist)
return ok
}

func (err ErrNotExist) Error() string {
return fmt.Sprintf("record does not exist [id: %d]", err.ID)
}

// ErrUserOwnRepos represents a "UserOwnRepos" kind of error.
type ErrUserOwnRepos struct {
UID int64
22 changes: 22 additions & 0 deletions models/issue_comment.go
Original file line number Diff line number Diff line change
@@ -1360,6 +1360,28 @@ func CreatePushPullComment(ctx context.Context, pusher *user_model.User, pr *Pul
return
}

// CreateAutoMergeComment is a internal function, only use it for CommentTypePRScheduledToAutoMerge and CommentTypePRUnScheduledToAutoMerge CommentTypes
func CreateAutoMergeComment(ctx context.Context, typ CommentType, pr *PullRequest, doer *user_model.User) (comment *Comment, err error) {
if typ != CommentTypePRScheduledToAutoMerge && typ != CommentTypePRUnScheduledToAutoMerge {
return nil, fmt.Errorf("comment type %d cannot be used to create an auto merge comment", typ)
}
if err = pr.LoadIssueCtx(ctx); err != nil {
return
}

if err = pr.LoadBaseRepoCtx(ctx); err != nil {
return
}

comment, err = CreateCommentCtx(ctx, &CreateCommentOptions{
Type: typ,
Doer: doer,
Repo: pr.BaseRepo,
Issue: pr.Issue,
})
return
}

// getCommitsFromRepo get commit IDs from repo in between oldCommitID and newCommitID
// isForcePush will be true if oldCommit isn't on the branch
// Commit on baseBranch will skip
6 changes: 3 additions & 3 deletions models/issue_tracked_time.go
Original file line number Diff line number Diff line change
@@ -251,7 +251,7 @@ func DeleteIssueUserTimes(issue *Issue, user *user_model.User) error {
return err
}
if removedTime == 0 {
return ErrNotExist{}
return db.ErrNotExist{}
}

if err := issue.LoadRepo(ctx); err != nil {
@@ -311,7 +311,7 @@ func deleteTimes(e db.Engine, opts FindTrackedTimesOptions) (removedTime int64,

func deleteTime(e db.Engine, t *TrackedTime) error {
if t.Deleted {
return ErrNotExist{ID: t.ID}
return db.ErrNotExist{ID: t.ID}
}
t.Deleted = true
_, err := e.ID(t.ID).Cols("deleted").Update(t)
@@ -325,7 +325,7 @@ func GetTrackedTimeByID(id int64) (*TrackedTime, error) {
if err != nil {
return nil, err
} else if !has {
return nil, ErrNotExist{ID: id}
return nil, db.ErrNotExist{ID: id}
}
return time, nil
}
2 changes: 1 addition & 1 deletion models/migrations/v215.go
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ func addReviewViewedFiles(x *xorm.Engine) error {
type ReviewState struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"NOT NULL UNIQUE(pull_commit_user)"`
PullID int64 `xorm:"NOT NULL UNIQUE(pull_commit_user) DEFAULT 0"`
PullID int64 `xorm:"NOT NULL INDEX UNIQUE(pull_commit_user) DEFAULT 0"`
CommitSHA string `xorm:"NOT NULL VARCHAR(40) UNIQUE(pull_commit_user)"`
UpdatedFiles map[string]pull.ViewedState `xorm:"NOT NULL LONGTEXT JSON"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
2 changes: 1 addition & 1 deletion models/notification.go
Original file line number Diff line number Diff line change
@@ -825,7 +825,7 @@ func getNotificationByID(e db.Engine, notificationID int64) (*Notification, erro
}

if !ok {
return nil, ErrNotExist{ID: notificationID}
return nil, db.ErrNotExist{ID: notificationID}
}

return notification, nil
20 changes: 20 additions & 0 deletions models/pull.go
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ import (
"strings"

"code.gitea.io/gitea/models/db"
pull_model "code.gitea.io/gitea/models/pull"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
@@ -96,6 +97,25 @@ func init() {
db.RegisterModel(new(PullRequest))
}

func deletePullsByBaseRepoID(sess db.Engine, repoID int64) error {
deleteCond := builder.Select("id").From("pull_request").Where(builder.Eq{"pull_request.base_repo_id": repoID})

// Delete scheduled auto merges
if _, err := sess.In("pull_id", deleteCond).
Delete(&pull_model.AutoMerge{}); err != nil {
return err
}

// Delete review states
if _, err := sess.In("pull_id", deleteCond).
Delete(&pull_model.ReviewState{}); err != nil {
return err
}

_, err := sess.Delete(&PullRequest{BaseRepoID: repoID})
return err
}

// MustHeadUserName returns the HeadRepo's username if failed return blank
func (pr *PullRequest) MustHeadUserName() string {
if err := pr.LoadHeadRepo(); err != nil {
65 changes: 10 additions & 55 deletions models/pull/automerge.go
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ import (
"context"
"fmt"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@@ -59,21 +58,12 @@ func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64,
return ErrAlreadyScheduledToAutoMerge{PullID: pullID}
}

if _, err := db.GetEngine(ctx).Insert(&AutoMerge{
_, err := db.GetEngine(ctx).Insert(&AutoMerge{
DoerID: doer.ID,
PullID: pullID,
MergeStyle: style,
Message: message,
}); err != nil {
return err
}

pr, err := models.GetPullRequestByID(ctx, pullID)
if err != nil {
return err
}

_, err = createAutoMergeComment(ctx, models.CommentTypePRScheduledToAutoMerge, pr, doer)
})
return err
}

@@ -94,50 +84,15 @@ func GetScheduledMergeByPullID(ctx context.Context, pullID int64) (bool, *AutoMe
return true, scheduledPRM, nil
}

// RemoveScheduledAutoMerge cancels a previously scheduled pull request
func RemoveScheduledAutoMerge(ctx context.Context, doer *user_model.User, pullID int64, comment bool) error {
return db.WithTx(func(ctx context.Context) error {
exist, scheduledPRM, err := GetScheduledMergeByPullID(ctx, pullID)
if err != nil {
return err
} else if !exist {
return models.ErrNotExist{ID: pullID}
}

if _, err := db.GetEngine(ctx).ID(scheduledPRM.ID).Delete(&AutoMerge{}); err != nil {
return err
}

// if pull got merged we don't need to add "auto-merge canceled comment"
if !comment || doer == nil {
return nil
}

pr, err := models.GetPullRequestByID(ctx, pullID)
if err != nil {
return err
}

_, err = createAutoMergeComment(ctx, models.CommentTypePRUnScheduledToAutoMerge, pr, doer)
// DeleteScheduledAutoMerge delete a scheduled pull request
func DeleteScheduledAutoMerge(ctx context.Context, pullID int64) error {
exist, scheduledPRM, err := GetScheduledMergeByPullID(ctx, pullID)
if err != nil {
return err
}, ctx)
}

// createAutoMergeComment is a internal function, only use it for CommentTypePRScheduledToAutoMerge and CommentTypePRUnScheduledToAutoMerge CommentTypes
func createAutoMergeComment(ctx context.Context, typ models.CommentType, pr *models.PullRequest, doer *user_model.User) (comment *models.Comment, err error) {
if err = pr.LoadIssueCtx(ctx); err != nil {
return
} else if !exist {
return db.ErrNotExist{ID: pullID}
}

if err = pr.LoadBaseRepoCtx(ctx); err != nil {
return
}

comment, err = models.CreateCommentCtx(ctx, &models.CreateCommentOptions{
Type: typ,
Doer: doer,
Repo: pr.BaseRepo,
Issue: pr.Issue,
})
return
_, err = db.GetEngine(ctx).ID(scheduledPRM.ID).Delete(&AutoMerge{})
return err
}
8 changes: 4 additions & 4 deletions models/pull/review_state.go
Original file line number Diff line number Diff line change
@@ -38,10 +38,10 @@ func (viewedState ViewedState) String() string {
type ReviewState struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"NOT NULL UNIQUE(pull_commit_user)"`
PullID int64 `xorm:"NOT NULL UNIQUE(pull_commit_user) DEFAULT 0"` // Which PR was the review on?
CommitSHA string `xorm:"NOT NULL VARCHAR(40) UNIQUE(pull_commit_user)"` // Which commit was the head commit for the review?
UpdatedFiles map[string]ViewedState `xorm:"NOT NULL LONGTEXT JSON"` // Stores for each of the changed files of a PR whether they have been viewed, changed since last viewed, or not viewed
UpdatedUnix timeutil.TimeStamp `xorm:"updated"` // Is an accurate indicator of the order of commits as we do not expect it to be possible to make reviews on previous commits
PullID int64 `xorm:"NOT NULL INDEX UNIQUE(pull_commit_user) DEFAULT 0"` // Which PR was the review on?
CommitSHA string `xorm:"NOT NULL VARCHAR(40) UNIQUE(pull_commit_user)"` // Which commit was the head commit for the review?
UpdatedFiles map[string]ViewedState `xorm:"NOT NULL LONGTEXT JSON"` // Stores for each of the changed files of a PR whether they have been viewed, changed since last viewed, or not viewed
UpdatedUnix timeutil.TimeStamp `xorm:"updated"` // Is an accurate indicator of the order of commits as we do not expect it to be possible to make reviews on previous commits
}

func init() {
6 changes: 5 additions & 1 deletion models/repo.go
Original file line number Diff line number Diff line change
@@ -704,7 +704,6 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
&Notification{RepoID: repoID},
&ProtectedBranch{RepoID: repoID},
&ProtectedTag{RepoID: repoID},
&PullRequest{BaseRepoID: repoID},
&repo_model.PushMirror{RepoID: repoID},
&Release{RepoID: repoID},
&repo_model.RepoIndexerStatus{RepoID: repoID},
@@ -723,6 +722,11 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
return err
}

// Delete Pulls and related objects
if err := deletePullsByBaseRepoID(sess, repoID); err != nil {
return err
}

// Delete Issues and related objects
var attachmentPaths []string
if attachmentPaths, err = deleteIssuesByRepoID(sess, repoID); err != nil {
3 changes: 3 additions & 0 deletions models/user.go
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
pull_model "code.gitea.io/gitea/models/pull"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
@@ -82,6 +83,8 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
&Collaboration{UserID: u.ID},
&Stopwatch{UserID: u.ID},
&user_model.Setting{UserID: u.ID},
&pull_model.AutoMerge{DoerID: u.ID},
&pull_model.ReviewState{UserID: u.ID},
); err != nil {
return fmt.Errorf("deleteBeans: %v", err)
}
3 changes: 2 additions & 1 deletion routers/api/v1/notify/threads.go
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ import (
"net/http"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
)
@@ -102,7 +103,7 @@ func ReadThread(ctx *context.APIContext) {
func getThread(ctx *context.APIContext) *models.Notification {
n, err := models.GetNotificationByID(ctx.ParamsInt64(":id"))
if err != nil {
if models.IsErrNotExist(err) {
if db.IsErrNotExist(err) {
ctx.Error(http.StatusNotFound, "GetNotificationByID", err)
} else {
ctx.InternalServerError(err)
5 changes: 3 additions & 2 deletions routers/api/v1/repo/issue_tracked_time.go
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ import (
"time"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
@@ -281,7 +282,7 @@ func ResetIssueTime(ctx *context.APIContext) {

err = models.DeleteIssueUserTimes(issue, ctx.Doer)
if err != nil {
if models.IsErrNotExist(err) {
if db.IsErrNotExist(err) {
ctx.Error(http.StatusNotFound, "DeleteIssueUserTimes", err)
} else {
ctx.Error(http.StatusInternalServerError, "DeleteIssueUserTimes", err)
@@ -352,7 +353,7 @@ func DeleteTime(ctx *context.APIContext) {

time, err := models.GetTrackedTimeByID(ctx.ParamsInt64(":id"))
if err != nil {
if models.IsErrNotExist(err) {
if db.IsErrNotExist(err) {
ctx.NotFound(err)
return
}
2 changes: 1 addition & 1 deletion routers/api/v1/repo/pull.go
Original file line number Diff line number Diff line change
@@ -1208,7 +1208,7 @@ func CancelScheduledAutoMerge(ctx *context.APIContext) {
}
}

if err := pull_model.RemoveScheduledAutoMerge(ctx, ctx.Doer, pull.ID, true); err != nil {
if err := automerge.RemoveScheduledAutoMerge(ctx, ctx.Doer, pull); err != nil {
ctx.InternalServerError(err)
} else {
ctx.Status(http.StatusNoContent)
3 changes: 2 additions & 1 deletion routers/web/repo/issue_timetrack.go
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ import (
"time"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
@@ -63,7 +64,7 @@ func DeleteTime(c *context.Context) {

t, err := models.GetTrackedTimeByID(c.ParamsInt64(":timeid"))
if err != nil {
if models.IsErrNotExist(err) {
if db.IsErrNotExist(err) {
c.NotFound("time not found", err)
return
}
Loading