31 namespace seqan3::detail
44 inline void call_server(std::string
const & command, std::promise<bool> prom)
47 if (system(command.c_str()))
48 prom.set_value(
false);
65 version_checker() =
delete;
66 version_checker(version_checker
const &) =
default;
67 version_checker & operator=(version_checker
const &) =
default;
68 version_checker(version_checker &&) =
default;
69 version_checker & operator=(version_checker &&) =
default;
70 ~version_checker() =
default;
77 version_checker(std::string name_, std::string
const & version_, std::string
const & app_url = std::string{}) :
78 name{std::move(name_)}
80 assert(std::regex_match(name, std::regex{
"^[a-zA-Z0-9_-]+$"}));
84 message_app_update.pop_back();
85 message_app_update.append(
"[APP INFO] :: Visit " + app_url +
" for updates.\n\n");
89 timestamp_filename = cookie_path / (name +
"_usr.timestamp");
91 timestamp_filename = cookie_path / (name +
"_dev.timestamp");
93 std::smatch versionMatch;
96 if (!version_.empty() &&
97 std::regex_search(version_, versionMatch, std::regex(
"^([[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+).*")))
99 version = versionMatch.str(1);
130 void operator()(std::promise<bool> prom)
132 std::array<int, 3> empty_version{0, 0, 0};
133 std::array<int, 3> srv_app_version{};
134 std::array<int, 3> srv_seqan_version{};
136 std::ifstream version_file{cookie_path / (name +
".version")};
138 if (version_file.is_open())
141 std::getline(version_file, line);
143 if (line != unregistered_app)
144 srv_app_version = get_numbers_from_version_string(line);
147 std::cerr << message_unregistered_app;
150 std::getline(version_file, line);
151 srv_seqan_version = get_numbers_from_version_string(line);
153 version_file.close();
157 if (srv_seqan_version != empty_version)
161 if (seqan_version < srv_seqan_version)
162 std::cerr << message_seqan3_update;
166 if (srv_app_version != empty_version)
169 if (get_numbers_from_version_string(version) < srv_app_version)
170 std::cerr << message_app_update;
174 if (get_numbers_from_version_string(version) > srv_app_version)
175 std::cerr << message_registered_app_update;
179 std::cerr << std::flush;
181 std::string program = get_program();
185 prom.set_value(
false);
190 std::filesystem::path out_file = cookie_path / (name +
".version");
193 std::string command = program +
197 std::string{
"http://seqan-update.informatik.uni-tuebingen.de/check/SeqAn3_"} +
202 #elif defined(_WIN32)
211 #if __x86_64__ || __ppc64__
220 "; exit [int] -not $?}\" > nul 2>&1";
226 std::thread(call_server, command, std::move(prom)).detach();
230 static std::filesystem::path get_path()
232 using namespace std::filesystem;
236 tmp_path = std::string{getenv(home_env_name)};
237 tmp_path /=
".config";
241 create_directory(tmp_path, err);
247 create_directory(tmp_path, err);
252 tmp_path = temp_directory_path();
255 path dummy = tmp_path /
"dummy.txt";
256 std::ofstream file{dummy};
257 detail::safe_filesystem_entry file_guard{dummy};
259 bool is_open = file.is_open();
260 bool is_good = file.good();
262 file_guard.remove_no_throw();
264 if (!is_good || !is_open)
297 bool decide_if_check_is_performed(
update_notifications developer_approval, std::optional<bool> user_approval)
302 if (std::getenv(
"SEQAN3_NO_VERSION_CHECK") !=
nullptr)
305 if (user_approval.has_value())
306 return user_approval.value();
309 if (std::filesystem::exists(cookie_path))
311 std::ifstream timestamp_file{timestamp_filename};
312 std::string cookie_line{};
314 if (timestamp_file.is_open())
316 std::getline(timestamp_file, cookie_line);
318 if (get_time_diff_to_current(cookie_line) < 86400)
323 std::getline(timestamp_file, cookie_line);
325 if (cookie_line ==
"NEVER")
329 else if (cookie_line ==
"ALWAYS")
335 timestamp_file.close();
343 if (detail::is_terminal())
346 #######################################################################
347 Automatic Update Notifications
348 #######################################################################
350 This app can look for updates automatically in the background,
351 do you want to do that?
353 [a] Always perform version checks for this app (the default).
354 [n] Never perform version checks for this app.
355 [y] Yes, perform a version check now, and ask again tomorrow.
356 [s] Skip the version check now, but ask again tomorrow.
358 Please enter one of [a, n, y, s] and press [RETURN].
360 For more information, see:
361 https://github.com/seqan/seqan3/wiki/Update-Notifications
363 #######################################################################
367 std::getline(std::cin, line);
382 write_cookie(std::string{
"NEVER"});
387 write_cookie(std::string{
"ALWAYS"});
395 #######################################################################
396 Automatic Update Notifications
397 #######################################################################
398 This app performs automatic checks for updates. For more information
399 see: https://github.com/seqan/seqan3/wiki/Update-Notifications
400 #######################################################################
408 static constexpr std::string_view unregistered_app =
"UNREGISTERED_APP";
410 static constexpr std::string_view message_seqan3_update =
411 "[SEQAN3 INFO] :: A new SeqAn version is available online.\n"
412 "[SEQAN3 INFO] :: Please visit www.github.com/seqan/seqan3.git for an update\n"
413 "[SEQAN3 INFO] :: or inform the developer of this app.\n"
414 "[SEQAN3 INFO] :: If you don't wish to receive further notifications, set --version-check OFF.\n\n";
416 static constexpr std::string_view message_unregistered_app =
417 "[SEQAN3 INFO] :: Thank you for using SeqAn!\n"
418 "[SEQAN3 INFO] :: Do you wish to register your app for update notifications?\n"
419 "[SEQAN3 INFO] :: Just send an email to support@seqan.de with your app name and version number.\n"
420 "[SEQAN3 INFO] :: If you don't wish to receive further notifications, set --version-check OFF.\n\n";
422 static constexpr std::string_view message_registered_app_update =
423 "[APP INFO] :: We noticed the app version you use is newer than the one registered with us.\n"
424 "[APP INFO] :: Please send us an email with the new version so we can correct it (support@seqan.de)\n\n";
426 std::string message_app_update =
427 "[APP INFO] :: A new version of this application is now available.\n"
428 "[APP INFO] :: If you don't wish to receive further notifications, set --version-check OFF.\n\n";
432 static constexpr
char const * home_env_name
444 std::string version{
"0.0.0"};
446 std::regex version_regex{
"^[[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+$"};
448 std::filesystem::path cookie_path = get_path();
450 std::filesystem::path timestamp_filename;
454 static std::string get_program()
457 return "powershell.exe -NoLogo -NonInteractive -Command \"& {Invoke-WebRequest -erroraction 'silentlycontinue' -OutFile";
459 if (!system(
"/usr/bin/env -i wget --version > /dev/null 2>&1"))
460 return "/usr/bin/env -i wget --timeout=10 --tries=1 -q -O";
461 else if (!system(
"/usr/bin/env -i curl --version > /dev/null 2>&1"))
462 return "/usr/bin/env -i curl --connect-timeout 10 -o";
465 #if defined(__OpenBSD__)
466 return "/usr/bin/env -i ftp -w10 -Vo";
467 #elif defined(__FreeBSD__)
468 return "/usr/bin/env -i fetch --timeout=10 -o";
476 double get_time_diff_to_current(std::string
const & str_time)
const
478 namespace co = std::chrono;
479 double curr = co::duration_cast<co::seconds>(co::system_clock::now().time_since_epoch()).count();
482 std::from_chars(str_time.data(), str_time.data() + str_time.size(), d_time);
484 return curr - d_time;
490 std::array<int, 3> get_numbers_from_version_string(std::string
const & str)
const
492 std::array<int, 3> result{};
494 if (!std::regex_match(str, version_regex))
497 auto res = std::from_chars(str.data(), str.data() + str.size(), result[0]);
498 res = std::from_chars(res.ptr + 1, str.data() + str.size(), result[1]);
499 res = std::from_chars(res.ptr + 1, str.data() + str.size(), result[2]);
508 template <
typename msg_type>
509 void write_cookie(msg_type && msg)
512 namespace co = std::chrono;
513 auto curr = co::duration_cast<co::seconds>(co::system_clock::now().time_since_epoch()).count();
515 std::ofstream timestamp_file{timestamp_filename};
517 if (timestamp_file.is_open())
519 timestamp_file << curr <<
'\n' << msg;
520 timestamp_file.close();
Provides auxiliary information.
Provides various utility functions.
update_notifications
Indicates whether application allows automatic update notifications by the seqan3::argument_parser.
Definition: auxiliary.hpp:257
@ off
Automatic update notifications should be disabled.
Provides seqan3::detail::safe_filesystem_entry.
Provides std::from_chars and std::to_chars if not defined in the stdlib <charconv> header.
Checks if program is run interactively and retrieves dimensions of terminal (Transferred from seqan2)...
Provides SeqAn version macros and global variables.
#define SEQAN3_VERSION_MAJOR
The major version as MACRO.
Definition: version.hpp:19
#define SEQAN3_VERSION_PATCH
The patch version as MACRO.
Definition: version.hpp:23
#define SEQAN3_VERSION_MINOR
The minor version as MACRO.
Definition: version.hpp:21