fix(actions): preserve admitted jobs and runs in their concurrency group (#39461)

This commit is contained in:
Stochastic
2026-09-29 12:36:33 +08:00
committed by GitHub
parent fba8d7eace
commit 6bb74ed65d
13 changed files with 255 additions and 103 deletions
@@ -977,14 +977,13 @@ jobs:
req = NewRequest(t, "POST", fmt.Sprintf("/%s/%s/actions/runs/%d/rerun", user2.Name, apiRepo.Name, run3.ID))
_ = session.MakeRequest(t, req, http.StatusOK)
assert.Equal(t, actions_model.StatusBlocked, unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: run3.ID}).Status)
runner.execTask(t, runner.fetchTask(t), &mockTaskOutcome{result: runnerv1.Result_RESULT_SUCCESS})
task6 := runner.fetchTask(t)
_, _, run3_2 := getTaskAndJobAndRunByTaskID(t, task6.Id)
assert.Equal(t, run3.ID, run3_2.ID)
assert.Equal(t, actions_model.StatusRunning, run3_2.Status)
assert.Equal(t, "workflow-dispatch-v1.22", getRunConcurrencyGroup(t, run3))
run2_2 = unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: run2_2.ID})
assert.Equal(t, actions_model.StatusCancelled, run2_2.Status) // cancelled by run3
})
}
@@ -1118,12 +1117,11 @@ jobs:
req = NewRequest(t, "POST", fmt.Sprintf("/%s/%s/actions/runs/%d/jobs/%d/rerun", user2.Name, apiRepo.Name, run3.ID, job3.ID))
_ = session.MakeRequest(t, req, http.StatusOK)
assert.Equal(t, actions_model.StatusBlocked, unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: run3.ID}).Status)
runner.execTask(t, runner.fetchTask(t), &mockTaskOutcome{result: runnerv1.Result_RESULT_SUCCESS})
task6 := runner.fetchTask(t)
_, _, run3 = getTaskAndJobAndRunByTaskID(t, task6.Id)
assert.Equal(t, "workflow-dispatch-v1.22", getRunConcurrencyGroup(t, run3))
run2_2 = unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: run2_2.ID})
assert.Equal(t, actions_model.StatusCancelled, run2_2.Status) // cancelled by run3
})
}
@@ -1360,9 +1358,8 @@ jobs:
w3Run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: repo.ID, WorkflowID: "concurrent-workflow-3.yml"})
w3j1Job := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{RunID: w3Run.ID, JobID: "wf3-job1"})
assert.Equal(t, actions_model.StatusBlocked, w3j1Job.Status)
// wf2-job1 is cancelled by wf3-job1
w2j1Job = unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: w2j1Job.ID})
assert.Equal(t, actions_model.StatusCancelled, w2j1Job.Status)
assert.Equal(t, actions_model.StatusBlocked, w2j1Job.Status)
// exec wf1-job1
runner1.execTask(t, w1j1Task, &mockTaskOutcome{
@@ -1403,6 +1400,8 @@ jobs:
// fetch wf4-job1
w4j1Task := runner2.fetchTask(t)
_, w2j1Job, _ = getTaskAndJobAndRunByTaskID(t, runner1.fetchTask(t).Id)
assert.Equal(t, "wf2-job1", w2j1Job.JobID)
// all tasks have been fetched
runner1.fetchNoTask(t)
runner2.fetchNoTask(t)
@@ -1410,7 +1409,7 @@ jobs:
_, w2j2Job, w2Run = getTaskAndJobAndRunByTaskID(t, w2j2Task.Id)
// wf2-job2 is cancelled because wf4-job1's cancel-in-progress is true
assert.Equal(t, actions_model.StatusCancelled, w2j2Job.Status)
assert.Equal(t, actions_model.StatusCancelled, w2Run.Status)
assert.Equal(t, actions_model.StatusRunning, w2Run.Status)
_, w4j1Job, w4Run := getTaskAndJobAndRunByTaskID(t, w4j1Task.Id)
assert.Equal(t, "job-group-2", w4j1Job.ConcurrencyGroup)
assert.Equal(t, "workflow-group-2", getRunConcurrencyGroup(t, w4Run))
@@ -911,7 +911,7 @@ jobs:
assert.Equal(t, actions_model.StatusSuccess, run.Status)
})
t.Run("No-needs caller if evaluated inline: false skips, true expands", func(t *testing.T) {
t.Run("No-needs callers: if evaluated inline, called job concurrency serializes them", func(t *testing.T) {
// A no-needs reusable-workflow caller is processed inline during InsertRun, where its own
// `if:` is now evaluated before expansion:
// - a false `if:` skips the caller without inserting any children, and the skip is
@@ -928,6 +928,8 @@ on:
jobs:
inner:
runs-on: ubuntu-latest
concurrency:
group: called-job
steps:
- run: echo inner
`)
@@ -946,6 +948,9 @@ jobs:
if: ${{ true }}
uses: ./.gitea/workflows/lib.yaml
will_run_too:
uses: ./.gitea/workflows/lib.yaml
after_skip:
needs: [will_skip]
runs-on: ubuntu-latest
@@ -970,11 +975,16 @@ jobs:
willRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{RunID: runID, JobID: "will_run"})
assert.True(t, willRun.IsReusableCaller)
assert.True(t, willRun.IsExpanded)
innerChild := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{RunID: runID, JobID: "inner"})
assert.Equal(t, willRun.ID, innerChild.ParentJobID)
unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{RunID: runID, JobID: "inner", ParentJobID: willRun.ID})
afterSkip := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{RunID: runID, JobID: "after_skip"})
assert.Equal(t, actions_model.StatusSkipped, afterSkip.Status)
unittest.AssertCount(t, &actions_model.ActionRunJob{RunID: runID, JobID: "inner", Status: actions_model.StatusBlocked}, 1)
runner := newMockRunner()
runner.registerAsRepoRunner(t, repo.OwnerName, repo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
runner.execTask(t, runner.fetchTask(t), &mockTaskOutcome{result: runnerv1.Result_RESULT_SUCCESS})
runner.fetchTask(t)
})
})
}