#include "env_validator.hpp" #include "envmanager.hpp" #include "ordered_env.hpp" #include "utils.hpp" #include "output.hpp" #include #include #include #include #include namespace dropshell { bool validate_and_fix_service_env( const std::string& template_service_env_path, const std::string& service_env_path, std::vector& missing_vars, std::vector& extra_vars, const std::string& template_info_env_path ) { missing_vars.clear(); extra_vars.clear(); // Load template service.env envmanager template_env(template_service_env_path); if (!template_env.load()) { error << "Failed to load template service.env from: " << template_service_env_path << std::endl; return false; } ordered_env_vars template_vars; template_env.get_all_variables(template_vars); // Create a set of template variable names for quick lookup std::set template_var_names; for (const auto& [key, value] : template_vars) { template_var_names.insert(key); } // Also load template_info.env if provided - these variables are allowed in service.env std::set template_info_var_names; if (!template_info_env_path.empty() && std::filesystem::exists(template_info_env_path)) { envmanager template_info_env(template_info_env_path); if (template_info_env.load()) { ordered_env_vars template_info_vars; template_info_env.get_all_variables(template_info_vars); for (const auto& [key, value] : template_info_vars) { template_info_var_names.insert(key); } } } // Load service service.env envmanager service_env(service_env_path); if (!service_env.load()) { error << "Failed to load service service.env from: " << service_env_path << std::endl; return false; } ordered_env_vars service_vars; service_env.get_all_variables(service_vars); // Create a set of service variable names for quick lookup std::set service_var_names; for (const auto& [key, value] : service_vars) { service_var_names.insert(key); } // Find missing variables (in template but not in service) for (const auto& template_var_name : template_var_names) { if (service_var_names.find(template_var_name) == service_var_names.end()) { missing_vars.push_back(template_var_name); } } // Find extra variables (in service but not in template or template_info) for (const auto& service_var_name : service_var_names) { if (template_var_names.find(service_var_name) == template_var_names.end() && template_info_var_names.find(service_var_name) == template_info_var_names.end()) { extra_vars.push_back(service_var_name); } } // If there are no differences, validation passes if (missing_vars.empty() && extra_vars.empty()) { return true; } // There are differences - need to fix the service.env file debug << "Fixing service.env file: " << service_env_path << std::endl; // Read the original file line by line std::ifstream infile(service_env_path); if (!infile.is_open()) { error << "Failed to open service.env for reading: " << service_env_path << std::endl; return false; } std::vector lines; std::string line; while (std::getline(infile, line)) { lines.push_back(line); } infile.close(); // Process lines and comment out extra variables std::set extra_vars_set(extra_vars.begin(), extra_vars.end()); for (auto& line : lines) { std::string trimmed = trim(line); // Skip empty lines and comments if (trimmed.empty() || trimmed[0] == '#') { continue; } // Check if this line defines a variable size_t pos = trimmed.find('='); if (pos != std::string::npos) { std::string key = trim(trimmed.substr(0, pos)); key = dequote(key); // If this is an extra variable, comment it out if (extra_vars_set.find(key) != extra_vars_set.end()) { // Only comment out if not already commented if (line[0] != '#') { line = "# " + line; } } } } // Add missing variables at the end if (!missing_vars.empty()) { lines.push_back(""); lines.push_back("# Variables added from template update:"); for (const auto& var_name : missing_vars) { // Get the value from the template std::string value = get_var(template_vars, var_name); lines.push_back(var_name + "=" + quote(value)); } } // Write the modified file back std::ofstream outfile(service_env_path); if (!outfile.is_open()) { error << "Failed to open service.env for writing: " << service_env_path << std::endl; return false; } for (const auto& l : lines) { outfile << l << std::endl; } outfile.close(); // Validation failed because we had to make changes return false; } } // namespace dropshell