Skip to content

Commit b276849

Browse files
authored
Fix Actions pull_request.paths being triggered incorrectly by rebase (#36045) (#36054)
Backport #36045 Partially fix #34710 The bug described in #34710 can be divided into two parts: `push.paths` and `pull_request.paths`. This PR fixes the issue related to `pull_request.paths`. The root cause is that the check for whether the workflow can be triggered happens **before** updating the PR’s merge base. This causes the file-change detection to use the old merge base. Therefore, we need to update the merge base first and then check whether the workflow can be triggered.
1 parent 46d1d15 commit b276849

File tree

3 files changed

+73
-13
lines changed

3 files changed

+73
-13
lines changed

services/pull/pull.go

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -425,10 +425,16 @@ func AddTestPullRequestTask(opts TestPullRequestOptions) {
425425
for _, pr := range prs {
426426
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
427427
if opts.NewCommitID != "" && opts.NewCommitID != objectFormat.EmptyObjectID().String() {
428-
changed, err := checkIfPRContentChanged(ctx, pr, opts.OldCommitID, opts.NewCommitID)
428+
changed, newMergeBase, err := checkIfPRContentChanged(ctx, pr, opts.OldCommitID, opts.NewCommitID)
429429
if err != nil {
430430
log.Error("checkIfPRContentChanged: %v", err)
431431
}
432+
if newMergeBase != "" && pr.MergeBase != newMergeBase {
433+
pr.MergeBase = newMergeBase
434+
if err := pr.UpdateColsIfNotMerged(ctx, "merge_base"); err != nil {
435+
log.Error("Update merge base for %-v: %v", pr, err)
436+
}
437+
}
432438
if changed {
433439
// Mark old reviews as stale if diff to mergebase has changed
434440
if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil {
@@ -502,30 +508,30 @@ func AddTestPullRequestTask(opts TestPullRequestOptions) {
502508

503509
// checkIfPRContentChanged checks if diff to target branch has changed by push
504510
// A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged
505-
func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) {
511+
func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, mergeBase string, err error) {
506512
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
507513
if err != nil {
508514
log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
509-
return false, err
515+
return false, "", err
510516
}
511517
defer cancel()
512518

513519
tmpRepo, err := git.OpenRepository(ctx, prCtx.tmpBasePath)
514520
if err != nil {
515-
return false, fmt.Errorf("OpenRepository: %w", err)
521+
return false, "", fmt.Errorf("OpenRepository: %w", err)
516522
}
517523
defer tmpRepo.Close()
518524

519525
// Find the merge-base
520-
_, base, err := tmpRepo.GetMergeBase("", "base", "tracking")
526+
mergeBase, _, err = tmpRepo.GetMergeBase("", "base", "tracking")
521527
if err != nil {
522-
return false, fmt.Errorf("GetMergeBase: %w", err)
528+
return false, "", fmt.Errorf("GetMergeBase: %w", err)
523529
}
524530

525-
cmd := gitcmd.NewCommand("diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base)
531+
cmd := gitcmd.NewCommand("diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, mergeBase)
526532
stdoutReader, stdoutWriter, err := os.Pipe()
527533
if err != nil {
528-
return false, fmt.Errorf("unable to open pipe for to run diff: %w", err)
534+
return false, mergeBase, fmt.Errorf("unable to open pipe for to run diff: %w", err)
529535
}
530536

531537
stderr := new(bytes.Buffer)
@@ -542,19 +548,19 @@ func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest,
542548
},
543549
}); err != nil {
544550
if err == util.ErrNotEmpty {
545-
return true, nil
551+
return true, mergeBase, nil
546552
}
547553
err = gitcmd.ConcatenateError(err, stderr.String())
548554

549555
log.Error("Unable to run diff on %s %s %s in tempRepo for PR[%d]%s/%s...%s/%s: Error: %v",
550-
newCommitID, oldCommitID, base,
556+
newCommitID, oldCommitID, mergeBase,
551557
pr.ID, pr.BaseRepo.FullName(), pr.BaseBranch, pr.HeadRepo.FullName(), pr.HeadBranch,
552558
err)
553559

554-
return false, fmt.Errorf("Unable to run git diff --name-only -z %s %s %s: %w", newCommitID, oldCommitID, base, err)
560+
return false, mergeBase, fmt.Errorf("Unable to run git diff --name-only -z %s %s %s: %w", newCommitID, oldCommitID, mergeBase, err)
555561
}
556562

557-
return false, nil
563+
return false, mergeBase, nil
558564
}
559565

560566
// PushToBaseRepo pushes commits from branches of head repository to

services/pull/review.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos
309309
if headCommitID == commitID {
310310
stale = false
311311
} else {
312-
stale, err = checkIfPRContentChanged(ctx, pr, commitID, headCommitID)
312+
stale, _, err = checkIfPRContentChanged(ctx, pr, commitID, headCommitID)
313313
if err != nil {
314314
return nil, nil, err
315315
}

tests/integration/actions_trigger_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"code.gitea.io/gitea/modules/test"
3232
"code.gitea.io/gitea/modules/timeutil"
3333
"code.gitea.io/gitea/modules/util"
34+
webhook_module "code.gitea.io/gitea/modules/webhook"
3435
issue_service "code.gitea.io/gitea/services/issue"
3536
pull_service "code.gitea.io/gitea/services/pull"
3637
release_service "code.gitea.io/gitea/services/release"
@@ -1604,3 +1605,56 @@ jobs:
16041605
assert.NotNil(t, run)
16051606
})
16061607
}
1608+
1609+
func TestPullRequestWithPathsRebase(t *testing.T) {
1610+
onGiteaRun(t, func(t *testing.T, u *url.URL) {
1611+
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
1612+
session := loginUser(t, user2.Name)
1613+
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
1614+
1615+
repoName := "actions-pr-paths-rebase"
1616+
apiRepo := createActionsTestRepo(t, token, repoName, false)
1617+
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID})
1618+
apiCtx := NewAPITestContext(t, "user2", repoName, auth_model.AccessTokenScopeWriteRepository)
1619+
runner := newMockRunner()
1620+
runner.registerAsRepoRunner(t, "user2", repoName, "mock-runner", []string{"ubuntu-latest"}, false)
1621+
1622+
// init files and dirs
1623+
testCreateFile(t, session, "user2", repoName, repo.DefaultBranch, "", "dir1/dir1.txt", "1")
1624+
testCreateFile(t, session, "user2", repoName, repo.DefaultBranch, "", "dir2/dir2.txt", "2")
1625+
wfFileContent := `name: ci
1626+
on:
1627+
pull_request:
1628+
paths:
1629+
- 'dir1/**'
1630+
jobs:
1631+
ci-job:
1632+
runs-on: ubuntu-latest
1633+
steps:
1634+
- run: echo 'ci'
1635+
`
1636+
testCreateFile(t, session, "user2", repoName, repo.DefaultBranch, "", ".gitea/workflows/ci.yml", wfFileContent)
1637+
1638+
// create a PR to modify "dir1/dir1.txt", the workflow will be triggered
1639+
testEditFileToNewBranch(t, session, "user2", repoName, repo.DefaultBranch, "update-dir1", "dir1/dir1.txt", "11")
1640+
_, err := doAPICreatePullRequest(apiCtx, "user2", repoName, repo.DefaultBranch, "update-dir1")(t)
1641+
assert.NoError(t, err)
1642+
pr1Task := runner.fetchTask(t)
1643+
_, _, pr1Run := getTaskAndJobAndRunByTaskID(t, pr1Task.Id)
1644+
assert.Equal(t, webhook_module.HookEventPullRequest, pr1Run.Event)
1645+
1646+
// create a PR to modify "dir2/dir2.txt" then update main branch and rebase, the workflow will not be triggered
1647+
testEditFileToNewBranch(t, session, "user2", repoName, repo.DefaultBranch, "update-dir2", "dir2/dir2.txt", "22")
1648+
apiPull, err := doAPICreatePullRequest(apiCtx, "user2", repoName, repo.DefaultBranch, "update-dir2")(t)
1649+
runner.fetchNoTask(t)
1650+
assert.NoError(t, err)
1651+
testEditFile(t, session, "user2", repoName, repo.DefaultBranch, "dir1/dir1.txt", "11") // change the file in "dir1"
1652+
req := NewRequestWithValues(t, "POST",
1653+
fmt.Sprintf("/%s/%s/pulls/%d/update?style=rebase", "user2", repoName, apiPull.Index), // update by rebase
1654+
map[string]string{
1655+
"_csrf": GetUserCSRFToken(t, session),
1656+
})
1657+
session.MakeRequest(t, req, http.StatusSeeOther)
1658+
runner.fetchNoTask(t)
1659+
})
1660+
}

0 commit comments

Comments
 (0)