Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ZauberNerd authored and KnisterPeter committed Dec 10, 2021
1 parent 58790da commit ed96e72
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 36 deletions.
59 changes: 53 additions & 6 deletions pkg/exprparser/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"reflect"
"strconv"
"strings"

"github.com/nektos/act/pkg/model"
)

func (impl interperterImpl) contains(search, item reflect.Value) (bool, error) {
Expand Down Expand Up @@ -141,17 +143,17 @@ func (impl interperterImpl) format(str reflect.Value, replaceValue ...reflect.Va

// TODO: add more test cases to see how the function deals with unexpected types
func (impl interperterImpl) join(array reflect.Value, sep reflect.Value) (string, error) {
if array.Kind() == reflect.Slice {
switch array.Kind() {
case reflect.Slice:
var items []string
for i := 0; i < array.Len(); i++ {
items = append(items, impl.coerceToString(array.Index(i)).String())
items = append(items, impl.coerceToString(array.Index(i).Elem()).String())
}

return strings.Join(items, sep.String()), nil
default:
return strings.Join([]string{impl.coerceToString(array).String()}, sep.String()), nil
}

// TODO: better error message
return "", fmt.Errorf("todo: join")
}

func (impl interperterImpl) toJSON(value reflect.Value) (string, error) {
Expand Down Expand Up @@ -202,7 +204,7 @@ func (impl interperterImpl) hashFiles(paths ...reflect.Value) (string, error) {
var files []string

for i := range filepaths {
newFiles, err := filepath.Glob(filepath.Join(impl.config.workingDir, filepaths[i]))
newFiles, err := filepath.Glob(filepath.Join(impl.config.WorkingDir, filepaths[i]))
if err != nil {
return "", fmt.Errorf("Unable to glob.Glob: %v", err)
}
Expand Down Expand Up @@ -233,3 +235,48 @@ func (impl interperterImpl) hashFiles(paths ...reflect.Value) (string, error) {

return hex.EncodeToString(hasher.Sum(nil)), nil
}

func (impl interperterImpl) getNeedsTransitive(job *model.Job) []string {
needs := job.Needs()

for _, need := range needs {
parentNeeds := impl.getNeedsTransitive(impl.config.Run.Workflow.GetJob(need))
needs = append(needs, parentNeeds...)
}

return needs
}

func (impl interperterImpl) always() (bool, error) {
return true, nil
}

func (impl interperterImpl) success() (bool, error) {
jobs := impl.config.Run.Workflow.Jobs
jobNeeds := impl.getNeedsTransitive(impl.config.Run.Job())

for _, needs := range jobNeeds {
if jobs[needs].Result != "success" {
return false, nil
}
}

return true, nil
}

func (impl interperterImpl) failure() (bool, error) {
jobs := impl.config.Run.Workflow.Jobs
jobNeeds := impl.getNeedsTransitive(impl.config.Run.Job())

for _, needs := range jobNeeds {
if jobs[needs].Result == "failure" {
return true, nil
}
}

return false, nil
}

func (impl interperterImpl) cancelled() (bool, error) {
return impl.env.Job.Status == "cancelled", nil
}
25 changes: 24 additions & 1 deletion pkg/exprparser/functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,29 @@ func TestFunctionEndsWith(t *testing.T) {
}
}

func TestFunctionJoin(t *testing.T) {
table := []struct {
input string
expected interface{}
name string
}{
{"join(fromJSON('[\"a\", \"b\"]'), ',') }}", "a,b", "join-arr"},
{"join('string', ',') }}", "string", "join-str"},
{"join(1, ',') }}", "1", "join-number"},
}

env := &EvaluationEnvironment{}

for _, tt := range table {
t.Run(tt.name, func(t *testing.T) {
output, _, err := NewInterpeter(env, Config{}).Evaluate(tt.input)
assert.Nil(t, err)

assert.Equal(t, tt.expected, output)
})
}
}

func TestFunctionToJSON(t *testing.T) {
table := []struct {
input string
Expand Down Expand Up @@ -155,7 +178,7 @@ func TestFunctionHashFiles(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
workdir, err := filepath.Abs("testdata")
assert.Nil(t, err)
output, _, err := NewInterpeter(env, Config{workingDir: workdir}).Evaluate(tt.input)
output, _, err := NewInterpeter(env, Config{WorkingDir: workdir}).Evaluate(tt.input)
assert.Nil(t, err)

assert.Equal(t, tt.expected, output)
Expand Down
24 changes: 22 additions & 2 deletions pkg/exprparser/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ type EvaluationEnvironment struct {
}

type Config struct {
workingDir string
Run *model.Run
WorkingDir string
}

type Interpreter interface {
Expand Down Expand Up @@ -185,12 +186,23 @@ func (impl interperterImpl) getPropertyValue(left reflect.Value, property string
return impl.getPropertyValue(left.Elem(), property)

case reflect.Struct:
leftType := left.Type()
for i := 0; i < leftType.NumField(); i++ {
jsonName := leftType.Field(i).Tag.Get("json")
if jsonName == property {
property = leftType.Field(i).Name
break
}
}

fieldValue := left.FieldByNameFunc(func(name string) bool {
return strings.ToLower(name) == property
return strings.ToLower(name) == strings.ToLower(property)
})

if fieldValue.Kind() == reflect.Invalid {
return "", nil
}

return fieldValue.Interface(), nil

case reflect.Map:
Expand Down Expand Up @@ -502,6 +514,14 @@ func (impl interperterImpl) evaluateFuncCall(funcCallNode *actionlint.FuncCallNo
return impl.fromJSON(args[0])
case "hashfiles":
return impl.hashFiles(args...)
case "always":
return impl.always()
case "success":
return impl.success()
case "failure":
return impl.failure()
case "cancelled":
return impl.cancelled()
default:
return nil, fmt.Errorf("TODO: '%s' not implemented", funcCallNode.Callee)
}
Expand Down
10 changes: 5 additions & 5 deletions pkg/runner/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ func (rc *RunContext) NewExpressionEvaluator() ExpressionEvaluator {
// Inputs: no inputs on run context,
}
return expressionEvaluator{
interpreter: exprparser.NewInterpeter(ee, exprparser.Config{}),
interpreter: exprparser.NewInterpeter(ee, exprparser.Config{
Run: rc.Run,
WorkingDir: rc.Config.Workdir,
}),
}
}

Expand Down Expand Up @@ -76,9 +79,7 @@ func (ee expressionEvaluator) Evaluate(in string) (string, bool, error) {
return "", false, err
}

fmt.Printf("%+v", evaluated)

return "", false, nil
return fmt.Sprint(evaluated), false, nil
}

func (ee expressionEvaluator) Interpolate(in string) string {
Expand All @@ -98,7 +99,6 @@ const (
)

func (ee expressionEvaluator) InterpolateWithStringCheck(in string) (string, bool) {

output := ""
skip := 0
// replacementIndex := ""
Expand Down
23 changes: 12 additions & 11 deletions pkg/runner/expression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,24 @@ func TestEvaluate(t *testing.T) {
{"'my text'", "my text", ""},
{"contains('my text', 'te')", "true", ""},
{"contains('my TEXT', 'te')", "true", ""},
{"contains(['my text'], 'te')", "false", ""},
{"contains(['foo','bar'], 'bar')", "true", ""},
{"contains(fromJSON('[\"my text\"]'), 'te')", "false", ""},
{"contains(fromJSON('[\"foo\",\"bar\"]'), 'bar')", "true", ""},
{"startsWith('hello world', 'He')", "true", ""},
{"endsWith('hello world', 'ld')", "true", ""},
{"format('0:{0} 2:{2} 1:{1}', 'zero', 'one', 'two')", "0:zero 2:two 1:one", ""},
{"join(['hello'],'octocat')", "hello octocat", ""},
{"join(['hello','mona','the'],'octocat')", "hello mona the octocat", ""},
{"join('hello','mona')", "hello mona", ""},
{"toJSON({'foo':'bar'})", "{\n \"foo\": \"bar\"\n}", ""},
{"toJson({'foo':'bar'})", "{\n \"foo\": \"bar\"\n}", ""},
{"join(fromJSON('[\"hello\"]'),'octocat')", "hello", ""},
{"join(fromJSON('[\"hello\",\"mona\",\"the\"]'),'octocat')", "hellooctocatmonaoctocatthe", ""},
{"join('hello','mona')", "hello", ""},
{"toJSON(env)", "{\n \"key\": \"value\"\n}", ""},
{"toJson(env)", "{\n \"key\": \"value\"\n}", ""},
{"(fromJSON('{\"foo\":\"bar\"}')).foo", "bar", ""},
{"(fromJson('{\"foo\":\"bar\"}')).foo", "bar", ""},
{"(fromJson('[\"foo\",\"bar\"]'))[1]", "bar", ""},
{"hashFiles('**/non-extant-files')", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""},
{"hashFiles('**/non-extant-files', '**/more-non-extant-files')", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""},
{"hashFiles('**/non.extant.files')", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""},
{"hashFiles('**/non''extant''files')", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""},
// github does return an empty string for non-existent files
{"hashFiles('**/non-extant-files')", "", ""},
{"hashFiles('**/non-extant-files', '**/more-non-extant-files')", "", ""},
{"hashFiles('**/non.extant.files')", "", ""},
{"hashFiles('**/non''extant''files')", "", ""},
{"success()", "true", ""},
{"failure()", "false", ""},
{"always()", "true", ""},
Expand Down
11 changes: 0 additions & 11 deletions pkg/runner/run_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,17 +478,6 @@ func (rc *RunContext) getStepsContext() map[string]*model.StepResult {
return rc.StepResults
}

func (rc *RunContext) getNeedsTransitive(job *model.Job) []string {
needs := job.Needs()

for _, need := range needs {
parentNeeds := rc.getNeedsTransitive(rc.Run.Workflow.GetJob(need))
needs = append(needs, parentNeeds...)
}

return needs
}

type githubContext struct {
Event map[string]interface{} `json:"event"`
EventPath string `json:"event_path"`
Expand Down

0 comments on commit ed96e72

Please sign in to comment.