PowerToys/src/runner/update_utils.cpp
Seraphima Zykova b96b4fcb0f
[Auto-update] Auto-update improvements (#11356)
* [Updating] Refactor autoupdate mechanism to use Settings window buttons

* [Updating] Don't use underscores in update_state (#11029)

* [Updating] Rename action_runner to be consisent with accepted format

* [Updating] Make UpdateState values explicit

* [Setup] Set default bootstrapper log severity to debug

* [BugReport] Include all found bootstrapper logs

* [Setup] Use capital letter for ActionRunner

* [Updating] Simple UI to test UpdateState

* [Action Runner]  cleanup and coding style

* [BugReportTool] fix coding convension

* [Auto-update][PT Settings] Updated general page in the Settings (#11227)

* [Auto-update][PT Settings] File watcher monitoring UpdateState.json (#11282)

* Handle button clicks (#11288)

* [Updating] Document ActionRunner cmd flags

* [Auto-update][PT Settings] Updated UI (#11335)

* [Updating] Do not reset update state when msi cancellation detected

* [Updating] Directly launch update_now PT action instead of using custom URI scheme

* Checking for updates UI (#11354)

* [Updating] Fix cannotDownload state in action runner

* [Updating] Reset update state to CannotDownload if action runner encountered an error

* [Updating][PT Settings] downloading label, disable button in error state

* Changed error message

* [Updating rename CannotDownload to ErrorDownloading

* [Updating] Add trace logging for Check for updates callback

* [Updating][PT Settings] simplify downloading checks

* [Updating][PT Settings] Updated text labels

* [Updating][PT Settings] Retry to load settings if failed

* [Updating][PT Settings] Text fix

* [Updating][PT Settings] Installed version links removed

* [Updating][PT Settings] Error text updated

* [Updating][PT Settings] Show label after version checked

* [Updating][PT Settings] Text foreground fix

* [Updating][PT Settings] Clean up

* [Updating] Do not reset releasePageUrl in case of error/cancellation

* [Updating][PT Settings] fixed missing string

* [Updating][PT Settings] checked for updates state fix

Co-authored-by: yuyoyuppe <a.yuyoyuppe@gmail.com>
Co-authored-by: Andrey Nekrasov <yuyoyuppe@users.noreply.github.com>
Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com>
2021-05-21 12:32:34 +02:00

189 lines
6.3 KiB
C++

#include "pch.h"
#include "Generated Files/resource.h"
#include "action_runner_utils.h"
#include "general_settings.h"
#include "update_utils.h"
#include <common/logger/logger.h>
#include <common/updating/installer.h>
#include <common/updating/http_client.h>
#include <common/updating/updating.h>
#include <common/updating/updateState.h>
#include <common/utils/resources.h>
#include <common/utils/timeutil.h>
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()
{
const auto package_path = updating::get_msi_package_path();
if (package_path.empty())
{
// No MSI version detected
return true;
}
if (!updating::offer_msi_uninstallation(Strings))
{
// User declined to uninstall or opted for "Don't show again"
return false;
}
auto sei = launch_action_runner(L"-uninstall_msi");
WaitForSingleObject(sei.hProcess, INFINITE);
DWORD exit_code = 0;
GetExitCodeProcess(sei.hProcess, &exit_code);
CloseHandle(sei.hProcess);
return exit_code == 0;
}
using namespace updating;
bool could_be_costly_connection()
{
using namespace winrt::Windows::Networking::Connectivity;
ConnectionProfile internetConnectionProfile = NetworkInformation::GetInternetConnectionProfile();
return internetConnectionProfile && internetConnectionProfile.IsWwanConnectionProfile();
}
void process_new_version_info(const github_version_info& version_info,
UpdateState& state,
const bool download_update,
const bool show_notifications)
{
state.githubUpdateLastCheckedDate.emplace(timeutil::now());
if (std::holds_alternative<version_up_to_date>(version_info))
{
state.state = UpdateState::upToDate;
state.releasePageUrl = {};
state.downloadedInstallerFilename = {};
Logger::trace(L"Version is up to date");
return;
}
const auto new_version_info = std::get<new_version_download_info>(version_info);
state.releasePageUrl = new_version_info.release_page_uri.ToString().c_str();
Logger::trace(L"Discovered new version {}", new_version_info.version.toWstring());
const bool already_downloaded = state.state == UpdateState::readyToInstall && state.downloadedInstallerFilename == new_version_info.installer_filename;
if (already_downloaded)
{
Logger::trace(L"New version is already downloaded");
return;
}
if (download_update)
{
Logger::trace(L"Downloading installer for a new version");
if (download_new_version(new_version_info).get())
{
state.state = UpdateState::readyToInstall;
state.downloadedInstallerFilename = new_version_info.installer_filename;
if (show_notifications)
{
notifications::show_new_version_available(new_version_info, Strings);
}
}
else
{
state.state = UpdateState::errorDownloading;
state.downloadedInstallerFilename = {};
Logger::error("Couldn't download new installer");
}
}
else
{
Logger::trace(L"New version is ready to download, showing notification");
state.state = UpdateState::readyToDownload;
state.downloadedInstallerFilename = {};
if (show_notifications)
{
notifications::show_open_settings_for_update(Strings);
}
}
}
void periodic_update_worker()
{
for (;;)
{
auto state = UpdateState::read();
int64_t sleep_minutes_till_next_update = 0;
if (state.githubUpdateLastCheckedDate.has_value())
{
int64_t last_checked_minutes_ago = timeutil::diff::in_minutes(timeutil::now(), *state.githubUpdateLastCheckedDate);
if (last_checked_minutes_ago < 0)
{
last_checked_minutes_ago = UPDATE_CHECK_INTERVAL_MINUTES;
}
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 });
const bool download_update = !could_be_costly_connection() && get_general_settings().downloadUpdatesAutomatically;
bool version_info_obtained = false;
try
{
const auto new_version_info = get_github_version_info_async(Strings).get();
if (new_version_info.has_value())
{
version_info_obtained = true;
process_new_version_info(*new_version_info, state, download_update, true);
}
else
{
Logger::error(L"Couldn't obtain version info from github: {}", new_version_info.error());
}
}
catch (...)
{
Logger::error("periodic_update_worker: error while processing version info");
}
if (version_info_obtained)
{
UpdateState::store([&](UpdateState& v) {
v = std::move(state);
});
}
else
{
std::this_thread::sleep_for(std::chrono::minutes{ UPDATE_CHECK_AFTER_FAILED_INTERVAL_MINUTES });
}
}
}
void check_for_updates_settings_callback()
{
Logger::trace(L"Check for updates callback invoked");
auto state = UpdateState::read();
try
{
auto new_version_info = get_github_version_info_async(Strings).get();
if (!new_version_info)
{
// If we couldn't get a new version from github for some reason, assume we're up to date, but also log error
new_version_info = version_up_to_date{};
Logger::error(L"Couldn't obtain version info from github: {}", new_version_info.error());
}
const bool download_update = !could_be_costly_connection() && get_general_settings().downloadUpdatesAutomatically;
process_new_version_info(*new_version_info, state, download_update, false);
UpdateState::store([&](UpdateState& v) {
v = std::move(state);
});
}
catch (...)
{
Logger::error("check_for_updates_settings_callback: error while processing version info");
}
}