Add overrides.env support for per-location service env overrides
All checks were successful
Build-Test-Publish / build (linux/amd64) (push) Successful in 48s
Build-Test-Publish / build (linux/arm64) (push) Successful in 3m44s

This commit is contained in:
j
2026-03-30 08:04:23 +13:00
parent bf9cdeccc7
commit 735e2f083a
7 changed files with 169 additions and 3 deletions

View File

@@ -7,6 +7,7 @@
#include <libassert/assert.hpp>
#include "utils/utils.hpp"
#include "utils/service_env_validator.hpp"
#include "utils/service_env_overrides.hpp"
#include "services.hpp"
#include <fstream>
@@ -230,6 +231,13 @@ namespace dropshell
return false;
}
// apply overrides from overrides.env (if present in server definition path)
{
auto changes = apply_overrides(server_name, service_name);
for (const auto& c : changes)
debug << "Override applied: " << c.key << "=" << c.new_val << std::endl;
}
// check docker.
if (service_info.requires_docker)
{

View File

@@ -8,7 +8,9 @@
#include "templates.hpp"
#include <unistd.h>
#include <termios.h>
#include <cstring>
#include <fstream>
#include <iostream>
#include <sstream>
#include <filesystem>
@@ -32,12 +34,13 @@ struct EditCommandRegister {
false, // requires_install
0, // min_args (after command)
2, // max_args (after command)
"edit [SERVER] [SERVICE]",
"Edit dropshell, server or service configuration",
"edit [SERVER] [SERVICE] | edit override",
"Edit dropshell, server, service, or override configuration",
// heredoc
R"(
Edit dropshell, server or service configuration.
edit edit the dropshell config.
edit override edit the overrides.env for a server location.
edit <server> edit the server config.
edit <server> <service> edit the service config.
)"
@@ -241,12 +244,70 @@ int edit_service_config(const std::string &server, const std::string &service)
return 0;
}
// ------------------------------------------------------------------------------------------------
// edit override
// ------------------------------------------------------------------------------------------------
int edit_override()
{
auto paths = gConfig().get_local_server_definition_paths();
if (paths.empty()) {
error << "No server definition paths configured." << std::endl;
return 1;
}
std::string chosen_path;
if (paths.size() == 1) {
chosen_path = paths[0];
} else {
// Multiple paths - ask user to choose with single keypress
info << "Multiple server locations found. Choose one:" << std::endl;
for (size_t i = 0; i < paths.size() && i < 9; ++i)
info << " " << (i + 1) << ") " << paths[i] << std::endl;
info << "Enter choice [1-" << std::min(paths.size(), (size_t)9) << "]: " << std::flush;
// Read single keypress
struct termios oldt, newt;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
int ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
info << (char)ch << std::endl;
int idx = ch - '1';
if (idx < 0 || idx >= (int)paths.size()) {
error << "Invalid choice." << std::endl;
return 1;
}
chosen_path = paths[idx];
}
std::string override_file = (std::filesystem::path(chosen_path) / filenames::overrides_env).string();
// Create with comment header if it doesn't exist
if (!std::filesystem::exists(override_file)) {
std::ofstream f(override_file);
f << "# Overrides for all services in this server location." << std::endl;
f << "# Variables set here will be forced into every service.env" << std::endl;
f << "# during create-service and install." << std::endl;
f << "#" << std::endl;
f << "# Format is the same as service.env:" << std::endl;
f << "# VARIABLE_NAME=\"value\"" << std::endl;
f << std::endl;
info << "Created new overrides file: " << override_file << std::endl;
}
return edit_file(override_file, true) ? 0 : 1;
}
// ------------------------------------------------------------------------------------------------
// edit command handler
// ------------------------------------------------------------------------------------------------
int edit_handler(const CommandContext& ctx) {
// edit dropshell config
if (ctx.args.size() < 1)
if (ctx.args.size() < 1)
{
if (ctx.command=="create-config")
{
@@ -258,6 +319,10 @@ int edit_handler(const CommandContext& ctx) {
return edit_config();
}
// edit override - check before server/service dispatch
if (safearg(ctx.args, 0) == "override")
return edit_override();
// edit server config
if (ctx.args.size() < 2) {
edit_server(safearg(ctx.args,0));

View File

@@ -10,6 +10,7 @@
#include "services.hpp"
#include "utils/output.hpp"
#include "utils/service_env_validator.hpp"
#include "utils/service_env_overrides.hpp"
#include <fstream>
#include <unistd.h>
@@ -147,6 +148,13 @@ namespace dropshell
}
}
// Apply overrides from overrides.env (if present in server definition path)
{
auto changes = apply_overrides(server, service);
for (const auto& c : changes)
warning << "Override: " << c.key << " changed from \"" << c.old_val << "\" to \"" << c.new_val << "\"" << std::endl;
}
// ── Stage new template + config to _staging folder on remote ──
std::string staging_path = remote_service_path + "/_staging";
std::string staging_template = staging_path + "/template";

View File

@@ -43,6 +43,14 @@ namespace dropshell
return localpath::agent_remote() + "/agent.hash";
}
std::string overrides_env_for_server(const std::string &server_name)
{
std::string serverpath = localpath::server(server_name);
if (serverpath.empty())
return "";
return (fs::path(get_parent(serverpath)) / filenames::overrides_env).string();
}
} // namespace localfile
std::string get_local_agent_hash()

View File

@@ -43,6 +43,7 @@ namespace dropshell {
static const std::string ds_run = "ds_run.sh";
static const std::string template_paths_json = "template_paths.json";
static const std::string dropshell_templates_list = "dropshell-templates.list";
static const std::string overrides_env = "overrides.env";
} // namespace filenames.
namespace localfile {
@@ -51,6 +52,7 @@ namespace dropshell {
std::string service_env(const std::string &server_name, const std::string &service_name);
std::string bb64();
std::string agent_hash(); // Returns path to agent.hash file
std::string overrides_env_for_server(const std::string &server_name); // overrides.env in server's definition path
} // namespace localfile
// Get the content of the local agent hash (empty string if not found)

View File

@@ -0,0 +1,51 @@
#include "service_env_overrides.hpp"
#include "directories.hpp"
#include "envmanager.hpp"
#include "service_env_validator.hpp"
#include <filesystem>
namespace dropshell {
std::vector<OverrideChange> apply_overrides(
const std::string& server_name,
const std::string& service_name)
{
std::vector<OverrideChange> changes;
std::string overrides_path = localfile::overrides_env_for_server(server_name);
if (overrides_path.empty() || !std::filesystem::exists(overrides_path))
return changes;
std::string service_env_path = localfile::service_env(server_name, service_name);
if (service_env_path.empty() || !std::filesystem::exists(service_env_path))
return changes;
// Load overrides
envmanager overrides(overrides_path);
if (!overrides.load())
return changes;
ordered_env_vars override_vars;
overrides.get_all_variables(override_vars);
if (override_vars.empty())
return changes;
// Load current service env
envmanager service_env(service_env_path);
if (!service_env.load())
return changes;
// Apply each override
for (const auto& [key, new_val] : override_vars) {
std::string current_val = service_env.get_variable(key);
if (current_val != new_val) {
set_env_variable(service_env_path, key, new_val);
changes.push_back({key, current_val, new_val});
}
}
return changes;
}
} // namespace dropshell

View File

@@ -0,0 +1,24 @@
#ifndef SERVICE_ENV_OVERRIDES_HPP
#define SERVICE_ENV_OVERRIDES_HPP
#include <string>
#include <vector>
namespace dropshell {
struct OverrideChange {
std::string key;
std::string old_val;
std::string new_val;
};
// Apply overrides from overrides.env (in the server's definition path) to a service's service.env.
// Returns a list of changes that were made.
// If no overrides.env exists, returns empty (no-op).
std::vector<OverrideChange> apply_overrides(
const std::string& server_name,
const std::string& service_name);
} // namespace dropshell
#endif // SERVICE_ENV_OVERRIDES_HPP