From 0a2036cbd7cc938ecf1257b6bcb50c2bd25bdba1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 21 Apr 2025 13:15:47 +1200 Subject: [PATCH] Add install option --- src/autocomplete.hpp | 14 ++ src/dropshell-completion.bash | 14 +- src/dropshell.hpp | 2 - src/main.cpp | 237 ++++++++++++++++++++-------------- src/server_service.cpp | 2 +- 5 files changed, 162 insertions(+), 107 deletions(-) create mode 100644 src/autocomplete.hpp diff --git a/src/autocomplete.hpp b/src/autocomplete.hpp new file mode 100644 index 0000000..591f6ab --- /dev/null +++ b/src/autocomplete.hpp @@ -0,0 +1,14 @@ +#ifndef __AUTOCOMPLETE_H +#define __AUTOCOMPLETE_H + +#include +#include + +namespace dropshell { + +std::vector autocomplete_list_servers(); +std::vector autocomplete_list_services(const std::string& server_name); + +} // namespace dropshell + +#endif // __AUTOCOMPLETE_H diff --git a/src/dropshell-completion.bash b/src/dropshell-completion.bash index 3173025..e324c2d 100755 --- a/src/dropshell-completion.bash +++ b/src/dropshell-completion.bash @@ -7,7 +7,7 @@ _dropshell_completions() { prev="${COMP_WORDS[COMP_CWORD-1]}" # List of main commands - opts="help version status servers templates autocomplete_list_servers autocomplete_list_services run" + opts="help version status servers templates autocomplete_list_servers autocomplete_list_services run install" # If we're completing the first argument, show all commands if [[ ${COMP_CWORD} -eq 1 ]] ; then @@ -37,23 +37,23 @@ _dropshell_completions() { COMPREPLY=( $(compgen -W "${servers[*]}" -- ${cur}) ) return 0 ;; - run) - # First argument after run is server name + run|install) + # First argument after run/install is server name local servers=($(dropshell autocomplete_list_servers)) COMPREPLY=( $(compgen -W "${servers[*]}" -- ${cur}) ) return 0 ;; *) - # Handle completion for service names and commands after run - if [[ ${COMP_CWORD} -ge 2 ]] && [[ "${COMP_WORDS[1]}" == "run" ]]; then + # Handle completion for service names and commands after run/install + if [[ ${COMP_CWORD} -ge 2 ]] && [[ "${COMP_WORDS[1]}" == "run" || "${COMP_WORDS[1]}" == "install" ]]; then if [[ ${COMP_CWORD} -eq 3 ]]; then # Second argument is service name local server_name="${COMP_WORDS[2]}" local services=($(dropshell autocomplete_list_services "$server_name")) COMPREPLY=( $(compgen -W "${services[*]}" -- ${cur}) ) return 0 - elif [[ ${COMP_CWORD} -eq 4 ]]; then - # Third argument is command name + elif [[ ${COMP_CWORD} -eq 4 && "${COMP_WORDS[1]}" == "run" ]]; then + # Third argument is command name (only for run command) # For now, we'll just complete with common commands local common_commands="status start stop update backup" COMPREPLY=( $(compgen -W "${common_commands}" -- ${cur}) ) diff --git a/src/dropshell.hpp b/src/dropshell.hpp index f49463d..13215cf 100644 --- a/src/dropshell.hpp +++ b/src/dropshell.hpp @@ -31,7 +31,5 @@ void show_server_details(const std::string& server_name); // Utility functions std::vector get_configured_servers(); -std::vector autocomplete_list_servers(); -std::vector autocomplete_list_services(const std::string& server_name); } // namespace dropshell \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 53b5950..6f7187b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,128 +1,171 @@ #include "dropshell.hpp" +#include "server_service.hpp" +#include "autocomplete.hpp" #include "init_user_directory.hpp" #include "config.hpp" #include -#include -#include +#include +#include -namespace po = boost::program_options; -namespace fs = boost::filesystem; +namespace dropshell { + +void print_help() { + std::cout << "Usage: dropshell [OPTIONS] COMMAND [ARGS]" << std::endl; + std::cout << std::endl; + std::cout << "A tool for managing server configurations" << std::endl; + std::cout << std::endl; + std::cout << "Commands:" << std::endl; + std::cout << " help Show this help message" << std::endl; + std::cout << " version Show version information" << std::endl; + std::cout << " init DIR Initialize the user directory for server configurations" << std::endl; + std::cout << std::endl; + std::cout << " status Check system status" << std::endl; + std::cout << " servers List configured servers" << std::endl; + std::cout << " servers NAME Show details for specific server" << std::endl; + std::cout << " templates List available templates" << std::endl; + std::cout << " install SERVER SERVICE Install a service on a server" << std::endl; + std::cout << " run SERVER SERVICE COMMAND Run a command on a specific service" << std::endl; + std::cout << std::endl; + std::cout << "Examples:" << std::endl; + std::cout << " dropshell servers" << std::endl; + std::cout << " dropshell servers myserver" << std::endl; + std::cout << " dropshell init /path/to/directory" << std::endl; + std::cout << " dropshell templates" << std::endl; + std::cout << " dropshell install myserver myservice" << std::endl; + std::cout << " dropshell run myserver myservice status" << std::endl; +} + +} // namespace dropshell int main(int argc, char* argv[]) { try { - // Define command line options - po::options_description desc("Usage: dropshell [options]"); - desc.add_options() - ("help,h", "Show help message") - ("version,V", "Show version information") - ("command", po::value(), "Command to execute") - ("directory", po::value(), "Directory path for init command") - ("verbose,v", "Enable verbose output"); - - po::positional_options_description p; - p.add("command", 1); - p.add("directory", 1); // Add directory as a positional argument - - po::variables_map vm; - po::store(po::command_line_parser(argc, argv) - .options(desc) - .positional(p) - .run(), vm); - po::notify(vm); - // Load configuration if (!dropshell::load_config()) { std::cerr << "Error: Failed to load configuration" << std::endl; return 1; } + // No arguments provided + if (argc < 2) { + dropshell::print_help(); + return 1; + } + // Handle commands - if (vm.count("help") || (vm.count("command") && vm["command"].as() == "help")) { - dropshell::print_help(desc); + std::string cmd = argv[1]; + + if (cmd == "help" || cmd == "-h" || cmd == "--help") { + dropshell::print_help(); return 0; } - if (vm.count("version") || (vm.count("command") && vm["command"].as() == "version")) { + if (cmd == "version" || cmd == "-V" || cmd == "--version") { dropshell::print_version(); return 0; } - if (vm.count("command")) { - std::string cmd = vm["command"].as(); - if (cmd == "status") { - dropshell::check_status(); - return 0; - } else if (cmd == "servers") { - if (argc > 2) { - // Show details for specific server - dropshell::show_server_details(argv[2]); - } else { - // List all servers - dropshell::list_servers(); - } - return 0; - } else if (cmd == "templates") { - dropshell::list_templates(); - return 0; - } else if (cmd == "init") { - if (!vm.count("directory")) { - std::cerr << "Error: init command requires a directory argument" << std::endl; - return 1; - } - try { - dropshell::init_user_directory(vm["directory"].as()); - return 0; - } catch (const std::exception& e) { - std::cerr << "Error: " << e.what() << std::endl; - return 1; - } - } else if (cmd == "autocomplete_list_servers") { - auto servers = dropshell::autocomplete_list_servers(); - for (const auto& server : servers) { - std::cout << server << std::endl; - } - return 0; - } else if (cmd == "autocomplete_list_services") { - if (argc < 3) { - std::cerr << "Error: autocomplete_list_services requires a server name" << std::endl; - return 1; - } - auto services = dropshell::autocomplete_list_services(argv[2]); - for (const auto& service : services) { - std::cout << service << std::endl; - } - return 0; - } else if (cmd == "run") { - if (argc < 5) { - std::cerr << "Error: run command requires server name, service name, and command" << std::endl; - return 1; - } - std::string server_name = argv[2]; - std::string service_name = argv[3]; - std::string command = argv[4]; - - dropshell::server_service service; - if (!service.init(server_name, service_name)) { - std::cerr << "Error: Failed to initialize service" << std::endl; - return 1; - } - - if (!service.run_command(command)) { - std::cerr << "Error: Failed to run command" << std::endl; - return 1; - } - return 0; + if (cmd == "status") { + dropshell::check_status(); + return 0; + } + + if (cmd == "servers") { + if (argc > 2) { + // Show details for specific server + dropshell::show_server_details(argv[2]); } else { - std::cerr << "Error: Unknown command '" << cmd << "'" << std::endl; - dropshell::print_help(desc); + // List all servers + dropshell::list_servers(); + } + return 0; + } + + if (cmd == "templates") { + dropshell::list_templates(); + return 0; + } + + if (cmd == "init") { + if (argc < 3) { + std::cerr << "Error: init command requires a directory argument" << std::endl; + return 1; + } + try { + dropshell::init_user_directory(argv[2]); + return 0; + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; return 1; } } - // No command provided - std::cerr << "Error: No command provided" << std::endl; - dropshell::print_help(desc); + if (cmd == "autocomplete_list_servers") { + auto servers = dropshell::autocomplete_list_servers(); + for (const auto& server : servers) { + std::cout << server << std::endl; + } + return 0; + } + + if (cmd == "autocomplete_list_services") { + if (argc < 3) { + std::cerr << "Error: autocomplete_list_services requires a server name" << std::endl; + return 1; + } + auto services = dropshell::autocomplete_list_services(argv[2]); + for (const auto& service : services) { + std::cout << service << std::endl; + } + return 0; + } + + if (cmd == "install") { + if (argc < 4) { + std::cerr << "Error: install command requires server name and service name" << std::endl; + return 1; + } + std::string server_name = argv[2]; + std::string service_name = argv[3]; + + dropshell::server_service service; + if (!service.init(server_name, service_name)) { + std::cerr << "Error: Failed to initialize service" << std::endl; + return 1; + } + + if (!service.install()) { + std::cerr << "Error: Failed to install service" << std::endl; + return 1; + } + return 0; + } + + if (cmd == "run") { + if (argc < 5) { + std::cerr << "Error: run command requires server name, service name, and command" << std::endl; + return 1; + } + std::string server_name = argv[2]; + std::string service_name = argv[3]; + std::string command = argv[4]; + + dropshell::server_service service; + if (!service.init(server_name, service_name)) { + std::cerr << "Error: Failed to initialize service" << std::endl; + return 1; + } + + if (!service.run_command(command)) { + std::cerr << "Error: Failed to run command" << std::endl; + return 1; + } + return 0; + } + + // Unknown command + std::cerr << "Error: Unknown command '" << cmd << "'" << std::endl; + dropshell::print_help(); return 1; } catch (const std::exception& e) { diff --git a/src/server_service.cpp b/src/server_service.cpp index 1120e9d..5c38e75 100644 --- a/src/server_service.cpp +++ b/src/server_service.cpp @@ -158,7 +158,7 @@ bool server_service::run_command(const std::string& command) { // Check if service directory exists std::string check_dir_cmd = ssh_cmd.str() + "'test -d " + service_dir + "'"; if (system(check_dir_cmd.c_str()) != 0) { - std::cerr << "Error: Service directory not found on server" << std::endl; + std::cerr << "Error: Service directory not found on server - has it been installed?" << std::endl; return false; }