Skip to content

Commit

Permalink
verify restart and resume behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
ryfu-msft committed Nov 6, 2023
1 parent 76e6a7c commit d58ee56
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 51 deletions.
2 changes: 1 addition & 1 deletion schemas/JSON/settings/settings.schema.0.2.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@
"maxResumes": {
"description": "The maximum number of resumes allowed for a single resume id. This is to prevent continuous reboots if an install that requires a reboot is not properly detected.",
"type": "integer",
"default": 4,
"default": 3,
"minimum": 1
}
}
Expand Down
46 changes: 38 additions & 8 deletions src/AppInstallerCLICore/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <winget/UserSettings.h>
#include <AppInstallerRuntime.h>
#include <winget/Locale.h>
#include <winget/Reboot.h>

using namespace std::string_view_literals;
using namespace AppInstaller::Utility::literals;
Expand Down Expand Up @@ -870,16 +871,45 @@ namespace AppInstaller::CLI
ExecuteInternal(context);
}

if (context.Args.Contains(Execution::Args::Type::OpenLogs))
{
// TODO: Consider possibly adding functionality that if the context contains 'Execution::Args::Type::Log' to open the path provided for the log
// The above was omitted initially as a security precaution to ensure that user input to '--log' wouldn't be passed directly to ShellExecute
ShellExecute(NULL, NULL, Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation).wstring().c_str(), NULL, NULL, SW_SHOWNORMAL);
}
if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Reboot) &&
context.Args.Contains(Execution::Args::Type::AllowReboot) &&
WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::RebootRequired))
{
context.Reporter.Warn() << Resource::String::InitiatingReboot << std::endl;

if (context.Args.Contains(Execution::Args::Type::Wait))
{
context.Reporter.PromptForEnter();
}

context.ClearFlags(Execution::ContextFlag::RebootRequired);

if (!Reboot::InitiateReboot())
{
context.Reporter.Error() << Resource::String::FailedToInitiateReboot << std::endl;
}
else if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::RegisterResume))
{
context.ClearFlags(Execution::ContextFlag::RegisterResume);

if (context.Args.Contains(Execution::Args::Type::Wait))
// For Windows Error Reporting to restart this process, the process must be rebooted while it is still running.
// Workaround is to have the program sleep while the reboot process is kicked off. Only applies to resume.
Sleep(5000);
}
}
else
{
context.Reporter.PromptForEnter();
if (context.Args.Contains(Execution::Args::Type::OpenLogs))
{
// TODO: Consider possibly adding functionality that if the context contains 'Execution::Args::Type::Log' to open the path provided for the log
// The above was omitted initially as a security precaution to ensure that user input to '--log' wouldn't be passed directly to ShellExecute
ShellExecute(NULL, NULL, Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation).wstring().c_str(), NULL, NULL, SW_SHOWNORMAL);
}

if (context.Args.Contains(Execution::Args::Type::Wait))
{
context.Reporter.PromptForEnter();
}
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/AppInstallerCLICore/Workflows/InstallFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -597,8 +597,7 @@ namespace AppInstaller::CLI::Workflow
Workflow::InstallDependencies <<
Workflow::DownloadInstaller <<
Workflow::InstallPackageInstaller <<
Workflow::RegisterForReboot() <<
Workflow::InitiateRebootIfApplicable();
Workflow::RegisterForReboot();
}

void EnsureSupportForInstall(Execution::Context& context)
Expand Down
28 changes: 0 additions & 28 deletions src/AppInstallerCLICore/Workflows/ResumeFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,6 @@ namespace AppInstaller::CLI::Workflow
context.Checkpoint(m_checkpointName, m_contextData);
}

void InitiateRebootIfApplicable::operator()(Execution::Context& context) const
{
if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Reboot))
{
return;
}

if (!context.Args.Contains(Execution::Args::Type::AllowReboot))
{
AICLI_LOG(CLI, Info, << "No reboot flag found; skipping reboot flow.");
return;
}

if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::RebootRequired))
{
context.ClearFlags(Execution::ContextFlag::RebootRequired);

if (Reboot::InitiateReboot())
{
context.Reporter.Warn() << Resource::String::InitiatingReboot << std::endl;
}
else
{
context.Reporter.Error() << Resource::String::FailedToInitiateReboot << std::endl;
}
}
}

void RegisterForReboot::operator()(Execution::Context& context) const
{
if (!Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Resume) &&
Expand Down
11 changes: 0 additions & 11 deletions src/AppInstallerCLICore/Workflows/ResumeFlow.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,6 @@ namespace AppInstaller::CLI::Workflow
std::vector<Execution::Data> m_contextData;
};

// Initiates a reboot if applicable. This task always executes even if context terminates.
// Required Args: None
// Inputs: None
// Outputs: None
struct InitiateRebootIfApplicable : public WorkflowTask
{
InitiateRebootIfApplicable() : WorkflowTask("InitiateRebootIfApplicable", /* executeAlways */ true) {}

void operator()(Execution::Context& context) const override;
};

// Registers the resume command to execute upon reboot if applicable. This task always executes even if context terminates.
// Required Args: None
// Inputs: None
Expand Down
14 changes: 14 additions & 0 deletions src/AppInstallerCLITests/UserSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -562,3 +562,17 @@ TEST_CASE("SettingsInstallScope", "[settings]")
REQUIRE(userSettingTest.Get<Setting::InstallScopeRequirement>() == AppInstaller::Manifest::ScopeEnum::Machine);
}
}

TEST_CASE("SettingsMaxResumes", "[settings]")
{
auto again = DeleteUserSettingsFiles();

SECTION("Modify max number of resumes")
{
std::string_view json = R"({ "installBehavior": { "maxResumes": 5 } })";
SetSetting(Stream::PrimaryUserSettings, json);
UserSettingsTest userSettingTest;

REQUIRE(userSettingTest.Get<Setting::MaxResumes>() == 5);
}
}
2 changes: 1 addition & 1 deletion src/AppInstallerCommonCore/Public/winget/UserSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ namespace AppInstaller::Settings
SETTINGMAPPING_SPECIALIZATION(Setting::PortablePackageUserRoot, std::string, std::filesystem::path, {}, ".installBehavior.portablePackageUserRoot"sv);
SETTINGMAPPING_SPECIALIZATION(Setting::PortablePackageMachineRoot, std::string, std::filesystem::path, {}, ".installBehavior.portablePackageMachineRoot"sv);
SETTINGMAPPING_SPECIALIZATION(Setting::InstallDefaultRoot, std::string, std::filesystem::path, {}, ".installBehavior.defaultInstallRoot"sv);
SETTINGMAPPING_SPECIALIZATION(Setting::MaxResumes, uint32_t, int, {}, ".installBehavior.maxResumes"sv);
SETTINGMAPPING_SPECIALIZATION(Setting::MaxResumes, uint32_t, int, 3, ".installBehavior.maxResumes"sv);
// Uninstall behavior
SETTINGMAPPING_SPECIALIZATION(Setting::UninstallPurgePortablePackage, bool, bool, false, ".uninstallBehavior.purgePortablePackage"sv);
// Download behavior
Expand Down

0 comments on commit d58ee56

Please sign in to comment.