From ffc0b7c371793d59d85c731640a1c47219a6735d Mon Sep 17 00:00:00 2001 From: Szabolcs Toth <54896607+tothszabi@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:44:33 +0100 Subject: [PATCH 1/3] Add missing graph pipeline validation --- models/models.go | 35 ++++++++++++++++++++++++-- models/models_test.go | 58 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/models/models.go b/models/models.go index f28833cb2..e275124d4 100644 --- a/models/models.go +++ b/models/models.go @@ -2,6 +2,7 @@ package models import ( "fmt" + "slices" "strings" "time" @@ -9,8 +10,15 @@ import ( stepmanModels "github.com/bitrise-io/stepman/models" ) +type DagAlwaysRunMode string + +const ( + DagAlwaysRunModeOff DagAlwaysRunMode = "off" + DagAlwaysRunModeWorkflow DagAlwaysRunMode = "workflow" +) + const ( - FormatVersion = "17" + FormatVersion = "18" StepListItemWithKey = "with" StepListItemStepBundleKeyPrefix = "bundle::" ) @@ -68,7 +76,30 @@ type StageWorkflowModel struct { type DagWorkflowListItemModel map[string]DagWorkflowModel type DagWorkflowModel struct { - DependsOn []string `json:"depends_on,omitempty" yaml:"depends_on,omitempty"` + DependsOn []string `json:"depends_on,omitempty" yaml:"depends_on,omitempty"` + AbortOnFail bool `json:"abort_on_fail,omitempty" yaml:"abort_on_fail,omitempty"` + RunIf DagRunIfModel `json:"run_if,omitempty" yaml:"run_if,omitempty"` + ShouldAlwaysRun DagAlwaysRunMode `json:"should_always_run,omitempty" yaml:"should_always_run,omitempty"` +} + +type DagRunIfModel struct { + Expression string `json:"expression,omitempty" yaml:"expression,omitempty"` +} + +func (d *DagAlwaysRunMode) UnmarshalYAML(unmarshal func(interface{}) error) error { + var value string + if err := unmarshal(&value); err != nil { + return err + } + + allowedValues := []string{string(DagAlwaysRunModeOff), string(DagAlwaysRunModeWorkflow)} + if !slices.Contains(allowedValues, value) { + return fmt.Errorf("%s is not a valid should always run value", value) + } + + *d = DagAlwaysRunMode(value) + + return nil } type WorkflowListItemModel map[string]WorkflowModel diff --git a/models/models_test.go b/models/models_test.go index b9d2fa05c..87acb3ccc 100644 --- a/models/models_test.go +++ b/models/models_test.go @@ -6,6 +6,8 @@ import ( "github.com/bitrise-io/stepman/models" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" ) func TestStatusReasonSuccess(t *testing.T) { @@ -191,3 +193,59 @@ func TestShortReason(t *testing.T) { assert.Equal(t, expected, actual) } + +func TestDagWorkflow(t *testing.T) { + testCases := []struct { + rawYML string + errorExpected bool + }{ + { + rawYML: ` +pipelines: + pipeline1: + workflows: + workflow1: + abort_on_fail: true + should_always_run: off + run_if: + expression: "custom-expression" +workflows: + workflow1: {} +`, + }, + { + rawYML: ` +pipelines: + pipeline1: + workflows: + workflow1: + abort_on_fail: false + should_always_run: workflow +workflows: + workflow1: {} +`, + }, + { + rawYML: ` +pipelines: + pipeline1: + workflows: + workflow1: + should_always_run: none +workflows: + workflow1: {} +`, + errorExpected: true, + }, + } + + for _, testCase := range testCases { + config := BitriseDataModel{} + err := yaml.Unmarshal([]byte(testCase.rawYML), &config) + if testCase.errorExpected { + require.Error(t, err) + } else { + require.NoError(t, err) + } + } +} From c48b6978df185b35d581414793fe880d9d607097 Mon Sep 17 00:00:00 2001 From: Szabolcs Toth <54896607+tothszabi@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:39:46 +0100 Subject: [PATCH 2/3] Update name --- models/models.go | 38 +++++++++++++++++++------------------- models/models_test.go | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/models/models.go b/models/models.go index e275124d4..d02d69220 100644 --- a/models/models.go +++ b/models/models.go @@ -10,11 +10,11 @@ import ( stepmanModels "github.com/bitrise-io/stepman/models" ) -type DagAlwaysRunMode string +type GraphPipelineAlwaysRunMode string const ( - DagAlwaysRunModeOff DagAlwaysRunMode = "off" - DagAlwaysRunModeWorkflow DagAlwaysRunMode = "workflow" + GraphPipelineAlwaysRunModeOff GraphPipelineAlwaysRunMode = "off" + GraphPipelineAlwaysRunModeWorkflow GraphPipelineAlwaysRunMode = "workflow" ) const ( @@ -47,12 +47,12 @@ type StepListStepItemModel map[string]stepmanModels.StepModel type StepListItemModel map[string]interface{} type PipelineModel struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` - Description string `json:"description,omitempty" yaml:"description,omitempty"` - Triggers Triggers `json:"triggers,omitempty" yaml:"triggers,omitempty"` - Stages []StageListItemModel `json:"stages,omitempty" yaml:"stages,omitempty"` - Workflows DagWorkflowListItemModel `json:"workflows,omitempty" yaml:"workflows,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Triggers Triggers `json:"triggers,omitempty" yaml:"triggers,omitempty"` + Stages []StageListItemModel `json:"stages,omitempty" yaml:"stages,omitempty"` + Workflows GraphPipelineWorkflowListItemModel `json:"workflows,omitempty" yaml:"workflows,omitempty"` } type StageListItemModel map[string]StageModel @@ -73,31 +73,31 @@ type StageWorkflowModel struct { RunIf string `json:"run_if,omitempty" yaml:"run_if,omitempty"` } -type DagWorkflowListItemModel map[string]DagWorkflowModel +type GraphPipelineWorkflowListItemModel map[string]GraphPipelineWorkflowModel -type DagWorkflowModel struct { - DependsOn []string `json:"depends_on,omitempty" yaml:"depends_on,omitempty"` - AbortOnFail bool `json:"abort_on_fail,omitempty" yaml:"abort_on_fail,omitempty"` - RunIf DagRunIfModel `json:"run_if,omitempty" yaml:"run_if,omitempty"` - ShouldAlwaysRun DagAlwaysRunMode `json:"should_always_run,omitempty" yaml:"should_always_run,omitempty"` +type GraphPipelineWorkflowModel struct { + DependsOn []string `json:"depends_on,omitempty" yaml:"depends_on,omitempty"` + AbortOnFail bool `json:"abort_on_fail,omitempty" yaml:"abort_on_fail,omitempty"` + RunIf GraphPipelineRunIfModel `json:"run_if,omitempty" yaml:"run_if,omitempty"` + ShouldAlwaysRun GraphPipelineAlwaysRunMode `json:"should_always_run,omitempty" yaml:"should_always_run,omitempty"` } -type DagRunIfModel struct { +type GraphPipelineRunIfModel struct { Expression string `json:"expression,omitempty" yaml:"expression,omitempty"` } -func (d *DagAlwaysRunMode) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (d *GraphPipelineAlwaysRunMode) UnmarshalYAML(unmarshal func(interface{}) error) error { var value string if err := unmarshal(&value); err != nil { return err } - allowedValues := []string{string(DagAlwaysRunModeOff), string(DagAlwaysRunModeWorkflow)} + allowedValues := []string{string(GraphPipelineAlwaysRunModeOff), string(GraphPipelineAlwaysRunModeWorkflow)} if !slices.Contains(allowedValues, value) { return fmt.Errorf("%s is not a valid should always run value", value) } - *d = DagAlwaysRunMode(value) + *d = GraphPipelineAlwaysRunMode(value) return nil } diff --git a/models/models_test.go b/models/models_test.go index 87acb3ccc..7058e759e 100644 --- a/models/models_test.go +++ b/models/models_test.go @@ -194,7 +194,7 @@ func TestShortReason(t *testing.T) { assert.Equal(t, expected, actual) } -func TestDagWorkflow(t *testing.T) { +func TestGraphPipelineWorkflow(t *testing.T) { testCases := []struct { rawYML string errorExpected bool From af293d971f1c58227326f65d74305fcb6210f08c Mon Sep 17 00:00:00 2001 From: Szabolcs Toth <54896607+tothszabi@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:59:18 +0100 Subject: [PATCH 3/3] Update error message --- models/models.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/models.go b/models/models.go index d02d69220..96b3f587e 100644 --- a/models/models.go +++ b/models/models.go @@ -94,7 +94,7 @@ func (d *GraphPipelineAlwaysRunMode) UnmarshalYAML(unmarshal func(interface{}) e allowedValues := []string{string(GraphPipelineAlwaysRunModeOff), string(GraphPipelineAlwaysRunModeWorkflow)} if !slices.Contains(allowedValues, value) { - return fmt.Errorf("%s is not a valid should always run value", value) + return fmt.Errorf("%s is not a valid should_always_run value (%s)", value, allowedValues) } *d = GraphPipelineAlwaysRunMode(value)