Add run-actions

Signed-off-by: Tiago Garcia <tiago.rgarcia@ua.pt>
This commit is contained in:
Tiago Garcia 2024-07-19 16:51:53 +01:00
parent 52ea711718
commit d0135da4fd
Signed by: TiagoRG
GPG Key ID: DFCD48E3F420DB42
5 changed files with 165 additions and 21 deletions

View File

@ -137,6 +137,33 @@ The configuration file must contain the `update-files` field, which is an object
}
```
### `/run-actions`
#### Webhook event: `push`
This endpoint allows the application to run specific actions when a push to a specific branch is made. This way, there's no need to manually run the actions on the server.
The configuration file must contain the `run-actions` field, which is an object with the following format:
```json
"run-actions": {
"owner/repo-name": {
"branch": "main",
"actions": [
{
"name": "action-name",
"command": "command-to-run",
"args": [
"arg1",
"arg2",
"..."
]
}
]
}
}
```
## License
This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details.

View File

@ -5,6 +5,6 @@
#include <crow/http_response.h>
#include <nlohmann/json.hpp>
crow::response run_scripts(const nlohmann::json &, const nlohmann::json &,const crow::request &);
crow::response run_actions(const nlohmann::json &, const nlohmann::json &,const crow::request &);
#endif

View File

@ -0,0 +1,128 @@
#include "endpoints/run-actions.hpp"
#include "logger.hpp"
crow::response run_actions(const nlohmann::json &run_actions, const nlohmann::json &tokens, const crow::request &req) {
nlohmann::json payload;
try {
payload = nlohmann::json::parse(req.body);
} catch (nlohmann::json::parse_error& e) {
Logger::error("[/run-actions] Error parsing payload: " + std::string(e.what()));
nlohmann::json response = {
{"status", 400},
{"error", "Error parsing payload"}
};
return crow::response(400, response.dump());
}
std::string ref;
std::string repo;
try {
ref = payload["ref"];
if (size_t last_slash = ref.find_last_of('/'); last_slash != std::string::npos && last_slash + 1 < ref.length())
ref = ref.substr(last_slash + 1);
repo = payload["repository"]["full_name"];
} catch (nlohmann::json::out_of_range& e) {
Logger::error("[/run-actions] Invalid JSON payload: " + std::string(e.what()));
nlohmann::json response = {
{"status", 400},
{"error", "Invalid JSON payload"}
};
return crow::response(400, response.dump());
}
Logger::info("[/run-actions] Received push to " + repo + ":" + ref);
if (run_actions.find(repo) == run_actions.end()) {
Logger::warn("[/run-actions] No run-actions webhook configuration for repo " + repo);
nlohmann::json response = {
{"status", 404},
{"error", "No run-actions webhook configuration for repo"}
};
return crow::response(404, response.dump());
}
Logger::info("[/run-actions] Found run-actions webhook configuration for repo " + repo);
nlohmann::json config;
std::string config_branch;
nlohmann::json config_actions;
try {
config = run_actions[repo];
config_branch = config["branch"];
config_actions = config["actions"];
} catch (nlohmann::json::out_of_range& e) {
Logger::error("[/run-actions] Invalid run-actions configuration for repo " + repo + ": " + std::string(e.what()));
nlohmann::json response = {
{"status", 500},
{"error", "Invalid run-actions configuration"}
};
return crow::response(500, response.dump());
}
if (ref != config_branch) {
Logger::info("[/run-actions] Ignoring push to " + repo + " on branch " + ref);
nlohmann::json response = {
{"status", 200},
{"message", "Ignoring push to " + repo + " on branch " + ref}
};
return crow::response(200, response.dump());
}
if (config_actions.empty()) {
Logger::info("[/run-actions] No actions configured for " + repo + " on branch " + ref);
nlohmann::json response = {
{"status", 200},
{"message", "No actions configured for " + repo + " on branch " + ref}
};
return crow::response(200, response.dump());
}
Logger::info("[/run-actions] Running actions for " + repo + " on branch " + ref);
nlohmann::json response = {
{"status", 200},
{"message", "OK"},
{"successful-actions", nlohmann::json::array()},
{"failed-actions", nlohmann::json::array()}
};
for (const auto &action : config_actions) {
std::string action_name;
std::string action_command;
nlohmann::json action_args;
try {
action_name = action["name"];
action_command = action["command"];
action_args = action["args"];
} catch (nlohmann::json::out_of_range& e) {
Logger::error("[/run-actions] Invalid action configuration for repo " + repo + ": " + std::string(e.what()));
nlohmann::json response = {
{"status", 500},
{"error", "Invalid action configuration"}
};
return crow::response(500, response.dump());
}
Logger::info("[/run-actions] Running action '" + action_name + "'");
std::string action_command_with_args = action_command;
for (const auto &arg : action_args) {
action_command_with_args += " " + arg.get<std::string>();
}
int ret = std::system(action_command_with_args.c_str());
if (ret == 0) {
Logger::info("[/run-actions] Action " + action_name + " completed successfully");
response["successful-actions"].push_back(action_name);
} else {
Logger::error("[/run-actions] Action " + action_name + " failed with exit code " + std::to_string(ret));
response["failed-actions"].push_back(action_name);
}
}
Logger::success("[/run-actions] Finished running actions for " + repo + " on branch " + ref);
return crow::response(200, response.dump());
}

View File

@ -1,11 +0,0 @@
#include "endpoints/run-scripts.hpp"
crow::response run_scripts(const nlohmann::json &run_scripts, const nlohmann::json &tokens, const crow::request &req) {
// TODO: Implement run_scripts
nlohmann::json response = {
{"status", 501},
{"error", "Not Implemented"}
};
return crow::response(501, response.dump());
}

View File

@ -1,6 +1,6 @@
#include "routes.hpp"
#include "endpoints/update-files.hpp"
#include "endpoints/run-scripts.hpp"
#include "endpoints/run-actions.hpp"
#include <crow/app.h>
#include "logger.hpp"
@ -8,7 +8,7 @@
Routes::Routes(nlohmann::json config) {
Logger::info("Partitioning configuration");
const nlohmann::json config_update_files = config["update-files"];
const nlohmann::json config_run_scripts = config["run-scripts"];
const nlohmann::json config_run_actions = config["run-actions"];
const nlohmann::json config_tokens = config["tokens"];
Logger::info("Registering route \"/\"");
@ -42,16 +42,16 @@ Routes::Routes(nlohmann::json config) {
});
}
if (!config_run_scripts.is_null()) {
Logger::info("Registering route \"/run-scripts\"");
CROW_ROUTE(this->app, "/run-scripts")
if (!config_run_actions.is_null()) {
Logger::info("Registering route \"/run-actions\"");
CROW_ROUTE(this->app, "/run-actions")
.methods("POST"_method)
.name("Run Scripts")
([&config_run_scripts, &config_tokens](const crow::request &req) {
.name("Run Actions")
([&config_run_actions, &config_tokens](const crow::request &req) {
try {
return run_scripts(config_run_scripts, config_tokens, req);
return run_actions(config_run_actions, config_tokens, req);
} catch (const std::exception &e) {
Logger::error("Unknown error in run_scripts: " + std::string(e.what()));
Logger::error("Unknown error in run_actions: " + std::string(e.what()));
nlohmann::json response = {
{"status", 500},
{"error", "Internal server error"}