Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 19 additions & 27 deletions internal/config/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,20 @@ var varExprPattern = regexp.MustCompile(`\$\{([A-Za-z_][A-Za-z0-9_]*)\}`)

var logValidation = logger.New("config:validation")

// expandVariables expands variable expressions in a string
// Returns the expanded string and error if any variable is undefined
func expandVariables(value, jsonPath string) (string, error) {
logValidation.Printf("Expanding variables: jsonPath=%s", jsonPath)
// expandVariablesCore is the shared implementation for variable expansion.
// It works with byte slices and handles the core expansion logic, tracking undefined variables.
// This eliminates code duplication between expandVariables and ExpandRawJSONVariables.
func expandVariablesCore(data []byte, contextDesc string) ([]byte, []string, error) {
logValidation.Printf("Expanding variables: context=%s", contextDesc)
Comment on lines +20 to +24
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expandVariablesCore returns an error but currently always returns nil, and both callers explicitly ignore it (_, _ := ...). Consider removing the error return from expandVariablesCore (and adjusting callers), or if you anticipate future error paths, handle/propagate it now to avoid dead code and underscore-ignored values.

Copilot uses AI. Check for mistakes.
var undefinedVars []string

result := varExprPattern.ReplaceAllStringFunc(value, func(match string) string {
result := varExprPattern.ReplaceAllFunc(data, func(match []byte) []byte {
// Extract variable name (remove ${ and })
varName := match[2 : len(match)-1]
varName := string(match[2 : len(match)-1])

if envValue, exists := os.LookupEnv(varName); exists {
logValidation.Printf("Expanded variable: %s (found in environment)", varName)
return envValue
return []byte(envValue)
}

// Track undefined variable
Expand All @@ -38,43 +39,34 @@ func expandVariables(value, jsonPath string) (string, error) {
return match // Keep original if undefined
Comment on lines 31 to 39
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The per-variable log lines in expandVariablesCore (expanded/undefined variable) don't include contextDesc, so logs from string expansion vs raw JSON expansion can be hard to attribute when both paths run. Consider including the context in these log messages or reducing per-variable logging to avoid noisy logs in normal validation flows.

See below for a potential fix:

			logValidation.Printf("Expanded variable: %s (found in environment) context=%s", varName, contextDesc)
			return []byte(envValue)
		}

		// Track undefined variable
		undefinedVars = append(undefinedVars, varName)
		logValidation.Printf("Undefined variable: %s context=%s", varName, contextDesc)

Copilot uses AI. Check for mistakes.
})

logValidation.Printf("Variable expansion completed: context=%s, undefined_count=%d", contextDesc, len(undefinedVars))
return result, undefinedVars, nil
}

// expandVariables expands variable expressions in a string
// Returns the expanded string and error if any variable is undefined
func expandVariables(value, jsonPath string) (string, error) {
result, undefinedVars, _ := expandVariablesCore([]byte(value), fmt.Sprintf("jsonPath=%s", jsonPath))

if len(undefinedVars) > 0 {
logValidation.Printf("Variable expansion failed: undefined variables=%v", undefinedVars)
return "", rules.UndefinedVariable(undefinedVars[0], jsonPath)
}

logValidation.Print("Variable expansion completed successfully")
return result, nil
return string(result), nil
}

// ExpandRawJSONVariables expands all ${VAR} expressions in JSON data before schema validation.
// This ensures the schema validates the expanded values, not the variable syntax.
// It collects all undefined variables and reports them in a single error.
func ExpandRawJSONVariables(data []byte) ([]byte, error) {
logValidation.Print("Expanding variables in raw JSON data")
var undefinedVars []string

result := varExprPattern.ReplaceAllFunc(data, func(match []byte) []byte {
// Extract variable name (remove ${ and })
varName := string(match[2 : len(match)-1])

if envValue, exists := os.LookupEnv(varName); exists {
logValidation.Printf("Expanded variable in JSON: %s", varName)
return []byte(envValue)
}

// Track undefined variable
undefinedVars = append(undefinedVars, varName)
logValidation.Printf("Undefined variable in JSON: %s", varName)
return match // Keep original if undefined
})
result, undefinedVars, _ := expandVariablesCore(data, "raw JSON data")

if len(undefinedVars) > 0 {
logValidation.Printf("Variable expansion failed: undefined variables=%v", undefinedVars)
return nil, rules.UndefinedVariable(undefinedVars[0], "configuration")
}
Comment on lines 59 to 68
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment says ExpandRawJSONVariables "collects all undefined variables and reports them in a single error", but the implementation only returns rules.UndefinedVariable(undefinedVars[0], ...) (first undefined var). Either update the comment to match the behavior, or update the error construction to include all undefined variable names in the reported error message/suggestion.

Copilot uses AI. Check for mistakes.

logValidation.Print("Raw JSON variable expansion completed")
return result, nil
}

Expand Down