@@ -21,89 +21,110 @@ const (
21
21
npmScriptsCondition = "npmScripts"
22
22
)
23
23
24
- // EvaluateConditionsV1 validates stage conditions and updates runSteps in runConfig according to V1 schema
25
- func (r * RunConfigV1 ) evaluateConditionsV1 (config * Config , filters map [string ]StepFilters , parameters map [string ][]StepParameters ,
26
- secrets map [string ][]StepSecrets , stepAliases map [string ][]Alias , utils piperutils.FileUtils , envRootPath string ) error {
27
-
28
- // initialize in case not initialized
29
- if r .RunConfig .RunSteps == nil {
30
- r .RunConfig .RunSteps = map [string ]map [string ]bool {}
24
+ // evaluateConditionsV1 validates stage conditions and updates runSteps in runConfig according to V1 schema.
25
+ // Priority of step activation/deactivation is follow:
26
+ // - stepNotActiveCondition (highest, if any)
27
+ // - explicit activation/deactivation (medium, if any)
28
+ // - stepActiveConditions (lowest, step is active by default if no conditions are configured)
29
+ func (r * RunConfigV1 ) evaluateConditionsV1 (config * Config , utils piperutils.FileUtils , envRootPath string ) error {
30
+ if r .RunSteps == nil {
31
+ r .RunSteps = make (map [string ]map [string ]bool , len (r .PipelineConfig .Spec .Stages ))
31
32
}
32
- if r .RunConfig . RunStages == nil {
33
- r .RunConfig . RunStages = map [string ]bool {}
33
+ if r .RunStages == nil {
34
+ r .RunStages = make ( map [string ]bool , len ( r . PipelineConfig . Spec . Stages ))
34
35
}
35
36
37
+ currentOrchestrator := orchestrator .DetectOrchestrator ().String ()
36
38
for _ , stage := range r .PipelineConfig .Spec .Stages {
37
- runStep := map [string ]bool {}
38
- stageActive := false
39
-
40
- // currently displayName is used, may need to consider to use technical name as well
39
+ // Currently, the displayName is being used, but it may be necessary
40
+ // to also consider using the technical name.
41
41
stageName := stage .DisplayName
42
42
43
+ // Check #1: Apply explicit activation/deactivation from config file (if any)
44
+ // and then evaluate stepActive conditions
45
+ runStep := make (map [string ]bool , len (stage .Steps ))
46
+ stepConfigCache := make (map [string ]StepConfig , len (stage .Steps ))
43
47
for _ , step := range stage .Steps {
44
- // Only consider orchestrator-specific steps in case orchestrator limitation is set
45
- currentOrchestrator := orchestrator .DetectOrchestrator ().String ()
48
+ // Consider only orchestrator-specific steps if the orchestrator limitation is set.
46
49
if len (step .Orchestrators ) > 0 && ! piperutils .ContainsString (step .Orchestrators , currentOrchestrator ) {
47
50
continue
48
51
}
49
52
50
- stepActive := false
51
- stepNotActive := false
52
-
53
- stepConfig , err := r .getStepConfig (config , stageName , step .Name , filters , parameters , secrets , stepAliases )
53
+ stepConfig , err := r .getStepConfig (config , stageName , step .Name , nil , nil , nil , nil )
54
54
if err != nil {
55
55
return err
56
56
}
57
+ stepConfigCache [step .Name ] = stepConfig
57
58
59
+ // Respect explicit activation/deactivation if available.
60
+ // Note that this has higher priority than step conditions
58
61
if active , ok := stepConfig .Config [step .Name ].(bool ); ok {
59
- // respect explicit activation/de-activation if available
60
- stepActive = active
61
- } else {
62
- if step .Conditions == nil || len (step .Conditions ) == 0 {
63
- // if no condition is available, step will be active by default
64
- stepActive = true
65
- } else {
66
- for _ , condition := range step .Conditions {
67
- stepActive , err = condition .evaluateV1 (stepConfig , utils , step .Name , envRootPath )
68
- if err != nil {
69
- return fmt .Errorf ("failed to evaluate stage conditions: %w" , err )
70
- }
71
- if stepActive {
72
- // first condition which matches will be considered to activate the step
73
- break
74
- }
75
- }
62
+ runStep [step .Name ] = active
63
+ continue
64
+ }
65
+
66
+ // If no condition is available, the step will be active by default.
67
+ stepActive := true
68
+ for _ , condition := range step .Conditions {
69
+ stepActive , err = condition .evaluateV1 (stepConfig , utils , step .Name , envRootPath , runStep )
70
+ if err != nil {
71
+ return fmt .Errorf ("failed to evaluate step conditions: %w" , err )
72
+ }
73
+ if stepActive {
74
+ // The first condition that matches will be considered to activate the step.
75
+ break
76
76
}
77
77
}
78
78
79
- // TODO: PART 1 : if explicit activation/de-activation is available should notActiveConditions be checked ?
80
- // Fortify has no anchor, so if we explicitly set it to true then it may run even during commit pipelines, if we implement TODO PART 1??
79
+ runStep [step .Name ] = stepActive
80
+ }
81
+
82
+ // Check #2: Evaluate stepNotActive conditions (if any) and deactivate the step if the condition is met.
83
+ //
84
+ // TODO: PART 1 : if explicit activation/de-activation is available should notActiveConditions be checked ?
85
+ // Fortify has no anchor, so if we explicitly set it to true then it may run even during commit pipelines, if we implement TODO PART 1??
86
+ for _ , step := range stage .Steps {
87
+ stepConfig , found := stepConfigCache [step .Name ]
88
+ if ! found {
89
+ // If no stepConfig exists here, it means that this step was skipped in previous checks.
90
+ continue
91
+ }
92
+
81
93
for _ , condition := range step .NotActiveConditions {
82
- stepNotActive , err = condition .evaluateV1 (stepConfig , utils , step .Name , envRootPath )
94
+ stepNotActive , err : = condition .evaluateV1 (stepConfig , utils , step .Name , envRootPath , runStep )
83
95
if err != nil {
84
- return fmt .Errorf ("failed to evaluate not active stage conditions: %w" , err )
96
+ return fmt .Errorf ("failed to evaluate not active step conditions: %w" , err )
85
97
}
98
+
99
+ // Deactivate the step if the notActive condition is met.
86
100
if stepNotActive {
87
- // first condition which matches will be considered to not activate the step
101
+ runStep [ step . Name ] = false
88
102
break
89
103
}
90
104
}
105
+ }
91
106
92
- // final decision is when step is activated and negate when not active is true
93
- stepActive = stepActive && ! stepNotActive
107
+ r .RunSteps [stageName ] = runStep
94
108
95
- if stepActive {
109
+ stageActive := false
110
+ for _ , anyStepIsActive := range r .RunSteps [stageName ] {
111
+ if anyStepIsActive {
96
112
stageActive = true
97
113
}
98
- runStep [step .Name ] = stepActive
99
- r .RunSteps [stageName ] = runStep
100
114
}
101
115
r .RunStages [stageName ] = stageActive
102
116
}
117
+
103
118
return nil
104
119
}
105
120
106
- func (s * StepCondition ) evaluateV1 (config StepConfig , utils piperutils.FileUtils , stepName string , envRootPath string ) (bool , error ) {
121
+ func (s * StepCondition ) evaluateV1 (
122
+ config StepConfig ,
123
+ utils piperutils.FileUtils ,
124
+ stepName string ,
125
+ envRootPath string ,
126
+ runSteps map [string ]bool ,
127
+ ) (bool , error ) {
107
128
108
129
// only the first condition will be evaluated.
109
130
// if multiple conditions should be checked they need to provided via the Conditions list
@@ -189,6 +210,14 @@ func (s *StepCondition) evaluateV1(config StepConfig, utils piperutils.FileUtils
189
210
return false , nil
190
211
}
191
212
213
+ if s .OnlyActiveStepInStage {
214
+ // Used only in NotActiveConditions.
215
+ // Returns true if all other steps are inactive, so step will be deactivated
216
+ // if it's the only active step in stage.
217
+ // For example, sapCumulusUpload step must be deactivated in a stage where others steps are inactive.
218
+ return ! anyOtherStepIsActive (stepName , runSteps ), nil
219
+ }
220
+
192
221
// needs to be checked last:
193
222
// if none of the other conditions matches, step will be active unless set to inactive
194
223
if s .Inactive == true {
@@ -491,3 +520,15 @@ func checkForNpmScriptsInPackagesV1(npmScript string, config StepConfig, utils p
491
520
}
492
521
return false , nil
493
522
}
523
+
524
+ // anyOtherStepIsActive loops through previous steps active states and returns true
525
+ // if at least one of them is active, otherwise result is false. Ignores the step that is being checked.
526
+ func anyOtherStepIsActive (targetStep string , runSteps map [string ]bool ) bool {
527
+ for step , isActive := range runSteps {
528
+ if isActive && step != targetStep {
529
+ return true
530
+ }
531
+ }
532
+
533
+ return false
534
+ }
0 commit comments