Skip to content

Commit e23223a

Browse files
Philipp HinrichsenBjörn BrauerMarcus Nolljhioderobertkowalski
authored
refactor: extract RunContext Executor in JobExecutor (#984)
This splits the executor from the RunContext into its own function called newJobExecutor. We defined an interface called jobInfo which is implemented by the RunContext. This enables better unit testing because only a small interface needs to be mocked. This is a preparation for implementing pre and post actions. Co-authored-by: Björn Brauer <bjoern.brauer@new-work.se> Co-authored-by: Marcus Noll <marcus.noll@new-work.se> Co-authored-by: Jonas Holland <jonas.holland@new-work.se> Co-authored-by: Robert Kowalski <robert.kowalski@new-work.se> Co-authored-by: Markus Wolf <markus.wolf@new-work.se> Co-authored-by: Björn Brauer <bjoern.brauer@new-work.se> Co-authored-by: Marcus Noll <marcus.noll@new-work.se> Co-authored-by: Jonas Holland <jonas.holland@new-work.se> Co-authored-by: Robert Kowalski <robert.kowalski@new-work.se> Co-authored-by: Markus Wolf <markus.wolf@new-work.se>
1 parent b1f5963 commit e23223a

4 files changed

Lines changed: 232 additions & 44 deletions

File tree

‎go.sum‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,7 @@ github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h
11171117
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
11181118
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
11191119
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1120+
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
11201121
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
11211122
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
11221123
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=

‎pkg/runner/job_executor.go‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package runner
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/nektos/act/pkg/common"
8+
"github.com/nektos/act/pkg/model"
9+
)
10+
11+
type jobInfo interface {
12+
matrix() map[string]interface{}
13+
steps() []*model.Step
14+
startContainer() common.Executor
15+
stopContainer() common.Executor
16+
closeContainer() common.Executor
17+
newStepExecutor(step *model.Step) common.Executor
18+
interpolateOutputs() common.Executor
19+
result(result string)
20+
}
21+
22+
func newJobExecutor(info jobInfo) common.Executor {
23+
steps := make([]common.Executor, 0)
24+
25+
steps = append(steps, func(ctx context.Context) error {
26+
if len(info.matrix()) > 0 {
27+
common.Logger(ctx).Infof("\U0001F9EA Matrix: %v", info.matrix())
28+
}
29+
return nil
30+
})
31+
32+
steps = append(steps, info.startContainer())
33+
34+
for i, step := range info.steps() {
35+
if step.ID == "" {
36+
step.ID = fmt.Sprintf("%d", i)
37+
}
38+
stepExec := info.newStepExecutor(step)
39+
steps = append(steps, func(ctx context.Context) error {
40+
err := stepExec(ctx)
41+
if err != nil {
42+
common.Logger(ctx).Errorf("%v", err)
43+
common.SetJobError(ctx, err)
44+
} else if ctx.Err() != nil {
45+
common.Logger(ctx).Errorf("%v", ctx.Err())
46+
common.SetJobError(ctx, ctx.Err())
47+
}
48+
return nil
49+
})
50+
}
51+
steps = append(steps, func(ctx context.Context) error {
52+
err := info.stopContainer()(ctx)
53+
if err != nil {
54+
return err
55+
}
56+
57+
jobError := common.JobError(ctx)
58+
if jobError != nil {
59+
info.result("failure")
60+
} else {
61+
info.result("success")
62+
}
63+
64+
return nil
65+
})
66+
67+
return common.NewPipelineExecutor(steps...).Finally(info.interpolateOutputs()).Finally(func(ctx context.Context) error {
68+
info.closeContainer()
69+
return nil
70+
})
71+
}

‎pkg/runner/job_executor_test.go‎

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package runner
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/nektos/act/pkg/common"
9+
"github.com/nektos/act/pkg/model"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/mock"
12+
)
13+
14+
type jobInfoMock struct {
15+
mock.Mock
16+
}
17+
18+
func (jpm *jobInfoMock) matrix() map[string]interface{} {
19+
args := jpm.Called()
20+
return args.Get(0).(map[string]interface{})
21+
}
22+
23+
func (jpm *jobInfoMock) steps() []*model.Step {
24+
args := jpm.Called()
25+
26+
return args.Get(0).([]*model.Step)
27+
}
28+
29+
func (jpm *jobInfoMock) startContainer() common.Executor {
30+
args := jpm.Called()
31+
32+
return args.Get(0).(func(context.Context) error)
33+
}
34+
35+
func (jpm *jobInfoMock) stopContainer() common.Executor {
36+
args := jpm.Called()
37+
38+
return args.Get(0).(func(context.Context) error)
39+
}
40+
41+
func (jpm *jobInfoMock) closeContainer() common.Executor {
42+
args := jpm.Called()
43+
44+
return args.Get(0).(func(context.Context) error)
45+
}
46+
47+
func (jpm *jobInfoMock) newStepExecutor(step *model.Step) common.Executor {
48+
args := jpm.Called(step)
49+
50+
return args.Get(0).(func(context.Context) error)
51+
}
52+
53+
func (jpm *jobInfoMock) interpolateOutputs() common.Executor {
54+
args := jpm.Called()
55+
56+
return args.Get(0).(func(context.Context) error)
57+
}
58+
59+
func (jpm *jobInfoMock) result(result string) {
60+
jpm.Called(result)
61+
}
62+
63+
func TestNewJobExecutor(t *testing.T) {
64+
table := []struct {
65+
name string
66+
steps []*model.Step
67+
result string
68+
hasError bool
69+
}{
70+
{
71+
"zeroSteps",
72+
[]*model.Step{},
73+
"success",
74+
false,
75+
},
76+
{
77+
"stepWithoutPrePost",
78+
[]*model.Step{{
79+
ID: "1",
80+
}},
81+
"success",
82+
false,
83+
},
84+
{
85+
"stepWithFailure",
86+
[]*model.Step{{
87+
ID: "1",
88+
}},
89+
"failure",
90+
true,
91+
},
92+
}
93+
94+
for _, tt := range table {
95+
t.Run(tt.name, func(t *testing.T) {
96+
ctx := common.WithJobErrorContainer(context.Background())
97+
jpm := &jobInfoMock{}
98+
99+
jpm.On("startContainer").Return(func(ctx context.Context) error {
100+
return nil
101+
})
102+
103+
jpm.On("steps").Return(tt.steps)
104+
105+
for _, stepMock := range tt.steps {
106+
jpm.On("newStepExecutor", stepMock).Return(func(ctx context.Context) error {
107+
if tt.hasError {
108+
return fmt.Errorf("error")
109+
}
110+
return nil
111+
})
112+
}
113+
114+
jpm.On("interpolateOutputs").Return(func(ctx context.Context) error {
115+
return nil
116+
})
117+
118+
jpm.On("matrix").Return(map[string]interface{}{})
119+
120+
jpm.On("stopContainer").Return(func(ctx context.Context) error {
121+
return nil
122+
})
123+
124+
jpm.On("result", tt.result)
125+
126+
jpm.On("closeContainer").Return(func(ctx context.Context) error {
127+
return nil
128+
})
129+
130+
executor := newJobExecutor(jpm)
131+
err := executor(ctx)
132+
assert.Nil(t, err)
133+
})
134+
}
135+
}

‎pkg/runner/run_context.go‎

Lines changed: 25 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -244,57 +244,38 @@ func (rc *RunContext) interpolateOutputs() common.Executor {
244244
}
245245
}
246246

247-
// Executor returns a pipeline executor for all the steps in the job
248-
func (rc *RunContext) Executor() common.Executor {
249-
steps := make([]common.Executor, 0)
250-
251-
steps = append(steps, func(ctx context.Context) error {
252-
if len(rc.Matrix) > 0 {
253-
common.Logger(ctx).Infof("\U0001F9EA Matrix: %v", rc.Matrix)
254-
}
255-
return nil
256-
})
247+
func (rc *RunContext) startContainer() common.Executor {
248+
return rc.startJobContainer()
249+
}
257250

258-
steps = append(steps, rc.startJobContainer())
251+
func (rc *RunContext) stopContainer() common.Executor {
252+
return rc.stopJobContainer()
253+
}
259254

260-
for i, step := range rc.Run.Job().Steps {
261-
if step.ID == "" {
262-
step.ID = fmt.Sprintf("%d", i)
255+
func (rc *RunContext) closeContainer() common.Executor {
256+
return func(ctx context.Context) error {
257+
if rc.JobContainer != nil {
258+
return rc.JobContainer.Close()(ctx)
263259
}
264-
stepExec := rc.newStepExecutor(step)
265-
steps = append(steps, func(ctx context.Context) error {
266-
err := stepExec(ctx)
267-
if err != nil {
268-
common.Logger(ctx).Errorf("%v", err)
269-
common.SetJobError(ctx, err)
270-
} else if ctx.Err() != nil {
271-
common.Logger(ctx).Errorf("%v", ctx.Err())
272-
common.SetJobError(ctx, ctx.Err())
273-
}
274-
return nil
275-
})
260+
return nil
276261
}
277-
steps = append(steps, func(ctx context.Context) error {
278-
err := rc.stopJobContainer()(ctx)
279-
if err != nil {
280-
return err
281-
}
262+
}
282263

283-
rc.Run.Job().Result = "success"
284-
jobError := common.JobError(ctx)
285-
if jobError != nil {
286-
rc.Run.Job().Result = "failure"
287-
}
264+
func (rc *RunContext) matrix() map[string]interface{} {
265+
return rc.Matrix
266+
}
288267

289-
return nil
290-
})
268+
func (rc *RunContext) result(result string) {
269+
rc.Run.Job().Result = result
270+
}
291271

292-
return common.NewPipelineExecutor(steps...).Finally(rc.interpolateOutputs()).Finally(func(ctx context.Context) error {
293-
if rc.JobContainer != nil {
294-
return rc.JobContainer.Close()(ctx)
295-
}
296-
return nil
297-
}).If(rc.isEnabled)
272+
func (rc *RunContext) steps() []*model.Step {
273+
return rc.Run.Job().Steps
274+
}
275+
276+
// Executor returns a pipeline executor for all the steps in the job
277+
func (rc *RunContext) Executor() common.Executor {
278+
return newJobExecutor(rc).If(rc.isEnabled)
298279
}
299280

300281
// Executor returns a pipeline executor for all the steps in the job

0 commit comments

Comments
 (0)