Skip to content

Commit 159f740

Browse files
authored
Fix missing workflow_run notifications when updating jobs from multiple runs (go-gitea#36997) (go-gitea#37003)
Backport go-gitea#36997 This PR fixes `notifyWorkflowJobStatusUpdate` to send `WorkflowRunStatusUpdate` for each affected workflow run instead of only the first run in the input job list.
1 parent af29b81 commit 159f740

2 files changed

Lines changed: 103 additions & 7 deletions

File tree

services/actions/clear_tasks.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,27 @@ func StopEndlessTasks(ctx context.Context) error {
3636
}
3737

3838
func notifyWorkflowJobStatusUpdate(ctx context.Context, jobs []*actions_model.ActionRunJob) {
39-
if len(jobs) > 0 {
40-
CreateCommitStatus(ctx, jobs...)
41-
for _, job := range jobs {
42-
_ = job.LoadAttributes(ctx)
43-
notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
39+
if len(jobs) == 0 {
40+
return
41+
}
42+
43+
CreateCommitStatus(ctx, jobs...)
44+
45+
runs := make(map[int64]*actions_model.ActionRun, len(jobs))
46+
for _, job := range jobs {
47+
if err := job.LoadAttributes(ctx); err != nil {
48+
log.Error("Failed to load job attributes: %v", err)
49+
continue
4450
}
45-
job := jobs[0]
46-
notify_service.WorkflowRunStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job.Run)
51+
52+
notify_service.WorkflowJobStatusUpdate(ctx, job.Run.Repo, job.Run.TriggerUser, job, nil)
53+
if _, ok := runs[job.RunID]; !ok {
54+
runs[job.RunID] = job.Run
55+
}
56+
}
57+
58+
for _, run := range runs {
59+
notify_service.WorkflowRunStatusUpdate(ctx, run.Repo, run.TriggerUser, run)
4760
}
4861
}
4962

tests/integration/repo_webhook_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"testing"
1515
"time"
1616

17+
actions_model "code.gitea.io/gitea/models/actions"
1718
auth_model "code.gitea.io/gitea/models/auth"
1819
"code.gitea.io/gitea/models/perm"
1920
"code.gitea.io/gitea/models/repo"
@@ -1150,6 +1151,10 @@ func Test_WebhookWorkflowRun(t *testing.T) {
11501151
testWorkflowRunEventsOnCancellingAbandonedRun(t, webhookData, false)
11511152
},
11521153
},
1154+
{
1155+
name: "WorkflowRunOnStoppingEndlessTasksForMultipleRuns",
1156+
testFunc: testWorkflowRunOnStoppingEndlessTasksForMultipleRuns,
1157+
},
11531158
}
11541159
for _, obj := range testCases {
11551160
t.Run(obj.name, func(t *testing.T) {
@@ -1586,6 +1591,84 @@ jobs:
15861591
assert.Equal(t, "user2/"+repoName, webhookData.payloads[1].Repo.FullName)
15871592
}
15881593

1594+
func testWorkflowRunOnStoppingEndlessTasksForMultipleRuns(t *testing.T, webhookData *workflowRunWebhook) {
1595+
defer test.MockVariableValue(&setting.Actions.EndlessTaskTimeout, time.Second)()
1596+
1597+
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
1598+
session := loginUser(t, "user2")
1599+
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
1600+
1601+
repoName := "test-workflow-run-stop-endless-tasks"
1602+
testRepo := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: createActionsTestRepo(t, token, repoName, false).ID})
1603+
1604+
testAPICreateWebhookForRepo(t, session, "user2", repoName, webhookData.URL, "workflow_run")
1605+
1606+
runners := make([]*mockRunner, 2)
1607+
for i := range runners {
1608+
runners[i] = newMockRunner()
1609+
runners[i].registerAsRepoRunner(t, "user2", repoName, fmt.Sprintf("mock-runner-%d", i), []string{"ubuntu-latest"}, false)
1610+
}
1611+
1612+
workflowPath1 := ".gitea/workflows/endless-1.yml"
1613+
workflowPath2 := ".gitea/workflows/endless-2.yml"
1614+
workflowContent1 := `name: endless-1
1615+
on:
1616+
push:
1617+
paths:
1618+
- '.gitea/workflows/endless-1.yml'
1619+
jobs:
1620+
job-1:
1621+
runs-on: ubuntu-latest
1622+
steps:
1623+
- run: echo 'job-1'
1624+
`
1625+
workflowContent2 := `name: endless-2
1626+
on:
1627+
push:
1628+
paths:
1629+
- '.gitea/workflows/endless-2.yml'
1630+
jobs:
1631+
job-2:
1632+
runs-on: ubuntu-latest
1633+
steps:
1634+
- run: echo 'job-2'
1635+
`
1636+
1637+
opts1 := getWorkflowCreateFileOptions(user2, testRepo.DefaultBranch, "create "+workflowPath1, workflowContent1)
1638+
createWorkflowFile(t, token, "user2", repoName, workflowPath1, opts1)
1639+
opts2 := getWorkflowCreateFileOptions(user2, testRepo.DefaultBranch, "create "+workflowPath2, workflowContent2)
1640+
createWorkflowFile(t, token, "user2", repoName, workflowPath2, opts2)
1641+
1642+
task1 := runners[0].fetchTask(t)
1643+
task2 := runners[1].fetchTask(t)
1644+
_, job1, _ := getTaskAndJobAndRunByTaskID(t, task1.Id)
1645+
_, job2, _ := getTaskAndJobAndRunByTaskID(t, task2.Id)
1646+
require.NotEqual(t, job1.RunID, job2.RunID)
1647+
1648+
initialRunEventsLen := len(webhookData.payloads)
1649+
1650+
time.Sleep(2 * time.Second)
1651+
1652+
require.NoError(t, actions.StopEndlessTasks(t.Context()))
1653+
1654+
require.Len(t, webhookData.payloads, initialRunEventsLen+2)
1655+
1656+
var completedRunIDs []int64
1657+
for _, payload := range webhookData.payloads[initialRunEventsLen:] {
1658+
assert.Equal(t, "completed", payload.Action)
1659+
assert.Equal(t, "completed", payload.WorkflowRun.Status)
1660+
completedRunIDs = append(completedRunIDs, payload.WorkflowRun.ID)
1661+
}
1662+
assert.Len(t, completedRunIDs, 2)
1663+
assert.Contains(t, completedRunIDs, job1.RunID)
1664+
assert.Contains(t, completedRunIDs, job2.RunID)
1665+
1666+
run1 := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: job1.RunID})
1667+
run2 := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: job2.RunID})
1668+
assert.Equal(t, actions_model.StatusFailure, run1.Status)
1669+
assert.Equal(t, actions_model.StatusFailure, run2.Status)
1670+
}
1671+
15891672
func testWebhookWorkflowRun(t *testing.T, webhookData *workflowRunWebhook) {
15901673
// 1. create a new webhook with special webhook for repo1
15911674
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})

0 commit comments

Comments
 (0)