-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: extract setupAction into ActionReader (#986)
This change extracts the functionality of reading an `action.y(a)ml` or creation of a `(Synthetic Action)` into its own type to enable better unit testing / mocking of those IO operations. This is done in preparation for the implementation of pre/post action support in act. Co-authored-by: Markus Wolf <markus.wolf@new-work.se> Co-authored-by: Markus Wolf <markus.wolf@new-work.se>
- Loading branch information
1 parent
e23223a
commit e4f0080
Showing
3 changed files
with
246 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package runner | ||
|
||
import ( | ||
"embed" | ||
"io" | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/nektos/act/pkg/model" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
type ActionReader interface { | ||
readAction(step *model.Step, actionDir string, actionPath string, readFile actionyamlReader) (*model.Action, error) | ||
} | ||
|
||
type actionyamlReader func(filename string) (io.Reader, io.Closer, error) | ||
type fileWriter func(filename string, data []byte, perm fs.FileMode) error | ||
|
||
//go:embed res/trampoline.js | ||
var trampoline embed.FS | ||
|
||
func (sc *StepContext) readAction(step *model.Step, actionDir string, actionPath string, readFile actionyamlReader, writeFile fileWriter) (*model.Action, error) { | ||
reader, closer, err := readFile("action.yml") | ||
if os.IsNotExist(err) { | ||
reader, closer, err = readFile("action.yaml") | ||
if err != nil { | ||
if _, closer, err2 := readFile("Dockerfile"); err2 == nil { | ||
closer.Close() | ||
action := &model.Action{ | ||
Name: "(Synthetic)", | ||
Runs: model.ActionRuns{ | ||
Using: "docker", | ||
Image: "Dockerfile", | ||
}, | ||
} | ||
log.Debugf("Using synthetic action %v for Dockerfile", action) | ||
return action, nil | ||
} | ||
if step.With != nil { | ||
if val, ok := step.With["args"]; ok { | ||
var b []byte | ||
if b, err = trampoline.ReadFile("res/trampoline.js"); err != nil { | ||
return nil, err | ||
} | ||
err2 := writeFile(filepath.Join(actionDir, actionPath, "trampoline.js"), b, 0400) | ||
if err2 != nil { | ||
return nil, err2 | ||
} | ||
action := &model.Action{ | ||
Name: "(Synthetic)", | ||
Inputs: map[string]model.Input{ | ||
"cwd": { | ||
Description: "(Actual working directory)", | ||
Required: false, | ||
Default: filepath.Join(actionDir, actionPath), | ||
}, | ||
"command": { | ||
Description: "(Actual program)", | ||
Required: false, | ||
Default: val, | ||
}, | ||
}, | ||
Runs: model.ActionRuns{ | ||
Using: "node12", | ||
Main: "trampoline.js", | ||
}, | ||
} | ||
log.Debugf("Using synthetic action %v", action) | ||
return action, nil | ||
} | ||
} | ||
return nil, err | ||
} | ||
} else if err != nil { | ||
return nil, err | ||
} | ||
defer closer.Close() | ||
|
||
action, err := model.ReadAction(reader) | ||
log.Debugf("Read action %v from '%s'", action, "Unknown") | ||
return action, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package runner | ||
|
||
import ( | ||
"io" | ||
"io/fs" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/nektos/act/pkg/model" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/mock" | ||
) | ||
|
||
type closerMock struct { | ||
mock.Mock | ||
} | ||
|
||
func (m *closerMock) Close() error { | ||
m.Called() | ||
return nil | ||
} | ||
|
||
func TestActionReader(t *testing.T) { | ||
yaml := strings.ReplaceAll(` | ||
name: 'name' | ||
runs: | ||
using: 'node16' | ||
main: 'main.js' | ||
`, "\t", " ") | ||
|
||
table := []struct { | ||
name string | ||
step *model.Step | ||
filename string | ||
fileContent string | ||
expected *model.Action | ||
}{ | ||
{ | ||
name: "readActionYml", | ||
step: &model.Step{}, | ||
filename: "action.yml", | ||
fileContent: yaml, | ||
expected: &model.Action{ | ||
Name: "name", | ||
Runs: model.ActionRuns{ | ||
Using: "node16", | ||
Main: "main.js", | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "readActionYaml", | ||
step: &model.Step{}, | ||
filename: "action.yaml", | ||
fileContent: yaml, | ||
expected: &model.Action{ | ||
Name: "name", | ||
Runs: model.ActionRuns{ | ||
Using: "node16", | ||
Main: "main.js", | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "readDockerfile", | ||
step: &model.Step{}, | ||
filename: "Dockerfile", | ||
fileContent: "FROM ubuntu:20.04", | ||
expected: &model.Action{ | ||
Name: "(Synthetic)", | ||
Runs: model.ActionRuns{ | ||
Using: "docker", | ||
Image: "Dockerfile", | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "readWithArgs", | ||
step: &model.Step{ | ||
With: map[string]string{ | ||
"args": "cmd", | ||
}, | ||
}, | ||
expected: &model.Action{ | ||
Name: "(Synthetic)", | ||
Inputs: map[string]model.Input{ | ||
"cwd": { | ||
Description: "(Actual working directory)", | ||
Required: false, | ||
Default: "actionDir/actionPath", | ||
}, | ||
"command": { | ||
Description: "(Actual program)", | ||
Required: false, | ||
Default: "cmd", | ||
}, | ||
}, | ||
Runs: model.ActionRuns{ | ||
Using: "node12", | ||
Main: "trampoline.js", | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range table { | ||
t.Run(tt.name, func(t *testing.T) { | ||
closerMock := &closerMock{} | ||
|
||
readFile := func(filename string) (io.Reader, io.Closer, error) { | ||
if tt.filename != filename { | ||
return nil, nil, fs.ErrNotExist | ||
} | ||
|
||
return strings.NewReader(tt.fileContent), closerMock, nil | ||
} | ||
|
||
writeFile := func(filename string, data []byte, perm fs.FileMode) error { | ||
assert.Equal(t, "actionDir/actionPath/trampoline.js", filename) | ||
assert.Equal(t, fs.FileMode(0400), perm) | ||
return nil | ||
} | ||
|
||
closerMock.On("Close") | ||
|
||
sc := &StepContext{} | ||
action, err := sc.readAction(tt.step, "actionDir", "actionPath", readFile, writeFile) | ||
|
||
assert.Nil(t, err) | ||
assert.Equal(t, tt.expected, action) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters