From 47f3163981b6a2cbd78c1a5003d613ce9546b99b Mon Sep 17 00:00:00 2001 From: pransh62390 <63577123+pransh62390@users.noreply.github.com> Date: Fri, 24 Jan 2025 01:08:02 +0530 Subject: [PATCH 1/6] added stage in spec and solved issue of checking duplicates of multiple stages solves issue: #1795 --- backend/services/spec.go | 51 ++++++++++++++++++++++++++++++---------- libs/spec/models.go | 1 + 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/backend/services/spec.go b/backend/services/spec.go index 152f7199c..986ab60eb 100644 --- a/backend/services/spec.go +++ b/backend/services/spec.go @@ -60,7 +60,7 @@ func GetRunNameFromJob(job models.DiggerJob) (*string, error) { return &runName, nil } -func getVariablesSpecFromEnvMap(envVars map[string]string) []spec.VariableSpec { +func getVariablesSpecFromEnvMap(envVars map[string]string, stage string) []spec.VariableSpec { variablesSpec := make([]spec.VariableSpec, 0) for k, v := range envVars { if strings.HasPrefix(v, "$DIGGER_") { @@ -70,6 +70,7 @@ func getVariablesSpecFromEnvMap(envVars map[string]string) []spec.VariableSpec { Value: val, IsSecret: false, IsInterpolated: true, + Stage: stage, }) } else { variablesSpec = append(variablesSpec, spec.VariableSpec{ @@ -77,6 +78,7 @@ func getVariablesSpecFromEnvMap(envVars map[string]string) []spec.VariableSpec { Value: v, IsSecret: false, IsInterpolated: false, + Stage: stage, }) } @@ -84,6 +86,27 @@ func getVariablesSpecFromEnvMap(envVars map[string]string) []spec.VariableSpec { return variablesSpec } +func findDuplicatesInStage(variablesSpec []spec.VariableSpec, stage string) (error) { + // Extract the names from VariableSpec + justNames := lo.Map(variablesSpec, func(item VariableSpec, i int) string { + return item.Name + }) + + // Group names by their occurrence + nameCounts := lo.CountValues(justNames) + + // Filter names that occur more than once + duplicates := lo.Keys(lo.Filter(nameCounts, func(count int, name string) bool { + return count > 1 + })) + + if len(duplicates) > 0 { + return fmt.Errorf("In %v stage, found duplicate variables: %v", stage, duplicates) + } + + return nil // No duplicates found +} + func GetSpecFromJob(job models.DiggerJob) (*spec.Spec, error) { var jobSpec scheduler.JobJson err := json.Unmarshal([]byte(job.SerializedJobSpec), &jobSpec) @@ -92,23 +115,25 @@ func GetSpecFromJob(job models.DiggerJob) (*spec.Spec, error) { return nil, fmt.Errorf("could not marshal json string: %v", err) } + stateVariables := getVariablesSpecFromEnvMap(jobSpec.StateEnvVars, "state") + commandVariables := getVariablesSpecFromEnvMap(jobSpec.CommandEnvVars, "commands") + runVariables := getVariablesSpecFromEnvMap(jobSpec.RunEnvVars, "run") + + if err := checkDuplicatesInStage(stateVariables, "state"); err != nil { + return nil, err + } + if err := checkDuplicatesInStage(commandVariables, "commands"); err != nil { + return nil, err + } + if err := checkDuplicatesInStage(runVariables, "run"); err != nil { + return nil, err + } + variablesSpec := make([]spec.VariableSpec, 0) - stateVariables := getVariablesSpecFromEnvMap(jobSpec.StateEnvVars) - commandVariables := getVariablesSpecFromEnvMap(jobSpec.CommandEnvVars) - runVariables := getVariablesSpecFromEnvMap(jobSpec.RunEnvVars) variablesSpec = append(variablesSpec, stateVariables...) variablesSpec = append(variablesSpec, commandVariables...) variablesSpec = append(variablesSpec, runVariables...) - // check for duplicates in list of variablesSpec - justNames := lo.Map(variablesSpec, func(item spec.VariableSpec, i int) string { - return item.Name - }) - hasDuplicates := len(justNames) != len(lo.Uniq(justNames)) - if hasDuplicates { - return nil, fmt.Errorf("could not load variables due to duplicates") - } - batch := job.Batch spec := spec.Spec{ diff --git a/libs/spec/models.go b/libs/spec/models.go index 3637f920b..6818e1ed3 100644 --- a/libs/spec/models.go +++ b/libs/spec/models.go @@ -45,6 +45,7 @@ type VariableSpec struct { Value string `json:"value"` IsSecret bool `json:"is_secret"` IsInterpolated bool `json:"is_interpolated"` + Stage string `json:"stage"` } type SpecType string From 34e2b2a3a290260e363c6827efbc810efc30c518 Mon Sep 17 00:00:00 2001 From: pransh62390 <63577123+pransh62390@users.noreply.github.com> Date: Fri, 24 Jan 2025 01:24:22 +0530 Subject: [PATCH 2/6] fixed errors and error logging message --- backend/services/spec.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/services/spec.go b/backend/services/spec.go index 986ab60eb..725f36085 100644 --- a/backend/services/spec.go +++ b/backend/services/spec.go @@ -88,7 +88,7 @@ func getVariablesSpecFromEnvMap(envVars map[string]string, stage string) []spec. func findDuplicatesInStage(variablesSpec []spec.VariableSpec, stage string) (error) { // Extract the names from VariableSpec - justNames := lo.Map(variablesSpec, func(item VariableSpec, i int) string { + justNames := lo.Map(variablesSpec, func(item spec.VariableSpec, i int) string { return item.Name }) @@ -101,7 +101,7 @@ func findDuplicatesInStage(variablesSpec []spec.VariableSpec, stage string) (err })) if len(duplicates) > 0 { - return fmt.Errorf("In %v stage, found duplicate variables: %v", stage, duplicates) + return fmt.Errorf("duplicate variable names found in '%s' stage: %v", stage, strings.Join(duplicates, ", ")) } return nil // No duplicates found @@ -119,13 +119,13 @@ func GetSpecFromJob(job models.DiggerJob) (*spec.Spec, error) { commandVariables := getVariablesSpecFromEnvMap(jobSpec.CommandEnvVars, "commands") runVariables := getVariablesSpecFromEnvMap(jobSpec.RunEnvVars, "run") - if err := checkDuplicatesInStage(stateVariables, "state"); err != nil { + if err := findDuplicatesInStage(stateVariables, "state"); err != nil { return nil, err } - if err := checkDuplicatesInStage(commandVariables, "commands"); err != nil { + if err := findDuplicatesInStage(commandVariables, "commands"); err != nil { return nil, err } - if err := checkDuplicatesInStage(runVariables, "run"); err != nil { + if err := findDuplicatesInStage(runVariables, "run"); err != nil { return nil, err } From 318babd00ae007d19da3e9578a063be6b31ef74c Mon Sep 17 00:00:00 2001 From: pransh62390 <63577123+pransh62390@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:52:34 +0530 Subject: [PATCH 3/6] fixed compilation issue --- backend/services/spec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/services/spec.go b/backend/services/spec.go index 725f36085..590473fef 100644 --- a/backend/services/spec.go +++ b/backend/services/spec.go @@ -96,7 +96,7 @@ func findDuplicatesInStage(variablesSpec []spec.VariableSpec, stage string) (err nameCounts := lo.CountValues(justNames) // Filter names that occur more than once - duplicates := lo.Keys(lo.Filter(nameCounts, func(count int, name string) bool { + duplicates := lo.Keys(lo.PickBy(nameCounts, func(name string, count int) bool { return count > 1 })) From 0d23fe7be75aef0c91568e51923660fd691ac7bd Mon Sep 17 00:00:00 2001 From: octocamocoder47 Date: Thu, 4 Sep 2025 12:45:35 +0530 Subject: [PATCH 4/6] added test for spec.go file and modified variables_provider to filter new 3 stages --- backend/services/spec_test.go | 44 +++++++++++++++++++++++++ libs/spec/variables_provider.go | 58 ++++++++++++++++++++++++--------- 2 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 backend/services/spec_test.go diff --git a/backend/services/spec_test.go b/backend/services/spec_test.go new file mode 100644 index 000000000..36c84c54b --- /dev/null +++ b/backend/services/spec_test.go @@ -0,0 +1,44 @@ +// filepath: digger/backend/services/spec_test.go +package services + +import ( + // "fmt" + "github.com/diggerhq/digger/libs/spec" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestFindDuplicatesInStage_NoDuplicates(t *testing.T) { + variablesSpec := []spec.VariableSpec{ + {Name: "VAR1"}, + {Name: "VAR2"}, + {Name: "VAR3"}, + } + err := findDuplicatesInStage(variablesSpec, "test-stage") + assert.Nil(t, err, "Expected no error when there are no duplicates") +} + +func TestFindDuplicatesInStage_WithDuplicates(t *testing.T) { + variablesSpec := []spec.VariableSpec{ + {Name: "VAR1"}, + {Name: "VAR2"}, + {Name: "VAR1"}, + } + err := findDuplicatesInStage(variablesSpec, "test-stage") + assert.NotNil(t, err, "Expected an error when duplicates are present") + assert.Contains(t, err.Error(), "duplicate variable names found in 'test-stage' stage: VAR1") +} + +func TestFindDuplicatesInStage_EmptyVariablesSpec(t *testing.T) { + variablesSpec := []spec.VariableSpec{} + err := findDuplicatesInStage(variablesSpec, "test-stage") + assert.Nil(t, err, "Expected no error when variablesSpec is empty") +} + +func TestFindDuplicatesInStage_SingleVariable(t *testing.T) { + variablesSpec := []spec.VariableSpec{ + {Name: "VAR1"}, + } + err := findDuplicatesInStage(variablesSpec, "test-stage") + assert.Nil(t, err, "Expected no error when there is only one variable") +} diff --git a/libs/spec/variables_provider.go b/libs/spec/variables_provider.go index 8742d0024..27ee21161 100644 --- a/libs/spec/variables_provider.go +++ b/libs/spec/variables_provider.go @@ -9,31 +9,57 @@ import ( type VariablesProvider struct{} -func (p VariablesProvider) GetVariables(variables []VariableSpec) (map[string]string, error) { +func (p VariablesProvider) GetVariables(variables []VariableSpec) (map[string]map[string]string, error) { private_key := os.Getenv("DIGGER_PRIVATE_KEY") - secrets := lo.Filter(variables, func(variable VariableSpec, i int) bool { - return variable.IsSecret + + // Group variables by their stage + stagedVariables := lo.GroupBy(variables, func(variable VariableSpec) string { + return variable.Stage }) - if len(secrets) > 0 && private_key == "" { - return nil, fmt.Errorf("digger private key not supplied, unable to decrypt secrets") - } - res := make(map[string]string) + result := make(map[string]map[string]string) + + for stage, vars := range stagedVariables { + stageResult := make(map[string]string) + + // Filter variables into three categories + secrets := lo.Filter(vars, func(variable VariableSpec, i int) bool { + return variable.IsSecret + }) + interpolated := lo.Filter(vars, func(variable VariableSpec, i int) bool { + return variable.IsInterpolated + }) + plain := lo.Filter(vars, func(variable VariableSpec, i int) bool { + return !variable.IsSecret && !variable.IsInterpolated + }) + + // Check if private key is required for secrets + if len(secrets) > 0 && private_key == "" { + return nil, fmt.Errorf("digger private key not supplied, unable to decrypt secrets") + } - for _, v := range variables { - if v.IsSecret { + // Process secret variables + for _, v := range secrets { value, err := digger_crypto.DecryptValueUsingPrivateKey(v.Value, private_key) if err != nil { return nil, fmt.Errorf("could not decrypt value using private key: %v", err) } - res[v.Name] = string(value) - } else if v.IsInterpolated { - // if it is an interpolated value we get it form env variable of the variable - res[v.Name] = os.Getenv(v.Value) - } else { - res[v.Name] = v.Value + stageResult[v.Name] = string(value) } + + // Process interpolated variables + for _, v := range interpolated { + stageResult[v.Name] = os.Getenv(v.Value) + } + + // Process plain variables + for _, v := range plain { + stageResult[v.Name] = v.Value + } + + // Add the processed variables for the current stage to the result + result[stage] = stageResult } - return res, nil + return result, nil } From a82ef6c68e36fe7d7776c8d770f4332b885a5eb4 Mon Sep 17 00:00:00 2001 From: octocamocoder47 Date: Thu, 4 Sep 2025 13:21:21 +0530 Subject: [PATCH 5/6] fixed code for VariablesProvider struct callers --- cli/pkg/spec/spec.go | 10 +++++++--- libs/spec/variables_provider_test.go | 15 ++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/cli/pkg/spec/spec.go b/cli/pkg/spec/spec.go index a035e6ebe..b5a00a4be 100644 --- a/cli/pkg/spec/spec.go +++ b/cli/pkg/spec/spec.go @@ -131,9 +131,13 @@ func RunSpec( reportError(spec, backendApi, msg, err) usage.ReportErrorAndExit(spec.VCS.Actor, fmt.Sprintf("could not get variables from provider: %v", err), 1) } - job.StateEnvVars = lo.Assign(job.StateEnvVars, variablesMap) - job.CommandEnvVars = lo.Assign(job.CommandEnvVars, variablesMap) - job.RunEnvVars = lo.Assign(job.RunEnvVars, variablesMap) + + // Merge variables for each stage into the job's environment variables + for _, vars := range variablesMap { + job.StateEnvVars = lo.Assign(job.StateEnvVars, vars) + job.CommandEnvVars = lo.Assign(job.CommandEnvVars, vars) + job.RunEnvVars = lo.Assign(job.RunEnvVars, vars) + } jobs := []scheduler.Job{job} diff --git a/libs/spec/variables_provider_test.go b/libs/spec/variables_provider_test.go index be9d91de6..96f2c37ef 100644 --- a/libs/spec/variables_provider_test.go +++ b/libs/spec/variables_provider_test.go @@ -6,9 +6,10 @@ import ( "crypto/x509" "encoding/base64" "encoding/pem" - "github.com/stretchr/testify/assert" "os" "testing" + + "github.com/stretchr/testify/assert" ) // generateTestKeyPair generates a test RSA key pair @@ -76,6 +77,7 @@ func TestDecryptProvider(t *testing.T) { } tc.variables[0].Value = base64.StdEncoding.EncodeToString(v) } + variables, err := VariablesProvider{}.GetVariables(tc.variables) if tc.expectError { if err == nil { @@ -85,8 +87,15 @@ func TestDecryptProvider(t *testing.T) { if err != nil { t.Errorf("Unexpected error: %v", err) } - value := variables[tc.variables[0].Name] - assert.Equal(t, tc.plaintext, value) + + // Since the return type is map[string]map[string]string, we need to check the stage and variable name + stage := tc.variables[0].Stage + if _, ok := variables[stage]; !ok { + t.Errorf("Expected stage '%s' not found in variables map", stage) + } else { + value := variables[stage][tc.variables[0].Name] + assert.Equal(t, tc.plaintext, value) + } } }) } From 955e6814a0c8055dc501041abf2ac207fc7427f8 Mon Sep 17 00:00:00 2001 From: octocamocoder47 Date: Thu, 4 Sep 2025 14:57:49 +0530 Subject: [PATCH 6/6] fixed issue --- backend/services/spec.go | 15 +++--- backend/services/spec_test.go | 86 ++++++++++++++++++++++++++++++--- cli/pkg/spec/spec.go | 13 +++-- libs/spec/variables_provider.go | 3 ++ 4 files changed, 98 insertions(+), 19 deletions(-) diff --git a/backend/services/spec.go b/backend/services/spec.go index 74eb36781..81904239a 100644 --- a/backend/services/spec.go +++ b/backend/services/spec.go @@ -149,29 +149,26 @@ func getVariablesSpecFromEnvMap(envVars map[string]string, stage string) []spec. return variablesSpec } -func findDuplicatesInStage(variablesSpec []spec.VariableSpec, stage string) (error) { +func findDuplicatesInStage(variablesSpec []spec.VariableSpec, stage string, jobId string) (error) { // Extract the names from VariableSpec justNames := lo.Map(variablesSpec, func(item spec.VariableSpec, i int) string { return item.Name }) - // Group names by their occurrence nameCounts := lo.CountValues(justNames) - // Filter names that occur more than once duplicates := lo.Keys(lo.PickBy(nameCounts, func(name string, count int) bool { return count > 1 })) - if len(duplicates) > 0 { - return fmt.Errorf("duplicate variable names found in '%s' stage: %v", stage, strings.Join(duplicates, ", ")) + return fmt.Errorf("duplicate variable names found in '%s' stage for job %s: %v", stage, jobId, strings.Join(duplicates, ", ")) } - return nil // No duplicates found } func GetSpecFromJob(job models.DiggerJob) (*spec.Spec, error) { var jobSpec scheduler.JobJson + var jobId = job.DiggerJobID; err := json.Unmarshal([]byte(job.SerializedJobSpec), &jobSpec) if err != nil { slog.Error("Could not unmarshal job spec", "jobId", job.DiggerJobID, "error", err) @@ -182,13 +179,13 @@ func GetSpecFromJob(job models.DiggerJob) (*spec.Spec, error) { commandVariables := getVariablesSpecFromEnvMap(jobSpec.CommandEnvVars, "commands") runVariables := getVariablesSpecFromEnvMap(jobSpec.RunEnvVars, "run") - if err := findDuplicatesInStage(stateVariables, "state"); err != nil { + if err := findDuplicatesInStage(stateVariables, "state", jobId); err != nil { return nil, err } - if err := findDuplicatesInStage(commandVariables, "commands"); err != nil { + if err := findDuplicatesInStage(commandVariables, "commands", jobId); err != nil { return nil, err } - if err := findDuplicatesInStage(runVariables, "run"); err != nil { + if err := findDuplicatesInStage(runVariables, "run", jobId); err != nil { return nil, err } diff --git a/backend/services/spec_test.go b/backend/services/spec_test.go index 36c84c54b..cec3d879c 100644 --- a/backend/services/spec_test.go +++ b/backend/services/spec_test.go @@ -2,10 +2,11 @@ package services import ( - // "fmt" + "os" + "testing" + "github.com/diggerhq/digger/libs/spec" "github.com/stretchr/testify/assert" - "testing" ) func TestFindDuplicatesInStage_NoDuplicates(t *testing.T) { @@ -14,7 +15,7 @@ func TestFindDuplicatesInStage_NoDuplicates(t *testing.T) { {Name: "VAR2"}, {Name: "VAR3"}, } - err := findDuplicatesInStage(variablesSpec, "test-stage") + err := findDuplicatesInStage(variablesSpec, "test-stage", "") assert.Nil(t, err, "Expected no error when there are no duplicates") } @@ -24,14 +25,14 @@ func TestFindDuplicatesInStage_WithDuplicates(t *testing.T) { {Name: "VAR2"}, {Name: "VAR1"}, } - err := findDuplicatesInStage(variablesSpec, "test-stage") + err := findDuplicatesInStage(variablesSpec, "test-stage", "") assert.NotNil(t, err, "Expected an error when duplicates are present") assert.Contains(t, err.Error(), "duplicate variable names found in 'test-stage' stage: VAR1") } func TestFindDuplicatesInStage_EmptyVariablesSpec(t *testing.T) { variablesSpec := []spec.VariableSpec{} - err := findDuplicatesInStage(variablesSpec, "test-stage") + err := findDuplicatesInStage(variablesSpec, "test-stage", "") assert.Nil(t, err, "Expected no error when variablesSpec is empty") } @@ -39,6 +40,79 @@ func TestFindDuplicatesInStage_SingleVariable(t *testing.T) { variablesSpec := []spec.VariableSpec{ {Name: "VAR1"}, } - err := findDuplicatesInStage(variablesSpec, "test-stage") + err := findDuplicatesInStage(variablesSpec, "test-stage", "") assert.Nil(t, err, "Expected no error when there is only one variable") } + +// TestGetVariables_Success tests the GetVariables function for successful variable retrieval. +func TestGetVariables_Success(t *testing.T) { + variablesSpec := []spec.VariableSpec{ + {Name: "VAR1", Value: "value1", Stage: "stage1"}, + {Name: "VAR2", Value: "value2", Stage: "stage1"}, + {Name: "VAR3", Value: "value3", Stage: "stage2"}, + } + + variablesProvider := spec.VariablesProvider{} + variablesMap, err := variablesProvider.GetVariables(variablesSpec) + + assert.Nil(t, err, "Expected no error when retrieving variables") + assert.Equal(t, "value1", variablesMap["stage1"]["VAR1"]) + assert.Equal(t, "value2", variablesMap["stage1"]["VAR2"]) + assert.Equal(t, "value3", variablesMap["stage2"]["VAR3"]) +} + +// TestGetVariables_MissingPrivateKey tests the GetVariables function when a private key is required but missing. +func TestGetVariables_MissingPrivateKey(t *testing.T) { + variablesSpec := []spec.VariableSpec{ + {Name: "VAR1", Value: "encryptedValue", IsSecret: true, Stage: "stage1"}, + } + + variablesProvider := spec.VariablesProvider{} + _, err := variablesProvider.GetVariables(variablesSpec) + + assert.NotNil(t, err, "Expected an error when private key is missing") + assert.Contains(t, err.Error(), "digger private key not supplied, unable to decrypt secrets") +} + +// TestGetVariables_DecryptError tests the GetVariables function when decryption fails. +func TestGetVariables_DecryptError(t *testing.T) { + os.Setenv("DIGGER_PRIVATE_KEY", "invalidPrivateKey") + defer os.Unsetenv("DIGGER_PRIVATE_KEY") + + variablesSpec := []spec.VariableSpec{ + {Name: "VAR1", Value: "encryptedValue", IsSecret: true, Stage: "stage1"}, + } + + variablesProvider := spec.VariablesProvider{} + _, err := variablesProvider.GetVariables(variablesSpec) + + assert.NotNil(t, err, "Expected an error when decryption fails") + assert.Contains(t, err.Error(), "could not decrypt value using private key") +} + +// TestGetVariables_InterpolatedVariables tests the GetVariables function for interpolated variables. +func TestGetVariables_InterpolatedVariables(t *testing.T) { + os.Setenv("INTERPOLATED_VAR", "interpolatedValue") + defer os.Unsetenv("INTERPOLATED_VAR") + + variablesSpec := []spec.VariableSpec{ + {Name: "VAR1", Value: "INTERPOLATED_VAR", IsInterpolated: true, Stage: "stage1"}, + } + + variablesProvider := spec.VariablesProvider{} + variablesMap, err := variablesProvider.GetVariables(variablesSpec) + + assert.Nil(t, err, "Expected no error when retrieving interpolated variables") + assert.Equal(t, "interpolatedValue", variablesMap["stage1"]["VAR1"]) +} + +// TestGetVariables_EmptyVariables tests the GetVariables function with an empty variables spec. +func TestGetVariables_EmptyVariables(t *testing.T) { + variablesSpec := []spec.VariableSpec{} + + variablesProvider := spec.VariablesProvider{} + variablesMap, err := variablesProvider.GetVariables(variablesSpec) + + assert.Nil(t, err, "Expected no error when variablesSpec is empty") + assert.Empty(t, variablesMap, "Expected variablesMap to be empty") +} diff --git a/cli/pkg/spec/spec.go b/cli/pkg/spec/spec.go index b5a00a4be..1eff199c1 100644 --- a/cli/pkg/spec/spec.go +++ b/cli/pkg/spec/spec.go @@ -133,10 +133,15 @@ func RunSpec( } // Merge variables for each stage into the job's environment variables - for _, vars := range variablesMap { - job.StateEnvVars = lo.Assign(job.StateEnvVars, vars) - job.CommandEnvVars = lo.Assign(job.CommandEnvVars, vars) - job.RunEnvVars = lo.Assign(job.RunEnvVars, vars) + // Each stage has its own set of variables + if stateVars, ok := variablesMap["state"]; ok { + job.StateEnvVars = lo.Assign(job.StateEnvVars, stateVars) + } + if commandVars, ok := variablesMap["commands"]; ok { + job.CommandEnvVars = lo.Assign(job.CommandEnvVars, commandVars) + } + if runVars, ok := variablesMap["run"]; ok { + job.RunEnvVars = lo.Assign(job.RunEnvVars, runVars) } jobs := []scheduler.Job{job} diff --git a/libs/spec/variables_provider.go b/libs/spec/variables_provider.go index 27ee21161..6a626e1b1 100644 --- a/libs/spec/variables_provider.go +++ b/libs/spec/variables_provider.go @@ -14,6 +14,9 @@ func (p VariablesProvider) GetVariables(variables []VariableSpec) (map[string]ma // Group variables by their stage stagedVariables := lo.GroupBy(variables, func(variable VariableSpec) string { + if variable.Stage == "" { + return "default" + } return variable.Stage })