From cdfd9af967d75cee0aa6e7102cfe8accbffc206f Mon Sep 17 00:00:00 2001 From: m1r4c Date: Tue, 28 Jan 2025 18:34:40 +0100 Subject: [PATCH 1/5] Added support for dereferenced properties --- pkg/exprparser/functions_test.go | 23 ++++++++++++++++++++ pkg/exprparser/interpreter.go | 35 +++++++++++++++++++++++++++++- pkg/exprparser/interpreter_test.go | 8 +++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/pkg/exprparser/functions_test.go b/pkg/exprparser/functions_test.go index ea51a2bc8ba..fb9360aca49 100644 --- a/pkg/exprparser/functions_test.go +++ b/pkg/exprparser/functions_test.go @@ -31,6 +31,9 @@ func TestFunctionContains(t *testing.T) { {`contains(fromJSON('[3.14,"second"]'), 3.14) }}`, true, "contains-item-number-number"}, {`contains(fromJSON('["","second"]'), fromJSON('[]')) }}`, false, "contains-item-str-arr"}, {`contains(fromJSON('["","second"]'), fromJSON('{}')) }}`, false, "contains-item-str-obj"}, + {`contains(fromJSON('[{ "first": { "result": "success" }},{ "second": { "result": "success" }}]').first.result, 'success') }}`, true, "multiple-contains-item"}, + {`contains(fromJSON('[{ "result": "success" },{ "result": "failure" }]').*.result, 'failure') }}`, true, "multiple-contains-dereferenced-failure-item"}, + {`contains(fromJSON('[{ "result": "failure" },{ "result": "success" }]').*.result, 'success') }}`, true, "multiple-contains-dereferenced-success-item"}, } env := &EvaluationEnvironment{} @@ -249,3 +252,23 @@ func TestFunctionFormat(t *testing.T) { }) } } + +func TestMapContains(t *testing.T) { + env := &EvaluationEnvironment{} + + env.Needs = map[string]Needs{ + "first-job": { + Outputs: map[string]string{}, + Result: "success", + }, + "second-job": { + Outputs: map[string]string{}, + Result: "failure", + }, + } + + output, err := NewInterpeter(env, Config{}).Evaluate("contains(needs.*.result, 'failure')", DefaultStatusCheckNone) + assert.Nil(t, err) + + assert.Equal(t, true, output) +} diff --git a/pkg/exprparser/interpreter.go b/pkg/exprparser/interpreter.go index 7630854f28e..39ff190b051 100644 --- a/pkg/exprparser/interpreter.go +++ b/pkg/exprparser/interpreter.go @@ -229,7 +229,12 @@ func (impl *interperterImpl) evaluateObjectDeref(objectDerefNode *actionlint.Obj return nil, err } - return impl.getPropertyValue(reflect.ValueOf(left), objectDerefNode.Property) + _, receiverIsDeref := objectDerefNode.Receiver.(*actionlint.ArrayDerefNode) + if receiverIsDeref { + return impl.getPropertyValueDereferenced(reflect.ValueOf(left), objectDerefNode.Property) + } else { + return impl.getPropertyValue(reflect.ValueOf(left), objectDerefNode.Property) + } } func (impl *interperterImpl) evaluateArrayDeref(arrayDerefNode *actionlint.ArrayDerefNode) (interface{}, error) { @@ -312,6 +317,34 @@ func (impl *interperterImpl) getPropertyValue(left reflect.Value, property strin return nil, nil } +func (impl *interperterImpl) getPropertyValueDereferenced(left reflect.Value, property string) (value interface{}, err error) { + switch left.Kind() { + case reflect.Ptr: + return impl.getPropertyValue(left, property) + + case reflect.Struct: + return impl.getPropertyValue(left, property) + case reflect.Map: + iter := left.MapRange() + + var values []interface{} + for iter.Next() { + value, err := impl.getPropertyValue(iter.Value(), property) + if err != nil { + return nil, err + } + + values = append(values, value) + } + + return values, nil + case reflect.Slice: + return impl.getPropertyValue(left, property) + } + + return nil, nil +} + func (impl *interperterImpl) getMapValue(value reflect.Value) (interface{}, error) { if value.Kind() == reflect.Ptr { return impl.getMapValue(value.Elem()) diff --git a/pkg/exprparser/interpreter_test.go b/pkg/exprparser/interpreter_test.go index f45851d1f44..8019ace1ef0 100644 --- a/pkg/exprparser/interpreter_test.go +++ b/pkg/exprparser/interpreter_test.go @@ -562,6 +562,8 @@ func TestContexts(t *testing.T) { {"matrix.os", "Linux", "matrix-context"}, {"needs.job-id.outputs.output-name", "value", "needs-context"}, {"needs.job-id.result", "success", "needs-context"}, + {"contains(needs.*.result, 'success')", true, "needs-wildcard-context-contains-success"}, + {"contains(needs.*.result, 'failure')", false, "needs-wildcard-context-contains-failure"}, {"inputs.name", "value", "inputs-context"}, } @@ -610,6 +612,12 @@ func TestContexts(t *testing.T) { }, Result: "success", }, + "another-job-id": { + Outputs: map[string]string{ + "output-name": "value", + }, + Result: "success", + }, }, Inputs: map[string]interface{}{ "name": "value", From f00e611b6b2692ee0c8d5aeb83a0dac040b8d1f3 Mon Sep 17 00:00:00 2001 From: m1r4c Date: Tue, 28 Jan 2025 18:39:59 +0100 Subject: [PATCH 2/5] Added negative test --- pkg/exprparser/functions_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/exprparser/functions_test.go b/pkg/exprparser/functions_test.go index fb9360aca49..95a38863677 100644 --- a/pkg/exprparser/functions_test.go +++ b/pkg/exprparser/functions_test.go @@ -34,6 +34,7 @@ func TestFunctionContains(t *testing.T) { {`contains(fromJSON('[{ "first": { "result": "success" }},{ "second": { "result": "success" }}]').first.result, 'success') }}`, true, "multiple-contains-item"}, {`contains(fromJSON('[{ "result": "success" },{ "result": "failure" }]').*.result, 'failure') }}`, true, "multiple-contains-dereferenced-failure-item"}, {`contains(fromJSON('[{ "result": "failure" },{ "result": "success" }]').*.result, 'success') }}`, true, "multiple-contains-dereferenced-success-item"}, + {`contains(fromJSON('[{ "result": "failure" },{ "result": "success" }]').*.result, 'notthere') }}`, false, "multiple-contains-dereferenced-missing-item"}, } env := &EvaluationEnvironment{} From 1a46efb24d8c484554c634156c877b43a096bc35 Mon Sep 17 00:00:00 2001 From: m1r4c Date: Thu, 30 Jan 2025 19:16:06 +0100 Subject: [PATCH 3/5] Update pkg/exprparser/functions_test.go Co-authored-by: ChristopherHX --- pkg/exprparser/functions_test.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/exprparser/functions_test.go b/pkg/exprparser/functions_test.go index 95a38863677..2a8992c9b73 100644 --- a/pkg/exprparser/functions_test.go +++ b/pkg/exprparser/functions_test.go @@ -255,16 +255,16 @@ func TestFunctionFormat(t *testing.T) { } func TestMapContains(t *testing.T) { - env := &EvaluationEnvironment{} - - env.Needs = map[string]Needs{ - "first-job": { - Outputs: map[string]string{}, - Result: "success", - }, - "second-job": { - Outputs: map[string]string{}, - Result: "failure", + env := &EvaluationEnvironment{ + Needs: map[string]Needs{ + "first-job": { + Outputs: map[string]string{}, + Result: "success", + }, + "second-job": { + Outputs: map[string]string{}, + Result: "failure", + }, }, } From d56029604aa8300042ec2c34c2416e9eafe44377 Mon Sep 17 00:00:00 2001 From: m1r4c Date: Thu, 30 Jan 2025 19:16:41 +0100 Subject: [PATCH 4/5] Update pkg/exprparser/functions_test.go Co-authored-by: ChristopherHX --- pkg/exprparser/functions_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/exprparser/functions_test.go b/pkg/exprparser/functions_test.go index 2a8992c9b73..7241ddf82e6 100644 --- a/pkg/exprparser/functions_test.go +++ b/pkg/exprparser/functions_test.go @@ -35,6 +35,9 @@ func TestFunctionContains(t *testing.T) { {`contains(fromJSON('[{ "result": "success" },{ "result": "failure" }]').*.result, 'failure') }}`, true, "multiple-contains-dereferenced-failure-item"}, {`contains(fromJSON('[{ "result": "failure" },{ "result": "success" }]').*.result, 'success') }}`, true, "multiple-contains-dereferenced-success-item"}, {`contains(fromJSON('[{ "result": "failure" },{ "result": "success" }]').*.result, 'notthere') }}`, false, "multiple-contains-dereferenced-missing-item"}, + {`contains(fromJSON('[{ "result": "failure", "outputs": { "key": "val1" } },{ "result": "success", "outputs": { "key": "val2" } }]').*.outputs.key, 'val1') }}`, true, "multiple-contains-dereferenced-output-item"}, + {`contains(fromJSON('[{ "result": "failure", "outputs": { "key": "val1" } },{ "result": "success", "outputs": { "key": "val2" } }]').*.outputs.key, 'val2') }}`, true, "multiple-contains-dereferenced-output-item-2"}, + {`contains(fromJSON('[{ "result": "failure", "outputs": { "key": "val1" } },{ "result": "success", "outputs": { "key": "val2" } }]').*.outputs.key, 'missing') }}`, false, "multiple-contains-dereferenced-output-misssing-item"}, } env := &EvaluationEnvironment{} From 518c7f3cc84c25847dc1b4839bb45e5db2a749f4 Mon Sep 17 00:00:00 2001 From: ChristopherHX Date: Thu, 30 Jan 2025 19:44:02 +0100 Subject: [PATCH 5/5] fix lint --- pkg/exprparser/interpreter.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/exprparser/interpreter.go b/pkg/exprparser/interpreter.go index 39ff190b051..8d50913d9d3 100644 --- a/pkg/exprparser/interpreter.go +++ b/pkg/exprparser/interpreter.go @@ -232,9 +232,8 @@ func (impl *interperterImpl) evaluateObjectDeref(objectDerefNode *actionlint.Obj _, receiverIsDeref := objectDerefNode.Receiver.(*actionlint.ArrayDerefNode) if receiverIsDeref { return impl.getPropertyValueDereferenced(reflect.ValueOf(left), objectDerefNode.Property) - } else { - return impl.getPropertyValue(reflect.ValueOf(left), objectDerefNode.Property) } + return impl.getPropertyValue(reflect.ValueOf(left), objectDerefNode.Property) } func (impl *interperterImpl) evaluateArrayDeref(arrayDerefNode *actionlint.ArrayDerefNode) (interface{}, error) {