1+ // clang-format off
2+ #include " utils/cortex_utils.h"
3+ // clang-format on
4+ #include " cortex_upd_cmd.h"
5+ #include " httplib.h"
6+ #include " nlohmann/json.hpp"
7+ #include " services/download_service.h"
8+ #include " utils/archive_utils.h"
9+ #include " utils/file_manager_utils.h"
10+ #include " utils/logging_utils.h"
11+ #include " utils/system_info_utils.h"
12+
13+ namespace commands {
14+
15+ namespace {
16+ const std::string kCortexBinary = " cortex-cpp" ;
17+ }
18+
19+ CortexUpdCmd::CortexUpdCmd () {}
20+
21+ void CortexUpdCmd::Exec (std::string v) {
22+ // TODO(sang) stop server if it is running
23+ // Check if the architecture and OS are supported
24+ auto system_info = system_info_utils::GetSystemInfo ();
25+ if (system_info.arch == system_info_utils::kUnsupported ||
26+ system_info.os == system_info_utils::kUnsupported ) {
27+ CTL_ERR (" Unsupported OS or architecture: " << system_info.os << " , "
28+ << system_info.arch );
29+ return ;
30+ }
31+ CTL_INF (" OS: " << system_info.os << " , Arch: " << system_info.arch );
32+
33+ // Download file
34+ constexpr auto github_host = " https://api.github.com" ;
35+ // std::string version = v.empty() ? "latest" : std::move(v);
36+ // TODO(sang): support download with version
37+ std::string version = " latest" ;
38+ std::ostringstream release_path;
39+ release_path << " /repos/janhq/cortex.cpp/releases/" << version;
40+ CTL_INF (" Engine release path: " << github_host << release_path.str ());
41+
42+ httplib::Client cli (github_host);
43+ if (auto res = cli.Get (release_path.str ())) {
44+ if (res->status == httplib::StatusCode::OK_200) {
45+ try {
46+ auto jsonResponse = nlohmann::json::parse (res->body );
47+ auto assets = jsonResponse[" assets" ];
48+ auto os_arch{system_info.os + " -" + system_info.arch };
49+
50+ std::string matched_variant = " " ;
51+ for (auto & asset : assets) {
52+ auto asset_name = asset[" name" ].get <std::string>();
53+ if (asset_name.find (" cortex-cpp" ) != std::string::npos &&
54+ asset_name.find (os_arch) != std::string::npos) {
55+ matched_variant = asset_name;
56+ break ;
57+ }
58+ CTL_INF (asset_name);
59+ }
60+ if (matched_variant.empty ()) {
61+ CTL_ERR (" No variant found for " << os_arch);
62+ return ;
63+ }
64+ CTL_INF (" Matched variant: " << matched_variant);
65+
66+ for (auto & asset : assets) {
67+ auto asset_name = asset[" name" ].get <std::string>();
68+ if (asset_name == matched_variant) {
69+ std::string host{" https://github.com" };
70+
71+ auto full_url = asset[" browser_download_url" ].get <std::string>();
72+ std::string path = full_url.substr (host.length ());
73+
74+ auto fileName = asset[" name" ].get <std::string>();
75+ CTL_INF (" URL: " << full_url);
76+
77+ auto download_task = DownloadTask{.id = " cortex" ,
78+ .type = DownloadType::Cortex,
79+ .error = std::nullopt ,
80+ .items = {DownloadItem{
81+ .id = " cortex" ,
82+ .host = host,
83+ .fileName = fileName,
84+ .type = DownloadType::Cortex,
85+ .path = path,
86+ }}};
87+
88+ DownloadService download_service;
89+ download_service.AddDownloadTask (
90+ download_task,
91+ [this ](const std::string& absolute_path, bool unused) {
92+ // try to unzip the downloaded file
93+ std::filesystem::path download_path{absolute_path};
94+ CTL_INF (" Downloaded engine path: " << download_path.string ());
95+
96+ std::filesystem::path extract_path =
97+ download_path.parent_path ().parent_path ();
98+
99+ archive_utils::ExtractArchive (download_path.string (),
100+ extract_path.string ());
101+
102+ // remove the downloaded file
103+ // TODO(any) Could not delete file on Windows because it is currently hold by httplib(?)
104+ // Not sure about other platforms
105+ try {
106+ std::filesystem::remove (absolute_path);
107+ } catch (const std::exception& e) {
108+ CTL_WRN (" Could not delete file: " << e.what ());
109+ }
110+ CTL_INF (" Finished!" );
111+ });
112+ break ;
113+ }
114+ }
115+ } catch (const nlohmann::json::parse_error& e) {
116+ std::cerr << " JSON parse error: " << e.what () << std::endl;
117+ return ;
118+ }
119+ } else {
120+ CTL_ERR (" HTTP error: " << res->status );
121+ return ;
122+ }
123+ } else {
124+ auto err = res.error ();
125+ CTL_ERR (" HTTP error: " << httplib::to_string (err));
126+ return ;
127+ }
128+ #if defined(_WIN32)
129+ auto executable_path = file_manager_utils::GetExecutableFolderContainerPath ();
130+ auto temp = executable_path / " cortex_tmp.exe" ;
131+ remove (temp.string ().c_str ()); // ignore return code
132+
133+ auto src =
134+ executable_path / " cortex" / kCortexBinary / (kCortexBinary + " .exe" );
135+ auto dst = executable_path / (kCortexBinary + " .exe" );
136+ // Rename
137+ rename (dst.string ().c_str (), temp.string ().c_str ());
138+ // Update
139+ CopyFile (const_cast <char *>(src.string ().c_str ()),
140+ const_cast <char *>(dst.string ().c_str ()), false );
141+ auto download_folder = executable_path / " cortex" ;
142+ remove (download_folder);
143+ remove (temp.string ().c_str ());
144+ #else
145+ auto executable_path = file_manager_utils::GetExecutableFolderContainerPath ();
146+ auto temp = executable_path / " cortex_tmp" ;
147+ auto src = executable_path / " cortex" / kCortexBinary / kCortexBinary ;
148+ auto dst = executable_path / kCortexBinary ;
149+ if (std::rename (dst.string ().c_str (), temp.string ().c_str ())) {
150+ CTL_ERR (" Failed to rename from " << dst.string () << " to "
151+ << temp.string ());
152+ return ;
153+ }
154+ try {
155+ std::filesystem::copy_file (
156+ src, dst, std::filesystem::copy_options::overwrite_existing);
157+ std::filesystem::permissions (dst, std::filesystem::perms::owner_all |
158+ std::filesystem::perms::group_all |
159+ std::filesystem::perms::others_read |
160+ std::filesystem::perms::others_exec);
161+ std::filesystem::remove (temp);
162+ auto download_folder = executable_path / " cortex/" ;
163+ std::filesystem::remove_all (download_folder);
164+ } catch (const std::exception& e) {
165+ CTL_WRN (" Something wrong happened: " << e.what ());
166+ return ;
167+ }
168+ #endif
169+ CLI_LOG (" Update cortex sucessfully" );
170+ }
171+ } // namespace commands
0 commit comments