| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645 |
- // Copyright 2024 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package integration
-
- import (
- "encoding/base64"
- "fmt"
- "net/http"
- "net/url"
- "reflect"
- "strconv"
- "testing"
- "time"
-
- actions_model "code.gitea.io/gitea/models/actions"
- auth_model "code.gitea.io/gitea/models/auth"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- actions_service "code.gitea.io/gitea/services/actions"
-
- runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
- "connectrpc.com/connect"
- "github.com/stretchr/testify/assert"
- )
-
- func TestJobWithNeeds(t *testing.T) {
- testCases := []struct {
- treePath string
- fileContent string
- outcomes map[string]*mockTaskOutcome
- expectedStatuses map[string]string
- }{
- {
- treePath: ".gitea/workflows/job-with-needs.yml",
- fileContent: `name: job-with-needs
- on:
- push:
- paths:
- - '.gitea/workflows/job-with-needs.yml'
- jobs:
- job1:
- runs-on: ubuntu-latest
- steps:
- - run: echo job1
- job2:
- runs-on: ubuntu-latest
- needs: [job1]
- steps:
- - run: echo job2
- `,
- outcomes: map[string]*mockTaskOutcome{
- "job1": {
- result: runnerv1.Result_RESULT_SUCCESS,
- },
- "job2": {
- result: runnerv1.Result_RESULT_SUCCESS,
- },
- },
- expectedStatuses: map[string]string{
- "job1": actions_model.StatusSuccess.String(),
- "job2": actions_model.StatusSuccess.String(),
- },
- },
- {
- treePath: ".gitea/workflows/job-with-needs-fail.yml",
- fileContent: `name: job-with-needs-fail
- on:
- push:
- paths:
- - '.gitea/workflows/job-with-needs-fail.yml'
- jobs:
- job1:
- runs-on: ubuntu-latest
- steps:
- - run: echo job1
- job2:
- runs-on: ubuntu-latest
- needs: [job1]
- steps:
- - run: echo job2
- `,
- outcomes: map[string]*mockTaskOutcome{
- "job1": {
- result: runnerv1.Result_RESULT_FAILURE,
- },
- },
- expectedStatuses: map[string]string{
- "job1": actions_model.StatusFailure.String(),
- "job2": actions_model.StatusSkipped.String(),
- },
- },
- {
- treePath: ".gitea/workflows/job-with-needs-fail-if.yml",
- fileContent: `name: job-with-needs-fail-if
- on:
- push:
- paths:
- - '.gitea/workflows/job-with-needs-fail-if.yml'
- jobs:
- job1:
- runs-on: ubuntu-latest
- steps:
- - run: echo job1
- job2:
- runs-on: ubuntu-latest
- if: ${{ always() }}
- needs: [job1]
- steps:
- - run: echo job2
- `,
- outcomes: map[string]*mockTaskOutcome{
- "job1": {
- result: runnerv1.Result_RESULT_FAILURE,
- },
- "job2": {
- result: runnerv1.Result_RESULT_SUCCESS,
- },
- },
- expectedStatuses: map[string]string{
- "job1": actions_model.StatusFailure.String(),
- "job2": actions_model.StatusSuccess.String(),
- },
- },
- }
- onGiteaRun(t, func(t *testing.T, u *url.URL) {
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- session := loginUser(t, user2.Name)
- token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
-
- apiRepo := createActionsTestRepo(t, token, "actions-jobs-with-needs", false)
- runner := newMockRunner()
- runner.registerAsRepoRunner(t, user2.Name, apiRepo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
-
- for _, tc := range testCases {
- t.Run("test "+tc.treePath, func(t *testing.T) {
- // create the workflow file
- opts := getWorkflowCreateFileOptions(user2, apiRepo.DefaultBranch, "create "+tc.treePath, tc.fileContent)
- fileResp := createWorkflowFile(t, token, user2.Name, apiRepo.Name, tc.treePath, opts)
-
- // fetch and execute task
- for i := 0; i < len(tc.outcomes); i++ {
- task := runner.fetchTask(t)
- jobName := getTaskJobNameByTaskID(t, token, user2.Name, apiRepo.Name, task.Id)
- outcome := tc.outcomes[jobName]
- assert.NotNil(t, outcome)
- runner.execTask(t, task, outcome)
- }
-
- // check result
- req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/actions/tasks", user2.Name, apiRepo.Name)).
- AddTokenAuth(token)
- resp := MakeRequest(t, req, http.StatusOK)
- var actionTaskRespAfter api.ActionTaskResponse
- DecodeJSON(t, resp, &actionTaskRespAfter)
- for _, apiTask := range actionTaskRespAfter.Entries {
- if apiTask.HeadSHA != fileResp.Commit.SHA {
- continue
- }
- status := apiTask.Status
- assert.Equal(t, status, tc.expectedStatuses[apiTask.Name])
- }
- })
- }
- })
- }
-
- func TestJobNeedsMatrix(t *testing.T) {
- testCases := []struct {
- treePath string
- fileContent string
- outcomes map[string]*mockTaskOutcome
- expectedTaskNeeds map[string]*runnerv1.TaskNeed // jobID => TaskNeed
- }{
- {
- treePath: ".gitea/workflows/jobs-outputs-with-matrix.yml",
- fileContent: `name: jobs-outputs-with-matrix
- on:
- push:
- paths:
- - '.gitea/workflows/jobs-outputs-with-matrix.yml'
- jobs:
- job1:
- runs-on: ubuntu-latest
- outputs:
- output_1: ${{ steps.gen_output.outputs.output_1 }}
- output_2: ${{ steps.gen_output.outputs.output_2 }}
- output_3: ${{ steps.gen_output.outputs.output_3 }}
- strategy:
- matrix:
- version: [1, 2, 3]
- steps:
- - name: Generate output
- id: gen_output
- run: |
- version="${{ matrix.version }}"
- echo "output_${version}=${version}" >> "$GITHUB_OUTPUT"
- job2:
- runs-on: ubuntu-latest
- needs: [job1]
- steps:
- - run: echo '${{ toJSON(needs.job1.outputs) }}'
- `,
- outcomes: map[string]*mockTaskOutcome{
- "job1 (1)": {
- result: runnerv1.Result_RESULT_SUCCESS,
- outputs: map[string]string{
- "output_1": "1",
- "output_2": "",
- "output_3": "",
- },
- },
- "job1 (2)": {
- result: runnerv1.Result_RESULT_SUCCESS,
- outputs: map[string]string{
- "output_1": "",
- "output_2": "2",
- "output_3": "",
- },
- },
- "job1 (3)": {
- result: runnerv1.Result_RESULT_SUCCESS,
- outputs: map[string]string{
- "output_1": "",
- "output_2": "",
- "output_3": "3",
- },
- },
- },
- expectedTaskNeeds: map[string]*runnerv1.TaskNeed{
- "job1": {
- Result: runnerv1.Result_RESULT_SUCCESS,
- Outputs: map[string]string{
- "output_1": "1",
- "output_2": "2",
- "output_3": "3",
- },
- },
- },
- },
- {
- treePath: ".gitea/workflows/jobs-outputs-with-matrix-failure.yml",
- fileContent: `name: jobs-outputs-with-matrix-failure
- on:
- push:
- paths:
- - '.gitea/workflows/jobs-outputs-with-matrix-failure.yml'
- jobs:
- job1:
- runs-on: ubuntu-latest
- outputs:
- output_1: ${{ steps.gen_output.outputs.output_1 }}
- output_2: ${{ steps.gen_output.outputs.output_2 }}
- output_3: ${{ steps.gen_output.outputs.output_3 }}
- strategy:
- matrix:
- version: [1, 2, 3]
- steps:
- - name: Generate output
- id: gen_output
- run: |
- version="${{ matrix.version }}"
- echo "output_${version}=${version}" >> "$GITHUB_OUTPUT"
- job2:
- runs-on: ubuntu-latest
- if: ${{ always() }}
- needs: [job1]
- steps:
- - run: echo '${{ toJSON(needs.job1.outputs) }}'
- `,
- outcomes: map[string]*mockTaskOutcome{
- "job1 (1)": {
- result: runnerv1.Result_RESULT_SUCCESS,
- outputs: map[string]string{
- "output_1": "1",
- "output_2": "",
- "output_3": "",
- },
- },
- "job1 (2)": {
- result: runnerv1.Result_RESULT_FAILURE,
- outputs: map[string]string{
- "output_1": "",
- "output_2": "",
- "output_3": "",
- },
- },
- "job1 (3)": {
- result: runnerv1.Result_RESULT_SUCCESS,
- outputs: map[string]string{
- "output_1": "",
- "output_2": "",
- "output_3": "3",
- },
- },
- },
- expectedTaskNeeds: map[string]*runnerv1.TaskNeed{
- "job1": {
- Result: runnerv1.Result_RESULT_FAILURE,
- Outputs: map[string]string{
- "output_1": "1",
- "output_2": "",
- "output_3": "3",
- },
- },
- },
- },
- }
- onGiteaRun(t, func(t *testing.T, u *url.URL) {
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- session := loginUser(t, user2.Name)
- token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
-
- apiRepo := createActionsTestRepo(t, token, "actions-jobs-outputs-with-matrix", false)
- runner := newMockRunner()
- runner.registerAsRepoRunner(t, user2.Name, apiRepo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
-
- for _, tc := range testCases {
- t.Run("test "+tc.treePath, func(t *testing.T) {
- opts := getWorkflowCreateFileOptions(user2, apiRepo.DefaultBranch, "create "+tc.treePath, tc.fileContent)
- createWorkflowFile(t, token, user2.Name, apiRepo.Name, tc.treePath, opts)
-
- for i := 0; i < len(tc.outcomes); i++ {
- task := runner.fetchTask(t)
- jobName := getTaskJobNameByTaskID(t, token, user2.Name, apiRepo.Name, task.Id)
- outcome := tc.outcomes[jobName]
- assert.NotNil(t, outcome)
- runner.execTask(t, task, outcome)
- }
-
- task := runner.fetchTask(t)
- actualTaskNeeds := task.Needs
- assert.Len(t, actualTaskNeeds, len(tc.expectedTaskNeeds))
- for jobID, tn := range tc.expectedTaskNeeds {
- actualNeed := actualTaskNeeds[jobID]
- assert.Equal(t, tn.Result, actualNeed.Result)
- assert.Len(t, actualNeed.Outputs, len(tn.Outputs))
- for outputKey, outputValue := range tn.Outputs {
- assert.Equal(t, outputValue, actualNeed.Outputs[outputKey])
- }
- }
- })
- }
- })
- }
-
- func TestActionsGiteaContext(t *testing.T) {
- onGiteaRun(t, func(t *testing.T, u *url.URL) {
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- user2Session := loginUser(t, user2.Name)
- user2Token := getTokenForLoggedInUser(t, user2Session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
-
- apiBaseRepo := createActionsTestRepo(t, user2Token, "actions-gitea-context", false)
- baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiBaseRepo.ID})
- user2APICtx := NewAPITestContext(t, baseRepo.OwnerName, baseRepo.Name, auth_model.AccessTokenScopeWriteRepository)
-
- runner := newMockRunner()
- runner.registerAsRepoRunner(t, baseRepo.OwnerName, baseRepo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
-
- // init the workflow
- wfTreePath := ".gitea/workflows/pull.yml"
- wfFileContent := `name: Pull Request
- on: pull_request
- jobs:
- wf1-job:
- runs-on: ubuntu-latest
- steps:
- - run: echo 'test the pull'
- `
- opts := getWorkflowCreateFileOptions(user2, baseRepo.DefaultBranch, "create "+wfTreePath, wfFileContent)
- createWorkflowFile(t, user2Token, baseRepo.OwnerName, baseRepo.Name, wfTreePath, opts)
- // user2 creates a pull request
- doAPICreateFile(user2APICtx, "user2-patch.txt", &api.CreateFileOptions{
- FileOptions: api.FileOptions{
- NewBranchName: "user2/patch-1",
- Message: "create user2-patch.txt",
- Author: api.Identity{
- Name: user2.Name,
- Email: user2.Email,
- },
- Committer: api.Identity{
- Name: user2.Name,
- Email: user2.Email,
- },
- Dates: api.CommitDateOptions{
- Author: time.Now(),
- Committer: time.Now(),
- },
- },
- ContentBase64: base64.StdEncoding.EncodeToString([]byte("user2-fix")),
- })(t)
- apiPull, err := doAPICreatePullRequest(user2APICtx, baseRepo.OwnerName, baseRepo.Name, baseRepo.DefaultBranch, "user2/patch-1")(t)
- assert.NoError(t, err)
- task := runner.fetchTask(t)
- gtCtx := task.Context.GetFields()
- actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: task.Id})
- actionRunJob := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: actionTask.JobID})
- actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: actionRunJob.RunID})
- assert.NoError(t, actionRun.LoadAttributes(t.Context()))
-
- assert.Equal(t, user2.Name, gtCtx["actor"].GetStringValue())
- assert.Equal(t, setting.AppURL+"api/v1", gtCtx["api_url"].GetStringValue())
- assert.Equal(t, apiPull.Base.Ref, gtCtx["base_ref"].GetStringValue())
- runEvent := map[string]any{}
- assert.NoError(t, json.Unmarshal([]byte(actionRun.EventPayload), &runEvent))
- assert.True(t, reflect.DeepEqual(gtCtx["event"].GetStructValue().AsMap(), runEvent))
- assert.Equal(t, actionRun.TriggerEvent, gtCtx["event_name"].GetStringValue())
- assert.Equal(t, apiPull.Head.Ref, gtCtx["head_ref"].GetStringValue())
- assert.Equal(t, actionRunJob.JobID, gtCtx["job"].GetStringValue())
- assert.Equal(t, actionRun.Ref, gtCtx["ref"].GetStringValue())
- assert.Equal(t, (git.RefName(actionRun.Ref)).ShortName(), gtCtx["ref_name"].GetStringValue())
- assert.False(t, gtCtx["ref_protected"].GetBoolValue())
- assert.Equal(t, string((git.RefName(actionRun.Ref)).RefType()), gtCtx["ref_type"].GetStringValue())
- assert.Equal(t, actionRun.Repo.OwnerName+"/"+actionRun.Repo.Name, gtCtx["repository"].GetStringValue())
- assert.Equal(t, actionRun.Repo.OwnerName, gtCtx["repository_owner"].GetStringValue())
- assert.Equal(t, actionRun.Repo.HTMLURL(), gtCtx["repositoryUrl"].GetStringValue())
- assert.Equal(t, strconv.FormatInt(actionRunJob.RunID, 10), gtCtx["run_id"].GetStringValue())
- assert.Equal(t, strconv.FormatInt(actionRun.Index, 10), gtCtx["run_number"].GetStringValue())
- assert.Equal(t, strconv.FormatInt(actionRunJob.Attempt, 10), gtCtx["run_attempt"].GetStringValue())
- assert.Equal(t, "Actions", gtCtx["secret_source"].GetStringValue())
- assert.Equal(t, setting.AppURL, gtCtx["server_url"].GetStringValue())
- assert.Equal(t, actionRun.CommitSHA, gtCtx["sha"].GetStringValue())
- assert.Equal(t, actionRun.WorkflowID, gtCtx["workflow"].GetStringValue())
- assert.Equal(t, setting.Actions.DefaultActionsURL.URL(), gtCtx["gitea_default_actions_url"].GetStringValue())
- token := gtCtx["token"].GetStringValue()
- assert.Equal(t, actionTask.TokenLastEight, token[len(token)-8:])
- })
- }
-
- // Ephemeral
- func TestActionsGiteaContextEphemeral(t *testing.T) {
- onGiteaRun(t, func(t *testing.T, u *url.URL) {
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- user2Session := loginUser(t, user2.Name)
- user2Token := getTokenForLoggedInUser(t, user2Session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
-
- apiBaseRepo := createActionsTestRepo(t, user2Token, "actions-gitea-context", false)
- baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiBaseRepo.ID})
- user2APICtx := NewAPITestContext(t, baseRepo.OwnerName, baseRepo.Name, auth_model.AccessTokenScopeWriteRepository)
-
- runner := newMockRunner()
- runner.registerAsRepoRunner(t, baseRepo.OwnerName, baseRepo.Name, "mock-runner", []string{"ubuntu-latest"}, true)
-
- // verify CleanupEphemeralRunners does not remove this runner
- err := actions_service.CleanupEphemeralRunners(t.Context())
- assert.NoError(t, err)
-
- // init the workflow
- wfTreePath := ".gitea/workflows/pull.yml"
- wfFileContent := `name: Pull Request
- on: pull_request
- jobs:
- wf1-job:
- runs-on: ubuntu-latest
- steps:
- - run: echo 'test the pull'
- wf2-job:
- runs-on: ubuntu-latest
- steps:
- - run: echo 'test the pull'
- `
- opts := getWorkflowCreateFileOptions(user2, baseRepo.DefaultBranch, "create "+wfTreePath, wfFileContent)
- createWorkflowFile(t, user2Token, baseRepo.OwnerName, baseRepo.Name, wfTreePath, opts)
- // user2 creates a pull request
- doAPICreateFile(user2APICtx, "user2-patch.txt", &api.CreateFileOptions{
- FileOptions: api.FileOptions{
- NewBranchName: "user2/patch-1",
- Message: "create user2-patch.txt",
- Author: api.Identity{
- Name: user2.Name,
- Email: user2.Email,
- },
- Committer: api.Identity{
- Name: user2.Name,
- Email: user2.Email,
- },
- Dates: api.CommitDateOptions{
- Author: time.Now(),
- Committer: time.Now(),
- },
- },
- ContentBase64: base64.StdEncoding.EncodeToString([]byte("user2-fix")),
- })(t)
- apiPull, err := doAPICreatePullRequest(user2APICtx, baseRepo.OwnerName, baseRepo.Name, baseRepo.DefaultBranch, "user2/patch-1")(t)
- assert.NoError(t, err)
- task := runner.fetchTask(t)
- gtCtx := task.Context.GetFields()
- actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: task.Id})
- actionRunJob := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: actionTask.JobID})
- actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: actionRunJob.RunID})
- assert.NoError(t, actionRun.LoadAttributes(t.Context()))
-
- assert.Equal(t, user2.Name, gtCtx["actor"].GetStringValue())
- assert.Equal(t, setting.AppURL+"api/v1", gtCtx["api_url"].GetStringValue())
- assert.Equal(t, apiPull.Base.Ref, gtCtx["base_ref"].GetStringValue())
- runEvent := map[string]any{}
- assert.NoError(t, json.Unmarshal([]byte(actionRun.EventPayload), &runEvent))
- assert.True(t, reflect.DeepEqual(gtCtx["event"].GetStructValue().AsMap(), runEvent))
- assert.Equal(t, actionRun.TriggerEvent, gtCtx["event_name"].GetStringValue())
- assert.Equal(t, apiPull.Head.Ref, gtCtx["head_ref"].GetStringValue())
- assert.Equal(t, actionRunJob.JobID, gtCtx["job"].GetStringValue())
- assert.Equal(t, actionRun.Ref, gtCtx["ref"].GetStringValue())
- assert.Equal(t, (git.RefName(actionRun.Ref)).ShortName(), gtCtx["ref_name"].GetStringValue())
- assert.False(t, gtCtx["ref_protected"].GetBoolValue())
- assert.Equal(t, string((git.RefName(actionRun.Ref)).RefType()), gtCtx["ref_type"].GetStringValue())
- assert.Equal(t, actionRun.Repo.OwnerName+"/"+actionRun.Repo.Name, gtCtx["repository"].GetStringValue())
- assert.Equal(t, actionRun.Repo.OwnerName, gtCtx["repository_owner"].GetStringValue())
- assert.Equal(t, actionRun.Repo.HTMLURL(), gtCtx["repositoryUrl"].GetStringValue())
- assert.Equal(t, strconv.FormatInt(actionRunJob.RunID, 10), gtCtx["run_id"].GetStringValue())
- assert.Equal(t, strconv.FormatInt(actionRun.Index, 10), gtCtx["run_number"].GetStringValue())
- assert.Equal(t, strconv.FormatInt(actionRunJob.Attempt, 10), gtCtx["run_attempt"].GetStringValue())
- assert.Equal(t, "Actions", gtCtx["secret_source"].GetStringValue())
- assert.Equal(t, setting.AppURL, gtCtx["server_url"].GetStringValue())
- assert.Equal(t, actionRun.CommitSHA, gtCtx["sha"].GetStringValue())
- assert.Equal(t, actionRun.WorkflowID, gtCtx["workflow"].GetStringValue())
- assert.Equal(t, setting.Actions.DefaultActionsURL.URL(), gtCtx["gitea_default_actions_url"].GetStringValue())
- token := gtCtx["token"].GetStringValue()
- assert.Equal(t, actionTask.TokenLastEight, token[len(token)-8:])
-
- // verify CleanupEphemeralRunners does not remove this runner
- err = actions_service.CleanupEphemeralRunners(t.Context())
- assert.NoError(t, err)
-
- resp, err := runner.client.runnerServiceClient.FetchTask(t.Context(), connect.NewRequest(&runnerv1.FetchTaskRequest{
- TasksVersion: 0,
- }))
- assert.NoError(t, err)
- assert.Nil(t, resp.Msg.Task)
-
- // verify CleanupEphemeralRunners does not remove this runner
- err = actions_service.CleanupEphemeralRunners(t.Context())
- assert.NoError(t, err)
-
- _, err = runner.client.runnerServiceClient.UpdateTask(t.Context(), connect.NewRequest(&runnerv1.UpdateTaskRequest{
- State: &runnerv1.TaskState{
- Id: actionTask.ID,
- Result: runnerv1.Result_RESULT_SUCCESS,
- },
- }))
- assert.NoError(t, err)
-
- resp, err = runner.client.runnerServiceClient.FetchTask(t.Context(), connect.NewRequest(&runnerv1.FetchTaskRequest{
- TasksVersion: 0,
- }))
- assert.Error(t, err)
- assert.Nil(t, resp)
-
- resp, err = runner.client.runnerServiceClient.FetchTask(t.Context(), connect.NewRequest(&runnerv1.FetchTaskRequest{
- TasksVersion: 0,
- }))
- assert.Error(t, err)
- assert.Nil(t, resp)
-
- // create a runner that picks a job and get force cancelled
- runnerToBeRemoved := newMockRunner()
- runnerToBeRemoved.registerAsRepoRunner(t, baseRepo.OwnerName, baseRepo.Name, "mock-runner-to-be-removed", []string{"ubuntu-latest"}, true)
-
- taskToStopAPIObj := runnerToBeRemoved.fetchTask(t)
-
- taskToStop := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: taskToStopAPIObj.Id})
-
- // verify CleanupEphemeralRunners does not remove the custom crafted runner
- err = actions_service.CleanupEphemeralRunners(t.Context())
- assert.NoError(t, err)
-
- runnerToRemove := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{ID: taskToStop.RunnerID})
-
- err = actions_model.StopTask(t.Context(), taskToStop.ID, actions_model.StatusFailure)
- assert.NoError(t, err)
-
- // verify CleanupEphemeralRunners does remove the custom crafted runner
- err = actions_service.CleanupEphemeralRunners(t.Context())
- assert.NoError(t, err)
-
- unittest.AssertNotExistsBean(t, &actions_model.ActionRunner{ID: runnerToRemove.ID})
- })
- }
-
- func createActionsTestRepo(t *testing.T, authToken, repoName string, isPrivate bool) *api.Repository {
- req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", &api.CreateRepoOption{
- Name: repoName,
- Private: isPrivate,
- Readme: "Default",
- AutoInit: true,
- DefaultBranch: "main",
- }).AddTokenAuth(authToken)
- resp := MakeRequest(t, req, http.StatusCreated)
- var apiRepo api.Repository
- DecodeJSON(t, resp, &apiRepo)
- return &apiRepo
- }
-
- func getWorkflowCreateFileOptions(u *user_model.User, branch, msg, content string) *api.CreateFileOptions {
- return &api.CreateFileOptions{
- FileOptions: api.FileOptions{
- BranchName: branch,
- Message: msg,
- Author: api.Identity{
- Name: u.Name,
- Email: u.Email,
- },
- Committer: api.Identity{
- Name: u.Name,
- Email: u.Email,
- },
- Dates: api.CommitDateOptions{
- Author: time.Now(),
- Committer: time.Now(),
- },
- },
- ContentBase64: base64.StdEncoding.EncodeToString([]byte(content)),
- }
- }
-
- func createWorkflowFile(t *testing.T, authToken, ownerName, repoName, treePath string, opts *api.CreateFileOptions) *api.FileResponse {
- req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", ownerName, repoName, treePath), opts).
- AddTokenAuth(authToken)
- resp := MakeRequest(t, req, http.StatusCreated)
- var fileResponse api.FileResponse
- DecodeJSON(t, resp, &fileResponse)
- return &fileResponse
- }
-
- // getTaskJobNameByTaskID get the job name of the task by task ID
- // there is currently not an API for querying a task by ID so we have to list all the tasks
- func getTaskJobNameByTaskID(t *testing.T, authToken, ownerName, repoName string, taskID int64) string {
- // FIXME: we may need to query several pages
- req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/actions/tasks", ownerName, repoName)).
- AddTokenAuth(authToken)
- resp := MakeRequest(t, req, http.StatusOK)
- var taskRespBefore api.ActionTaskResponse
- DecodeJSON(t, resp, &taskRespBefore)
- for _, apiTask := range taskRespBefore.Entries {
- if apiTask.ID == taskID {
- return apiTask.Name
- }
- }
- return ""
- }
|