Skip to content

Commit

Permalink
[CI-3303] DAG validation (#984)
Browse files Browse the repository at this point in the history
* Add graph package

* Add dag validation

* Bump format version

* Update error messages

* Use different sorting

* Update test
  • Loading branch information
tothszabi committed Jul 22, 2024
1 parent 6c62088 commit 4f5395b
Show file tree
Hide file tree
Showing 55 changed files with 4,828 additions and 25 deletions.
202 changes: 202 additions & 0 deletions cli/run_util_pipeline_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package cli

import (
"encoding/base64"
"testing"

"github.com/stretchr/testify/require"
)

const validStagedPipeline = `
format_version: '13'
pipelines:
staged:
stages:
- s1: {}
- s2: {}
- s3: {}
stages:
s1:
workflows:
- a: {}
s2:
workflows:
- b: {}
- c: {}
- d: {}
s3:
workflows:
- e: {}
workflows:
a: {}
b: {}
c: {}
d: {}
e: {}
`

const validDAGPipeline = `
format_version: '13'
pipelines:
dag:
workflows:
a: {}
b: { depends_on: [a] }
c: { depends_on: [a] }
d: { depends_on: [a] }
e: { depends_on: [b, d] }
f: { depends_on: [e] }
g: { depends_on: [a, e, f] }
workflows:
a: {}
b: {}
c: {}
d: {}
e: {}
f: {}
g: {}
`

const mixedStagedAndDAGPipeline = `
format_version: '13'
pipelines:
dag:
workflows:
a: {}
b: { depends_on: [a] }
stages:
- stage1: {}
stages:
stage1:
workflows:
- a: {}
- b: {}
workflows:
a: {}
b: {}
`

const missingWorkflowInDAGPipelineDefinition = `
format_version: '13'
pipelines:
dag:
workflows:
a: {}
b: { depends_on: [c] }
workflows:
a: {}
b: {}
c: {}
`

const missingWorkflowInWorkflowDefinitionForDAGPipeline = `
format_version: '13'
pipelines:
dag:
workflows:
a: {}
b: { depends_on: [c] }
c: {}
workflows:
a: {}
b: {}
`

const duplicatedDependencyDAGPipeline = `
format_version: '13'
pipelines:
dag:
workflows:
a: {}
b: { depends_on: [a, a] }
workflows:
a: {}
b: {}
`

const utilityWorkflowDAGPipeline = `
format_version: '13'
pipelines:
dag:
workflows:
_a: {}
b: { depends_on: [_a] }
workflows:
_a: {}
b: {}
`

const cycleInDAGPipeline = `
format_version: '13'
pipelines:
dag:
workflows:
a: {}
b: { depends_on: [c] }
c: { depends_on: [b] }
workflows:
a: {}
b: {}
c: {}
`

func TestValidation(t *testing.T) {
tests := []struct {
name string
config string
wantErr string
}{
{
name: "Mixing stages and workflows in the same pipeline",
config: mixedStagedAndDAGPipeline,
wantErr: "Failed to get config (bitrise.yml) from base 64 data, err: Failed to parse bitrise config, error: pipeline (dag) has both stages and workflows",
},
{
name: "Workflow is missing from the DAG pipeline definition",
config: missingWorkflowInDAGPipelineDefinition,
wantErr: "Failed to get config (bitrise.yml) from base 64 data, err: Failed to parse bitrise config, error: workflow (c) defined in dependencies (b) is not part of pipeline (dag)",
},
{
name: "Workflow is missing from the Workflow definition",
config: missingWorkflowInWorkflowDefinitionForDAGPipeline,
wantErr: "Failed to get config (bitrise.yml) from base 64 data, err: Failed to parse bitrise config, error: workflow (c) defined in pipeline (dag) is not found in the workflow definitions",
},
{
name: "Utility workflow is referenced in the DAG pipeline",
config: utilityWorkflowDAGPipeline,
wantErr: "Failed to get config (bitrise.yml) from base 64 data, err: Failed to parse bitrise config, error: workflow (_a) defined in pipeline (dag) is a utility workflow",
},
{
name: "Duplicated dependency in the DAG pipeline",
config: duplicatedDependencyDAGPipeline,
wantErr: "Failed to get config (bitrise.yml) from base 64 data, err: Failed to parse bitrise config, error: workflow (a) is duplicated in the dependency list (b)",
},
{
name: "Cycle in the DAG pipeline",
config: cycleInDAGPipeline,
wantErr: "Failed to get config (bitrise.yml) from base 64 data, err: Failed to parse bitrise config, error: the dependency between workflow 'b' and workflow 'c' creates a cycle in the graph",
},
{
name: "Valid DAG pipeline",
config: validDAGPipeline,
wantErr: "",
},
{
name: "Valid staged pipeline",
config: validStagedPipeline,
wantErr: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b64Data := base64.StdEncoding.EncodeToString([]byte(tt.config))
_, _, err := CreateBitriseConfigFromCLIParams(b64Data, "")

if tt.wantErr != "" {
require.EqualError(t, err, tt.wantErr)
} else {
require.NoError(t, err)
}
})
}
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/bitrise-io/stepman v0.0.0-20240628161214-6bfe26aacd00
github.com/gofrs/uuid v4.3.1+incompatible
github.com/hashicorp/go-version v1.4.0
github.com/heimdalr/dag v1.4.0
github.com/ryanuber/go-glob v1.0.0
github.com/stretchr/testify v1.9.0
github.com/urfave/cli v1.22.15
Expand All @@ -24,6 +25,8 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,20 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI=
github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
Expand All @@ -51,6 +57,8 @@ github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISH
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4=
github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/heimdalr/dag v1.4.0 h1:zG3JA4RDVLc55k3AXAgfwa+EgBNZ0TkfOO3C29Ucpmg=
github.com/heimdalr/dag v1.4.0/go.mod h1:OCh6ghKmU0hPjtwMqWBoNxPmtRioKd1xSu7Zs4sbIqM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
Expand Down
17 changes: 12 additions & 5 deletions models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

const (
FormatVersion = "16"
FormatVersion = "17"
StepListItemWithKey = "with"
StepListItemStepBundleKeyPrefix = "bundle::"
)
Expand Down Expand Up @@ -39,10 +39,11 @@ 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"`
Stages []StageListItemModel `json:"stages,omitempty" yaml:"stages,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"`
Stages []StageListItemModel `json:"stages,omitempty" yaml:"stages,omitempty"`
Workflows DagWorkflowListItemModel `json:"workflows,omitempty" yaml:"workflows,omitempty"`
}

type StageListItemModel map[string]StageModel
Expand All @@ -63,6 +64,12 @@ type StageWorkflowModel struct {
RunIf string `json:"run_if,omitempty" yaml:"run_if,omitempty"`
}

type DagWorkflowListItemModel map[string]DagWorkflowModel

type DagWorkflowModel struct {
DependsOn []string `json:"depends_on,omitempty" yaml:"depends_on,omitempty"`
}

type WorkflowListItemModel map[string]WorkflowModel

type WorkflowModel struct {
Expand Down
Loading

0 comments on commit 4f5395b

Please sign in to comment.