loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request 'Do not update PRs based on events that happened before they existed' (#2932) from earl-warren/forgejo:wip-superfluous into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2932
Reviewed-by: oliverpool <oliverpool@noreply.codeberg.org>

+424 -98
+12
models/fixtures/TestGetUnmergedPullRequestsByHeadInfoMax/issue.yml
··· 1 + - 2 + id: 1001 3 + repo_id: 1 4 + index: 1001 5 + poster_id: 1 6 + name: issue1 7 + content: content for the first issue 8 + is_pull: true 9 + created: 111111111 10 + created_unix: 946684800 11 + updated_unix: 978307200 12 + is_closed: false
+13
models/fixtures/TestGetUnmergedPullRequestsByHeadInfoMax/pull_request.yml
··· 1 + - 2 + id: 1001 3 + type: 0 # pull request 4 + status: 2 # mergable 5 + issue_id: 1001 6 + index: 1001 7 + head_repo_id: 1 8 + base_repo_id: 1 9 + head_branch: branchmax 10 + base_branch: master 11 + merge_base: 4a357436d925b5c974181ff12a994538ddc5a269 12 + has_merged: false 13 + flow: 0
+2
models/forgejo_migrations/migrate.go
··· 59 59 // v9 -> v10 60 60 NewMigration("Add pronouns to user", forgejo_v1_22.AddPronounsToUser), 61 61 // v11 -> v12 62 + NewMigration("Add the `created` column to the `issue` table", forgejo_v1_22.AddCreatedToIssue), 63 + // v12 -> v13 62 64 NewMigration("Add repo_archive_download_count table", forgejo_v1_22.AddRepoArchiveDownloadCount), 63 65 } 64 66
+10 -9
models/forgejo_migrations/v1_22/v11.go
··· 3 3 4 4 package v1_22 //nolint 5 5 6 - import "xorm.io/xorm" 6 + import ( 7 + "code.gitea.io/gitea/modules/timeutil" 7 8 8 - func AddRepoArchiveDownloadCount(x *xorm.Engine) error { 9 - type RepoArchiveDownloadCount struct { 10 - ID int64 `xorm:"pk autoincr"` 11 - RepoID int64 `xorm:"index unique(s)"` 12 - ReleaseID int64 `xorm:"index unique(s)"` 13 - Type int `xorm:"unique(s)"` 14 - Count int64 9 + "xorm.io/xorm" 10 + ) 11 + 12 + func AddCreatedToIssue(x *xorm.Engine) error { 13 + type Issue struct { 14 + ID int64 `xorm:"pk autoincr"` 15 + Created timeutil.TimeStampNano 15 16 } 16 17 17 - return x.Sync(&RepoArchiveDownloadCount{}) 18 + return x.Sync(&Issue{}) 18 19 }
+18
models/forgejo_migrations/v1_22/v12.go
··· 1 + // Copyright 2024 The Forgejo Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package v1_22 //nolint 5 + 6 + import "xorm.io/xorm" 7 + 8 + func AddRepoArchiveDownloadCount(x *xorm.Engine) error { 9 + type RepoArchiveDownloadCount struct { 10 + ID int64 `xorm:"pk autoincr"` 11 + RepoID int64 `xorm:"index unique(s)"` 12 + ReleaseID int64 `xorm:"index unique(s)"` 13 + Type int `xorm:"unique(s)"` 14 + Count int64 15 + } 16 + 17 + return x.Sync(&RepoArchiveDownloadCount{}) 18 + }
+2
models/issues/issue.go
··· 124 124 125 125 DeadlineUnix timeutil.TimeStamp `xorm:"INDEX"` 126 126 127 + Created timeutil.TimeStampNano 128 + 127 129 CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` 128 130 UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` 129 131 ClosedUnix timeutil.TimeStamp `xorm:"INDEX"`
+10 -2
models/issues/issue_index.go
··· 9 9 "code.gitea.io/gitea/models/db" 10 10 ) 11 11 12 + func GetMaxIssueIndexForRepo(ctx context.Context, repoID int64) (int64, error) { 13 + var max int64 14 + if _, err := db.GetEngine(ctx).Select("MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil { 15 + return 0, err 16 + } 17 + return max, nil 18 + } 19 + 12 20 // RecalculateIssueIndexForRepo create issue_index for repo if not exist and 13 21 // update it based on highest index of existing issues assigned to a repo 14 22 func RecalculateIssueIndexForRepo(ctx context.Context, repoID int64) error { ··· 18 26 } 19 27 defer committer.Close() 20 28 21 - var max int64 22 - if _, err = db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil { 29 + max, err := GetMaxIssueIndexForRepo(ctx, repoID) 30 + if err != nil { 23 31 return err 24 32 } 25 33
+38
models/issues/issue_index_test.go
··· 1 + // Copyright 2024 The Forgejo Authors 2 + // SPDX-License-Identifier: MIT 3 + 4 + package issues_test 5 + 6 + import ( 7 + "testing" 8 + 9 + "code.gitea.io/gitea/models/db" 10 + issues_model "code.gitea.io/gitea/models/issues" 11 + repo_model "code.gitea.io/gitea/models/repo" 12 + "code.gitea.io/gitea/models/unittest" 13 + 14 + "github.com/stretchr/testify/assert" 15 + ) 16 + 17 + func TestGetMaxIssueIndexForRepo(t *testing.T) { 18 + assert.NoError(t, unittest.PrepareTestDatabase()) 19 + 20 + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 21 + 22 + maxPR, err := issues_model.GetMaxIssueIndexForRepo(db.DefaultContext, repo.ID) 23 + assert.NoError(t, err) 24 + 25 + issue := testCreateIssue(t, repo.ID, repo.OwnerID, "title1", "content1", false) 26 + assert.Greater(t, issue.Index, maxPR) 27 + 28 + maxPR, err = issues_model.GetMaxIssueIndexForRepo(db.DefaultContext, repo.ID) 29 + assert.NoError(t, err) 30 + 31 + pull := testCreateIssue(t, repo.ID, repo.OwnerID, "title2", "content2", true) 32 + assert.Greater(t, pull.Index, maxPR) 33 + 34 + maxPR, err = issues_model.GetMaxIssueIndexForRepo(db.DefaultContext, repo.ID) 35 + assert.NoError(t, err) 36 + 37 + assert.Equal(t, maxPR, pull.Index) 38 + }
+2
models/issues/issue_update.go
··· 325 325 return fmt.Errorf("issue exist") 326 326 } 327 327 328 + opts.Issue.Created = timeutil.TimeStampNanoNow() 329 + 328 330 if _, err := e.Insert(opts.Issue); err != nil { 329 331 return err 330 332 }
+8
models/issues/pull_list.go
··· 47 47 return sess, nil 48 48 } 49 49 50 + func GetUnmergedPullRequestsByHeadInfoMax(ctx context.Context, repoID, olderThan int64, branch string) ([]*PullRequest, error) { 51 + prs := make([]*PullRequest, 0, 2) 52 + sess := db.GetEngine(ctx). 53 + Join("INNER", "issue", "issue.id = `pull_request`.issue_id"). 54 + Where("`pull_request`.head_repo_id = ? AND `pull_request`.head_branch = ? AND `pull_request`.has_merged = ? AND `issue`.is_closed = ? AND `pull_request`.flow = ? AND (`issue`.`created` IS NULL OR `issue`.`created` <= ?)", repoID, branch, false, false, PullRequestFlowGithub, olderThan) 55 + return prs, sess.Find(&prs) 56 + } 57 + 50 58 // GetUnmergedPullRequestsByHeadInfo returns all pull requests that are open and has not been merged 51 59 func GetUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch string) ([]*PullRequest, error) { 52 60 prs := make([]*PullRequest, 0, 2)
+97
models/issues/pull_test.go
··· 4 4 package issues_test 5 5 6 6 import ( 7 + "fmt" 7 8 "testing" 9 + "time" 8 10 9 11 "code.gitea.io/gitea/models/db" 10 12 issues_model "code.gitea.io/gitea/models/issues" ··· 12 14 "code.gitea.io/gitea/models/unittest" 13 15 user_model "code.gitea.io/gitea/models/user" 14 16 "code.gitea.io/gitea/modules/setting" 17 + "code.gitea.io/gitea/tests" 15 18 16 19 "github.com/stretchr/testify/assert" 17 20 ) ··· 153 156 for _, pr := range prs { 154 157 assert.Equal(t, int64(1), pr.HeadRepoID) 155 158 assert.Equal(t, "branch2", pr.HeadBranch) 159 + } 160 + } 161 + 162 + func TestGetUnmergedPullRequestsByHeadInfoMax(t *testing.T) { 163 + defer tests.AddFixtures("models/fixtures/TestGetUnmergedPullRequestsByHeadInfoMax/")() 164 + assert.NoError(t, unittest.PrepareTestDatabase()) 165 + 166 + repoID := int64(1) 167 + olderThan := int64(0) 168 + 169 + // for NULL created field the olderThan condition is ignored 170 + prs, err := issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, olderThan, "branch2") 171 + assert.NoError(t, err) 172 + assert.Equal(t, int64(1), prs[0].HeadRepoID) 173 + 174 + // test for when the created field is set 175 + branch := "branchmax" 176 + prs, err = issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, olderThan, branch) 177 + assert.NoError(t, err) 178 + assert.Len(t, prs, 0) 179 + olderThan = time.Now().UnixNano() 180 + assert.NoError(t, err) 181 + prs, err = issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, olderThan, branch) 182 + assert.NoError(t, err) 183 + assert.Len(t, prs, 1) 184 + for _, pr := range prs { 185 + assert.Equal(t, int64(1), pr.HeadRepoID) 186 + assert.Equal(t, branch, pr.HeadBranch) 187 + } 188 + pr := prs[0] 189 + 190 + for _, testCase := range []struct { 191 + table string 192 + field string 193 + id int64 194 + match any 195 + nomatch any 196 + }{ 197 + { 198 + table: "issue", 199 + field: "is_closed", 200 + id: pr.IssueID, 201 + match: false, 202 + nomatch: true, 203 + }, 204 + { 205 + table: "pull_request", 206 + field: "flow", 207 + id: pr.ID, 208 + match: issues_model.PullRequestFlowGithub, 209 + nomatch: issues_model.PullRequestFlowAGit, 210 + }, 211 + { 212 + table: "pull_request", 213 + field: "head_repo_id", 214 + id: pr.ID, 215 + match: pr.HeadRepoID, 216 + nomatch: 0, 217 + }, 218 + { 219 + table: "pull_request", 220 + field: "head_branch", 221 + id: pr.ID, 222 + match: pr.HeadBranch, 223 + nomatch: "something else", 224 + }, 225 + { 226 + table: "pull_request", 227 + field: "has_merged", 228 + id: pr.ID, 229 + match: false, 230 + nomatch: true, 231 + }, 232 + } { 233 + t.Run(testCase.field, func(t *testing.T) { 234 + update := fmt.Sprintf("UPDATE `%s` SET `%s` = ? WHERE `id` = ?", testCase.table, testCase.field) 235 + 236 + // expect no match 237 + _, err = db.GetEngine(db.DefaultContext).Exec(update, testCase.nomatch, testCase.id) 238 + assert.NoError(t, err) 239 + prs, err = issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, olderThan, branch) 240 + assert.NoError(t, err) 241 + assert.Len(t, prs, 0) 242 + 243 + // expect one match 244 + _, err = db.GetEngine(db.DefaultContext).Exec(update, testCase.match, testCase.id) 245 + assert.NoError(t, err) 246 + prs, err = issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, olderThan, branch) 247 + assert.NoError(t, err) 248 + assert.Len(t, prs, 1) 249 + 250 + // identical to the known PR 251 + assert.Equal(t, pr.ID, prs[0].ID) 252 + }) 156 253 } 157 254 } 158 255
+1
modules/repository/push.go
··· 16 16 RefFullName git.RefName // branch, tag or other name to push 17 17 OldCommitID string 18 18 NewCommitID string 19 + TimeNano int64 19 20 } 20 21 21 22 // IsNewRef return true if it's a first-time push to a branch, tag or etc.
+2
routers/private/hook_post_receive.go
··· 7 7 "fmt" 8 8 "net/http" 9 9 "strconv" 10 + "time" 10 11 11 12 git_model "code.gitea.io/gitea/models/git" 12 13 issues_model "code.gitea.io/gitea/models/issues" ··· 71 72 PusherName: opts.UserName, 72 73 RepoUserName: ownerName, 73 74 RepoName: repoName, 75 + TimeNano: time.Now().UnixNano(), 74 76 } 75 77 updates = append(updates, option) 76 78 if repo.IsEmpty && (refFullName.BranchName() == "master" || refFullName.BranchName() == "main") {
+1 -1
services/pull/merge.go
··· 187 187 } 188 188 189 189 defer func() { 190 - go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "") 190 + AddTestPullRequestTask(ctx, doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "", 0) 191 191 }() 192 192 193 193 pr.MergedCommitID, err = doMergeAndPush(ctx, pr, doer, mergeStyle, expectedHeadCommitID, message)
+96 -83
services/pull/pull.go
··· 296 296 297 297 // AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch, 298 298 // and generate new patch for testing as needed. 299 - func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, isSync bool, oldCommitID, newCommitID string) { 300 - description := fmt.Sprintf("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch) 299 + func AddTestPullRequestTask(ctx context.Context, doer *user_model.User, repoID int64, branch string, isSync bool, oldCommitID, newCommitID string, timeNano int64) { 300 + description := fmt.Sprintf("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: only pull requests created before nano time %d will be considered", repoID, branch, timeNano) 301 301 log.Trace(description) 302 - graceful.GetManager().RunWithShutdownContext(func(shutdownCtx context.Context) { 302 + go graceful.GetManager().RunWithShutdownContext(func(shutdownCtx context.Context) { 303 303 // make it a process to allow for cancellation (especially during integration tests where no global shutdown happens) 304 304 ctx, _, finished := process.GetManager().AddContext(shutdownCtx, description) 305 305 defer finished() 306 306 // There is no sensible way to shut this down ":-(" 307 307 // If you don't let it run all the way then you will lose data 308 - // TODO: graceful: AddTestPullRequestTask needs to become a queue! 308 + // TODO: graceful: TestPullRequest needs to become a queue! 309 + 310 + TestPullRequest(ctx, doer, repoID, timeNano, branch, isSync, oldCommitID, newCommitID) 311 + }) 312 + } 309 313 310 - // GetUnmergedPullRequestsByHeadInfo() only return open and unmerged PR. 311 - prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(ctx, repoID, branch) 312 - if err != nil { 313 - log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err) 314 - return 315 - } 314 + func TestPullRequest(ctx context.Context, doer *user_model.User, repoID, olderThan int64, branch string, isSync bool, oldCommitID, newCommitID string) { 315 + // Only consider PR that are older than olderThan, which is the time at 316 + // which the newCommitID was added to repoID. 317 + // 318 + // * commit C is pushed 319 + // * the git hook queues AddTestPullRequestTask for processing and returns with success 320 + // * TestPullRequest is not called yet 321 + // * a pull request P with commit C as the head is created 322 + // * TestPullRequest runs and ignores P because it was created after the commit was received 323 + // 324 + // In other words, a PR must not be updated based on events that happened before it existed 325 + prs, err := issues_model.GetUnmergedPullRequestsByHeadInfoMax(ctx, repoID, olderThan, branch) 326 + if err != nil { 327 + log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err) 328 + return 329 + } 316 330 317 - for _, pr := range prs { 318 - log.Trace("Updating PR[%d]: composing new test task", pr.ID) 319 - if pr.Flow == issues_model.PullRequestFlowGithub { 320 - if err := PushToBaseRepo(ctx, pr); err != nil { 321 - log.Error("PushToBaseRepo: %v", err) 322 - continue 323 - } 324 - } else { 331 + for _, pr := range prs { 332 + log.Trace("Updating PR[id=%d,index=%d]: composing new test task", pr.ID, pr.Index) 333 + if pr.Flow == issues_model.PullRequestFlowGithub { 334 + if err := PushToBaseRepo(ctx, pr); err != nil { 335 + log.Error("PushToBaseRepo: %v", err) 325 336 continue 326 337 } 338 + } else { 339 + continue 340 + } 341 + 342 + AddToTaskQueue(ctx, pr) 343 + comment, err := CreatePushPullComment(ctx, doer, pr, oldCommitID, newCommitID) 344 + if err == nil && comment != nil { 345 + notify_service.PullRequestPushCommits(ctx, doer, pr, comment) 346 + } 347 + } 327 348 328 - AddToTaskQueue(ctx, pr) 329 - comment, err := CreatePushPullComment(ctx, doer, pr, oldCommitID, newCommitID) 330 - if err == nil && comment != nil { 331 - notify_service.PullRequestPushCommits(ctx, doer, pr, comment) 332 - } 349 + if isSync { 350 + requests := issues_model.PullRequestList(prs) 351 + if err = requests.LoadAttributes(ctx); err != nil { 352 + log.Error("PullRequestList.LoadAttributes: %v", err) 353 + } 354 + if invalidationErr := checkForInvalidation(ctx, requests, repoID, doer, branch); invalidationErr != nil { 355 + log.Error("checkForInvalidation: %v", invalidationErr) 333 356 } 357 + if err == nil { 358 + for _, pr := range prs { 359 + objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) 360 + if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() { 361 + changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID) 362 + if err != nil { 363 + log.Error("checkIfPRContentChanged: %v", err) 364 + } 365 + if changed { 366 + // Mark old reviews as stale if diff to mergebase has changed 367 + if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil { 368 + log.Error("MarkReviewsAsStale: %v", err) 369 + } 334 370 335 - if isSync { 336 - requests := issues_model.PullRequestList(prs) 337 - if err = requests.LoadAttributes(ctx); err != nil { 338 - log.Error("PullRequestList.LoadAttributes: %v", err) 339 - } 340 - if invalidationErr := checkForInvalidation(ctx, requests, repoID, doer, branch); invalidationErr != nil { 341 - log.Error("checkForInvalidation: %v", invalidationErr) 342 - } 343 - if err == nil { 344 - for _, pr := range prs { 345 - objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) 346 - if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() { 347 - changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID) 371 + // dismiss all approval reviews if protected branch rule item enabled. 372 + pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch) 348 373 if err != nil { 349 - log.Error("checkIfPRContentChanged: %v", err) 374 + log.Error("GetFirstMatchProtectedBranchRule: %v", err) 350 375 } 351 - if changed { 352 - // Mark old reviews as stale if diff to mergebase has changed 353 - if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil { 354 - log.Error("MarkReviewsAsStale: %v", err) 355 - } 356 - 357 - // dismiss all approval reviews if protected branch rule item enabled. 358 - pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch) 359 - if err != nil { 360 - log.Error("GetFirstMatchProtectedBranchRule: %v", err) 361 - } 362 - if pb != nil && pb.DismissStaleApprovals { 363 - if err := DismissApprovalReviews(ctx, doer, pr); err != nil { 364 - log.Error("DismissApprovalReviews: %v", err) 365 - } 376 + if pb != nil && pb.DismissStaleApprovals { 377 + if err := DismissApprovalReviews(ctx, doer, pr); err != nil { 378 + log.Error("DismissApprovalReviews: %v", err) 366 379 } 367 380 } 368 - if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, newCommitID); err != nil { 369 - log.Error("MarkReviewsAsNotStale: %v", err) 370 - } 371 - divergence, err := GetDiverging(ctx, pr) 381 + } 382 + if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, newCommitID); err != nil { 383 + log.Error("MarkReviewsAsNotStale: %v", err) 384 + } 385 + divergence, err := GetDiverging(ctx, pr) 386 + if err != nil { 387 + log.Error("GetDiverging: %v", err) 388 + } else { 389 + err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind) 372 390 if err != nil { 373 - log.Error("GetDiverging: %v", err) 374 - } else { 375 - err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind) 376 - if err != nil { 377 - log.Error("UpdateCommitDivergence: %v", err) 378 - } 391 + log.Error("UpdateCommitDivergence: %v", err) 379 392 } 380 393 } 381 - 382 - notify_service.PullRequestSynchronized(ctx, doer, pr) 383 394 } 395 + 396 + notify_service.PullRequestSynchronized(ctx, doer, pr) 384 397 } 385 398 } 399 + } 386 400 387 - log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch) 388 - prs, err = issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, repoID, branch) 401 + log.Trace("TestPullRequest [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch) 402 + prs, err = issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, repoID, branch) 403 + if err != nil { 404 + log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err) 405 + return 406 + } 407 + for _, pr := range prs { 408 + divergence, err := GetDiverging(ctx, pr) 389 409 if err != nil { 390 - log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err) 391 - return 392 - } 393 - for _, pr := range prs { 394 - divergence, err := GetDiverging(ctx, pr) 395 - if err != nil { 396 - if git_model.IsErrBranchNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) { 397 - log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch) 398 - } else { 399 - log.Error("GetDiverging: %v", err) 400 - } 410 + if git_model.IsErrBranchNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) { 411 + log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch) 401 412 } else { 402 - err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind) 403 - if err != nil { 404 - log.Error("UpdateCommitDivergence: %v", err) 405 - } 413 + log.Error("GetDiverging: %v", err) 414 + } 415 + } else { 416 + err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind) 417 + if err != nil { 418 + log.Error("UpdateCommitDivergence: %v", err) 406 419 } 407 - AddToTaskQueue(ctx, pr) 408 420 } 409 - }) 421 + AddToTaskQueue(ctx, pr) 422 + } 410 423 } 411 424 412 425 // checkIfPRContentChanged checks if diff to target branch has changed by push
+2 -2
services/pull/update.go
··· 36 36 37 37 if rebase { 38 38 defer func() { 39 - go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "") 39 + AddTestPullRequestTask(ctx, doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "", 0) 40 40 }() 41 41 42 42 return updateHeadByRebaseOnToBase(ctx, pr, doer, message) ··· 75 75 _, err = doMergeAndPush(ctx, reversePR, doer, repo_model.MergeStyleMerge, "", message) 76 76 77 77 defer func() { 78 - go AddTestPullRequestTask(doer, reversePR.HeadRepo.ID, reversePR.HeadBranch, false, "", "") 78 + AddTestPullRequestTask(ctx, doer, reversePR.HeadRepo.ID, reversePR.HeadBranch, false, "", "", 0) 79 79 }() 80 80 81 81 return err
+1 -1
services/repository/push.go
··· 166 166 branch := opts.RefFullName.BranchName() 167 167 if !opts.IsDelRef() { 168 168 log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name) 169 - go pull_service.AddTestPullRequestTask(pusher, repo.ID, branch, true, opts.OldCommitID, opts.NewCommitID) 169 + pull_service.AddTestPullRequestTask(ctx, pusher, repo.ID, branch, true, opts.OldCommitID, opts.NewCommitID, opts.TimeNano) 170 170 171 171 newCommit, err := gitRepo.GetCommit(opts.NewCommitID) 172 172 if err != nil {
+109
tests/integration/pull_request_task_test.go
··· 1 + // Copyright 2024 The Forgejo Authors 2 + // SPDX-License-Identifier: MIT 3 + 4 + package integration 5 + 6 + import ( 7 + "context" 8 + "testing" 9 + "time" 10 + 11 + "code.gitea.io/gitea/models/db" 12 + issues_model "code.gitea.io/gitea/models/issues" 13 + repo_model "code.gitea.io/gitea/models/repo" 14 + "code.gitea.io/gitea/models/unittest" 15 + user_model "code.gitea.io/gitea/models/user" 16 + "code.gitea.io/gitea/modules/git" 17 + "code.gitea.io/gitea/modules/log" 18 + repo_module "code.gitea.io/gitea/modules/repository" 19 + "code.gitea.io/gitea/modules/test" 20 + "code.gitea.io/gitea/modules/timeutil" 21 + pull_service "code.gitea.io/gitea/services/pull" 22 + repo_service "code.gitea.io/gitea/services/repository" 23 + "code.gitea.io/gitea/tests" 24 + 25 + "github.com/stretchr/testify/assert" 26 + "github.com/stretchr/testify/require" 27 + ) 28 + 29 + func TestPullRequestSynchronized(t *testing.T) { 30 + defer tests.PrepareTestEnv(t)() 31 + 32 + // unmerged pull request of user2/repo1 from branch2 to master 33 + pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}) 34 + // tip of tests/gitea-repositories-meta/user2/repo1 branch2 35 + pull.HeadCommitID = "985f0301dba5e7b34be866819cd15ad3d8f508ee" 36 + pull.LoadIssue(db.DefaultContext) 37 + pull.Issue.Created = timeutil.TimeStampNanoNow() 38 + issues_model.UpdateIssueCols(db.DefaultContext, pull.Issue, "created") 39 + 40 + require.Equal(t, pull.HeadRepoID, pull.BaseRepoID) 41 + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pull.HeadRepoID}) 42 + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) 43 + 44 + for _, testCase := range []struct { 45 + name string 46 + timeNano int64 47 + expected bool 48 + }{ 49 + { 50 + name: "AddTestPullRequestTask process PR", 51 + timeNano: int64(pull.Issue.Created), 52 + expected: true, 53 + }, 54 + { 55 + name: "AddTestPullRequestTask skip PR", 56 + timeNano: 0, 57 + expected: false, 58 + }, 59 + } { 60 + t.Run(testCase.name, func(t *testing.T) { 61 + logChecker, cleanup := test.NewLogChecker(log.DEFAULT, log.TRACE) 62 + logChecker.Filter("Updating PR").StopMark("TestPullRequest ") 63 + defer cleanup() 64 + 65 + opt := &repo_module.PushUpdateOptions{ 66 + PusherID: owner.ID, 67 + PusherName: owner.Name, 68 + RepoUserName: owner.Name, 69 + RepoName: repo.Name, 70 + RefFullName: git.RefName("refs/heads/branch2"), 71 + OldCommitID: pull.HeadCommitID, 72 + NewCommitID: pull.HeadCommitID, 73 + TimeNano: testCase.timeNano, 74 + } 75 + require.NoError(t, repo_service.PushUpdate(opt)) 76 + logFiltered, logStopped := logChecker.Check(5 * time.Second) 77 + assert.True(t, logStopped) 78 + assert.Equal(t, testCase.expected, logFiltered[0]) 79 + }) 80 + } 81 + 82 + for _, testCase := range []struct { 83 + name string 84 + olderThan int64 85 + expected bool 86 + }{ 87 + { 88 + name: "TestPullRequest process PR", 89 + olderThan: int64(pull.Issue.Created), 90 + expected: true, 91 + }, 92 + { 93 + name: "TestPullRequest skip PR", 94 + olderThan: int64(pull.Issue.Created) - 1, 95 + expected: false, 96 + }, 97 + } { 98 + t.Run(testCase.name, func(t *testing.T) { 99 + logChecker, cleanup := test.NewLogChecker(log.DEFAULT, log.TRACE) 100 + logChecker.Filter("Updating PR").StopMark("TestPullRequest ") 101 + defer cleanup() 102 + 103 + pull_service.TestPullRequest(context.Background(), owner, repo.ID, testCase.olderThan, "branch2", true, pull.HeadCommitID, pull.HeadCommitID) 104 + logFiltered, logStopped := logChecker.Check(5 * time.Second) 105 + assert.True(t, logStopped) 106 + assert.Equal(t, testCase.expected, logFiltered[0]) 107 + }) 108 + } 109 + }