From d070baed0ab240d3de1f56102581dd39f4f15467 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 10 May 2025 12:51:19 +1200 Subject: [PATCH] . --- runner/README.md | 1 + runner/examples/ssh_working_dir.json | 1 + runner/examples/working_dir.json | 1 + runner/include/runner.h | 6 ++++ runner/src/runner.cpp | 40 ++++++++++++++++++++++---- runner/test.sh | 42 ++++++++++++++++++++++++---- 6 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 runner/examples/ssh_working_dir.json create mode 100644 runner/examples/working_dir.json diff --git a/runner/README.md b/runner/README.md index 8312ec7..4a1c8f6 100644 --- a/runner/README.md +++ b/runner/README.md @@ -21,6 +21,7 @@ The c++ library used, which is contained in this codebase, has two simple functi "user": "username", // Username for SSH connection "key": "path/to/key" // Path to SSH key file or "auto" to use current user's key }, + "working_directory": "/path", // Optional: Directory to change to before executing the command "env": { // Optional: Environment variables "VAR1": "value1", "VAR2": "value2" diff --git a/runner/examples/ssh_working_dir.json b/runner/examples/ssh_working_dir.json new file mode 100644 index 0000000..c62f88e --- /dev/null +++ b/runner/examples/ssh_working_dir.json @@ -0,0 +1 @@ +{"ssh":{"host":"localhost","user":"USERNAME","key":"auto"},"command":"pwd","working_directory":"/tmp"} \ No newline at end of file diff --git a/runner/examples/working_dir.json b/runner/examples/working_dir.json new file mode 100644 index 0000000..89902f0 --- /dev/null +++ b/runner/examples/working_dir.json @@ -0,0 +1 @@ +{"command":"pwd","working_directory":"/tmp"} \ No newline at end of file diff --git a/runner/include/runner.h b/runner/include/runner.h index 73158b2..17b05b6 100644 --- a/runner/include/runner.h +++ b/runner/include/runner.h @@ -33,6 +33,7 @@ private: * Execute a command locally * @param command Command to execute * @param args Command arguments + * @param working_dir Working directory to change to before execution * @param env Environment variables * @param silent Whether to suppress output * @param interactive Whether to enable interactive mode @@ -43,6 +44,7 @@ private: static int execute_local( const std::string& command, const std::vector& args, + const std::string& working_dir, const std::map& env, bool silent, bool interactive, @@ -55,6 +57,7 @@ private: * @param ssh_config SSH configuration * @param command Command to execute * @param args Command arguments + * @param working_dir Working directory to change to on the remote server * @param env Environment variables * @param silent Whether to suppress output * @param interactive Whether to enable interactive mode @@ -66,6 +69,7 @@ private: const nlohmann::json& ssh_config, const std::string& command, const std::vector& args, + const std::string& working_dir, const std::map& env, bool silent, bool interactive, @@ -78,6 +82,7 @@ private: * @param run_json JSON specification * @param command Output parameter for command * @param args Output parameter for command arguments + * @param working_dir Output parameter for working directory * @param env Output parameter for environment variables * @param silent Output parameter for silent option * @param interactive Output parameter for interactive option @@ -87,6 +92,7 @@ private: const nlohmann::json& run_json, std::string& command, std::vector& args, + std::string& working_dir, std::map& env, bool& silent, bool& interactive diff --git a/runner/src/runner.cpp b/runner/src/runner.cpp index 8222651..d041f4a 100644 --- a/runner/src/runner.cpp +++ b/runner/src/runner.cpp @@ -17,18 +17,19 @@ namespace dropshell { bool Runner::run(const nlohmann::json& run_json) { std::string command; std::vector args; + std::string working_dir; std::map env; bool silent, interactive; - if (!parse_json(run_json, command, args, env, silent, interactive)) { + if (!parse_json(run_json, command, args, working_dir, env, silent, interactive)) { return false; } int exit_code; if (run_json.contains("ssh")) { - exit_code = execute_ssh(run_json["ssh"], command, args, env, silent, interactive); + exit_code = execute_ssh(run_json["ssh"], command, args, working_dir, env, silent, interactive); } else { - exit_code = execute_local(command, args, env, silent, interactive); + exit_code = execute_local(command, args, working_dir, env, silent, interactive); } return exit_code == 0; @@ -37,18 +38,19 @@ bool Runner::run(const nlohmann::json& run_json) { bool Runner::run(const nlohmann::json& run_json, std::string& output) { std::string command; std::vector args; + std::string working_dir; std::map env; bool silent, interactive; - if (!parse_json(run_json, command, args, env, silent, interactive)) { + if (!parse_json(run_json, command, args, working_dir, env, silent, interactive)) { return false; } int exit_code; if (run_json.contains("ssh")) { - exit_code = execute_ssh(run_json["ssh"], command, args, env, silent, interactive, &output, true); + exit_code = execute_ssh(run_json["ssh"], command, args, working_dir, env, silent, interactive, &output, true); } else { - exit_code = execute_local(command, args, env, silent, interactive, &output, true); + exit_code = execute_local(command, args, working_dir, env, silent, interactive, &output, true); } return exit_code == 0; @@ -58,6 +60,7 @@ bool Runner::parse_json( const nlohmann::json& run_json, std::string& command, std::vector& args, + std::string& working_dir, std::map& env, bool& silent, bool& interactive @@ -86,6 +89,16 @@ bool Runner::parse_json( } } + // Working directory is optional + working_dir = ""; + if (run_json.contains("working_directory")) { + if (!run_json["working_directory"].is_string()) { + std::cerr << "Error: 'working_directory' field must be a string" << std::endl; + return false; + } + working_dir = run_json["working_directory"]; + } + // Environment variables are optional env.clear(); if (run_json.contains("env")) { @@ -138,6 +151,7 @@ bool Runner::parse_json( int Runner::execute_local( const std::string& command, const std::vector& args, + const std::string& working_dir, const std::map& env, bool silent, bool interactive, @@ -178,6 +192,14 @@ int Runner::execute_local( } } + // Change to working directory if specified + if (!working_dir.empty()) { + if (chdir(working_dir.c_str()) != 0) { + std::cerr << "Error changing to directory " << working_dir << ": " << strerror(errno) << std::endl; + exit(1); + } + } + // Set environment variables for (const auto& [key, value] : env) { setenv(key.c_str(), value.c_str(), 1); @@ -267,6 +289,7 @@ int Runner::execute_ssh( const nlohmann::json& ssh_config, const std::string& command, const std::vector& args, + const std::string& working_dir, const std::map& env, bool silent, bool interactive, @@ -369,6 +392,11 @@ int Runner::execute_ssh( cmd_stream << "export " << key << "=\"" << value << "\"; "; } + // Add cd command if working_directory is specified + if (!working_dir.empty()) { + cmd_stream << "cd \"" << working_dir << "\" && "; + } + // Add command and args cmd_stream << command; for (const auto& arg : args) { diff --git a/runner/test.sh b/runner/test.sh index c20f2df..fc4e29f 100755 --- a/runner/test.sh +++ b/runner/test.sh @@ -74,26 +74,58 @@ else fi echo +# Test working directory feature +echo "Test 5: Working directory" +JSON='{"command":"pwd","working_directory":"/tmp"}' +BASE64=$(echo -n "$JSON" | base64) +echo "JSON: $JSON" +echo "Running command (should show /tmp)..." +OUTPUT=$(build/runner "$BASE64") +if [ $? -eq 0 ] && [[ "$OUTPUT" == "/tmp"* ]]; then + echo "✅ Test 5 passed (command ran in the correct directory)" +else + echo "❌ Test 5 failed, unexpected output: '$OUTPUT'" + exit 1 +fi +echo + # Optional SSH test (disabled by default) # Set ENABLE_SSH_TEST=1 to enable this test if [ "${ENABLE_SSH_TEST}" = "1" ]; then - echo "Test 5: SSH to localhost (make sure SSH server is running and you can connect to localhost)" + echo "Test 6: SSH to localhost (make sure SSH server is running and you can connect to localhost)" if [ -f "examples/local_ssh.json" ]; then echo "Using examples/local_ssh.json for the test..." ./run.sh examples/local_ssh.json || { - echo "❌ Test 5 failed" + echo "❌ Test 6 failed" echo "Note: This test requires SSH server running on localhost and proper key-based authentication." echo "Check your SSH configuration or disable this test." exit 1 } - echo "✅ Test 5 passed" + echo "✅ Test 6 passed" else - echo "❌ Test 5 failed: examples/local_ssh.json not found" + echo "❌ Test 6 failed: examples/local_ssh.json not found" + exit 1 + fi + echo + + echo "Test 7: SSH with working directory (make sure SSH server is running and you can connect to localhost)" + if [ -f "examples/ssh_working_dir.json" ]; then + echo "Using examples/ssh_working_dir.json for the test..." + OUTPUT=$(./run.sh examples/ssh_working_dir.json | grep "/tmp") + if [[ "$OUTPUT" == *"/tmp"* ]]; then + echo "✅ Test 7 passed (SSH command ran in the correct directory)" + else + echo "❌ Test 7 failed, directory was not changed correctly" + echo "Note: This test requires SSH server running on localhost and proper key-based authentication." + exit 1 + fi + else + echo "❌ Test 7 failed: examples/ssh_working_dir.json not found" exit 1 fi echo else - echo "Test 5: SSH test disabled (set ENABLE_SSH_TEST=1 to enable)" + echo "Test 6-7: SSH tests disabled (set ENABLE_SSH_TEST=1 to enable)" echo fi