From a3c2c09a679d251413d4b4a5319e8c3adc494531 Mon Sep 17 00:00:00 2001 From: ChristopherHX Date: Tue, 10 Sep 2024 16:02:14 +0000 Subject: [PATCH 1/3] fix: schema validation for job if functions * Add Tests --- pkg/schema/schema.go | 32 +++++++++++--- pkg/schema/schema_test.go | 92 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 pkg/schema/schema_test.go diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go index 0d5584a8089..6b24878f537 100644 --- a/pkg/schema/schema.go +++ b/pkg/schema/schema.go @@ -5,7 +5,9 @@ import ( "encoding/json" "errors" "fmt" + "math" "regexp" + "strconv" "strings" "github.com/rhysd/actionlint" @@ -18,6 +20,8 @@ var workflowSchema string //go:embed action_schema.json var actionSchema string +var functions = regexp.MustCompile(`^([a-zA-Z0-9_]+)\(([0-9]+),([0-9]+|MAX)\)$`) + type Schema struct { Definitions map[string]Definition } @@ -138,10 +142,10 @@ func (s *Node) checkSingleExpression(exprNode actionlint.ExprNode) error { for _, v := range *funcs { if strings.EqualFold(funcCallNode.Callee, v.name) { if v.min > len(funcCallNode.Args) { - err = errors.Join(err, fmt.Errorf("Missing parameters for %s expected > %v got %v", funcCallNode.Callee, v.min, len(funcCallNode.Args))) + err = errors.Join(err, fmt.Errorf("Missing parameters for %s expected >= %v got %v", funcCallNode.Callee, v.min, len(funcCallNode.Args))) } if v.max < len(funcCallNode.Args) { - err = errors.Join(err, fmt.Errorf("To many parameters for %s expected < %v got %v", funcCallNode.Callee, v.max, len(funcCallNode.Args))) + err = errors.Join(err, fmt.Errorf("To many parameters for %s expected <= %v got %v", funcCallNode.Callee, v.max, len(funcCallNode.Args))) } return } @@ -174,11 +178,22 @@ func (s *Node) GetFunctions() *[]FunctionInfo { if i == -1 { continue } - fun := FunctionInfo{ - name: v[:i], - } - if n, err := fmt.Sscanf(v[i:], "(%d,%d)", &fun.min, &fun.max); n == 2 && err == nil { - *funcs = append(*funcs, fun) + smatch := functions.FindStringSubmatch(v) + if len(smatch) > 0 { + functionName := smatch[1] + minParameters, _ := strconv.ParseInt(smatch[2], 10, 32) + maxParametersRaw := smatch[3] + var maxParameters int64 + if strings.EqualFold(maxParametersRaw, "MAX") { + maxParameters = math.MaxInt32 + } else { + maxParameters, _ = strconv.ParseInt(smatch[2], 10, 32) + } + *funcs = append(*funcs, FunctionInfo{ + name: functionName, + min: int(minParameters), + max: int(maxParameters), + }) } } return funcs @@ -220,6 +235,9 @@ func AddFunction(funcs *[]FunctionInfo, s string, i1, i2 int) { } func (s *Node) UnmarshalYAML(node *yaml.Node) error { + if node != nil && node.Kind == yaml.DocumentNode { + return s.UnmarshalYAML(node.Content[0]) + } def := s.Schema.GetDefinition(s.Definition) if s.Context == nil { s.Context = def.Context diff --git a/pkg/schema/schema_test.go b/pkg/schema/schema_test.go new file mode 100644 index 00000000000..ce571c96533 --- /dev/null +++ b/pkg/schema/schema_test.go @@ -0,0 +1,92 @@ +package schema + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" +) + +func TestAdditionalFunctions(t *testing.T) { + var node yaml.Node + err := yaml.Unmarshal([]byte(` +on: push +jobs: + job-with-condition: + runs-on: self-hosted + if: success() || success('joba', 'jobb') || failure() || failure('joba', 'jobb') || always() || cancelled() + steps: + - run: exit 0 +`), &node) + if !assert.NoError(t, err) { + return + } + err = (&Node{ + Definition: "workflow-root-strict", + Schema: GetWorkflowSchema(), + }).UnmarshalYAML(&node) + assert.NoError(t, err) +} + +func TestAdditionalFunctionsFailure(t *testing.T) { + var node yaml.Node + err := yaml.Unmarshal([]byte(` +on: push +jobs: + job-with-condition: + runs-on: self-hosted + if: success() || success('joba', 'jobb') || failure() || failure('joba', 'jobb') || always('error') + steps: + - run: exit 0 +`), &node) + if !assert.NoError(t, err) { + return + } + err = (&Node{ + Definition: "workflow-root-strict", + Schema: GetWorkflowSchema(), + }).UnmarshalYAML(&node) + assert.Error(t, err) +} + +func TestAdditionalFunctionsSteps(t *testing.T) { + var node yaml.Node + err := yaml.Unmarshal([]byte(` +on: push +jobs: + job-with-condition: + runs-on: self-hosted + steps: + - run: exit 0 + if: success() || failure() || always() +`), &node) + if !assert.NoError(t, err) { + return + } + err = (&Node{ + Definition: "workflow-root-strict", + Schema: GetWorkflowSchema(), + }).UnmarshalYAML(&node) + assert.NoError(t, err) +} + +func TestAdditionalFunctionsStepsExprSyntax(t *testing.T) { + var node yaml.Node + err := yaml.Unmarshal([]byte(` +on: push +jobs: + job-with-condition: + runs-on: self-hosted + steps: + - run: exit 0 + if: ${{ success() || failure() || always() }} +`), &node) + if !assert.NoError(t, err) { + return + } + err = (&Node{ + Definition: "workflow-root-strict", + Schema: GetWorkflowSchema(), + }).UnmarshalYAML(&node) + assert.NoError(t, err) +} From 050440f2afe1a4ec1ef23fec7c8c6de636480f5c Mon Sep 17 00:00:00 2001 From: ChristopherHX Date: Tue, 10 Sep 2024 19:43:59 +0200 Subject: [PATCH 2/3] Update pkg/schema/schema.go Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- pkg/schema/schema.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go index 6b24878f537..eebd536b3b4 100644 --- a/pkg/schema/schema.go +++ b/pkg/schema/schema.go @@ -145,7 +145,7 @@ func (s *Node) checkSingleExpression(exprNode actionlint.ExprNode) error { err = errors.Join(err, fmt.Errorf("Missing parameters for %s expected >= %v got %v", funcCallNode.Callee, v.min, len(funcCallNode.Args))) } if v.max < len(funcCallNode.Args) { - err = errors.Join(err, fmt.Errorf("To many parameters for %s expected <= %v got %v", funcCallNode.Callee, v.max, len(funcCallNode.Args))) + err = errors.Join(err, fmt.Errorf("Too many parameters for %s expected <= %v got %v", funcCallNode.Callee, v.max, len(funcCallNode.Args))) } return } From 71fe49322cb7463b2420dc69147a8c01189b2d3c Mon Sep 17 00:00:00 2001 From: ChristopherHX Date: Tue, 10 Sep 2024 19:46:05 +0200 Subject: [PATCH 3/3] Update pkg/schema/schema.go --- pkg/schema/schema.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go index eebd536b3b4..a523e721f6d 100644 --- a/pkg/schema/schema.go +++ b/pkg/schema/schema.go @@ -187,7 +187,7 @@ func (s *Node) GetFunctions() *[]FunctionInfo { if strings.EqualFold(maxParametersRaw, "MAX") { maxParameters = math.MaxInt32 } else { - maxParameters, _ = strconv.ParseInt(smatch[2], 10, 32) + maxParameters, _ = strconv.ParseInt(maxParametersRaw, 10, 32) } *funcs = append(*funcs, FunctionInfo{ name: functionName,