@@ -16,6 +16,7 @@ import (
1616 repo_model "code.gitea.io/gitea/models/repo"
1717 "code.gitea.io/gitea/models/unittest"
1818 user_model "code.gitea.io/gitea/models/user"
19+ "code.gitea.io/gitea/modules/setting"
1920 api "code.gitea.io/gitea/modules/structs"
2021 "code.gitea.io/gitea/modules/timeutil"
2122 "code.gitea.io/gitea/modules/util"
@@ -1247,6 +1248,105 @@ jobs:
12471248 })
12481249}
12491250
1251+ func TestAbandonConcurrentRun (t * testing.T ) {
1252+ onGiteaRun (t , func (t * testing.T , u * url.URL ) {
1253+ user2 := unittest .AssertExistsAndLoadBean (t , & user_model.User {ID : 2 })
1254+ user2Session := loginUser (t , user2 .Name )
1255+ user2Token := getTokenForLoggedInUser (t , user2Session , auth_model .AccessTokenScopeWriteRepository , auth_model .AccessTokenScopeWriteUser )
1256+
1257+ apiRepo := createActionsTestRepo (t , user2Token , "actions-concurrency" , false )
1258+ repo := unittest .AssertExistsAndLoadBean (t , & repo_model.Repository {ID : apiRepo .ID })
1259+ user2APICtx := NewAPITestContext (t , repo .OwnerName , repo .Name , auth_model .AccessTokenScopeWriteRepository )
1260+ defer doAPIDeleteRepository (user2APICtx )(t )
1261+
1262+ runner := newMockRunner ()
1263+ runner .registerAsRepoRunner (t , repo .OwnerName , repo .Name , "mock-runner" , []string {"ubuntu-latest" }, false )
1264+
1265+ wf1TreePath := ".gitea/workflows/workflow-1.yml"
1266+ wf1FileContent := `name: Workflow-1
1267+ on:
1268+ push:
1269+ paths:
1270+ - '.gitea/workflows/workflow-1.yml'
1271+ concurrency:
1272+ group: test-group
1273+ jobs:
1274+ wf1-job1:
1275+ runs-on: ubuntu-latest
1276+ steps:
1277+ - run: echo 'wf1-job1'
1278+ wf1-job2:
1279+ runs-on: customized-runner
1280+ steps:
1281+ - run: echo 'wf1-job1'
1282+ `
1283+
1284+ wf2TreePath := ".gitea/workflows/workflow-2.yml"
1285+ wf2FileContent := `name: Workflow-2
1286+ on:
1287+ push:
1288+ paths:
1289+ - '.gitea/workflows/workflow-2.yml'
1290+ concurrency:
1291+ group: test-group
1292+ jobs:
1293+ wf2-job1:
1294+ runs-on: ubuntu-latest
1295+ steps:
1296+ - run: echo 'wf2-job1'
1297+ `
1298+ // push workflow1
1299+ opts1 := getWorkflowCreateFileOptions (user2 , repo .DefaultBranch , "create %s" + wf1TreePath , wf1FileContent )
1300+ createWorkflowFile (t , user2Token , repo .OwnerName , repo .Name , wf1TreePath , opts1 )
1301+
1302+ // fetch wf1-job1
1303+ w1j1Task := runner .fetchTask (t )
1304+ _ , _ , run1 := getTaskAndJobAndRunByTaskID (t , w1j1Task .Id )
1305+ assert .Equal (t , "test-group" , run1 .ConcurrencyGroup )
1306+ assert .Equal (t , actions_model .StatusRunning , run1 .Status )
1307+ // query wf1-job2 from db and check its status
1308+ w1j2Job := unittest .AssertExistsAndLoadBean (t , & actions_model.ActionRunJob {RunID : run1 .ID , JobID : "wf1-job2" })
1309+ // wf1-job2 is waiting but no runner will run it
1310+ assert .Equal (t , actions_model .StatusWaiting , w1j2Job .Status )
1311+
1312+ time .Sleep (time .Second )
1313+ now := time .Now ()
1314+ time .Sleep (time .Second )
1315+
1316+ // push workflow2
1317+ opts2 := getWorkflowCreateFileOptions (user2 , repo .DefaultBranch , "create %s" + wf2TreePath , wf2FileContent )
1318+ createWorkflowFile (t , user2Token , repo .OwnerName , repo .Name , wf2TreePath , opts2 )
1319+
1320+ // query run2 from db and check its status
1321+ run2 := unittest .AssertExistsAndLoadBean (t , & actions_model.ActionRun {RepoID : repo .ID , WorkflowID : "workflow-2.yml" })
1322+ // run2 is blocked because it is blocked by workflow1's concurrency group "test-group"
1323+ assert .Equal (t , actions_model .StatusBlocked , run2 .Status )
1324+
1325+ // mock time
1326+ fakeNow := now .Add (setting .Actions .AbandonedJobTimeout )
1327+ timeutil .MockSet (fakeNow )
1328+ defer timeutil .MockUnset ()
1329+
1330+ // call CancelAbandonedJobs manually
1331+ assert .NoError (t , actions_service .CancelAbandonedJobs (t .Context ()))
1332+
1333+ // check the status of wf1-job2
1334+ w1j2Job = unittest .AssertExistsAndLoadBean (t , & actions_model.ActionRunJob {ID : w1j2Job .ID })
1335+ assert .Equal (t , actions_model .StatusCancelled , w1j2Job .Status )
1336+ // check the status of run1
1337+ run1 = unittest .AssertExistsAndLoadBean (t , & actions_model.ActionRun {ID : run1 .ID })
1338+ assert .Equal (t , actions_model .StatusCancelled , run1 .Status )
1339+
1340+ // fetch wf2-job1 and check
1341+ w2j1Task := runner .fetchTask (t )
1342+ _ , w2j1Job , run2 := getTaskAndJobAndRunByTaskID (t , w2j1Task .Id )
1343+ assert .Equal (t , "test-group" , run2 .ConcurrencyGroup )
1344+ assert .Equal (t , "wf2-job1" , w2j1Job .JobID )
1345+ assert .Equal (t , actions_model .StatusRunning , run2 .Status )
1346+ assert .Equal (t , actions_model .StatusRunning , w2j1Job .Status )
1347+ })
1348+ }
1349+
12501350func TestRunAndJobWithSameConcurrencyGroup (t * testing.T ) {
12511351 onGiteaRun (t , func (t * testing.T , u * url.URL ) {
12521352 user2 := unittest .AssertExistsAndLoadBean (t , & user_model.User {ID : 2 })
0 commit comments