mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 17:56:44 +02:00
[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>
This commit is contained in:
@@ -94,54 +94,18 @@
|
||||
<data name="GITHUB_NEW_VERSION_AVAILABLE" xml:space="preserve">
|
||||
<value>An update to PowerToys is available.</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_DOWNLOAD_STARTED" xml:space="preserve">
|
||||
<value>PowerToys download started.</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_READY_TO_INSTALL" xml:space="preserve">
|
||||
<value>An update to PowerToys is ready to install.</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR" xml:space="preserve">
|
||||
<value>Error: couldn't download PowerToys installer. Visit our GitHub page to update.</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_UPDATE_NOW" xml:space="preserve">
|
||||
<value>Update now</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART" xml:space="preserve">
|
||||
<value>At next launch</value>
|
||||
</data>
|
||||
<data name="UNINSTALLATION_UNKNOWN_ERROR" xml:space="preserve">
|
||||
<value>Error: please uninstall the previous version of PowerToys manually.</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT" xml:space="preserve">
|
||||
<value>An update to PowerToys is available. Visit our GitHub page to update.</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_UP_TO_DATE" xml:space="preserve">
|
||||
<value>PowerToys is up to date.</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_VISIT" xml:space="preserve">
|
||||
<value>Visit</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_MORE_INFO" xml:space="preserve">
|
||||
<value>More info...</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_ABORT" xml:space="preserve">
|
||||
<value>Abort</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_SNOOZE_TITLE" xml:space="preserve">
|
||||
<value>Click Snooze to be reminded in:</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D" xml:space="preserve">
|
||||
<value>1 day</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D" xml:space="preserve">
|
||||
<value>5 days</value>
|
||||
</data>
|
||||
<data name="DOWNLOAD_IN_PROGRESS" xml:space="preserve">
|
||||
<value>Downloading...</value>
|
||||
</data>
|
||||
<data name="DOWNLOAD_COMPLETE" xml:space="preserve">
|
||||
<value>Download complete</value>
|
||||
</data>
|
||||
<data name="TOAST_TITLE" xml:space="preserve">
|
||||
<value>PowerToys Update</value>
|
||||
</data>
|
||||
@@ -151,9 +115,6 @@
|
||||
<data name="OFFER_UNINSTALL_MSI_TITLE" xml:space="preserve">
|
||||
<value>PowerToys: uninstall previous version?</value>
|
||||
</data>
|
||||
<data name="SNOOZE_BUTTON" xml:space="preserve">
|
||||
<value>Snooze</value>
|
||||
</data>
|
||||
<data name="SETTINGS_MENU_TEXT" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
@@ -167,11 +128,4 @@
|
||||
<data name="BUGREPORT_SUCCESS" xml:space="preserve">
|
||||
<value>Bug report .zip file has been created on your Desktop.</value>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_USING_LOCAL_BUILD_ERROR" xml:space="preserve">
|
||||
<value>Updating from a local build is not supported.</value>
|
||||
<comment>User cannot autoupdate from a locally-built PowerToys version</comment>
|
||||
</data>
|
||||
<data name="GITHUB_NEW_VERSION_CHECK_ERROR" xml:space="preserve">
|
||||
<value>Failed to connect to the server. Check your network connection or retry later.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -17,7 +17,7 @@ SHELLEXECUTEINFOW launch_action_runner(const wchar_t* cmdline)
|
||||
action_runner_path = get_module_folderpath();
|
||||
}
|
||||
|
||||
action_runner_path += L"\\action_runner.exe";
|
||||
action_runner_path += L"\\PowerToys.ActionRunner.exe";
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS };
|
||||
sei.lpFile = action_runner_path.c_str();
|
||||
|
||||
@@ -5,15 +5,19 @@
|
||||
|
||||
SHELLEXECUTEINFOW launch_action_runner(const wchar_t* cmdline);
|
||||
|
||||
const inline wchar_t* UPDATE_NOW_LAUNCH_STAGE1_START_PT_CMDARG = L"-update_now_and_start_pt";
|
||||
const inline wchar_t* UPDATE_NOW_LAUNCH_STAGE1_CMDARG = L"-update_now";
|
||||
namespace cmdArg
|
||||
{
|
||||
// Starts first stage of the PowerToys auto-update process, which involves copying action runner to a temp path and
|
||||
// restarting it from there, so it doesn't interfere with the installation process.
|
||||
const inline wchar_t* UPDATE_NOW_LAUNCH_STAGE1 = L"-update_now";
|
||||
// Stage 2 consists of starting the installer and optionally launching newly installed PowerToys binary.
|
||||
// That's indicated by the following 2 flags.
|
||||
const inline wchar_t* UPDATE_NOW_LAUNCH_STAGE2 = L"-update_now_stage_2";
|
||||
const inline wchar_t* UPDATE_STAGE2_RESTART_PT = L"restart";
|
||||
const inline wchar_t* UPDATE_STAGE2_DONT_START_PT = L"dont_start";
|
||||
|
||||
const inline wchar_t* UPDATE_NOW_LAUNCH_STAGE2_CMDARG = L"-update_now_stage_2";
|
||||
const inline wchar_t* UPDATE_STAGE2_RESTART_PT_CMDARG = L"restart";
|
||||
const inline wchar_t* UPDATE_STAGE2_DONT_START_PT_CMDARG = L"dont_start";
|
||||
|
||||
const inline wchar_t * UNINSTALL_MSI_CMDARG = L"-uninstall_msi";
|
||||
const inline wchar_t * RUN_NONELEVATED_CMDARG = L"-run-non-elevated";
|
||||
|
||||
const inline wchar_t* UPDATE_REPORT_SUCCESS = L"-report_update_success";
|
||||
const inline wchar_t* UNINSTALL_MSI = L"-uninstall_msi";
|
||||
const inline wchar_t* RUN_NONELEVATED = L"-run-non-elevated";
|
||||
|
||||
const inline wchar_t* UPDATE_REPORT_SUCCESS = L"-report_update_success";
|
||||
}
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
#include <common/notifications/dont_show_again.h>
|
||||
#include <common/updating/installer.h>
|
||||
#include <common/updating/updating.h>
|
||||
#include <common/updating/updateState.h>
|
||||
#include <common/utils/appMutex.h>
|
||||
#include <common/utils/elevation.h>
|
||||
#include <common/utils/processApi.h>
|
||||
#include <common/utils/resources.h>
|
||||
#include <common/winstore/winstore.h>
|
||||
|
||||
#include "update_state.h"
|
||||
#include "update_utils.h"
|
||||
#include "action_runner_utils.h"
|
||||
|
||||
@@ -96,7 +96,7 @@ void debug_verify_launcher_assets()
|
||||
const auto assetPath = powertoysRoot / asset;
|
||||
if (!fs::is_regular_file(assetPath))
|
||||
{
|
||||
Logger::error("{} couldn't be found.", assetPath.string());
|
||||
Logger::error("{} couldn't be found.", assetPath.string());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ int runner(bool isProcessElevated, bool openSettings, bool openOobe)
|
||||
debug_verify_launcher_assets();
|
||||
|
||||
std::thread{ [] {
|
||||
github_update_worker();
|
||||
periodic_update_worker();
|
||||
} }.detach();
|
||||
|
||||
if (winstore::running_as_packaged())
|
||||
@@ -227,7 +227,7 @@ SpecialMode should_run_in_special_mode(const int n_cmd_args, LPWSTR* cmd_arg_lis
|
||||
{
|
||||
return SpecialMode::ToastNotificationHandler;
|
||||
}
|
||||
else if (n_cmd_args == 2 && !wcscmp(UPDATE_REPORT_SUCCESS, cmd_arg_list[i]))
|
||||
else if (n_cmd_args == 2 && !wcscmp(cmdArg::UPDATE_REPORT_SUCCESS, cmd_arg_list[i]))
|
||||
{
|
||||
return SpecialMode::ReportSuccessfulUpdate;
|
||||
}
|
||||
@@ -252,9 +252,7 @@ toast_notification_handler_result toast_notification_handler(const std::wstring_
|
||||
{
|
||||
const std::wstring_view cant_drag_elevated_disable = L"cant_drag_elevated_disable/";
|
||||
const std::wstring_view couldnt_toggle_powerpreview_modules_disable = L"couldnt_toggle_powerpreview_modules_disable/";
|
||||
const std::wstring_view download_and_install_update = L"download_and_install_update/";
|
||||
const std::wstring_view open_settings = L"open_settings/";
|
||||
const std::wstring_view schedule_update = L"schedule_update/";
|
||||
const std::wstring_view update_now = L"update_now/";
|
||||
|
||||
if (param == cant_drag_elevated_disable)
|
||||
@@ -263,46 +261,10 @@ toast_notification_handler_result toast_notification_handler(const std::wstring_
|
||||
}
|
||||
else if (param.starts_with(update_now))
|
||||
{
|
||||
std::wstring args{ UPDATE_NOW_LAUNCH_STAGE1_CMDARG };
|
||||
const auto installerFilename = param.data() + size(update_now);
|
||||
args += L' ';
|
||||
args += installerFilename;
|
||||
std::wstring args{ cmdArg::UPDATE_NOW_LAUNCH_STAGE1 };
|
||||
launch_action_runner(args.c_str());
|
||||
return toast_notification_handler_result::exit_success;
|
||||
}
|
||||
else if (param.starts_with(schedule_update))
|
||||
{
|
||||
const auto installerFilename = param.data() + size(schedule_update);
|
||||
UpdateState::store([=](UpdateState& state) {
|
||||
state.pending_update = true;
|
||||
state.pending_installer_filename = installerFilename;
|
||||
});
|
||||
|
||||
return toast_notification_handler_result::exit_success;
|
||||
}
|
||||
else if (param.starts_with(download_and_install_update))
|
||||
{
|
||||
try
|
||||
{
|
||||
std::wstring installer_filename = updating::download_update(Strings).get();
|
||||
|
||||
std::wstring args{ UPDATE_NOW_LAUNCH_STAGE1_CMDARG };
|
||||
args += L' ';
|
||||
args += installer_filename;
|
||||
launch_action_runner(args.c_str());
|
||||
|
||||
return toast_notification_handler_result::exit_success;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
MessageBoxW(nullptr,
|
||||
GET_RESOURCE_STRING(IDS_DOWNLOAD_UPDATE_ERROR).c_str(),
|
||||
L"PowerToys",
|
||||
MB_ICONWARNING | MB_OK);
|
||||
|
||||
return toast_notification_handler_result::exit_error;
|
||||
}
|
||||
}
|
||||
else if (param == couldnt_toggle_powerpreview_modules_disable)
|
||||
{
|
||||
return notifications::disable_toast(notifications::PreviewModulesDontShowAgainRegistryPath) ? toast_notification_handler_result::exit_success : toast_notification_handler_result::exit_error;
|
||||
@@ -334,11 +296,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
L"(ML;;NX;;;LW)"; // Integrity label on No execute up for Low mandatory level
|
||||
initializeCOMSecurity(securityDescriptor);
|
||||
|
||||
if (launch_pending_update())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int n_cmd_args = 0;
|
||||
LPWSTR* cmd_arg_list = CommandLineToArgvW(GetCommandLineW(), &n_cmd_args);
|
||||
switch (should_run_in_special_mode(n_cmd_args, cmd_arg_list))
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <ProjectTelemetry.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.Networking.Connectivity.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
|
||||
#include <wil/resource.h>
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
<ClCompile Include="tray_icon.cpp" />
|
||||
<ClCompile Include="unhandled_exception_handler.cpp" />
|
||||
<ClCompile Include="update_utils.cpp" />
|
||||
<ClCompile Include="update_state.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="action_runner_utils.h" />
|
||||
@@ -74,7 +73,6 @@
|
||||
<ClInclude Include="centralized_kb_hook.h" />
|
||||
<ClInclude Include="settings_telemetry.h" />
|
||||
<ClInclude Include="update_utils.h" />
|
||||
<ClInclude Include="update_state.h" />
|
||||
<ClInclude Include="powertoy_module.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="restart_elevated.h" />
|
||||
|
||||
@@ -27,9 +27,6 @@
|
||||
<ClCompile Include="restart_elevated.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="update_state.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="update_utils.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
@@ -75,9 +72,6 @@
|
||||
<ClInclude Include="restart_elevated.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="update_state.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="update_utils.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
#include <common/interop/two_way_pipe_message_ipc.h>
|
||||
#include "tray_icon.h"
|
||||
#include "general_settings.h"
|
||||
#include <common/themes/windows_colors.h>
|
||||
#include "restart_elevated.h"
|
||||
#include "update_state.h"
|
||||
#include "update_utils.h"
|
||||
#include "centralized_kb_hook.h"
|
||||
|
||||
@@ -23,6 +21,8 @@
|
||||
#include <common/utils/process_path.h>
|
||||
#include <common/utils/timeutil.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
#include <common/updating/updateState.h>
|
||||
#include <common/themes/windows_colors.h>
|
||||
|
||||
#define BUFSIZE 1024
|
||||
|
||||
@@ -84,34 +84,16 @@ std::optional<std::wstring> dispatch_json_action_to_module(const json::JsonObjec
|
||||
}
|
||||
else if (action == L"check_for_updates")
|
||||
{
|
||||
if (auto update_check_result = check_for_updates())
|
||||
{
|
||||
VersionHelper latestVersion{ 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));
|
||||
|
||||
result.emplace(json.Stringify());
|
||||
|
||||
UpdateState::store([](UpdateState& state) {
|
||||
state.github_update_last_checked_date.emplace(timeutil::now());
|
||||
});
|
||||
}
|
||||
check_for_updates_settings_callback();
|
||||
}
|
||||
else if (action == L"request_update_state_date")
|
||||
{
|
||||
json::JsonObject json;
|
||||
|
||||
auto update_state = UpdateState::read();
|
||||
if (update_state.github_update_last_checked_date)
|
||||
if (update_state.githubUpdateLastCheckedDate)
|
||||
{
|
||||
const time_t date = *update_state.github_update_last_checked_date;
|
||||
const time_t date = *update_state.githubUpdateLastCheckedDate;
|
||||
json.SetNamedValue(L"updateStateDate", json::value(std::to_wstring(date)));
|
||||
}
|
||||
|
||||
@@ -336,7 +318,7 @@ void run_settings_window(bool showOobeWindow)
|
||||
// Arg 6: elevated status
|
||||
bool isElevated{ get_general_settings().isElevated };
|
||||
std::wstring settings_elevatedStatus = isElevated ? L"true" : L"false";
|
||||
|
||||
|
||||
// Arg 7: is user an admin
|
||||
bool isAdmin{ get_general_settings().isAdmin };
|
||||
std::wstring settings_isUserAnAdmin = isAdmin ? L"true" : L"false";
|
||||
@@ -364,14 +346,14 @@ void run_settings_window(bool showOobeWindow)
|
||||
executable_args.append(settings_isUserAnAdmin);
|
||||
executable_args.append(L" ");
|
||||
executable_args.append(settings_showOobe);
|
||||
|
||||
|
||||
BOOL process_created = false;
|
||||
|
||||
if (is_process_elevated())
|
||||
{
|
||||
// TODO: Revisit this after switching to .NET 5
|
||||
// Due to a bug in .NET, running the Settings process as non-elevated
|
||||
// from an elevated process sometimes results in a crash.
|
||||
// from an elevated process sometimes results in a crash.
|
||||
// process_created = run_settings_non_elevated(executable_path.c_str(), executable_args.data(), &process_info);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "update_state.h"
|
||||
|
||||
#include <common/utils/json.h>
|
||||
#include <common/utils/timeutil.h>
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
const wchar_t PERSISTENT_STATE_FILENAME[] = L"\\update_state.json";
|
||||
const wchar_t UPDATE_STATE_MUTEX[] = L"Local\\PowerToys_Runner_UpdateStateMutex";
|
||||
}
|
||||
|
||||
UpdateState deserialize(const json::JsonObject& json)
|
||||
{
|
||||
UpdateState result;
|
||||
|
||||
result.github_update_last_checked_date = timeutil::from_string(json.GetNamedString(L"github_update_last_checked_date", L"invalid").c_str());
|
||||
result.pending_update = json.GetNamedBoolean(L"pending_update", false);
|
||||
result.pending_installer_filename = json.GetNamedString(L"pending_installer_filename", L"");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
json::JsonObject serialize(const UpdateState& state)
|
||||
{
|
||||
json::JsonObject json;
|
||||
if (state.github_update_last_checked_date.has_value())
|
||||
{
|
||||
json.SetNamedValue(L"github_update_last_checked_date", json::value(timeutil::to_string(*state.github_update_last_checked_date)));
|
||||
}
|
||||
json.SetNamedValue(L"pending_update", json::value(state.pending_update));
|
||||
json.SetNamedValue(L"pending_installer_filename", json::value(state.pending_installer_filename));
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
UpdateState UpdateState::read()
|
||||
{
|
||||
const auto file_name = PTSettingsHelper::get_root_save_folder_location() + PERSISTENT_STATE_FILENAME;
|
||||
std::optional<json::JsonObject> json;
|
||||
{
|
||||
wil::unique_mutex_nothrow mutex{ CreateMutexW(nullptr, FALSE, UPDATE_STATE_MUTEX) };
|
||||
auto lock = mutex.acquire();
|
||||
json = json::from_file(file_name);
|
||||
}
|
||||
return json ? deserialize(*json) : UpdateState{};
|
||||
}
|
||||
|
||||
void UpdateState::store(std::function<void(UpdateState&)> state_modifier)
|
||||
{
|
||||
const auto file_name = PTSettingsHelper::get_root_save_folder_location() + PERSISTENT_STATE_FILENAME;
|
||||
|
||||
std::optional<json::JsonObject> json;
|
||||
{
|
||||
wil::unique_mutex_nothrow mutex{ CreateMutexW(nullptr, FALSE, UPDATE_STATE_MUTEX) };
|
||||
auto lock = mutex.acquire();
|
||||
json = json::from_file(file_name);
|
||||
UpdateState state;
|
||||
if (json)
|
||||
{
|
||||
state = deserialize(*json);
|
||||
}
|
||||
state_modifier(state);
|
||||
json.emplace(serialize(state));
|
||||
json::to_file(file_name, *json);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <ctime>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
|
||||
// All fields must be default-initialized
|
||||
struct UpdateState
|
||||
{
|
||||
std::optional<std::time_t> github_update_last_checked_date;
|
||||
bool pending_update = false;
|
||||
std::wstring pending_installer_filename;
|
||||
|
||||
// To prevent concurrent modification of the file, we enforce this interface, which locks the file while
|
||||
// the state_modifier is active.
|
||||
static void store(std::function<void(UpdateState&)> state_modifier);
|
||||
static UpdateState read();
|
||||
};
|
||||
@@ -3,14 +3,16 @@
|
||||
#include "Generated Files/resource.h"
|
||||
|
||||
#include "action_runner_utils.h"
|
||||
#include "update_state.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>
|
||||
#include <runner/general_settings.h>
|
||||
|
||||
auto Strings = create_notifications_strings();
|
||||
|
||||
@@ -44,15 +46,80 @@ bool start_msi_uninstallation_sequence()
|
||||
return exit_code == 0;
|
||||
}
|
||||
|
||||
void github_update_worker()
|
||||
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.github_update_last_checked_date.has_value())
|
||||
if (state.githubUpdateLastCheckedDate.has_value())
|
||||
{
|
||||
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.githubUpdateLastCheckedDate);
|
||||
if (last_checked_minutes_ago < 0)
|
||||
{
|
||||
last_checked_minutes_ago = UPDATE_CHECK_INTERVAL_MINUTES;
|
||||
@@ -61,22 +128,31 @@ void github_update_worker()
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::minutes{ sleep_minutes_till_next_update });
|
||||
const bool download_updates_automatically = get_general_settings().downloadUpdatesAutomatically;
|
||||
bool update_check_ok = false;
|
||||
|
||||
const bool download_update = !could_be_costly_connection() && get_general_settings().downloadUpdatesAutomatically;
|
||||
bool version_info_obtained = false;
|
||||
try
|
||||
{
|
||||
update_check_ok = updating::try_autoupdate(download_updates_automatically, Strings).get();
|
||||
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 (...)
|
||||
{
|
||||
// Couldn't autoupdate
|
||||
update_check_ok = false;
|
||||
Logger::error("periodic_update_worker: error while processing version info");
|
||||
}
|
||||
|
||||
if (update_check_ok)
|
||||
if (version_info_obtained)
|
||||
{
|
||||
UpdateState::store([](UpdateState& state) {
|
||||
state.github_update_last_checked_date.emplace(timeutil::now());
|
||||
UpdateState::store([&](UpdateState& v) {
|
||||
v = std::move(state);
|
||||
});
|
||||
}
|
||||
else
|
||||
@@ -86,55 +162,27 @@ void github_update_worker()
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<updating::github_version_info> check_for_updates()
|
||||
void check_for_updates_settings_callback()
|
||||
{
|
||||
Logger::trace(L"Check for updates callback invoked");
|
||||
auto state = UpdateState::read();
|
||||
try
|
||||
{
|
||||
auto version_check_result = updating::get_github_version_info_async(Strings).get();
|
||||
if (!version_check_result)
|
||||
auto new_version_info = get_github_version_info_async(Strings).get();
|
||||
if (!new_version_info)
|
||||
{
|
||||
updating::notifications::show_unavailable(Strings, std::move(version_check_result.error()));
|
||||
return std::nullopt;
|
||||
// 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());
|
||||
}
|
||||
|
||||
if (std::holds_alternative<updating::version_up_to_date>(*version_check_result))
|
||||
{
|
||||
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);
|
||||
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 (...)
|
||||
{
|
||||
// Couldn't autoupdate
|
||||
Logger::error("check_for_updates_settings_callback: error while processing version info");
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool launch_pending_update()
|
||||
{
|
||||
try
|
||||
{
|
||||
auto update_state = UpdateState::read();
|
||||
if (update_state.pending_update)
|
||||
{
|
||||
UpdateState::store([](UpdateState& state) {
|
||||
state.pending_update = false;
|
||||
state.pending_installer_filename = {};
|
||||
});
|
||||
std::wstring args{ UPDATE_NOW_LAUNCH_STAGE1_START_PT_CMDARG };
|
||||
args += L' ';
|
||||
args += update_state.pending_installer_filename;
|
||||
|
||||
launch_action_runner(args.c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,5 @@
|
||||
#include <common/updating/updating.h>
|
||||
|
||||
bool start_msi_uninstallation_sequence();
|
||||
void github_update_worker();
|
||||
std::optional<updating::github_version_info> check_for_updates();
|
||||
bool launch_pending_update();
|
||||
void periodic_update_worker();
|
||||
void check_for_updates_settings_callback();
|
||||
|
||||
Reference in New Issue
Block a user