Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 175 additions & 1 deletion engine/commands/cortex_upd_cmd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,181 @@

namespace commands {

void CortexUpdCmd::Exec(std::string v) {
namespace {
std::chrono::seconds GetUpdateIntervalCheck() {
if (CORTEX_VARIANT == file_manager_utils::kNightlyVariant) {
return std::chrono::seconds(10 * 60);
} else if (CORTEX_VARIANT == file_manager_utils::kBetaVariant) {
return std::chrono::seconds(60 * 60);
} else {
return std::chrono::seconds(24 * 60 * 60);
}
}

std::chrono::seconds GetTimeSinceEpochMillisec() {
using namespace std::chrono;
return duration_cast<seconds>(system_clock::now().time_since_epoch());
}
} // namespace

std::optional<std::string> CheckNewUpdate(
std::optional<std::chrono::milliseconds> timeout) {
// Get info from .cortexrc
auto should_check_update = false;
auto config = file_manager_utils::GetCortexConfig();
auto now = GetTimeSinceEpochMillisec();
if (auto t = now - std::chrono::seconds(config.checkedForUpdateAt);
t > GetUpdateIntervalCheck()) {
should_check_update = true;
config.checkedForUpdateAt = now.count();
CTL_INF("Will check for new update, time from last check: " << t.count()
<< " seconds");
}

if (!should_check_update) {
CTL_INF("Will not check for new update, return the cache latest: "
<< config.latestRelease);
return config.latestRelease;
}

auto host_name = GetHostName();
auto release_path = GetReleasePath();
CTL_INF("Engine release path: " << host_name << release_path);

httplib::Client cli(host_name);
if (timeout.has_value()) {
cli.set_connection_timeout(*timeout);
cli.set_read_timeout(*timeout);
}
if (auto res = cli.Get(release_path)) {
if (res->status == httplib::StatusCode::OK_200) {
try {
auto get_latest = [](const nlohmann::json& data) -> std::string {
if (data.empty()) {
return "";
}

if (CORTEX_VARIANT == file_manager_utils::kBetaVariant) {
for (auto& d : data) {
if (auto tag = d["tag_name"].get<std::string>();
tag.find(kBetaComp) != std::string::npos) {
return tag;
}
}
return data[0]["tag_name"].get<std::string>();
} else {
return data["tag_name"].get<std::string>();
}
return "";
};

auto json_res = nlohmann::json::parse(res->body);
std::string latest_version = get_latest(json_res);
if (latest_version.empty()) {
CTL_WRN("Release not found!");
return std::nullopt;
}
std::string current_version = CORTEX_CPP_VERSION;
CTL_INF("Got the latest release, update to the config file: "
<< latest_version)
config.latestRelease = latest_version;
config_yaml_utils::DumpYamlConfig(
config, file_manager_utils::GetConfigurationPath().string());
if (current_version != latest_version) {
return latest_version;
}
} catch (const nlohmann::json::parse_error& e) {
CTL_INF("JSON parse error: " << e.what());
return std::nullopt;
}
} else {
CTL_INF("HTTP error: " << res->status);
return std::nullopt;
}
} else {
auto err = res.error();
CTL_INF("HTTP error: " << httplib::to_string(err));
return std::nullopt;
}
return std::nullopt;
}

bool ReplaceBinaryInflight(const std::filesystem::path& src,
const std::filesystem::path& dst) {
if (src == dst) {
// Already has the newest
return true;
}

std::filesystem::path temp = dst.parent_path() / "cortex_temp";
auto restore_binary = [&temp, &dst]() {
if (std::filesystem::exists(temp)) {
std::rename(temp.string().c_str(), dst.string().c_str());
CLI_LOG("Restored binary file");
}
};

try {
if (std::filesystem::exists(temp)) {
std::filesystem::remove(temp);
}
#if !defined(_WIN32)
// Get permissions of the executable file
struct stat dst_file_stat;
if (stat(dst.string().c_str(), &dst_file_stat) != 0) {
CTL_ERR("Error getting permissions of executable file: " << dst.string());
return false;
}

// Get owner and group of the executable file
uid_t dst_file_owner = dst_file_stat.st_uid;
gid_t dst_file_group = dst_file_stat.st_gid;
#endif

std::rename(dst.string().c_str(), temp.string().c_str());
std::filesystem::copy_file(
src, dst, std::filesystem::copy_options::overwrite_existing);

#if !defined(_WIN32)
// Set permissions of the executable file
if (chmod(dst.string().c_str(), dst_file_stat.st_mode) != 0) {
CTL_ERR("Error setting permissions of executable file: " << dst.string());
restore_binary();
return false;
}

// Set owner and group of the executable file
if (chown(dst.string().c_str(), dst_file_owner, dst_file_group) != 0) {
CTL_ERR(
"Error setting owner and group of executable file: " << dst.string());
restore_binary();
return false;
}

// Remove cortex_temp
if (unlink(temp.string().c_str()) != 0) {
CTL_ERR("Error deleting self: " << strerror(errno));
restore_binary();
return false;
}
#endif
} catch (const std::exception& e) {
CTL_ERR("Something went wrong: " << e.what());
restore_binary();
return false;
}

return true;
}

void CortexUpdCmd::Exec(const std::string& v) {
// Check for update, if current version is the latest, notify to user
if (auto latest_version = commands::CheckNewUpdate(std::nullopt);
latest_version.has_value() && *latest_version == CORTEX_CPP_VERSION) {
CLI_LOG("cortex is up to date");
return;
}

{
auto config = file_manager_utils::GetCortexConfig();
httplib::Client cli(config.apiServerHost + ":" + config.apiServerPort);
Expand Down
130 changes: 5 additions & 125 deletions engine/commands/cortex_upd_cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,131 +63,11 @@ inline std::string GetReleasePath() {
}
}

inline void CheckNewUpdate() {
auto host_name = GetHostName();
auto release_path = GetReleasePath();
CTL_INF("Engine release path: " << host_name << release_path);
std::optional<std::string> CheckNewUpdate(
std::optional<std::chrono::milliseconds> timeout);

httplib::Client cli(host_name);
cli.set_connection_timeout(kTimeoutCheckUpdate);
cli.set_read_timeout(kTimeoutCheckUpdate);
if (auto res = cli.Get(release_path)) {
if (res->status == httplib::StatusCode::OK_200) {
try {
auto get_latest = [](const nlohmann::json& data) -> std::string {
if (data.empty()) {
return "";
}

if (CORTEX_VARIANT == file_manager_utils::kBetaVariant) {
for (auto& d : data) {
if (auto tag = d["tag_name"].get<std::string>();
tag.find(kBetaComp) != std::string::npos) {
return tag;
}
}
return data[0]["tag_name"].get<std::string>();
} else {
return data["tag_name"].get<std::string>();
}
return "";
};

auto json_res = nlohmann::json::parse(res->body);
std::string latest_version = get_latest(json_res);
if (latest_version.empty()) {
CTL_WRN("Release not found!");
return;
}
std::string current_version = CORTEX_CPP_VERSION;
if (current_version != latest_version) {
CLI_LOG("\nA new release of cortex is available: "
<< current_version << " -> " << latest_version);
CLI_LOG("To upgrade, run: " << GetRole() << GetCortexBinary()
<< " update");
if (CORTEX_VARIANT == file_manager_utils::kProdVariant) {
CLI_LOG(json_res["html_url"].get<std::string>());
}
}
} catch (const nlohmann::json::parse_error& e) {
CTL_INF("JSON parse error: " << e.what());
}
} else {
CTL_INF("HTTP error: " << res->status);
}
} else {
auto err = res.error();
CTL_INF("HTTP error: " << httplib::to_string(err));
}
}

inline bool ReplaceBinaryInflight(const std::filesystem::path& src,
const std::filesystem::path& dst) {
if (src == dst) {
// Already has the newest
return true;
}

std::filesystem::path temp = dst.parent_path() / "cortex_temp";
auto restore_binary = [&temp, &dst]() {
if (std::filesystem::exists(temp)) {
std::rename(temp.string().c_str(), dst.string().c_str());
CLI_LOG("Restored binary file");
}
};

try {
if (std::filesystem::exists(temp)) {
std::filesystem::remove(temp);
}
#if !defined(_WIN32)
// Get permissions of the executable file
struct stat dst_file_stat;
if (stat(dst.string().c_str(), &dst_file_stat) != 0) {
CTL_ERR("Error getting permissions of executable file: " << dst.string());
return false;
}

// Get owner and group of the executable file
uid_t dst_file_owner = dst_file_stat.st_uid;
gid_t dst_file_group = dst_file_stat.st_gid;
#endif

std::rename(dst.string().c_str(), temp.string().c_str());
std::filesystem::copy_file(
src, dst, std::filesystem::copy_options::overwrite_existing);

#if !defined(_WIN32)
// Set permissions of the executable file
if (chmod(dst.string().c_str(), dst_file_stat.st_mode) != 0) {
CTL_ERR("Error setting permissions of executable file: " << dst.string());
restore_binary();
return false;
}

// Set owner and group of the executable file
if (chown(dst.string().c_str(), dst_file_owner, dst_file_group) != 0) {
CTL_ERR(
"Error setting owner and group of executable file: " << dst.string());
restore_binary();
return false;
}

// Remove cortex_temp
if (unlink(temp.string().c_str()) != 0) {
CTL_ERR("Error deleting self: " << strerror(errno));
restore_binary();
return false;
}
#endif
} catch (const std::exception& e) {
CTL_ERR("Something went wrong: " << e.what());
restore_binary();
return false;
}

return true;
}
bool ReplaceBinaryInflight(const std::filesystem::path& src,
const std::filesystem::path& dst);

// This class manages the 'cortex update' command functionality
// There are three release types available:
Expand All @@ -196,7 +76,7 @@ inline bool ReplaceBinaryInflight(const std::filesystem::path& src,
// - Nightly: Enables retrieval of the latest nightly build and specific versions using the -v flag
class CortexUpdCmd {
public:
void Exec(std::string version);
void Exec(const std::string& v);

private:
bool GetStable(const std::string& v);
Expand Down
8 changes: 7 additions & 1 deletion engine/controllers/command_line_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,13 @@ bool CommandLineParser::SetupCommand(int argc, char** argv) {
// Check new update, only check for stable release for now
#ifdef CORTEX_CPP_VERSION
if (cml_data_.check_upd) {
commands::CheckNewUpdate();
if (auto latest_version = commands::CheckNewUpdate(commands::kTimeoutCheckUpdate);
latest_version.has_value() && *latest_version != CORTEX_CPP_VERSION) {
CLI_LOG("\nA new release of cortex is available: "
<< CORTEX_CPP_VERSION << " -> " << *latest_version);
CLI_LOG("To upgrade, run: " << commands::GetRole()
<< commands::GetCortexBinary() << " update");
}
}
#endif

Expand Down
16 changes: 15 additions & 1 deletion engine/test/components/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,31 @@ project(test-components)

enable_testing()

add_executable(${PROJECT_NAME} ${SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/../../utils/modellist_utils.cc ${CMAKE_CURRENT_SOURCE_DIR}/../../config/yaml_config.cc ${CMAKE_CURRENT_SOURCE_DIR}/../../config/gguf_parser.cc)
add_executable(${PROJECT_NAME}
${SRCS}
${CMAKE_CURRENT_SOURCE_DIR}/../../utils/modellist_utils.cc
${CMAKE_CURRENT_SOURCE_DIR}/../../config/yaml_config.cc
${CMAKE_CURRENT_SOURCE_DIR}/../../config/gguf_parser.cc
${CMAKE_CURRENT_SOURCE_DIR}/../../commands/cortex_upd_cmd.cc
${CMAKE_CURRENT_SOURCE_DIR}/../../commands/server_stop_cmd.cc
${CMAKE_CURRENT_SOURCE_DIR}/../../services/download_service.cc
)

find_package(Drogon CONFIG REQUIRED)
find_package(GTest CONFIG REQUIRED)
find_package(yaml-cpp CONFIG REQUIRED)
find_package(httplib CONFIG REQUIRED)
find_package(unofficial-minizip CONFIG REQUIRED)
find_package(LibArchive REQUIRED)
find_package(CURL REQUIRED)

target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon GTest::gtest GTest::gtest_main yaml-cpp::yaml-cpp
${CMAKE_THREAD_LIBS_INIT})

target_link_libraries(${PROJECT_NAME} PRIVATE httplib::httplib)
target_link_libraries(${PROJECT_NAME} PRIVATE unofficial::minizip::minizip)
target_link_libraries(${PROJECT_NAME} PRIVATE LibArchive::LibArchive)
target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../)

add_test(NAME ${PROJECT_NAME}
Expand Down
Loading