mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-10 05:24:18 +09:00
Pairs with https://gitea.com/gitea/runner/pulls/1143. Gitea depends on `gitea.com/gitea/runner` for exactly two packages: `act/model` and `act/exprparser`, the workflow model and the expression evaluator it needs to parse workflows and to build the task payload the runner consumes. Pulling the whole runner module in for that is heavy and puts shared code in the repository of one of the two consumers. Both packages now live in `gitea.dev/actionslib` (`pkg/model`, `pkg/exprparser`), the module Gitea and the runner already share for the runner API, so the dependency on the runner repository is dropped here. ### Changes - `gitea.com/gitea/runner/act/model` -> `gitea.dev/actionslib/pkg/model`, `.../act/exprparser` -> `gitea.dev/actionslib/pkg/exprparser` (22 files, import paths only). - `routers/api/actions/runner/interceptor.go` takes the `x-runner-uuid` / `x-runner-token` names from `gitea.dev/actionslib/pkg/protocol` instead of repeating the literals the runner also has. - `go.mod`: `gitea.com/gitea/runner` removed. --------- Signed-off-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: Zettat123 <zettat123@gmail.com>
310 lines
9.6 KiB
Go
310 lines
9.6 KiB
Go
// Copyright 2024 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
runnerv1 "gitea.dev/actionslib/runner/v1"
|
|
actions_model "gitea.dev/models/actions"
|
|
auth_model "gitea.dev/models/auth"
|
|
repo_model "gitea.dev/models/repo"
|
|
"gitea.dev/models/unittest"
|
|
user_model "gitea.dev/models/user"
|
|
"gitea.dev/modules/setting"
|
|
"gitea.dev/modules/storage"
|
|
"gitea.dev/modules/test"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
)
|
|
|
|
func TestDownloadTaskLogs(t *testing.T) {
|
|
now := time.Now()
|
|
testCases := []struct {
|
|
treePath string
|
|
fileContent string
|
|
outcome []*mockTaskOutcome
|
|
zstdEnabled bool
|
|
}{
|
|
{
|
|
treePath: ".gitea/workflows/download-task-logs-zstd.yml",
|
|
fileContent: `name: download-task-logs-zstd
|
|
on:
|
|
push:
|
|
paths:
|
|
- '.gitea/workflows/download-task-logs-zstd.yml'
|
|
jobs:
|
|
job1:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- run: echo job1 with zstd enabled
|
|
job2:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- run: echo job2 with zstd enabled
|
|
`,
|
|
outcome: []*mockTaskOutcome{
|
|
{
|
|
result: runnerv1.Result_RESULT_SUCCESS,
|
|
logRows: []*runnerv1.LogRow{
|
|
{
|
|
Time: timestamppb.New(now.Add(1 * time.Second)),
|
|
Content: " \U0001F433 docker create image",
|
|
},
|
|
{
|
|
Time: timestamppb.New(now.Add(2 * time.Second)),
|
|
Content: "job1 zstd enabled",
|
|
},
|
|
{
|
|
Time: timestamppb.New(now.Add(3 * time.Second)),
|
|
Content: "\U0001F3C1 Job succeeded",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
result: runnerv1.Result_RESULT_SUCCESS,
|
|
logRows: []*runnerv1.LogRow{
|
|
{
|
|
Time: timestamppb.New(now.Add(1 * time.Second)),
|
|
Content: " \U0001F433 docker create image",
|
|
},
|
|
{
|
|
Time: timestamppb.New(now.Add(2 * time.Second)),
|
|
Content: "job2 zstd enabled",
|
|
},
|
|
{
|
|
Time: timestamppb.New(now.Add(3 * time.Second)),
|
|
Content: "\U0001F3C1 Job succeeded",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
zstdEnabled: true,
|
|
},
|
|
{
|
|
treePath: ".gitea/workflows/download-task-logs-no-zstd.yml",
|
|
fileContent: `name: download-task-logs-no-zstd
|
|
on:
|
|
push:
|
|
paths:
|
|
- '.gitea/workflows/download-task-logs-no-zstd.yml'
|
|
jobs:
|
|
job1:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- run: echo job1 with zstd disabled
|
|
job2:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- run: echo job2 with zstd disabled
|
|
`,
|
|
outcome: []*mockTaskOutcome{
|
|
{
|
|
result: runnerv1.Result_RESULT_SUCCESS,
|
|
logRows: []*runnerv1.LogRow{
|
|
{
|
|
Time: timestamppb.New(now.Add(4 * time.Second)),
|
|
Content: " \U0001F433 docker create image",
|
|
},
|
|
{
|
|
Time: timestamppb.New(now.Add(5 * time.Second)),
|
|
Content: "job1 zstd disabled",
|
|
},
|
|
{
|
|
Time: timestamppb.New(now.Add(6 * time.Second)),
|
|
Content: "\U0001F3C1 Job succeeded",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
result: runnerv1.Result_RESULT_SUCCESS,
|
|
logRows: []*runnerv1.LogRow{
|
|
{
|
|
Time: timestamppb.New(now.Add(4 * time.Second)),
|
|
Content: " \U0001F433 docker create image",
|
|
},
|
|
{
|
|
Time: timestamppb.New(now.Add(5 * time.Second)),
|
|
Content: "job2 zstd disabled",
|
|
},
|
|
{
|
|
Time: timestamppb.New(now.Add(6 * time.Second)),
|
|
Content: "\U0001F3C1 Job succeeded",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
zstdEnabled: false,
|
|
},
|
|
}
|
|
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-download-task-logs", false)
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID})
|
|
runner := newMockRunner()
|
|
runner.registerAsRepoRunner(t, user2.Name, repo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
|
|
|
|
for _, tc := range testCases {
|
|
t.Run("test "+tc.treePath, func(t *testing.T) {
|
|
var resetFunc func()
|
|
if tc.zstdEnabled {
|
|
resetFunc = test.MockVariableValue(&setting.Actions.LogCompression, "zstd")
|
|
assert.True(t, setting.Actions.LogCompression.IsZstd())
|
|
} else {
|
|
resetFunc = test.MockVariableValue(&setting.Actions.LogCompression, "none")
|
|
assert.False(t, setting.Actions.LogCompression.IsZstd())
|
|
}
|
|
|
|
// create the workflow file
|
|
opts := getWorkflowCreateFileOptions(user2, repo.DefaultBranch, "create "+tc.treePath, tc.fileContent)
|
|
createWorkflowFile(t, token, user2.Name, repo.Name, tc.treePath, opts)
|
|
|
|
// fetch and execute tasks
|
|
for _, outcome := range tc.outcome {
|
|
task := runner.fetchTask(t)
|
|
runner.execTask(t, task, outcome)
|
|
|
|
// check whether the log file exists
|
|
logFileName := fmt.Sprintf("%s/%02x/%d.log", repo.FullName(), task.Id%256, task.Id)
|
|
if setting.Actions.LogCompression.IsZstd() {
|
|
logFileName += ".zst"
|
|
}
|
|
_, err := storage.Actions.Stat(logFileName)
|
|
assert.NoError(t, err)
|
|
|
|
_, job, run := getTaskAndJobAndRunByTaskID(t, task.Id)
|
|
|
|
// download task logs and check content
|
|
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/actions/runs/%d/jobs/%d/logs", user2.Name, repo.Name, run.ID, job.ID)).
|
|
AddTokenAuth(token)
|
|
resp := MakeRequest(t, req, http.StatusOK)
|
|
logTextLines := strings.Split(strings.TrimSpace(resp.Body.String()), "\n")
|
|
assert.Len(t, logTextLines, len(outcome.logRows))
|
|
for idx, lr := range outcome.logRows {
|
|
assert.Equal(
|
|
t,
|
|
fmt.Sprintf("%s %s", lr.Time.AsTime().Format("2006-01-02T15:04:05.0000000Z07:00"), lr.Content),
|
|
logTextLines[idx],
|
|
)
|
|
}
|
|
|
|
// download task logs from API and check content
|
|
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/actions/jobs/%d/logs", user2.Name, repo.Name, job.ID)).
|
|
AddTokenAuth(token)
|
|
resp = MakeRequest(t, req, http.StatusOK)
|
|
logTextLines = strings.Split(strings.TrimSpace(resp.Body.String()), "\n")
|
|
assert.Len(t, logTextLines, len(outcome.logRows))
|
|
for idx, lr := range outcome.logRows {
|
|
assert.Equal(
|
|
t,
|
|
fmt.Sprintf("%s %s", lr.Time.AsTime().Format("2006-01-02T15:04:05.0000000Z07:00"), lr.Content),
|
|
logTextLines[idx],
|
|
)
|
|
}
|
|
}
|
|
resetFunc()
|
|
})
|
|
}
|
|
|
|
t.Run("DownloadRerunTaskLogs", func(t *testing.T) {
|
|
treePath := ".gitea/workflows/download-rerun-logs.yml"
|
|
fileContent := `name: download-rerun-logs
|
|
on:
|
|
push:
|
|
paths:
|
|
- '.gitea/workflows/download-rerun-logs.yml'
|
|
jobs:
|
|
job1:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- run: echo job1
|
|
job2:
|
|
runs-on: ubuntu-latest
|
|
needs: [job1]
|
|
steps:
|
|
- run: echo job2
|
|
`
|
|
|
|
// create the workflow file
|
|
opts := getWorkflowCreateFileOptions(user2, repo.DefaultBranch, "create "+treePath, fileContent)
|
|
createWorkflowFile(t, token, user2.Name, repo.Name, treePath, opts)
|
|
|
|
// first run
|
|
job1Task1 := runner.fetchTask(t)
|
|
_, job1, _ := getTaskAndJobAndRunByTaskID(t, job1Task1.Id)
|
|
runner.execTask(t, job1Task1, &mockTaskOutcome{
|
|
result: runnerv1.Result_RESULT_SUCCESS,
|
|
logRows: []*runnerv1.LogRow{
|
|
{
|
|
Time: timestamppb.New(now.Add(1 * time.Second)),
|
|
Content: "job1 first run",
|
|
},
|
|
},
|
|
})
|
|
job2Task1 := runner.fetchTask(t)
|
|
_, job2, run := getTaskAndJobAndRunByTaskID(t, job2Task1.Id)
|
|
runner.execTask(t, job2Task1, &mockTaskOutcome{
|
|
result: runnerv1.Result_RESULT_SUCCESS,
|
|
logRows: []*runnerv1.LogRow{
|
|
{
|
|
Time: timestamppb.New(now.Add(1 * time.Second)),
|
|
Content: "job2 first run",
|
|
},
|
|
},
|
|
})
|
|
|
|
// check job1 log
|
|
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/actions/runs/%d/jobs/%d/logs", user2.Name, repo.Name, run.ID, job1.ID)).
|
|
AddTokenAuth(token)
|
|
resp := MakeRequest(t, req, http.StatusOK)
|
|
assert.Contains(t, resp.Body.String(), "job1 first run")
|
|
// check job2 log
|
|
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s/actions/runs/%d/jobs/%d/logs", user2.Name, repo.Name, run.ID, job2.ID)).
|
|
AddTokenAuth(token)
|
|
resp = MakeRequest(t, req, http.StatusOK)
|
|
assert.Contains(t, resp.Body.String(), "job2 first run")
|
|
|
|
// only rerun job2
|
|
req = NewRequest(t, "POST", fmt.Sprintf("/%s/%s/actions/runs/%d/jobs/%d/rerun", user2.Name, repo.Name, run.ID, job2.ID))
|
|
session.MakeRequest(t, req, http.StatusOK)
|
|
job2TaskRerun := runner.fetchTask(t)
|
|
runner.execTask(t, job2TaskRerun, &mockTaskOutcome{
|
|
result: runnerv1.Result_RESULT_SUCCESS,
|
|
logRows: []*runnerv1.LogRow{
|
|
{
|
|
Time: timestamppb.New(now.Add(1 * time.Second)),
|
|
Content: "job2 rerun",
|
|
},
|
|
},
|
|
})
|
|
|
|
run = unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: run.ID})
|
|
job1Rerun := getLatestAttemptJobByTemplateJobID(t, run.ID, job1.ID)
|
|
assert.Equal(t, run.LatestAttemptID, job1Rerun.RunAttemptID)
|
|
job2Rerun := getLatestAttemptJobByTemplateJobID(t, run.ID, job2.ID)
|
|
assert.Equal(t, run.LatestAttemptID, job2Rerun.RunAttemptID)
|
|
|
|
// check job1 rerun log
|
|
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s/actions/runs/%d/jobs/%d/logs", user2.Name, repo.Name, run.ID, job1Rerun.ID)).
|
|
AddTokenAuth(token)
|
|
resp = MakeRequest(t, req, http.StatusOK)
|
|
assert.Contains(t, resp.Body.String(), "job1 first run") // should return the log of first run because job1 didn't rerun
|
|
// check job2 rerun log
|
|
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s/actions/runs/%d/jobs/%d/logs", user2.Name, repo.Name, run.ID, job2Rerun.ID)).
|
|
AddTokenAuth(token)
|
|
resp = MakeRequest(t, req, http.StatusOK)
|
|
assert.Contains(t, resp.Body.String(), "job2 rerun")
|
|
})
|
|
})
|
|
}
|