Version 0.1 - Initial Version

- App working to update files on every push
- Using simple JSON config

Signed-off-by: TiagoRG <tiago.rgarcia@ua.pt>
This commit is contained in:
Tiago Garcia 2023-12-30 12:06:34 +00:00 committed by Tiago Garcia
parent 6fbf20ceaa
commit fcf2b2b879
Signed by: TiagoRG
GPG Key ID: DFCD48E3F420DB42
4 changed files with 164 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.cmake/
CMakeFiles/
CMakeCache.txt
Makefile
cmake_install.cmake
gh-wh-handler
config.json

6
CMakeLists.txt Normal file
View File

@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.27)
project(gh-wh-handler)
set(CMAKE_CXX_STANDARD 23)
add_executable(gh-wh-handler main.cpp)

View File

@ -1 +1,39 @@
# GitHub Webhook Handler
## Simple C++ WebAPI to work with GitHub Webhooks
Currently creating a local copy of remote files on every push
## Usage
Compile the application:
```console
cmake .
make
```
Run the application using your configuration file:
```console
./gh-wh-handler /path/to/config.json
```
## Config File
The configuration file should be a JSON file with the following format:
```json
{
"port": 8080,
"repos": {
"repo_full_name": {
"branch": "target_branch",
"files": {
"remote_path": "local_path",
}
}
},
"tokens": {
"repo_full_name": "github_token"
}
}
```

113
main.cpp Normal file
View File

@ -0,0 +1,113 @@
#include <cstdio>
#include <fstream>
#include <iostream>
#include <crow/app.h>
#include <nlohmann/json.hpp>
#include <string>
int main(int argc, char **argv) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <config_file>" << std::endl;
return 1;
}
std::ifstream config_file(argv[1]);
if (!config_file.is_open()) {
std::cerr << "Failed to open config.json" << std::endl;
return 1;
}
nlohmann::json config;
try {
config_file >> config;
} catch (const std::exception &e) {
std::cerr << "Failed to parse config.json: " << e.what() << std::endl;
return 1;
}
int port = config["port"];
nlohmann::json &repos = config["repos"];
nlohmann::json &tokens = config["tokens"];
crow::SimpleApp app;
CROW_ROUTE(app, "/github-webhook")
.methods("POST"_method)
([&repos, &tokens](const crow::request &req) {
try {
// Parse JSON payload from the request body
nlohmann::json payload = nlohmann::json::parse(req.body);
nlohmann::json response;
std::string ref = payload["ref"];
size_t last_slash = ref.find_last_of('/');
if (last_slash != std::string::npos && last_slash + 1 < ref.length())
ref = ref.substr(last_slash + 1);
std::string repo = payload["repository"]["full_name"];
bool is_private = payload["repository"]["private"];
if (is_private && tokens.find(repo) == tokens.end()) {
printf("No token configured for private repo %s\n", repo.c_str());
response["status"] = 403;
response["error"] = "No token configured for private repo";
return crow::response(403, response.dump());
}
std::string token = tokens[repo];
printf("Received push to %s:%s (private: %s)\n", repo.c_str(), ref.c_str(), is_private ? "true" : "false");
if (repos.find(repo) == repos.end()) {
printf("No webhook configured for %s\n", repo.c_str());
response["status"] = 404;
response["error"] = "No webhook configured for repo";
return crow::response(404, response.dump());
}
nlohmann::json repo_data;
bool is_valid_branch = false;
for (auto c_repo = repos.begin(); c_repo != repos.end(); ++c_repo) {
std::string c_repo_name = c_repo.key();
if (c_repo_name != repo) continue;
if (c_repo.value()["branch"] != ref) continue;
is_valid_branch = true;
repo_data = c_repo.value();
}
if (!is_valid_branch) {
printf("No webhook configured for %s:%s\n", repo.c_str(), ref.c_str());
response["status"] = 404;
response["error"] = "No webhook configured for branch" + ref;
return crow::response(404, response.dump());
}
if (repo_data["files"].empty()) {
printf("No files configured for %s:%s\n", repo.c_str(), ref.c_str());
response["status"] = 404;
response["error"] = "No files configured for branch " + ref;
return crow::response(404, response.dump());
}
response["updated"] = nlohmann::json::array();
for (auto &commit : payload["commits"]) {
for (auto &file : commit["modified"]) {
std::string file_path = file;
if (repos[repo]["files"].find(file_path) == repos[repo]["files"].end()) continue;
std::string path = repos[repo]["files"][file_path];
std::string create_dir = "mkdir -p $(dirname " + path + ")";
std::string command = "curl -s https://raw.githubusercontent.com/" + repo + "/" + ref + "/" + file_path + " -H 'Authorization: token " + token + "' -o " + path;
std::system(create_dir.c_str());
std::system(command.c_str());
printf("Updated %s\n", path.c_str());
response["updated"].push_back(file_path);
}
}
response["status"] = 200;
return crow::response(200, response.dump());
} catch (const std::exception &e) {
std::cerr << "Error processing webhook: " << e.what() << std::endl;
return crow::response(500);
}
});
app.port(port).multithreaded().run();
}