@@ -725,6 +725,157 @@ jobs:
725725 })
726726}
727727
728+ func TestWorkflowDispatchRerunSingleJobConcurrency (t * testing.T ) {
729+ onGiteaRun (t , func (t * testing.T , u * url.URL ) {
730+ user2 := unittest .AssertExistsAndLoadBean (t , & user_model.User {ID : 2 })
731+ session := loginUser (t , user2 .Name )
732+ token := getTokenForLoggedInUser (t , session , auth_model .AccessTokenScopeWriteRepository , auth_model .AccessTokenScopeWriteUser )
733+
734+ apiRepo := createActionsTestRepo (t , token , "actions-concurrency" , false )
735+ repo := unittest .AssertExistsAndLoadBean (t , & repo_model.Repository {ID : apiRepo .ID })
736+ httpContext := NewAPITestContext (t , user2 .Name , repo .Name , auth_model .AccessTokenScopeWriteRepository )
737+ defer doAPIDeleteRepository (httpContext )(t )
738+
739+ runner := newMockRunner ()
740+ runner .registerAsRepoRunner (t , user2 .Name , repo .Name , "mock-runner" , []string {"ubuntu-latest" }, false )
741+
742+ wf1TreePath := ".gitea/workflows/workflow-dispatch-concurrency.yml"
743+ wf1FileContent := `name: workflow-dispatch-concurrency
744+ on:
745+ workflow_dispatch:
746+ inputs:
747+ appVersion:
748+ description: 'APP version'
749+ required: true
750+ default: 'v1.23'
751+ type: choice
752+ options:
753+ - v1.21
754+ - v1.22
755+ - v1.23
756+ cancel:
757+ description: 'Cancel running workflows'
758+ required: false
759+ type: boolean
760+ default: false
761+ concurrency:
762+ group: workflow-dispatch-${{ inputs.appVersion }}
763+ cancel-in-progress: ${{ inputs.cancel }}
764+ jobs:
765+ job:
766+ runs-on: ubuntu-latest
767+ steps:
768+ - run: echo 'workflow dispatch job'
769+ `
770+
771+ opts1 := getWorkflowCreateFileOptions (user2 , repo .DefaultBranch , "create %s" + wf1TreePath , wf1FileContent )
772+ createWorkflowFile (t , token , user2 .Name , repo .Name , wf1TreePath , opts1 )
773+
774+ // run the workflow with appVersion=v1.21 and cancel=false
775+ urlStr := fmt .Sprintf ("/%s/%s/actions/run?workflow=%s" , user2 .Name , repo .Name , "workflow-dispatch-concurrency.yml" )
776+ req := NewRequestWithValues (t , "POST" , urlStr , map [string ]string {
777+ "_csrf" : GetUserCSRFToken (t , session ),
778+ "ref" : "refs/heads/main" ,
779+ "appVersion" : "v1.21" ,
780+ })
781+ session .MakeRequest (t , req , http .StatusSeeOther )
782+ task1 := runner .fetchTask (t )
783+ _ , _ , run1 := getTaskAndJobAndRunByTaskID (t , task1 .Id )
784+ assert .Equal (t , "workflow-dispatch-v1.21" , run1 .ConcurrencyGroup )
785+
786+ req = NewRequestWithValues (t , "POST" , urlStr , map [string ]string {
787+ "_csrf" : GetUserCSRFToken (t , session ),
788+ "ref" : "refs/heads/main" ,
789+ "appVersion" : "v1.22" ,
790+ })
791+ session .MakeRequest (t , req , http .StatusSeeOther )
792+ task2 := runner .fetchTask (t )
793+ _ , _ , run2 := getTaskAndJobAndRunByTaskID (t , task2 .Id )
794+ assert .Equal (t , "workflow-dispatch-v1.22" , run2 .ConcurrencyGroup )
795+
796+ // run the workflow with appVersion=v1.22 and cancel=false again
797+ req = NewRequestWithValues (t , "POST" , urlStr , map [string ]string {
798+ "_csrf" : GetUserCSRFToken (t , session ),
799+ "ref" : "refs/heads/main" ,
800+ "appVersion" : "v1.22" ,
801+ })
802+ session .MakeRequest (t , req , http .StatusSeeOther )
803+
804+ runner .fetchNoTask (t ) // cannot fetch task because task2 is not completed
805+
806+ // run the workflow with appVersion=v1.22 and cancel=true
807+ req = NewRequestWithValues (t , "POST" , urlStr , map [string ]string {
808+ "_csrf" : GetUserCSRFToken (t , session ),
809+ "ref" : "refs/heads/main" ,
810+ "appVersion" : "v1.22" ,
811+ "cancel" : "on" ,
812+ })
813+ session .MakeRequest (t , req , http .StatusSeeOther )
814+ task4 := runner .fetchTask (t )
815+ _ , _ , run4 := getTaskAndJobAndRunByTaskID (t , task4 .Id )
816+ assert .Equal (t , actions_model .StatusRunning , run4 .Status )
817+ assert .Equal (t , "workflow-dispatch-v1.22" , run4 .ConcurrencyGroup )
818+ _ , _ , run2 = getTaskAndJobAndRunByTaskID (t , task2 .Id )
819+ assert .Equal (t , actions_model .StatusCancelled , run2 .Status )
820+
821+ runner .execTask (t , task4 , & mockTaskOutcome {
822+ result : runnerv1 .Result_RESULT_SUCCESS ,
823+ })
824+
825+ // rerun cancel true scenario
826+
827+ req = NewRequestWithValues (t , "POST" , fmt .Sprintf ("/%s/%s/actions/runs/%d/jobs/%d/rerun" , user2 .Name , apiRepo .Name , run2 .Index , 1 ), map [string ]string {
828+ "_csrf" : GetUserCSRFToken (t , session ),
829+ })
830+ _ = session .MakeRequest (t , req , http .StatusOK )
831+
832+ req = NewRequestWithValues (t , "POST" , fmt .Sprintf ("/%s/%s/actions/runs/%d/jobs/%d/rerun" , user2 .Name , apiRepo .Name , run4 .Index , 1 ), map [string ]string {
833+ "_csrf" : GetUserCSRFToken (t , session ),
834+ })
835+ _ = session .MakeRequest (t , req , http .StatusOK )
836+
837+ task5 := runner .fetchTask (t )
838+ _ , _ , run4_1 := getTaskAndJobAndRunByTaskID (t , task5 .Id )
839+ assert .Equal (t , "workflow-dispatch-v1.22" , run4_1 .ConcurrencyGroup )
840+ assert .Equal (t , run4 .ID , run4_1 .ID )
841+ _ , _ , run2_1 := getTaskAndJobAndRunByTaskID (t , task2 .Id )
842+ assert .Equal (t , actions_model .StatusCancelled , run2_1 .Status )
843+
844+ runner .execTask (t , task5 , & mockTaskOutcome {
845+ result : runnerv1 .Result_RESULT_CANCELLED ,
846+ })
847+
848+ // rerun cancel false scenario
849+
850+ req = NewRequestWithValues (t , "POST" , fmt .Sprintf ("/%s/%s/actions/runs/%d/jobs/%d/rerun" , user2 .Name , apiRepo .Name , run2 .Index , 1 ), map [string ]string {
851+ "_csrf" : GetUserCSRFToken (t , session ),
852+ })
853+ _ = session .MakeRequest (t , req , http .StatusOK )
854+
855+ req = NewRequestWithValues (t , "POST" , fmt .Sprintf ("/%s/%s/actions/runs/%d/jobs/%d/rerun" , user2 .Name , apiRepo .Name , run2 .Index + 1 , 1 ), map [string ]string {
856+ "_csrf" : GetUserCSRFToken (t , session ),
857+ })
858+ _ = session .MakeRequest (t , req , http .StatusOK )
859+
860+ task6 := runner .fetchTask (t )
861+ _ , _ , run2_2 := getTaskAndJobAndRunByTaskID (t , task6 .Id )
862+ assert .Equal (t , "workflow-dispatch-v1.22" , run2_2 .ConcurrencyGroup )
863+
864+ runner .fetchNoTask (t ) // cannot fetch task because task2 is not completed
865+
866+ runner .execTask (t , task6 , & mockTaskOutcome {
867+ result : runnerv1 .Result_RESULT_SUCCESS ,
868+ })
869+
870+ task7 := runner .fetchTask (t )
871+ _ , _ , run3 := getTaskAndJobAndRunByTaskID (t , task7 .Id )
872+ assert .Equal (t , "workflow-dispatch-v1.22" , run3 .ConcurrencyGroup )
873+ runner .execTask (t , task7 , & mockTaskOutcome {
874+ result : runnerv1 .Result_RESULT_SUCCESS ,
875+ })
876+ })
877+ }
878+
728879func TestScheduleConcurrency (t * testing.T ) {
729880 onGiteaRun (t , func (t * testing.T , u * url.URL ) {
730881 user2 := unittest .AssertExistsAndLoadBean (t , & user_model.User {ID : 2 })
0 commit comments