@@ -21,8 +21,10 @@ import (
2121 "code.gitea.io/gitea/modules/json"
2222 "code.gitea.io/gitea/modules/setting"
2323 api "code.gitea.io/gitea/modules/structs"
24+ actions_service "code.gitea.io/gitea/services/actions"
2425
2526 runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
27+ "connectrpc.com/connect"
2628 "github.com/stretchr/testify/assert"
2729)
2830
@@ -132,7 +134,7 @@ jobs:
132134
133135 apiRepo := createActionsTestRepo (t , token , "actions-jobs-with-needs" , false )
134136 runner := newMockRunner ()
135- runner .registerAsRepoRunner (t , user2 .Name , apiRepo .Name , "mock-runner" , []string {"ubuntu-latest" })
137+ runner .registerAsRepoRunner (t , user2 .Name , apiRepo .Name , "mock-runner" , []string {"ubuntu-latest" }, false )
136138
137139 for _ , tc := range testCases {
138140 t .Run (fmt .Sprintf ("test %s" , tc .treePath ), func (t * testing.T ) {
@@ -318,7 +320,7 @@ jobs:
318320
319321 apiRepo := createActionsTestRepo (t , token , "actions-jobs-outputs-with-matrix" , false )
320322 runner := newMockRunner ()
321- runner .registerAsRepoRunner (t , user2 .Name , apiRepo .Name , "mock-runner" , []string {"ubuntu-latest" })
323+ runner .registerAsRepoRunner (t , user2 .Name , apiRepo .Name , "mock-runner" , []string {"ubuntu-latest" }, false )
322324
323325 for _ , tc := range testCases {
324326 t .Run (fmt .Sprintf ("test %s" , tc .treePath ), func (t * testing.T ) {
@@ -363,7 +365,7 @@ func TestActionsGiteaContext(t *testing.T) {
363365 user2APICtx := NewAPITestContext (t , baseRepo .OwnerName , baseRepo .Name , auth_model .AccessTokenScopeWriteRepository )
364366
365367 runner := newMockRunner ()
366- runner .registerAsRepoRunner (t , baseRepo .OwnerName , baseRepo .Name , "mock-runner" , []string {"ubuntu-latest" })
368+ runner .registerAsRepoRunner (t , baseRepo .OwnerName , baseRepo .Name , "mock-runner" , []string {"ubuntu-latest" }, false )
367369
368370 // init the workflow
369371 wfTreePath := ".gitea/workflows/pull.yml"
@@ -437,6 +439,156 @@ jobs:
437439 })
438440}
439441
442+ // Ephemeral
443+ func TestActionsGiteaContextEphemeral (t * testing.T ) {
444+ onGiteaRun (t , func (t * testing.T , u * url.URL ) {
445+ user2 := unittest .AssertExistsAndLoadBean (t , & user_model.User {ID : 2 })
446+ user2Session := loginUser (t , user2 .Name )
447+ user2Token := getTokenForLoggedInUser (t , user2Session , auth_model .AccessTokenScopeWriteRepository , auth_model .AccessTokenScopeWriteUser )
448+
449+ apiBaseRepo := createActionsTestRepo (t , user2Token , "actions-gitea-context" , false )
450+ baseRepo := unittest .AssertExistsAndLoadBean (t , & repo_model.Repository {ID : apiBaseRepo .ID })
451+ user2APICtx := NewAPITestContext (t , baseRepo .OwnerName , baseRepo .Name , auth_model .AccessTokenScopeWriteRepository )
452+
453+ runner := newMockRunner ()
454+ runner .registerAsRepoRunner (t , baseRepo .OwnerName , baseRepo .Name , "mock-runner" , []string {"ubuntu-latest" }, true )
455+
456+ // verify CleanupEphemeralRunners does not remove this runner
457+ err := actions_service .CleanupEphemeralRunners (t .Context ())
458+ assert .NoError (t , err )
459+
460+ // init the workflow
461+ wfTreePath := ".gitea/workflows/pull.yml"
462+ wfFileContent := `name: Pull Request
463+ on: pull_request
464+ jobs:
465+ wf1-job:
466+ runs-on: ubuntu-latest
467+ steps:
468+ - run: echo 'test the pull'
469+ wf2-job:
470+ runs-on: ubuntu-latest
471+ steps:
472+ - run: echo 'test the pull'
473+ `
474+ opts := getWorkflowCreateFileOptions (user2 , baseRepo .DefaultBranch , fmt .Sprintf ("create %s" , wfTreePath ), wfFileContent )
475+ createWorkflowFile (t , user2Token , baseRepo .OwnerName , baseRepo .Name , wfTreePath , opts )
476+ // user2 creates a pull request
477+ doAPICreateFile (user2APICtx , "user2-patch.txt" , & api.CreateFileOptions {
478+ FileOptions : api.FileOptions {
479+ NewBranchName : "user2/patch-1" ,
480+ Message : "create user2-patch.txt" ,
481+ Author : api.Identity {
482+ Name : user2 .Name ,
483+ Email : user2 .Email ,
484+ },
485+ Committer : api.Identity {
486+ Name : user2 .Name ,
487+ Email : user2 .Email ,
488+ },
489+ Dates : api.CommitDateOptions {
490+ Author : time .Now (),
491+ Committer : time .Now (),
492+ },
493+ },
494+ ContentBase64 : base64 .StdEncoding .EncodeToString ([]byte ("user2-fix" )),
495+ })(t )
496+ apiPull , err := doAPICreatePullRequest (user2APICtx , baseRepo .OwnerName , baseRepo .Name , baseRepo .DefaultBranch , "user2/patch-1" )(t )
497+ assert .NoError (t , err )
498+ task := runner .fetchTask (t )
499+ gtCtx := task .Context .GetFields ()
500+ actionTask := unittest .AssertExistsAndLoadBean (t , & actions_model.ActionTask {ID : task .Id })
501+ actionRunJob := unittest .AssertExistsAndLoadBean (t , & actions_model.ActionRunJob {ID : actionTask .JobID })
502+ actionRun := unittest .AssertExistsAndLoadBean (t , & actions_model.ActionRun {ID : actionRunJob .RunID })
503+ assert .NoError (t , actionRun .LoadAttributes (t .Context ()))
504+
505+ assert .Equal (t , user2 .Name , gtCtx ["actor" ].GetStringValue ())
506+ assert .Equal (t , setting .AppURL + "api/v1" , gtCtx ["api_url" ].GetStringValue ())
507+ assert .Equal (t , apiPull .Base .Ref , gtCtx ["base_ref" ].GetStringValue ())
508+ runEvent := map [string ]any {}
509+ assert .NoError (t , json .Unmarshal ([]byte (actionRun .EventPayload ), & runEvent ))
510+ assert .True (t , reflect .DeepEqual (gtCtx ["event" ].GetStructValue ().AsMap (), runEvent ))
511+ assert .Equal (t , actionRun .TriggerEvent , gtCtx ["event_name" ].GetStringValue ())
512+ assert .Equal (t , apiPull .Head .Ref , gtCtx ["head_ref" ].GetStringValue ())
513+ assert .Equal (t , actionRunJob .JobID , gtCtx ["job" ].GetStringValue ())
514+ assert .Equal (t , actionRun .Ref , gtCtx ["ref" ].GetStringValue ())
515+ assert .Equal (t , (git .RefName (actionRun .Ref )).ShortName (), gtCtx ["ref_name" ].GetStringValue ())
516+ assert .False (t , gtCtx ["ref_protected" ].GetBoolValue ())
517+ assert .Equal (t , string ((git .RefName (actionRun .Ref )).RefType ()), gtCtx ["ref_type" ].GetStringValue ())
518+ assert .Equal (t , actionRun .Repo .OwnerName + "/" + actionRun .Repo .Name , gtCtx ["repository" ].GetStringValue ())
519+ assert .Equal (t , actionRun .Repo .OwnerName , gtCtx ["repository_owner" ].GetStringValue ())
520+ assert .Equal (t , actionRun .Repo .HTMLURL (), gtCtx ["repositoryUrl" ].GetStringValue ())
521+ assert .Equal (t , fmt .Sprint (actionRunJob .RunID ), gtCtx ["run_id" ].GetStringValue ())
522+ assert .Equal (t , fmt .Sprint (actionRun .Index ), gtCtx ["run_number" ].GetStringValue ())
523+ assert .Equal (t , fmt .Sprint (actionRunJob .Attempt ), gtCtx ["run_attempt" ].GetStringValue ())
524+ assert .Equal (t , "Actions" , gtCtx ["secret_source" ].GetStringValue ())
525+ assert .Equal (t , setting .AppURL , gtCtx ["server_url" ].GetStringValue ())
526+ assert .Equal (t , actionRun .CommitSHA , gtCtx ["sha" ].GetStringValue ())
527+ assert .Equal (t , actionRun .WorkflowID , gtCtx ["workflow" ].GetStringValue ())
528+ assert .Equal (t , setting .Actions .DefaultActionsURL .URL (), gtCtx ["gitea_default_actions_url" ].GetStringValue ())
529+ token := gtCtx ["token" ].GetStringValue ()
530+ assert .Equal (t , actionTask .TokenLastEight , token [len (token )- 8 :])
531+
532+ // verify CleanupEphemeralRunners does not remove this runner
533+ err = actions_service .CleanupEphemeralRunners (t .Context ())
534+ assert .NoError (t , err )
535+
536+ resp , err := runner .client .runnerServiceClient .FetchTask (t .Context (), connect .NewRequest (& runnerv1.FetchTaskRequest {
537+ TasksVersion : 0 ,
538+ }))
539+ assert .NoError (t , err )
540+ assert .Nil (t , resp .Msg .Task )
541+
542+ // verify CleanupEphemeralRunners does not remove this runner
543+ err = actions_service .CleanupEphemeralRunners (t .Context ())
544+ assert .NoError (t , err )
545+
546+ runner .client .runnerServiceClient .UpdateTask (t .Context (), connect .NewRequest (& runnerv1.UpdateTaskRequest {
547+ State : & runnerv1.TaskState {
548+ Id : actionTask .ID ,
549+ Result : runnerv1 .Result_RESULT_SUCCESS ,
550+ },
551+ }))
552+ resp , err = runner .client .runnerServiceClient .FetchTask (t .Context (), connect .NewRequest (& runnerv1.FetchTaskRequest {
553+ TasksVersion : 0 ,
554+ }))
555+ assert .Error (t , err )
556+ assert .Nil (t , resp )
557+
558+ resp , err = runner .client .runnerServiceClient .FetchTask (t .Context (), connect .NewRequest (& runnerv1.FetchTaskRequest {
559+ TasksVersion : 0 ,
560+ }))
561+ assert .Error (t , err )
562+ assert .Nil (t , resp )
563+
564+ // create an runner that picks a job and get force cancelled
565+ runnerToBeRemoved := newMockRunner ()
566+ runnerToBeRemoved .registerAsRepoRunner (t , baseRepo .OwnerName , baseRepo .Name , "mock-runner-to-be-removed" , []string {"ubuntu-latest" }, true )
567+
568+ taskToStopAPIObj := runnerToBeRemoved .fetchTask (t )
569+
570+ taskToStop := unittest .AssertExistsAndLoadBean (t , & actions_model.ActionTask {ID : taskToStopAPIObj .Id })
571+
572+ // verify CleanupEphemeralRunners does not remove the custom crafted runner
573+ err = actions_service .CleanupEphemeralRunners (t .Context ())
574+ assert .NoError (t , err )
575+
576+ runnerToRemove := unittest .AssertExistsAndLoadBean (t , & actions_model.ActionRunner {ID : taskToStop .RunnerID })
577+
578+ err = actions_model .StopTask (t .Context (), taskToStop .ID , actions_model .StatusFailure )
579+ assert .NoError (t , err )
580+
581+ // verify CleanupEphemeralRunners does remove the custom crafted runner
582+ err = actions_service .CleanupEphemeralRunners (t .Context ())
583+ assert .NoError (t , err )
584+
585+ unittest .AssertNotExistsBean (t , & actions_model.ActionRunner {ID : runnerToRemove .ID })
586+
587+ // this cleanup is required to allow further tests to pass
588+ doAPIDeleteRepository (user2APICtx )(t )
589+ })
590+ }
591+
440592func createActionsTestRepo (t * testing.T , authToken , repoName string , isPrivate bool ) * api.Repository {
441593 req := NewRequestWithJSON (t , "POST" , "/api/v1/user/repos" , & api.CreateRepoOption {
442594 Name : repoName ,
0 commit comments