From 76bd9a04975144c52a4348deff4da5a4258279fa Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 2 Sep 2023 21:53:36 -0300 Subject: [PATCH] managed to download a tarball --- src/dependency.cpp | 200 +++++++++++++++++++++++++++++++++++---------- src/dependency.h | 28 ++++++- src/loggger.cpp | 40 ++++++--- src/loggger.h | 10 ++- src/project.cpp | 16 +++- src/project.h | 3 + 6 files changed, 234 insertions(+), 63 deletions(-) diff --git a/src/dependency.cpp b/src/dependency.cpp index 9f83724..d75c854 100644 --- a/src/dependency.cpp +++ b/src/dependency.cpp @@ -4,7 +4,9 @@ #include "project.h" -const std::regex http("^https?://"); +constexpr std::string_view acceptJson("accept: application/json"); +const std::regex http("^https?:\\/\\/"); +const std::regex repo("(^https?):\\/\\/([\\w\\d\\.\\-\\_]+)\\/([\\w\\d\\-\\_]+)\\/([\\w\\d\\-\\_]+)"); Dependency::Dependency( const std::string& path, @@ -31,11 +33,15 @@ std::optional Dependency::getVersion() const { } bool Dependency::prepare(const std::filesystem::path& source, const std::filesystem::path& destination) { - if (std::regex_search(path, http)) { - Project::info("downloading project from " + path); - std::filesystem::create_directories(destination / "downloads"); + std::smatch results; + if (std::regex_search(path, results, repo)) { + Project::info(path + " appears to be a git repository"); + const std::string& protocol = results[1]; + const std::string& host = results[2]; + const std::string& owner = results[3]; + const std::string& repo = results[4]; - return download(destination); + return downloadRepo(destination, protocol, host, owner, repo); } else { Project::info("checking project at path " + path); std::filesystem::directory_entry srcDir(source / path); @@ -48,50 +54,160 @@ bool Dependency::prepare(const std::filesystem::path& source, const std::filesys return true; } -bool Dependency::download(const std::filesystem::path& destination) { - std::filesystem::path dwn = destination / "downloads"; - bool result = std::filesystem::create_directories(dwn); - // if (!result) { - // Project::error("couldn't create " + dwn.string()); - // return result; - // } +bool Dependency::downloadRepo( + const std::filesystem::path& destination, + const std::string& protocol, + const std::string& host, + const std::string& owner, + const std::string& repo +) { + nlohmann::json info; + Project::info("Trying Gitea v1 API"); + bool res = repoGiteaApi1(protocol, host, owner, repo, info); - CURL *curl_handle; - std::filesystem::path out = dwn / ("out" + std::to_string(std::rand())); - FILE *pagefile; + if (res) { + nlohmann::json::const_iterator itr = info.find("default_branch"); + if (itr != info.end() && itr->is_string()) { + std::string branchName = *itr; + Project::info("Gitea v1 API seem to have worked"); + Project::info("Default branch is " + branchName); - curl_global_init(CURL_GLOBAL_ALL); - /* init the curl session */ - curl_handle = curl_easy_init(); - /* set URL to get here */ - curl_easy_setopt(curl_handle, CURLOPT_URL, path.c_str()); - /* disable progress meter, set to 0L to enable it */ - curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L); - /* send all data to this function */ - curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write); - - /* open the file */ - pagefile = fopen(out.c_str(), "wb"); - if (pagefile) { - /* write the page body to this file handle */ - curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile); - /* get it! */ - curl_easy_perform(curl_handle); - /* close the header file */ - fclose(pagefile); - } else { - Project::error(std::string("couldn't open output file ") + out.c_str()); + std::string fileName = branchName + ".tar.gz"; + std::string url = protocol + "://" + host + "/api/v1/repos/" + owner + "/" + repo + "/archive/" + fileName; + res = download(url, destination / "downloads" / fileName); + if (res) + Project::info("Successfully downloaded " + fileName); + } else { + res = false; + } } - /* cleanup curl stuff */ - curl_easy_cleanup(curl_handle); - curl_global_cleanup(); + if (!res) { + Project::warn("Gitea v1 API didn't work"); + } - return true; + return res; +} + +bool Dependency::repoGiteaApi1( + const std::string& protocol, + const std::string& host, + const std::string& owner, + const std::string& repo, + nlohmann::json& json +) { + bool result = false; + std::string url = protocol + "://" + host + "/api/v1/repos/" + owner + "/" + repo; + std::string data; + CURLcode code = httpGet(url, data, {acceptJson}); + if (code == CURLE_OK) { + try { + json = nlohmann::json::parse(data); + result = true; + } catch (const nlohmann::json::exception& e) { + Project::warn(e.what()); + } + } + + return result; +} + +CURLcode Dependency::httpGet(const std::string& url, std::string& result, const std::list& headers) { + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + + struct curl_slist* curlHeaders = nullptr; + for (const std::string_view& header : headers) + curlHeaders = curl_slist_append(curlHeaders, header.data()); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curlHeaders); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeString); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result); + + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, this); + + CURLcode res = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + return res; } -size_t Dependency::write(void* file, size_t size, size_t nmemb, void* stream) { - size_t written = fwrite(file, size, nmemb, (FILE *)stream); +bool Dependency::download(const std::string& url, const std::filesystem::path& destination) { + std::filesystem::create_directories(destination.parent_path()); + if (std::filesystem::exists(destination)) + Project::minor("File " + destination.string() + " already exists, will be overwritten"); + + bool result = false; + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFile); + + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, this); + + FILE* pagefile = fopen(destination.c_str(), "wb"); + if (pagefile) { + curl_easy_setopt(curl, CURLOPT_WRITEDATA, pagefile); + CURLcode res = curl_easy_perform(curl); + + fclose(pagefile); + if (res != CURLE_OK) { + Project::warn("Couldn't download file " + url + ": " + curl_easy_strerror(res)); + } else { + uint32_t code; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); + if (code == 200) + result = true; + else + Project::warn("Couldn't download file " + url + ": response code " + std::to_string(code)); + } + } else { + Project::error(std::string("couldn't open output file ") + destination.c_str()); + } + + curl_easy_cleanup(curl); + + if (!result) { + Project::minor("Removing " + destination.string() + " since the donwload failed"); + std::filesystem::remove(destination); + } + + return result; +} + +size_t Dependency::writeString(void* data, size_t size, size_t nmemb, void* pointer) { + size_t finalSize = size * nmemb; + std::string* string = static_cast(pointer); + string->append(static_cast(data), finalSize); + + return finalSize; +} + +size_t Dependency::writeFile(void* data, size_t size, size_t nmemb, void* file) { + size_t written = fwrite(data, size, nmemb, (FILE *)file); return written; } + +int Dependency::trace(CURL* handle, curl_infotype type, char* data, size_t size, void* clientp) { + switch (type) { + case CURLINFO_TEXT: { + std::string message(data, size); + if (message[size - 1] == '\n') + message = message.substr(0, size - 1); + + Project::debug(message); + } break; + default: + break; + } + + return 0; +} + diff --git a/src/dependency.h b/src/dependency.h index fa203e1..86dbcbb 100644 --- a/src/dependency.h +++ b/src/dependency.h @@ -3,10 +3,12 @@ #include #include #include - -#include +#include #include +#include +#include + class Dependency { public: enum class Type { @@ -31,8 +33,26 @@ public: const std::string path; private: - static size_t write(void *file, size_t size, size_t nmemb, void *stream); - bool download(const std::filesystem::path& destination); + static size_t writeFile(void* data, size_t size, size_t nmemb, void* file); + static size_t writeString(void* data, size_t size, size_t nmemb, void* mem); + static int trace(CURL *handle, curl_infotype type, char *data, size_t size, void *clientp); + bool download(const std::string& url, const std::filesystem::path& destination); + + bool downloadRepo( + const std::filesystem::path& destination, + const std::string& protocol, + const std::string& host, + const std::string& owner, + const std::string& repo + ); + bool repoGiteaApi1( + const std::string& protocol, + const std::string& host, + const std::string& owner, + const std::string& repo, + nlohmann::json& json + ); + CURLcode httpGet(const std::string& url, std::string& result, const std::list& headers = {}); private: Type type; diff --git a/src/loggger.cpp b/src/loggger.cpp index 60f6ce8..82c3bd1 100644 --- a/src/loggger.cpp +++ b/src/loggger.cpp @@ -24,7 +24,8 @@ constexpr const std::string_view bold("\e[1m"); constexpr const std::string_view regular("\e[22m"); constexpr const std::string_view clearStyle("\e[0m"); -Logger::Logger(Logger::Severity severity): +Logger::Logger(Logger::Severity severity, bool accumulate): + accumulate(accumulate), currentSeverity(severity), history(), readMutex(), @@ -34,26 +35,41 @@ Logger::Logger(Logger::Severity severity): Logger::~Logger() {} -void Logger::printLog(bool colored) { +void Logger::printLog(bool colored) const { std::cout << std::endl; - for (const Message& message : history) { - if (colored) { - int severity = static_cast(message.first); - std::cout << logSettings[severity] << bold << logHeaders[severity] << regular; - } - std::cout << message.second << std::endl; - } + std::scoped_lock lock(readMutex); + for (const Message& message : history) + printMessage(message, colored); + if (colored) std::cout << clearStyle << std::flush; } -void Logger::log(Logger::Severity severity, const std::string& comment) const { - std::scoped_lock lock(readMutex, writeMutex); - if (severity >= currentSeverity) +void Logger::log(Logger::Severity severity, const std::string& comment, bool colored) const { + if (severity < currentSeverity) + return; + + if (accumulate) { + std::scoped_lock lock(readMutex, writeMutex); history.emplace_back(severity, comment); + } else { + std::scoped_lock lock(writeMutex); + printMessage({severity, comment}, colored); + + if (colored) + std::cout << clearStyle << std::flush; + } } std::list> Logger::getLog() const { std::scoped_lock lock(readMutex); return history; } + +void Logger::printMessage(const Message& message, bool colored) const { + if (colored) { + int severity = static_cast(message.first); + std::cout << logSettings[severity] << bold << logHeaders[severity] << regular; + } + std::cout << message.second << std::endl; +} diff --git a/src/loggger.h b/src/loggger.h index 5dbb0b4..c32af4c 100644 --- a/src/loggger.h +++ b/src/loggger.h @@ -20,14 +20,18 @@ public: }; typedef std::pair Message; - Logger(Severity severity = Severity::info); + Logger(Severity severity = Severity::info, bool accumulate = false); ~Logger(); - void log (Severity severity, const std::string& comment) const; + void log (Severity severity, const std::string& comment, bool colored = true) const; std::list getLog() const; - void printLog(bool colored = true); + void printLog(bool colored = true) const; private: + void printMessage(const Message& message, bool colored = false) const; + +private: + bool accumulate; const Severity currentSeverity; mutable std::list history; mutable std::mutex readMutex; diff --git a/src/project.cpp b/src/project.cpp index 114e5fe..44cca5d 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -25,12 +25,16 @@ Project::Project(const std::filesystem::path& location, const std::filesystem::p dependencies(), root(logger == nullptr) { - if (root) - logger = new Logger(); + if (root) { + curl_global_init(CURL_GLOBAL_ALL); + logger = new Logger(Logger::Severity::debug); + } } Project::~Project() { if (root) { + curl_global_cleanup(); + delete logger; logger = nullptr; } @@ -178,6 +182,14 @@ void Project::debug(const std::string& message) { log(Logger::Severity::debug, message); } +void Project::minor(const std::string& message) { + log(Logger::Severity::minor, message); +} + +void Project::major(const std::string& message) { + log(Logger::Severity::major, message); +} + void Project::error(const std::string& message) { log(Logger::Severity::error, message); } diff --git a/src/project.h b/src/project.h index f467cdd..2ea73e8 100644 --- a/src/project.h +++ b/src/project.h @@ -8,6 +8,7 @@ #include #include +#include #include "dependency.h" #include "loggger.h" @@ -33,6 +34,8 @@ public: static void log(Logger::Severity severity, const std::string& message); static void debug(const std::string& message); static void info(const std::string& message); + static void minor(const std::string& message); + static void major(const std::string& message); static void warn(const std::string& message); static void error(const std::string& message); static void fatal(const std::string& message);