# Module: Environment Detection & Management # Purpose: Detects current environment (dev/prod/test) and applies environment-specific configuration overrides. # Dependencies: None (core functions) # Environment Detection and Configuration Functions # Handles environment detection, validation, and environment-specific overrides # Detect current environment from various sources export def detect-current-environment [] { # Priority order for environment detection: # 1. PROVISIONING_ENV environment variable # 2. Environment-specific markers # 3. Directory-based detection # 4. Default fallback # Check explicit environment variable if ($env.PROVISIONING_ENV? | is-not-empty) { return $env.PROVISIONING_ENV } # Check CI/CD environments if ($env.CI? | is-not-empty) { if ($env.GITHUB_ACTIONS? | is-not-empty) { return "ci" } if ($env.GITLAB_CI? | is-not-empty) { return "ci" } if ($env.JENKINS_URL? | is-not-empty) { return "ci" } return "test" # Default for CI environments } # Check for development indicators if (($env.PWD | path join ".git" | path exists) or ($env.PWD | path join "development" | path exists) or ($env.PWD | path join "dev" | path exists)) { return "dev" } # Check for production indicators if (($env.HOSTNAME? | default "" | str contains "prod") or ($env.NODE_ENV? | default "" | str downcase) == "production" or ($env.ENVIRONMENT? | default "" | str downcase) == "production") { return "prod" } # Check for test indicators if (($env.NODE_ENV? | default "" | str downcase) == "test" or ($env.ENVIRONMENT? | default "" | str downcase) == "test") { return "test" } # Default to development for interactive usage if ($env.TERM? | is-not-empty) { return "dev" } # Fallback return "dev" } # Get available environments from configuration export def get-available-environments [ config: record ] { let environments_section = ($config | get -o "environments" | default {}) $environments_section | columns } # Validate environment name export def validate-environment [ environment: string config: record ] { let valid_environments = ["dev" "test" "prod" "ci" "staging" "local"] let configured_environments = (get-available-environments $config) let all_valid = ($valid_environments | append $configured_environments | uniq) if ($environment in $all_valid) { { valid: true, message: "" } } else { { valid: false, message: $"Invalid environment '($environment)'. Valid options: ($all_valid | str join ', ')" } } } # Apply environment variable overrides to configuration export def apply-environment-variable-overrides [ config: record debug = false ] { mut result = $config # Map of environment variables to config paths with type conversion let env_mappings = { "PROVISIONING_DEBUG": { path: "debug.enabled", type: "bool" }, "PROVISIONING_LOG_LEVEL": { path: "debug.log_level", type: "string" }, "PROVISIONING_NO_TERMINAL": { path: "debug.no_terminal", type: "bool" }, "PROVISIONING_CHECK": { path: "debug.check", type: "bool" }, "PROVISIONING_METADATA": { path: "debug.metadata", type: "bool" }, "PROVISIONING_OUTPUT_FORMAT": { path: "output.format", type: "string" }, "PROVISIONING_FILE_VIEWER": { path: "output.file_viewer", type: "string" }, "PROVISIONING_USE_SOPS": { path: "sops.use_sops", type: "bool" }, "PROVISIONING_PROVIDER": { path: "providers.default", type: "string" }, "PROVISIONING_WORKSPACE_PATH": { path: "paths.workspace", type: "string" }, "PROVISIONING_INFRA_PATH": { path: "paths.infra", type: "string" }, "PROVISIONING_SOPS": { path: "sops.config_path", type: "string" }, "PROVISIONING_KAGE": { path: "sops.age_key_file", type: "string" } } for env_var in ($env_mappings | columns) { let env_value = ($env | get -o $env_var | default null) if ($env_value | is-not-empty) { let mapping = ($env_mappings | get $env_var) let config_path = $mapping.path let config_type = $mapping.type # Convert value to appropriate type let converted_value = match $config_type { "bool" => { if ($env_value | describe) == "string" { match ($env_value | str downcase) { "true" | "1" | "yes" | "on" => true "false" | "0" | "no" | "off" => false _ => false } } else { $env_value | into bool } } "string" => $env_value _ => $env_value } if $debug { # log debug $"Applying env override: ($env_var) -> ($config_path) = ($converted_value)" } $result = (set-config-value $result $config_path $converted_value) } } $result } # Helper function to set nested config value using dot notation def set-config-value [ config: record path: string value: any ] { let path_parts = ($path | split row ".") mut current = $config mut result = $current # Navigate to parent of target # Use drop instead of range for Nushell 0.109+ compatibility let parent_parts = ($path_parts | drop) let leaf_key = ($path_parts | last) for part in $parent_parts { # Use upsert instead of insert to avoid column_already_exists error if ($result | get -o $part) == null { $result = ($result | upsert $part {}) } $current = ($result | get $part) # Update parent in result would go here (mutable record limitation) } # Set the value at the leaf if ($parent_parts | length) == 0 { # Top level $result | upsert $leaf_key $value } else { # Need to navigate back and update # This is a simplified approach - for deep nesting, a more complex function would be needed $result | upsert $leaf_key $value } }