updating: do not update update_check date when we couldn't do it (#9038)

* updating: do not update update_check date when we couldn't do it

- improve general settings page "Last Checked" feature
This commit is contained in:
Andrey Nekrasov
2021-01-12 18:34:02 +03:00
committed by GitHub
parent 7bcf4b7894
commit 1364f78b30
9 changed files with 110 additions and 56 deletions

View File

@@ -61,7 +61,7 @@ namespace updating
throw std::runtime_error("Release object doesn't have the required asset"); throw std::runtime_error("Release object doesn't have the required asset");
} }
std::future<nonstd::expected<new_version_download_info, std::wstring>> get_new_github_version_info_async(const notifications::strings& strings, const bool prerelease) std::future<nonstd::expected<github_version_info, std::wstring>> get_github_version_info_async(const notifications::strings& strings, const bool prerelease)
{ {
// If the current version starts with 0.0.*, it means we're on a local build from a farm and shouldn't check for updates. // If the current version starts with 0.0.*, it means we're on a local build from a farm and shouldn't check for updates.
if (VERSION_MAJOR == 0 && VERSION_MINOR == 0) if (VERSION_MAJOR == 0 && VERSION_MINOR == 0)
@@ -104,7 +104,7 @@ namespace updating
if (github_version <= current_version) if (github_version <= current_version)
{ {
co_return nonstd::make_unexpected(strings.GITHUB_NEW_VERSION_UP_TO_DATE); co_return version_up_to_date{};
} }
Uri release_page_url{ release_object.GetNamedString(L"html_url") }; Uri release_page_url{ release_object.GetNamedString(L"html_url") };
@@ -142,24 +142,29 @@ namespace updating
return installer_download_dst; return installer_download_dst;
} }
std::future<void> try_autoupdate(const bool download_updates_automatically, const notifications::strings& strings) std::future<bool> try_autoupdate(const bool download_updates_automatically, const notifications::strings& strings)
{ {
const auto new_version = co_await get_new_github_version_info_async(strings); const auto version_check_result = co_await get_github_version_info_async(strings);
if (!new_version) if (!version_check_result)
{ {
co_return; co_return false;
} }
if (std::holds_alternative<version_up_to_date>(*version_check_result))
{
co_return true;
}
const auto new_version = std::get<new_version_download_info>(*version_check_result);
if (download_updates_automatically && !could_be_costly_connection()) if (download_updates_automatically && !could_be_costly_connection())
{ {
auto installer_download_dst = create_download_path() / new_version->installer_filename; auto installer_download_dst = create_download_path() / new_version.installer_filename;
bool download_success = false; bool download_success = false;
for (size_t i = 0; i < MAX_DOWNLOAD_ATTEMPTS; ++i) for (size_t i = 0; i < MAX_DOWNLOAD_ATTEMPTS; ++i)
{ {
try try
{ {
http::HttpClient client; http::HttpClient client;
co_await client.download(new_version->installer_download_url, installer_download_dst); co_await client.download(new_version.installer_download_url, installer_download_dst);
download_success = true; download_success = true;
break; break;
} }
@@ -170,44 +175,45 @@ namespace updating
} }
if (!download_success) if (!download_success)
{ {
updating::notifications::show_install_error(new_version.value(), strings); updating::notifications::show_install_error(new_version, strings);
co_return; co_return false;
} }
updating::notifications::show_version_ready(new_version.value(), strings); updating::notifications::show_version_ready(new_version, strings);
} }
else else
{ {
updating::notifications::show_visit_github(new_version.value(), strings); updating::notifications::show_visit_github(new_version, strings);
} }
co_return true;
} }
std::future<std::wstring> download_update(const notifications::strings& strings) std::future<std::wstring> download_update(const notifications::strings& strings)
{ {
const auto new_version = co_await get_new_github_version_info_async(strings); const auto version_check_result = co_await get_github_version_info_async(strings);
if (!new_version) if (!version_check_result || std::holds_alternative<version_up_to_date>(*version_check_result))
{ {
co_return L""; co_return L"";
} }
const auto new_version = std::get<new_version_download_info>(*version_check_result);
auto installer_download_dst = create_download_path() / new_version->installer_filename; auto installer_download_dst = create_download_path() / new_version.installer_filename;
updating::notifications::show_download_start(new_version.value(), strings); updating::notifications::show_download_start(new_version, strings);
try try
{ {
auto progressUpdateHandle = [&](float progress) { auto progressUpdateHandle = [&](float progress) {
updating::notifications::update_download_progress(new_version.value(), progress, strings); updating::notifications::update_download_progress(new_version, progress, strings);
}; };
http::HttpClient client; http::HttpClient client;
co_await client.download(new_version->installer_download_url, installer_download_dst, progressUpdateHandle); co_await client.download(new_version.installer_download_url, installer_download_dst, progressUpdateHandle);
} }
catch (...) catch (...)
{ {
updating::notifications::show_install_error(new_version.value(), strings); updating::notifications::show_install_error(new_version, strings);
co_return L""; co_return L"";
} }
co_return new_version->installer_filename; co_return new_version.installer_filename;
} }
} }

View File

@@ -4,6 +4,7 @@
#include <string> #include <string>
#include <future> #include <future>
#include <filesystem> #include <filesystem>
#include <variant>
#include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.h>
#include <expected.hpp> #include <expected.hpp>
@@ -13,6 +14,9 @@
namespace updating namespace updating
{ {
using winrt::Windows::Foundation::Uri; using winrt::Windows::Foundation::Uri;
struct version_up_to_date {};
using github_version_info = std::variant<new_version_download_info, version_up_to_date>;
struct new_version_download_info struct new_version_download_info
{ {
Uri release_page_uri = nullptr; Uri release_page_uri = nullptr;
@@ -21,10 +25,11 @@ namespace updating
std::wstring installer_filename; std::wstring installer_filename;
}; };
std::future<void> try_autoupdate(const bool download_updates_automatically, const notifications::strings&); // Returns whether the update check has succeeded
std::future<bool> try_autoupdate(const bool download_updates_automatically, const notifications::strings&);
std::filesystem::path get_pending_updates_path(); std::filesystem::path get_pending_updates_path();
std::future<std::wstring> download_update(const notifications::strings&); std::future<std::wstring> download_update(const notifications::strings&);
std::future<nonstd::expected<new_version_download_info, std::wstring>> get_new_github_version_info_async(const notifications::strings& strings, const bool prerelease = false); std::future<nonstd::expected<github_version_info, std::wstring>> get_github_version_info_async(const notifications::strings& strings, const bool prerelease = false);
// non-localized // non-localized
constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup"; constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup";

View File

@@ -85,19 +85,25 @@ std::optional<std::wstring> dispatch_json_action_to_module(const json::JsonObjec
} }
else if (action == L"check_for_updates") else if (action == L"check_for_updates")
{ {
auto new_version_info = check_for_updates(); if (auto update_check_result = check_for_updates())
const VersionHelper latestVersion = {
new_version_info ? new_version_info->version : VersionHelper latestVersion{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION };
VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }; bool isVersionLatest = true;
if (auto new_version = std::get_if<updating::new_version_download_info>(&*update_check_result))
{
latestVersion = new_version->version;
isVersionLatest = false;
}
json::JsonObject json;
json.SetNamedValue(L"version", json::value(latestVersion.toWstring()));
json.SetNamedValue(L"isVersionLatest", json::value(isVersionLatest));
json::JsonObject json; result.emplace(json.Stringify());
json.SetNamedValue(L"version", json::JsonValue::CreateStringValue(latestVersion.toWstring()));
json.SetNamedValue(L"isVersionLatest", json::JsonValue::CreateBooleanValue(!new_version_info));
result.emplace(json.Stringify()); UpdateState::store([](UpdateState& state) {
UpdateState::store([](UpdateState& state) { state.github_update_last_checked_date.emplace(timeutil::now());
state.github_update_last_checked_date.emplace(timeutil::now()); });
}); }
} }
else if (action == L"request_update_state_date") else if (action == L"request_update_state_date")
{ {
@@ -107,7 +113,7 @@ std::optional<std::wstring> dispatch_json_action_to_module(const json::JsonObjec
if (update_state.github_update_last_checked_date) if (update_state.github_update_last_checked_date)
{ {
const time_t date = *update_state.github_update_last_checked_date; const time_t date = *update_state.github_update_last_checked_date;
json.SetNamedValue(L"updateStateDate", json::JsonValue::CreateStringValue(std::to_wstring(date))); json.SetNamedValue(L"updateStateDate", json::value(std::to_wstring(date)));
} }
result.emplace(json.Stringify()); result.emplace(json.Stringify());

View File

@@ -14,6 +14,12 @@
auto Strings = create_notifications_strings(); auto Strings = create_notifications_strings();
namespace
{
constexpr int64_t UPDATE_CHECK_INTERVAL_MINUTES = 60 * 24;
constexpr int64_t UPDATE_CHECK_AFTER_FAILED_INTERVAL_MINUTES = 60 * 2;
}
bool start_msi_uninstallation_sequence() bool start_msi_uninstallation_sequence()
{ {
const auto package_path = updating::get_msi_package_path(); const auto package_path = updating::get_msi_package_path();
@@ -40,8 +46,6 @@ bool start_msi_uninstallation_sequence()
void github_update_worker() void github_update_worker()
{ {
const int64_t update_check_period_minutes = 60 * 24;
for (;;) for (;;)
{ {
auto state = UpdateState::read(); auto state = UpdateState::read();
@@ -51,40 +55,57 @@ void github_update_worker()
int64_t last_checked_minutes_ago = timeutil::diff::in_minutes(timeutil::now(), *state.github_update_last_checked_date); int64_t last_checked_minutes_ago = timeutil::diff::in_minutes(timeutil::now(), *state.github_update_last_checked_date);
if (last_checked_minutes_ago < 0) if (last_checked_minutes_ago < 0)
{ {
last_checked_minutes_ago = update_check_period_minutes; last_checked_minutes_ago = UPDATE_CHECK_INTERVAL_MINUTES;
} }
sleep_minutes_till_next_update = max(0, update_check_period_minutes - last_checked_minutes_ago); sleep_minutes_till_next_update = max(0, UPDATE_CHECK_INTERVAL_MINUTES - last_checked_minutes_ago);
} }
std::this_thread::sleep_for(std::chrono::minutes(sleep_minutes_till_next_update)); std::this_thread::sleep_for(std::chrono::minutes{ sleep_minutes_till_next_update });
const bool download_updates_automatically = get_general_settings().downloadUpdatesAutomatically; const bool download_updates_automatically = get_general_settings().downloadUpdatesAutomatically;
bool update_check_ok = false;
try try
{ {
updating::try_autoupdate(download_updates_automatically, Strings).get(); update_check_ok = updating::try_autoupdate(download_updates_automatically, Strings).get();
} }
catch (...) catch (...)
{ {
// Couldn't autoupdate // Couldn't autoupdate
update_check_ok = false;
}
if (update_check_ok)
{
UpdateState::store([](UpdateState& state) {
state.github_update_last_checked_date.emplace(timeutil::now());
});
}
else
{
std::this_thread::sleep_for(std::chrono::minutes{ UPDATE_CHECK_AFTER_FAILED_INTERVAL_MINUTES });
} }
UpdateState::store([](UpdateState& state) {
state.github_update_last_checked_date.emplace(timeutil::now());
});
} }
} }
std::optional<updating::new_version_download_info> check_for_updates() std::optional<updating::github_version_info> check_for_updates()
{ {
try try
{ {
const auto new_version = updating::get_new_github_version_info_async(Strings).get(); auto version_check_result = updating::get_github_version_info_async(Strings).get();
if (!new_version) if (!version_check_result)
{ {
updating::notifications::show_unavailable(Strings, std::move(new_version.error())); updating::notifications::show_unavailable(Strings, std::move(version_check_result.error()));
return std::nullopt; return std::nullopt;
} }
updating::notifications::show_available(new_version.value(), Strings); if (std::holds_alternative<updating::version_up_to_date>(*version_check_result))
return std::move(new_version.value()); {
updating::notifications::show_unavailable(Strings, Strings.GITHUB_NEW_VERSION_UP_TO_DATE);
return std::move(*version_check_result);
}
auto new_version = std::get<updating::new_version_download_info>(*version_check_result);
updating::notifications::show_available(new_version, Strings);
return std::move(new_version);
} }
catch (...) catch (...)
{ {

View File

@@ -4,5 +4,5 @@
bool start_msi_uninstallation_sequence(); bool start_msi_uninstallation_sequence();
void github_update_worker(); void github_update_worker();
std::optional<updating::new_version_download_info> check_for_updates(); std::optional<updating::github_version_info> check_for_updates();
bool launch_pending_update(); bool launch_pending_update();

View File

@@ -237,6 +237,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
} }
} }
public static bool AutoUpdatesEnabled
{
get
{
return Helper.GetProductVersion() != "v0.0.1";
}
}
[SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "This may throw if the XAML page is not initialized in tests (https://github.com/microsoft/PowerToys/pull/2676)")] [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "This may throw if the XAML page is not initialized in tests (https://github.com/microsoft/PowerToys/pull/2676)")]
public bool IsDarkThemeRadioButtonChecked public bool IsDarkThemeRadioButtonChecked
{ {
@@ -388,10 +396,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings); GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);
SendCheckForUpdatesConfigMSG(customaction.ToString()); SendCheckForUpdatesConfigMSG(customaction.ToString());
RequestUpdateCheckedDate();
} }
private void RequestUpdateCheckedDate() public void RequestUpdateCheckedDate()
{ {
GeneralSettingsConfig.CustomActionName = "request_update_state_date"; GeneralSettingsConfig.CustomActionName = "request_update_state_date";

View File

@@ -705,7 +705,7 @@
<value>Version:</value> <value>Version:</value>
</data> </data>
<data name="General_VersionLastChecked.Text" xml:space="preserve"> <data name="General_VersionLastChecked.Text" xml:space="preserve">
<value>Last checked: </value> <value>Last successfully checked: </value>
</data> </data>
<data name="General_Version.AutomationProperties.Name" xml:space="preserve"> <data name="General_Version.AutomationProperties.Name" xml:space="preserve">
<value>Version</value> <value>Version</value>

View File

@@ -133,7 +133,9 @@
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="{StaticResource SmallBottomMargin}" Margin="{StaticResource SmallBottomMargin}"
AutomationProperties.LabeledBy="{Binding ElementName=General_VersionLastChecked}"> AutomationProperties.LabeledBy="{Binding ElementName=General_VersionLastChecked}"
Visibility="{Binding AutoUpdatesEnabled,
Converter={StaticResource VisibleIfTrueConverter}}">
<TextBlock x:Name="General_VersionLastChecked" x:Uid="General_VersionLastChecked" /> <TextBlock x:Name="General_VersionLastChecked" x:Uid="General_VersionLastChecked" />
<TextBlock Text="{x:Bind ViewModel.UpdateCheckedDate, Mode=OneWay}" <TextBlock Text="{x:Bind ViewModel.UpdateCheckedDate, Mode=OneWay}"
Foreground="{ThemeResource ListViewItemForegroundPointerOver}" Foreground="{ThemeResource ListViewItemForegroundPointerOver}"
@@ -144,12 +146,14 @@
Style="{StaticResource AccentButtonStyle}" Style="{StaticResource AccentButtonStyle}"
Foreground="White" Foreground="White"
Command="{Binding CheckForUpdatesEventHandler}" Command="{Binding CheckForUpdatesEventHandler}"
IsEnabled="{Binding AutoUpdatesEnabled}"
/> />
<ToggleSwitch x:Uid="GeneralPage_ToggleSwitch_AutoDownloadUpdates" <ToggleSwitch x:Uid="GeneralPage_ToggleSwitch_AutoDownloadUpdates"
Margin="{StaticResource MediumTopMargin}" Margin="{StaticResource MediumTopMargin}"
Visibility="{Binding Mode=TwoWay, Path=IsAdmin, Converter={StaticResource VisibleIfTrueConverter}}" Visibility="{Binding Mode=TwoWay, Path=IsAdmin, Converter={StaticResource VisibleIfTrueConverter}}"
IsOn="{Binding Mode=TwoWay, Path=AutoDownloadUpdates}"/> IsOn="{Binding Mode=TwoWay, Path=AutoDownloadUpdates}"
IsEnabled="{Binding AutoUpdatesEnabled}" />
</StackPanel> </StackPanel>

View File

@@ -56,6 +56,11 @@ namespace Microsoft.PowerToys.Settings.UI.Views
string version = json.GetNamedString("version", string.Empty); string version = json.GetNamedString("version", string.Empty);
bool isLatest = json.GetNamedBoolean("isVersionLatest", false); bool isLatest = json.GetNamedBoolean("isVersionLatest", false);
if (json.ContainsKey("version"))
{
ViewModel.RequestUpdateCheckedDate();
}
var str = string.Empty; var str = string.Empty;
if (isLatest) if (isLatest)
{ {