mirror of
https://gitea.com/gitea/runner.git
synced 2026-08-30 15:57:44 +00:00
test: speed up tests (#1181)
Parallelize isolated workflow tests, consolidate redundant fixtures, and replace fixed waits with deterministic synchronization. Keep the readable curl service probe and use `getent` for hostname resolution. Measured on the same machine with `make test`: 1. Wall time: 170.50s to 136.12s, down 34.38s or 20.2%. 1. `act/runner`: 138.417s to 124.291s, down 14.126s or 10.2%. 1. `act/runner` coverage: unchanged at 85.2%. 1. `TestDockerExecAbort`: 2.514s to 0.012s package time. Stability checks: 1. Cancellation and deadline tests: 100 race-enabled repetitions. 1. Host runner suite: 10 race-enabled repetitions. 1. Changed Docker fixtures: 3 consecutive repetitions. Full race suite, Go and Windows lint, source checks, and security scan pass. Assisted-by: Codex:GPT-5 Reviewed-on: https://gitea.com/gitea/runner/pulls/1181 Reviewed-by: bircni <bircni@icloud.com> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
@@ -16,7 +16,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.com/gitea/runner/act/common"
|
"gitea.com/gitea/runner/act/common"
|
||||||
|
|
||||||
@@ -148,12 +147,17 @@ func (m *mockDockerClient) NetworkRemove(ctx context.Context, id string, opts mo
|
|||||||
return args.Get(0).(mobyclient.NetworkRemoveResult), args.Error(1)
|
return args.Get(0).(mobyclient.NetworkRemoveResult), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
type endlessReader struct {
|
type interruptReader struct {
|
||||||
io.Reader
|
started chan struct{}
|
||||||
|
interrupted chan struct{}
|
||||||
|
stopped chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r endlessReader) Read(_ []byte) (n int, err error) {
|
func (r *interruptReader) Read(_ []byte) (int, error) {
|
||||||
return 1, nil
|
close(r.started)
|
||||||
|
<-r.interrupted
|
||||||
|
close(r.stopped)
|
||||||
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockConn struct {
|
type mockConn struct {
|
||||||
@@ -173,15 +177,18 @@ func (m *mockConn) Close() (err error) {
|
|||||||
func TestDockerExecAbort(t *testing.T) {
|
func TestDockerExecAbort(t *testing.T) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
reader := &interruptReader{started: make(chan struct{}), interrupted: make(chan struct{}), stopped: make(chan struct{})}
|
||||||
conn := &mockConn{}
|
conn := &mockConn{}
|
||||||
conn.On("Write", mock.AnythingOfType("[]uint8")).Return(1, nil)
|
conn.On("Write", []byte{3}).
|
||||||
|
Run(func(mock.Arguments) { close(reader.interrupted) }).
|
||||||
|
Return(1, nil)
|
||||||
|
|
||||||
client := &mockDockerClient{}
|
client := &mockDockerClient{}
|
||||||
client.On("ExecCreate", ctx, "123", mock.AnythingOfType("client.ExecCreateOptions")).Return(mobyclient.ExecCreateResult{ID: "id"}, nil)
|
client.On("ExecCreate", ctx, "123", mock.AnythingOfType("client.ExecCreateOptions")).Return(mobyclient.ExecCreateResult{ID: "id"}, nil)
|
||||||
client.On("ExecAttach", ctx, "id", mock.AnythingOfType("client.ExecAttachOptions")).Return(mobyclient.ExecAttachResult{
|
client.On("ExecAttach", ctx, "id", mock.AnythingOfType("client.ExecAttachOptions")).Return(mobyclient.ExecAttachResult{
|
||||||
HijackedResponse: mobyclient.HijackedResponse{
|
HijackedResponse: mobyclient.HijackedResponse{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
Reader: bufio.NewReader(endlessReader{}),
|
Reader: bufio.NewReader(reader),
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
@@ -199,11 +206,11 @@ func TestDockerExecAbort(t *testing.T) {
|
|||||||
channel <- cr.exec([]string{""}, map[string]string{}, "user", "workdir")(ctx)
|
channel <- cr.exec([]string{""}, map[string]string{}, "user", "workdir")(ctx)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
<-reader.started
|
||||||
|
|
||||||
cancel()
|
cancel()
|
||||||
|
|
||||||
err := <-channel
|
err := <-channel
|
||||||
|
<-reader.stopped
|
||||||
assert.ErrorIs(t, err, context.Canceled) //nolint:testifylint // pre-existing issue from nektos/act
|
assert.ErrorIs(t, err, context.Canceled) //nolint:testifylint // pre-existing issue from nektos/act
|
||||||
|
|
||||||
conn.AssertExpectations(t)
|
conn.AssertExpectations(t)
|
||||||
|
|||||||
@@ -107,17 +107,14 @@ func TestMainStepsExecutorMarksFailedOnTimeoutBetweenSteps(t *testing.T) {
|
|||||||
"job1": createJob(t, `runs-on: ubuntu-latest`, ""),
|
"job1": createJob(t, `runs-on: ubuntu-latest`, ""),
|
||||||
})
|
})
|
||||||
|
|
||||||
// A short deadline that we let elapse between steps, so no step records the error itself.
|
ctx := newControllableDeadlineContext(context.Background())
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
var ran []string
|
var ran []string
|
||||||
var laterStepCtxErr error
|
var laterStepCtxErr error
|
||||||
steps := []common.Executor{
|
steps := []common.Executor{
|
||||||
func(c context.Context) error {
|
func(context.Context) error {
|
||||||
ran = append(ran, "step1")
|
ran = append(ran, "step1")
|
||||||
// Block until the job deadline elapses, then return cleanly: the interrupt lands in the loop's between-steps check, not inside a step.
|
ctx.expire()
|
||||||
<-c.Done()
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
func(c context.Context) error {
|
func(c context.Context) error {
|
||||||
|
|||||||
@@ -528,18 +528,39 @@ func TestNewJobExecutor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type controllableDeadlineContext struct {
|
||||||
|
context.Context
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newControllableDeadlineContext(parent context.Context) *controllableDeadlineContext {
|
||||||
|
return &controllableDeadlineContext{Context: parent, done: make(chan struct{})}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *controllableDeadlineContext) Done() <-chan struct{} {
|
||||||
|
return ctx.done
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *controllableDeadlineContext) Err() error {
|
||||||
|
select {
|
||||||
|
case <-ctx.done:
|
||||||
|
return context.DeadlineExceeded
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *controllableDeadlineContext) expire() {
|
||||||
|
close(ctx.done)
|
||||||
|
}
|
||||||
|
|
||||||
// TestNewJobExecutorRunsPostStepsAfterTimeout guards the timeout-minutes cleanup
|
// TestNewJobExecutorRunsPostStepsAfterTimeout guards the timeout-minutes cleanup
|
||||||
// path: when a job exceeds its timeout the job context is DeadlineExceeded, but
|
// path: when a job exceeds its timeout the job context is DeadlineExceeded, but
|
||||||
// the post steps (cleanup hooks like actions/checkout post and cache save) must
|
// the post steps (cleanup hooks like actions/checkout post and cache save) must
|
||||||
// still run against a fresh, non-expired context, and the job must still be
|
// still run against a fresh, non-expired context, and the job must still be
|
||||||
// reported as failed.
|
// reported as failed.
|
||||||
func TestNewJobExecutorRunsPostStepsAfterTimeout(t *testing.T) {
|
func TestNewJobExecutorRunsPostStepsAfterTimeout(t *testing.T) {
|
||||||
ctx := common.WithJobErrorContainer(context.Background())
|
ctx := newControllableDeadlineContext(common.WithJobErrorContainer(context.Background()))
|
||||||
// The timeout is generous so the main step (which blocks on ctx.Done below) is
|
|
||||||
// always reached before the deadline fires; otherwise the pipeline would
|
|
||||||
// short-circuit before the step runs and the job error would never be set.
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, 200*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
jim := &jobInfoMock{}
|
jim := &jobInfoMock{}
|
||||||
sfm := &stepFactoryMock{}
|
sfm := &stepFactoryMock{}
|
||||||
@@ -570,11 +591,9 @@ func TestNewJobExecutorRunsPostStepsAfterTimeout(t *testing.T) {
|
|||||||
sm := &stepMock{}
|
sm := &stepMock{}
|
||||||
sfm.On("newStep", stepModel, rc).Return(sm, nil)
|
sfm.On("newStep", stepModel, rc).Return(sm, nil)
|
||||||
sm.On("pre").Return(func(ctx context.Context) error { return nil })
|
sm.On("pre").Return(func(ctx context.Context) error { return nil })
|
||||||
// The main step runs past the job timeout: it blocks until the job context is
|
sm.On("main").Return(func(stepCtx context.Context) error {
|
||||||
// done, mirroring a step that overruns timeout-minutes.
|
ctx.expire()
|
||||||
sm.On("main").Return(func(ctx context.Context) error {
|
return stepCtx.Err()
|
||||||
<-ctx.Done()
|
|
||||||
return ctx.Err()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
var postRan bool
|
var postRan bool
|
||||||
|
|||||||
+28
-47
@@ -14,6 +14,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -171,6 +172,23 @@ func TestGraphEvent(t *testing.T) {
|
|||||||
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
|
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
|
||||||
assert.NotNil(t, plan)
|
assert.NotNil(t, plan)
|
||||||
assert.Empty(t, plan.Stages)
|
assert.Empty(t, plan.Stages)
|
||||||
|
|
||||||
|
for _, workflowPath := range []string{
|
||||||
|
"testdata/workflow_dispatch_no_inputs_mapping/workflow_dispatch.yml",
|
||||||
|
"testdata/workflow_dispatch-scalar/workflow_dispatch.yml",
|
||||||
|
} {
|
||||||
|
planner, err := model.NewWorkflowPlanner(workflowPath, true)
|
||||||
|
if !assert.NoError(t, err, workflowPath) { //nolint:testifylint // pre-existing issue from nektos/act
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
plan, err := planner.PlanEvent("workflow_dispatch")
|
||||||
|
if !assert.NoError(t, err, workflowPath) || !assert.NotNil(t, plan, workflowPath) { //nolint:testifylint // pre-existing issue from nektos/act
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if assert.Len(t, plan.Stages, 1, workflowPath) {
|
||||||
|
assert.Len(t, plan.Stages[0].Runs, 1, workflowPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// these two build the same action Dockerfiles into one image tag, so they cannot overlap
|
// these two build the same action Dockerfiles into one image tag, so they cannot overlap
|
||||||
@@ -276,7 +294,6 @@ func TestRunEvent(t *testing.T) {
|
|||||||
{workdir, "fail", "push", "exit with `FAILURE`: 1", platforms, secrets},
|
{workdir, "fail", "push", "exit with `FAILURE`: 1", platforms, secrets},
|
||||||
{workdir, "checkout", "push", "", platforms, secrets},
|
{workdir, "checkout", "push", "", platforms, secrets},
|
||||||
{workdir, "job-container", "push", "", platforms, secrets},
|
{workdir, "job-container", "push", "", platforms, secrets},
|
||||||
{workdir, "job-container-non-root", "push", "", platforms, secrets},
|
|
||||||
{workdir, "job-container-invalid-credentials", "push", "failed to handle credentials: failed to interpolate container.credentials.password", platforms, secrets},
|
{workdir, "job-container-invalid-credentials", "push", "failed to handle credentials: failed to interpolate container.credentials.password", platforms, secrets},
|
||||||
{workdir, "container-hostname", "push", "", platforms, secrets},
|
{workdir, "container-hostname", "push", "", platforms, secrets},
|
||||||
{workdir, "matrix", "push", "", platforms, secrets},
|
{workdir, "matrix", "push", "", platforms, secrets},
|
||||||
@@ -286,17 +303,14 @@ func TestRunEvent(t *testing.T) {
|
|||||||
{workdir, "defaults-run", "push", "", platforms, secrets},
|
{workdir, "defaults-run", "push", "", platforms, secrets},
|
||||||
{workdir, "composite-fail-with-output", "push", "", platforms, secrets},
|
{workdir, "composite-fail-with-output", "push", "", platforms, secrets},
|
||||||
{workdir, "issue-597", "push", "", platforms, secrets},
|
{workdir, "issue-597", "push", "", platforms, secrets},
|
||||||
{workdir, "issue-598", "push", "", platforms, secrets},
|
|
||||||
{workdir, "if-env-act", "push", "", platforms, secrets},
|
{workdir, "if-env-act", "push", "", platforms, secrets},
|
||||||
{workdir, "env-and-path", "push", "", platforms, secrets},
|
|
||||||
{workdir, "environment-files", "push", "", platforms, secrets},
|
{workdir, "environment-files", "push", "", platforms, secrets},
|
||||||
{workdir, "GITHUB_STATE", "push", "", platforms, secrets},
|
{workdir, "GITHUB_STATE", "push", "", platforms, secrets},
|
||||||
{workdir, "environment-files-parser-bug", "push", "", platforms, secrets},
|
{workdir, "environment-files-parser-bug", "push", "", platforms, secrets},
|
||||||
{workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms, secrets},
|
{workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms, secrets},
|
||||||
{workdir, "outputs", "push", "", platforms, secrets},
|
{workdir, "outputs", "push", "", platforms, secrets},
|
||||||
{workdir, "networking", "push", "", platforms, secrets},
|
{workdir, "networking", "push", "", platforms, secrets},
|
||||||
{workdir, "steps-context/conclusion", "push", "", platforms, secrets},
|
{workdir, "steps-context", "push", "", platforms, secrets},
|
||||||
{workdir, "steps-context/outcome", "push", "", platforms, secrets},
|
|
||||||
{workdir, "job-status-check", "push", "job 'fail' failed", platforms, secrets},
|
{workdir, "job-status-check", "push", "job 'fail' failed", platforms, secrets},
|
||||||
{workdir, "if-expressions", "push", "Job 'mytest' failed", platforms, secrets},
|
{workdir, "if-expressions", "push", "Job 'mytest' failed", platforms, secrets},
|
||||||
{workdir, "actions-environment-and-context-tests", "push", "", platforms, secrets},
|
{workdir, "actions-environment-and-context-tests", "push", "", platforms, secrets},
|
||||||
@@ -306,8 +320,6 @@ func TestRunEvent(t *testing.T) {
|
|||||||
{workdir, "ensure-post-steps", "push", "Job 'second-post-step-should-fail' failed", platforms, secrets},
|
{workdir, "ensure-post-steps", "push", "Job 'second-post-step-should-fail' failed", platforms, secrets},
|
||||||
{workdir, "workflow_call_inputs", "workflow_call", "", platforms, secrets},
|
{workdir, "workflow_call_inputs", "workflow_call", "", platforms, secrets},
|
||||||
{workdir, "workflow_dispatch", "workflow_dispatch", "", platforms, secrets},
|
{workdir, "workflow_dispatch", "workflow_dispatch", "", platforms, secrets},
|
||||||
{workdir, "workflow_dispatch_no_inputs_mapping", "workflow_dispatch", "", platforms, secrets},
|
|
||||||
{workdir, "workflow_dispatch-scalar", "workflow_dispatch", "", platforms, secrets},
|
|
||||||
{workdir, "workflow_dispatch-scalar-composite-action", "workflow_dispatch", "", platforms, secrets},
|
{workdir, "workflow_dispatch-scalar-composite-action", "workflow_dispatch", "", platforms, secrets},
|
||||||
{workdir, "job-needs-context-contains-result", "push", "", platforms, secrets},
|
{workdir, "job-needs-context-contains-result", "push", "", platforms, secrets},
|
||||||
{workdir, "container-volumes", "push", "", platforms, secrets},
|
{workdir, "container-volumes", "push", "", platforms, secrets},
|
||||||
@@ -323,14 +335,17 @@ func TestRunEvent(t *testing.T) {
|
|||||||
{workdir, "services-empty-image", "push", "", platforms, secrets},
|
{workdir, "services-empty-image", "push", "", platforms, secrets},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sharedImageMu sync.Mutex
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
t.Run(table.workflowPath, func(t *testing.T) {
|
t.Run(table.workflowPath, func(t *testing.T) {
|
||||||
if table.workflowPath == "container-volumes" {
|
if table.workflowPath == "container-volumes" {
|
||||||
// host /proc bind mounts are Linux-Docker-only
|
// host /proc bind mounts are Linux-Docker-only
|
||||||
requireLinuxDocker(t)
|
requireLinuxDocker(t)
|
||||||
}
|
}
|
||||||
if !slices.Contains(sharedImageWorkflows, table.workflowPath) {
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
if slices.Contains(sharedImageWorkflows, table.workflowPath) {
|
||||||
|
sharedImageMu.Lock()
|
||||||
|
defer sharedImageMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &Config{
|
config := &Config{
|
||||||
@@ -386,20 +401,17 @@ func TestRunEventHostEnvironment(t *testing.T) {
|
|||||||
{workdir, "evalmatrix-merge-map", "push", "", platforms, secrets},
|
{workdir, "evalmatrix-merge-map", "push", "", platforms, secrets},
|
||||||
{workdir, "evalmatrix-merge-array", "push", "", platforms, secrets},
|
{workdir, "evalmatrix-merge-array", "push", "", platforms, secrets},
|
||||||
|
|
||||||
{workdir, "fail", "push", "exit with `FAILURE`: 1", platforms, secrets},
|
|
||||||
{workdir, "checkout", "push", "", platforms, secrets},
|
{workdir, "checkout", "push", "", platforms, secrets},
|
||||||
{workdir, "matrix", "push", "", platforms, secrets},
|
{workdir, "matrix", "push", "", platforms, secrets},
|
||||||
{workdir, "commands", "push", "", platforms, secrets},
|
{workdir, "commands", "push", "", platforms, secrets},
|
||||||
{workdir, "defaults-run", "push", "", platforms, secrets},
|
{workdir, "defaults-run", "push", "", platforms, secrets},
|
||||||
{workdir, "composite-fail-with-output", "push", "", platforms, secrets},
|
{workdir, "composite-fail-with-output", "push", "", platforms, secrets},
|
||||||
{workdir, "issue-597", "push", "", platforms, secrets},
|
{workdir, "issue-597", "push", "", platforms, secrets},
|
||||||
{workdir, "issue-598", "push", "", platforms, secrets},
|
|
||||||
{workdir, "if-env-act", "push", "", platforms, secrets},
|
{workdir, "if-env-act", "push", "", platforms, secrets},
|
||||||
{workdir, "env-and-path", "push", "", platforms, secrets},
|
{workdir, "env-and-path", "push", "", platforms, secrets},
|
||||||
{workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms, secrets},
|
{workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms, secrets},
|
||||||
{workdir, "outputs", "push", "", platforms, secrets},
|
{workdir, "outputs", "push", "", platforms, secrets},
|
||||||
{workdir, "steps-context/conclusion", "push", "", platforms, secrets},
|
{workdir, "steps-context", "push", "", platforms, secrets},
|
||||||
{workdir, "steps-context/outcome", "push", "", platforms, secrets},
|
|
||||||
{workdir, "job-status-check", "push", "job 'fail' failed", platforms, secrets},
|
{workdir, "job-status-check", "push", "job 'fail' failed", platforms, secrets},
|
||||||
{workdir, "if-expressions", "push", "Job 'mytest' failed", platforms, secrets},
|
{workdir, "if-expressions", "push", "Job 'mytest' failed", platforms, secrets},
|
||||||
{workdir, "evalenv", "push", "", platforms, secrets},
|
{workdir, "evalenv", "push", "", platforms, secrets},
|
||||||
@@ -432,6 +444,7 @@ func TestRunEventHostEnvironment(t *testing.T) {
|
|||||||
}...)
|
}...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hostPlanSlots := make(chan struct{}, 2)
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
t.Run(table.workflowPath, func(t *testing.T) {
|
t.Run(table.workflowPath, func(t *testing.T) {
|
||||||
switch table.workflowPath {
|
switch table.workflowPath {
|
||||||
@@ -440,6 +453,9 @@ func TestRunEventHostEnvironment(t *testing.T) {
|
|||||||
case "nix-prepend-path":
|
case "nix-prepend-path":
|
||||||
requireHostTools(t, "nix")
|
requireHostTools(t, "nix")
|
||||||
}
|
}
|
||||||
|
t.Parallel()
|
||||||
|
hostPlanSlots <- struct{}{}
|
||||||
|
defer func() { <-hostPlanSlots }()
|
||||||
table.runTest(ctx, t, &Config{})
|
table.runTest(ctx, t, &Config{})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -544,41 +560,6 @@ func TestRunEventSecrets(t *testing.T) {
|
|||||||
tjfi.runTest(context.Background(), t, &Config{Secrets: secrets, Env: env})
|
tjfi.runTest(context.Background(), t, &Config{Secrets: secrets, Env: env})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunWithService(t *testing.T) {
|
|
||||||
requireDocker(t)
|
|
||||||
|
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
platforms := map[string]string{
|
|
||||||
"ubuntu-latest": "node:24-bookworm-slim",
|
|
||||||
}
|
|
||||||
|
|
||||||
workflowPath := "services"
|
|
||||||
eventName := "push"
|
|
||||||
|
|
||||||
workdir, err := filepath.Abs("testdata")
|
|
||||||
assert.NoError(t, err, workflowPath) //nolint:testifylint // pre-existing issue from nektos/act
|
|
||||||
|
|
||||||
runnerConfig := &Config{
|
|
||||||
Workdir: workdir,
|
|
||||||
EventName: eventName,
|
|
||||||
PlatformPicker: mapPlatformPicker(platforms),
|
|
||||||
ContainerMaxLifetime: time.Hour, // otherwise the job container is `sleep 0` and exits at once
|
|
||||||
}
|
|
||||||
runner, err := New(runnerConfig)
|
|
||||||
assert.NoError(t, err, workflowPath) //nolint:testifylint // pre-existing issue from nektos/act
|
|
||||||
|
|
||||||
planner, err := model.NewWorkflowPlanner("testdata/"+workflowPath, true)
|
|
||||||
assert.NoError(t, err, workflowPath) //nolint:testifylint // pre-existing issue from nektos/act
|
|
||||||
|
|
||||||
plan, err := planner.PlanEvent(eventName)
|
|
||||||
assert.NoError(t, err, workflowPath) //nolint:testifylint // pre-existing issue from nektos/act
|
|
||||||
|
|
||||||
err = runner.NewPlanExecutor(plan)(ctx)
|
|
||||||
assert.NoError(t, err, workflowPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunEventPullRequest(t *testing.T) {
|
func TestRunEventPullRequest(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
requireDocker(t)
|
requireDocker(t)
|
||||||
|
|||||||
+4
-1
@@ -1,4 +1,4 @@
|
|||||||
name: issue-597
|
name: issues-597-598
|
||||||
on: push
|
on: push
|
||||||
|
|
||||||
|
|
||||||
@@ -13,6 +13,9 @@ jobs:
|
|||||||
- name: My first true step
|
- name: My first true step
|
||||||
if: ${{endsWith('Hello world', 'ld')}}
|
if: ${{endsWith('Hello world', 'ld')}}
|
||||||
run: echo "Renst the Octocat"
|
run: echo "Renst the Octocat"
|
||||||
|
- name: My second true step
|
||||||
|
if: "!endsWith('Hello world', 'od')"
|
||||||
|
run: echo "Renst the Octocat"
|
||||||
- name: My second false step
|
- name: My second false step
|
||||||
if: "endsWith('Should not evaluate', 'o2')"
|
if: "endsWith('Should not evaluate', 'o2')"
|
||||||
run: exit 1
|
run: exit 1
|
||||||
|
|||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
name: issue-598
|
|
||||||
on: push
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
my_first_job:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: My first false step
|
|
||||||
if: "endsWith('Hello world', 'o1')"
|
|
||||||
run: exit 1
|
|
||||||
- name: My first true step
|
|
||||||
if: "!endsWith('Hello world', 'od')"
|
|
||||||
run: echo "Renst the Octocat"
|
|
||||||
- name: My second false step
|
|
||||||
if: "endsWith('Hello world', 'o2')"
|
|
||||||
run: exit 1
|
|
||||||
- name: My third false step
|
|
||||||
if: "endsWith('Hello world', 'o2')"
|
|
||||||
run: exit 1
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
name: job-container
|
|
||||||
on: push
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: node:24-bookworm-slim
|
|
||||||
options: --user 1000
|
|
||||||
steps:
|
|
||||||
- run: echo PASS
|
|
||||||
Vendored
+2
-2
@@ -12,13 +12,13 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-18.04, macos-latest]
|
os: [ubuntu-18.04, macos-latest]
|
||||||
node: [4, 6, 8, 10]
|
node: [4, 10]
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node: [8.x, 10.x, 12.x, 13.x]
|
node: [8.x, 13.x]
|
||||||
steps:
|
steps:
|
||||||
- run: echo ${NODE_VERSION} | grep ${{ matrix.node }}
|
- run: echo ${NODE_VERSION} | grep ${{ matrix.node }}
|
||||||
env:
|
env:
|
||||||
|
|||||||
+2
-8
@@ -4,11 +4,5 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Install tools
|
- name: Resolve the container hostname
|
||||||
run: |
|
run: getent hosts "$(hostname -f)"
|
||||||
apt update
|
|
||||||
apt install -y iputils-ping
|
|
||||||
- name: Run hostname test
|
|
||||||
run: |
|
|
||||||
hostname -f
|
|
||||||
ping -c 4 $(hostname -f)
|
|
||||||
|
|||||||
Vendored
+4
-19
@@ -12,32 +12,17 @@ jobs:
|
|||||||
- id: set_2
|
- id: set_2
|
||||||
run: |
|
run: |
|
||||||
echo "::set-output name=var_3::$(echo var3)"
|
echo "::set-output name=var_3::$(echo var3)"
|
||||||
- id: set_3
|
|
||||||
run: |
|
|
||||||
echo "::set-output name=var_4::$(echo var4)"
|
|
||||||
outputs:
|
outputs:
|
||||||
variable_1: ${{ steps.set_1.outputs.var_1 }}
|
variable_1: ${{ steps.set_1.outputs.var_1 }}
|
||||||
variable_2: ${{ steps.set_1.outputs.var_2 }}
|
variable_2: ${{ steps.set_1.outputs.var_2 }}
|
||||||
variable_3: ${{ steps.set_2.outputs.var_3 }}
|
variable_3: ${{ steps.set_2.outputs.var_3 }}
|
||||||
variable_4: ${{ steps.set_3.outputs.var_4 }}
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
needs: build_output
|
needs: build_output
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check set_1 var1
|
- name: Check outputs
|
||||||
run: |
|
run: |
|
||||||
echo "${{ needs.build_output.outputs.variable_1 }}"
|
test "${{ needs.build_output.outputs.variable_1 }}" = var1
|
||||||
echo "${{ needs.build_output.outputs.variable_1 }}" | grep 'var1' || exit 1
|
test "${{ needs.build_output.outputs.variable_2 }}" = var2
|
||||||
- name: Check set_1 var2
|
test "${{ needs.build_output.outputs.variable_3 }}" = var3
|
||||||
run: |
|
|
||||||
echo "${{ needs.build_output.outputs.variable_2 }}"
|
|
||||||
echo "${{ needs.build_output.outputs.variable_2 }}" | grep 'var2' || exit 1
|
|
||||||
- name: Check set_2 var3
|
|
||||||
run: |
|
|
||||||
echo "${{ needs.build_output.outputs.variable_3 }}"
|
|
||||||
echo "${{ needs.build_output.outputs.variable_3 }}" | grep 'var3' || exit 1
|
|
||||||
- name: Check set_3 var4
|
|
||||||
run: |
|
|
||||||
echo "${{ needs.build_output.outputs.variable_4 }}"
|
|
||||||
echo "${{ needs.build_output.outputs.variable_4 }}" | grep 'var4' || exit 1
|
|
||||||
|
|||||||
+1
-5
@@ -10,14 +10,10 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- 80
|
- 80
|
||||||
steps:
|
steps:
|
||||||
- name: Echo the Postgres service ID / Network / Ports
|
|
||||||
run: |
|
|
||||||
echo "id: ${{ job.services.postgres.id }}"
|
|
||||||
echo "network: ${{ job.services.postgres.network }}"
|
|
||||||
echo "ports: ${{ job.services.postgres.ports }}"
|
|
||||||
- name: The job context describes the started containers
|
- name: The job context describes the started containers
|
||||||
run: |
|
run: |
|
||||||
test -n "${{ job.container.id }}"
|
test -n "${{ job.container.id }}"
|
||||||
test -n "${{ job.services.postgres.id }}"
|
test -n "${{ job.services.postgres.id }}"
|
||||||
|
test -n "${{ job.services.postgres.ports }}"
|
||||||
test -n "${{ job.services.postgres.ports['80'] }}"
|
test -n "${{ job.services.postgres.ports['80'] }}"
|
||||||
test "${{ job.services.postgres.network }}" = "${{ job.container.network }}"
|
test "${{ job.services.postgres.network }}" = "${{ job.container.network }}"
|
||||||
|
|||||||
+9
-12
@@ -4,6 +4,9 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: ${{ env.MY_SHELL }}
|
||||||
steps:
|
steps:
|
||||||
- shell: ${{ env.MY_SHELL }}
|
- shell: ${{ env.MY_SHELL }}
|
||||||
run: |
|
run: |
|
||||||
@@ -12,6 +15,12 @@ jobs:
|
|||||||
else
|
else
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
- run: |
|
||||||
|
if [[ -n "$BASH" ]]; then
|
||||||
|
echo "I'm $BASH!"
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
check-container:
|
check-container:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: node:24-bookworm-slim
|
container: node:24-bookworm-slim
|
||||||
@@ -23,15 +32,3 @@ jobs:
|
|||||||
else
|
else
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
check-job-default:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: ${{ env.MY_SHELL }}
|
|
||||||
steps:
|
|
||||||
- run: |
|
|
||||||
if [[ -n "$BASH" ]]; then
|
|
||||||
echo "I'm $BASH!"
|
|
||||||
else
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|||||||
+3
-6
@@ -3,16 +3,13 @@ env:
|
|||||||
MY_SHELL: pwsh
|
MY_SHELL: pwsh
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- shell: ${{ env.MY_SHELL }}
|
|
||||||
run: |
|
|
||||||
$PSVersionTable
|
|
||||||
check-job-default:
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: ${{ env.MY_SHELL }}
|
shell: ${{ env.MY_SHELL }}
|
||||||
steps:
|
steps:
|
||||||
|
- shell: ${{ env.MY_SHELL }}
|
||||||
|
run: |
|
||||||
|
$PSVersionTable
|
||||||
- run: |
|
- run: |
|
||||||
$PSVersionTable
|
$PSVersionTable
|
||||||
|
|||||||
+9
-12
@@ -4,6 +4,9 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: ${{ env.MY_SHELL }}
|
||||||
steps:
|
steps:
|
||||||
- shell: ${{ env.MY_SHELL }}
|
- shell: ${{ env.MY_SHELL }}
|
||||||
run: |
|
run: |
|
||||||
@@ -12,6 +15,12 @@ jobs:
|
|||||||
else
|
else
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
- run: |
|
||||||
|
if [ -z ${BASH+x} ]; then
|
||||||
|
echo "I'm sh!"
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
check-container:
|
check-container:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: alpine:latest
|
container: alpine:latest
|
||||||
@@ -23,15 +32,3 @@ jobs:
|
|||||||
else
|
else
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
check-job-default:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: ${{ env.MY_SHELL }}
|
|
||||||
steps:
|
|
||||||
- run: |
|
|
||||||
if [ -z ${BASH+x} ]; then
|
|
||||||
echo "I'm sh!"
|
|
||||||
else
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
name: conclusion
|
|
||||||
on: push
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- id: first
|
|
||||||
run: exit 0
|
|
||||||
- id: second
|
|
||||||
continue-on-error: true
|
|
||||||
run: exit 1
|
|
||||||
- run: echo '${{ steps.first.conclusion }}' | grep 'success'
|
|
||||||
- run: echo '${{ steps.second.conclusion }}' | grep 'success'
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
name: outcome
|
|
||||||
on: push
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- id: first
|
|
||||||
run: exit 0
|
|
||||||
- id: second
|
|
||||||
continue-on-error: true
|
|
||||||
run: exit 1
|
|
||||||
- run: echo '${{ steps.first.outcome }}' | grep 'success'
|
|
||||||
- run: echo '${{ steps.second.outcome }}' | grep 'failure'
|
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
name: steps context
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- id: first
|
||||||
|
run: exit 0
|
||||||
|
- id: second
|
||||||
|
continue-on-error: true
|
||||||
|
run: exit 1
|
||||||
|
- run: |
|
||||||
|
test '${{ steps.first.conclusion }}' = success
|
||||||
|
test '${{ steps.second.conclusion }}' = success
|
||||||
|
test '${{ steps.first.outcome }}' = success
|
||||||
|
test '${{ steps.second.outcome }}' = failure
|
||||||
Reference in New Issue
Block a user