Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation for Portable install flow #2078

Merged
merged 58 commits into from
Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
90d186f
add initial cli arguments
Feb 15, 2022
0f64c0c
add proposed schema changes for 1.2
Mar 3, 2022
ec39660
make adjustments to changes
ryfu-msft Mar 4, 2022
c6eee67
update DefaultLocale
ryfu-msft Mar 4, 2022
307f37a
add initial commit for portable install
ryfu-msft Mar 5, 2022
a63c7fe
Add initial portable flow and check for running exe
Mar 12, 2022
233e314
save state
Mar 14, 2022
b7523ed
extend write to registry functionality
Mar 23, 2022
ad1bcec
clean up code
Mar 24, 2022
336c073
add support for progress
Mar 24, 2022
f5b0aa7
refactor portable code
Mar 25, 2022
be0307b
revert changes
Mar 25, 2022
5badd5b
add initial registry tests
Mar 28, 2022
876decf
cleanup code and list tests
Mar 29, 2022
577f803
save work
Mar 29, 2022
4e03b2e
Write to path
Mar 31, 2022
a92eb04
Merge branch 'master' of https://github.com/ryfu-msft/winget-cli into…
Apr 1, 2022
aa7e99e
resolve merge conflicts
Apr 2, 2022
e4faa1f
clean up code
Apr 4, 2022
8f7c498
fix spell check
Apr 4, 2022
82b39bd
revert installer manifest
Apr 4, 2022
2954827
refactor code
Apr 5, 2022
ac12821
revert changes
Apr 6, 2022
63731b7
revert resource strings
Apr 6, 2022
13065c6
rearrange code
Apr 6, 2022
db5d9f3
refactor portable install flow
Apr 7, 2022
c45fc1c
add tests
Apr 8, 2022
3d420d7
Add warning strings and cleanup tests
Apr 8, 2022
f361ae0
fix spelling
Apr 8, 2022
342ab7e
fix e2e tests
Apr 8, 2022
1abc4a5
update E2E tests and return HRESULT
Apr 11, 2022
eb4ac00
Merge branch 'master' into portableInstallFlow
ryfu-msft Apr 11, 2022
b89dc77
update rename arg
Apr 12, 2022
abec833
Merge branch 'microsoft:master' into portableInstallFlow
ryfu-msft Apr 16, 2022
aaaf5a5
address PR comments
Apr 16, 2022
16a7d66
fix e2e tests and add error handling
Apr 18, 2022
e757679
Merge branch 'portableInstallFlow' of https://github.com/ryfu-msft/wi…
Apr 18, 2022
9392a99
fix mispellings
Apr 18, 2022
bee5586
fix unit test
Apr 18, 2022
3360ec8
Add portable-force argument to override checks
Apr 19, 2022
581a212
Separate test checks for debugging
Apr 19, 2022
3d049b9
add path print statements
Apr 19, 2022
48d2bf7
save work for pr feedback
Apr 20, 2022
331ee5d
Address PR feedback
Apr 22, 2022
6cb56a4
fix E2E tests
Apr 22, 2022
9c51ff8
add log and change fileexists method
Apr 22, 2022
32ac746
add print statements
Apr 22, 2022
d270e38
add unvirtualizesresources rescap
Apr 23, 2022
bba4466
refactor portableARPEntry and fix tests
Apr 25, 2022
e11260f
fix cleanup on E2E tests
Apr 25, 2022
2a45cb3
fix trenly comments
Apr 26, 2022
1886e68
fix reportInstallerResult and address comments
Apr 27, 2022
fcd3029
fix spelling
Apr 27, 2022
5b5f13d
fix tests
Apr 27, 2022
2be3590
remove portable checks from nonportable installertypes
Apr 27, 2022
8c0317d
Merge branch 'master' into portableInstallFlow
ryfu-msft Apr 27, 2022
b5c1ad2
address final comments and add test for existing directory
Apr 28, 2022
1b1a7e2
fix error message for invalid reserve
Apr 28, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/spelling/allow.txt
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ EXTRADEBUG
EXTRAFLAGS
FAILIFTHERE
fakeswitch
fallthrough
FATALEXIT
FEBAB
FIELDTAG
Expand Down
8 changes: 8 additions & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ llvm
localhost
localizationpriority
LPBYTE
LPDWORD
LPWSTR
LSTATUS
LTDA
Expand Down Expand Up @@ -259,6 +260,7 @@ netlify
Newtonsoft
NOEXPAND
nonetwork
nonterminated
normer
NOSEPARATOR
NOTAPROPERTY
Expand All @@ -272,6 +274,7 @@ objbase
objidl
ofile
Outptr
OSVERSION
Packagedx
packageinuse
parametermap
Expand Down Expand Up @@ -316,6 +319,7 @@ Redist
REFIID
regexes
REGSAM
reparse
restsource
rgex
rhs
Expand Down Expand Up @@ -359,6 +363,7 @@ SUSE
swervy
SYD
SYG
symlink
sysrefcomp
Tagit
TCpp
Expand Down Expand Up @@ -393,6 +398,7 @@ uninstalls
unknwn
unparsable
UNSCOPED
unvirtualized
UParse
UPSERT
uris
Expand All @@ -404,8 +410,10 @@ USHORT
utils
uuid
UWP
VALUENAMECASE
VERSI
VERSIE
virtualization
vns
vscode
vstest
Expand Down
4 changes: 2 additions & 2 deletions schemas/JSON/settings/settings.schema.0.2.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,12 @@
"type": "boolean",
"default": false
},
"PortableAppUserRoot": {
"PortablePackageUserRoot": {
"description": "The default root directory where packages are installed to under User scope. Applies to the portable installer type.",
"type": "string",
"default": "%LOCALAPPDATA%/Microsoft/WinGet/Packages/"
},
"PortableAppMachineRoot": {
"PortablePackageMachineRoot": {
"description": "The default root directory where packages are installed to under Machine scope. Applies to the portable installer type.",
"type": "string",
"default": "%PROGRAMFILES%/WinGet/Packages/"
Expand Down
2 changes: 2 additions & 0 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@
<ClInclude Include="Workflows\ImportExportFlow.h" />
<ClInclude Include="Workflows\MsiInstallFlow.h" />
<ClInclude Include="Workflows\MSStoreInstallerHandler.h" />
<ClInclude Include="Workflows\PortableInstallFlow.h" />
<ClInclude Include="Workflows\SettingsFlow.h" />
<ClInclude Include="Workflows\ShellExecuteInstallerHandler.h" />
<ClInclude Include="Workflows\InstallFlow.h" />
Expand Down Expand Up @@ -332,6 +333,7 @@
<ClCompile Include="Workflows\ImportExportFlow.cpp" />
<ClCompile Include="Workflows\MsiInstallFlow.cpp" />
<ClCompile Include="Workflows\MSStoreInstallerHandler.cpp" />
<ClCompile Include="Workflows\PortableInstallFlow.cpp" />
<ClCompile Include="Workflows\SettingsFlow.cpp" />
<ClCompile Include="Workflows\ShellExecuteInstallerHandler.cpp" />
<ClCompile Include="Workflows\InstallFlow.cpp" />
Expand Down
6 changes: 6 additions & 0 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@
<ClInclude Include="Public\COMContext.h">
<Filter>Public</Filter>
</ClInclude>
<ClInclude Include="Workflows\PortableInstallFlow.h">
<Filter>Workflows</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
Expand Down Expand Up @@ -319,6 +322,9 @@
<ClCompile Include="Workflows\DownloadFlow.cpp">
<Filter>Workflows</Filter>
</ClCompile>
<ClCompile Include="Workflows\PortableInstallFlow.cpp">
<Filter>Workflows</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
Expand Down
4 changes: 3 additions & 1 deletion src/AppInstallerCLICore/Argument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,15 @@ namespace AppInstaller::CLI
case Args::Type::ExperimentalArg:
return Argument{ "arg", NoAlias, Args::Type::ExperimentalArg, Resource::String::ExperimentalArgumentDescription, ArgumentType::Flag, ExperimentalFeature::Feature::ExperimentalArg };
case Args::Type::Rename:
return Argument{ "rename", NoAlias, Args::Type::Rename, Resource::String::RenameArgumentDescription, ArgumentType::Positional, false };
return Argument{ "rename", 'r', Args::Type::Rename, Resource::String::RenameArgumentDescription, ArgumentType::Standard, false };
case Args::Type::Purge:
return Argument{ "purge", NoAlias, Args::Type::Purge, Resource::String::PurgeArgumentDescription, ArgumentType::Flag, false };
case Args::Type::Preserve:
return Argument{ "preserve", NoAlias, Args::Type::Preserve, Resource::String::PreserveArgumentDescription, ArgumentType::Flag, false };
case Args::Type::Wait:
return Argument{ "wait", NoAlias, Args::Type::Wait, Resource::String::WaitArgumentDescription, ArgumentType::Flag, false };
case Args::Type::ProductCode:
return Argument{ "product-code", NoAlias, Args::Type::ProductCode, Resource::String::ProductCodeArgumentDescription, ArgumentType::Standard, false };
ryfu-msft marked this conversation as resolved.
Show resolved Hide resolved
default:
THROW_HR(E_UNEXPECTED);
}
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/Commands/InstallCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ namespace AppInstaller::CLI
Argument::ForType(Args::Type::AcceptPackageAgreements),
Argument::ForType(Args::Type::CustomHeader),
Argument::ForType(Args::Type::AcceptSourceAgreements),
Argument::ForType(Args::Type::Rename),
};
}

Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/ExecutionArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ namespace AppInstaller::CLI::Execution
// Uninstall behavior
Purge, // Removes all files and directories related to a package during an uninstall. Only applies to the portable installerType.
Preserve, // Retains any files and directories created by the portable exe.
ProductCode, // Uninstalls using the product code as the identifier.
ryfu-msft marked this conversation as resolved.
Show resolved Hide resolved

//Source Command
SourceName,
Expand Down
3 changes: 3 additions & 0 deletions src/AppInstallerCLICore/ExecutionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ namespace AppInstaller::CLI::Execution
// Returns a value indicating whether the context is terminated.
bool IsTerminated() const { return m_isTerminated; }

// Resets the context to a nonterminated state.
void ResetTermination() { m_terminationHR = S_OK; m_isTerminated = false; }

// Gets the HRESULT reason for the termination.
HRESULT GetTerminationHR() const { return m_terminationHR; }

Expand Down
6 changes: 6 additions & 0 deletions src/AppInstallerCLICore/Resources.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(ManifestValidationSuccess);
WINGET_DEFINE_RESOURCE_STRINGID(ManifestValidationWarning);
WINGET_DEFINE_RESOURCE_STRINGID(MissingArgumentError);
WINGET_DEFINE_RESOURCE_STRINGID(ModifiedPathRequiresShellRestart);
WINGET_DEFINE_RESOURCE_STRINGID(MonikerArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(MsixArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(MsixSignatureHashFailed);
Expand Down Expand Up @@ -197,6 +198,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(Options);
WINGET_DEFINE_RESOURCE_STRINGID(OutputFileArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(OverrideArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(OverwritingExistingFileAtMessage);
WINGET_DEFINE_RESOURCE_STRINGID(Package);
WINGET_DEFINE_RESOURCE_STRINGID(PackageAgreementsNotAgreedTo);
WINGET_DEFINE_RESOURCE_STRINGID(PackageAgreementsPrompt);
Expand All @@ -206,17 +208,21 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(PoliciesEnabled);
WINGET_DEFINE_RESOURCE_STRINGID(PoliciesPolicy);
WINGET_DEFINE_RESOURCE_STRINGID(PoliciesState);
WINGET_DEFINE_RESOURCE_STRINGID(PortableRegistryCollisionOverridden);
WINGET_DEFINE_RESOURCE_STRINGID(PositionArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(PreserveArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(PrivacyStatement);
WINGET_DEFINE_RESOURCE_STRINGID(ProductCodeArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(PromptOptionNo);
WINGET_DEFINE_RESOURCE_STRINGID(PromptOptionYes);
WINGET_DEFINE_RESOURCE_STRINGID(PurgeArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(QueryArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(RainbowArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(RenameArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(ReparsePointsNotSupportedError);
WINGET_DEFINE_RESOURCE_STRINGID(ReportIdentityFound);
WINGET_DEFINE_RESOURCE_STRINGID(RequiredArgError);
WINGET_DEFINE_RESOURCE_STRINGID(ReservedFilenameError);
WINGET_DEFINE_RESOURCE_STRINGID(RetroArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(SearchCommandLongDescription);
WINGET_DEFINE_RESOURCE_STRINGID(SearchCommandShortDescription);
Expand Down
67 changes: 4 additions & 63 deletions src/AppInstallerCLICore/Workflows/DownloadFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.
#include "pch.h"
#include "DownloadFlow.h"
#include "winget/Filesystem.h"

#include <AppInstallerMsixInfo.h>

Expand Down Expand Up @@ -38,6 +39,7 @@ namespace AppInstaller::CLI::Workflow
case InstallerTypeEnum::Exe:
case InstallerTypeEnum::Inno:
case InstallerTypeEnum::Nullsoft:
case InstallerTypeEnum::Portable:
return L".exe"sv;
case InstallerTypeEnum::Msi:
case InstallerTypeEnum::Wix:
Expand Down Expand Up @@ -122,68 +124,6 @@ namespace AppInstaller::CLI::Workflow

return false;
}

// Complicated rename algorithm due to somewhat arbitrary failures.
// 1. First, try to rename.
// 2. Then, create an empty file for the target, and attempt to rename.
// 3. Then, try repeatedly for 500ms in case it is a timing thing.
// 4. Attempt to use a hard link if available.
// 5. Copy the file if nothing else has worked so far.
void RenameFile(const std::filesystem::path& from, const std::filesystem::path& to)
{
// 1. First, try to rename.
try
{
// std::filesystem::rename() handles motw correctly if applicable.
std::filesystem::rename(from, to);
return;
}
CATCH_LOG();

// 2. Then, create an empty file for the target, and attempt to rename.
// This seems to fix things in certain cases, so we do it.
try
{
{
std::ofstream targetFile{ to };
}
std::filesystem::rename(from, to);
return;
}
CATCH_LOG();

// 3. Then, try repeatedly for 500ms in case it is a timing thing.
for (int i = 0; i < 5; ++i)
{
try
{
std::this_thread::sleep_for(100ms);
std::filesystem::rename(from, to);
return;
}
CATCH_LOG();
}

// 4. Attempt to use a hard link if available.
if (Runtime::SupportsHardLinks(from))
{
try
{
// Create a hard link to the file; the installer will be left in the temp directory afterward
// but it is better to succeed the operation and leave a file around than to fail.
// First we have to remove the target file as the function will not overwrite.
std::filesystem::remove(to);
std::filesystem::create_hard_link(from, to);
return;
}
CATCH_LOG();
}

// 5. Copy the file if nothing else has worked so far.
// Create a copy of the file; the installer will be left in the temp directory afterward
// but it is better to succeed the operation and leave a file around than to fail.
std::filesystem::copy_file(from, to, std::filesystem::copy_options::overwrite_existing);
}
}

void DownloadInstaller(Execution::Context& context)
Expand All @@ -210,6 +150,7 @@ namespace AppInstaller::CLI::Workflow
case InstallerTypeEnum::Inno:
case InstallerTypeEnum::Msi:
case InstallerTypeEnum::Nullsoft:
case InstallerTypeEnum::Portable:
case InstallerTypeEnum::Wix:
context << DownloadInstallerFile;
break;
Expand Down Expand Up @@ -513,7 +454,7 @@ namespace AppInstaller::CLI::Workflow
return;
}

RenameFile(installerPath, renamedDownloadedInstaller);
Filesystem::RenameFile(installerPath, renamedDownloadedInstaller);

installerPath.assign(renamedDownloadedInstaller);
AICLI_LOG(CLI, Info, << "Successfully renamed downloaded installer. Path: " << installerPath);
Expand Down
19 changes: 19 additions & 0 deletions src/AppInstallerCLICore/Workflows/InstallFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "ShellExecuteInstallerHandler.h"
#include "MSStoreInstallerHandler.h"
#include "MsiInstallFlow.h"
#include "PortableInstallFlow.h"
#include "WorkflowBase.h"
#include "Workflows/DependenciesFlow.h"
#include <AppInstallerDeployment.h>
Expand Down Expand Up @@ -116,6 +117,8 @@ namespace AppInstaller::CLI::Workflow
context.Reporter.Error() << Resource::String::NoApplicableInstallers << std::endl;
AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_INSTALLER);
}

context << EnsureSupportForInstall;
}

void ShowInstallationDisclaimer(Execution::Context& context)
Expand Down Expand Up @@ -257,6 +260,9 @@ namespace AppInstaller::CLI::Workflow
EnsureStorePolicySatisfied <<
(isUpdate ? MSStoreUpdate : MSStoreInstall);
break;
case InstallerTypeEnum::Portable:
context << PortableInstall;
break;
default:
THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
}
Expand All @@ -278,6 +284,13 @@ namespace AppInstaller::CLI::Workflow
ReportInstallerResult("MsiInstallProduct"sv, APPINSTALLER_CLI_ERROR_MSI_INSTALL_FAILED);
}

void PortableInstall(Execution::Context& context)
{
context <<
PortableInstallImpl <<
ReportInstallerResult("Portable"sv, APPINSTALLER_CLI_ERROR_PORTABLE_INSTALL_FAILED, true);
}

void MsixInstall(Execution::Context& context)
{
std::string uri;
Expand Down Expand Up @@ -398,6 +411,12 @@ namespace AppInstaller::CLI::Workflow
Workflow::InstallPackageInstaller;
}

void EnsureSupportForInstall(Execution::Context& context)
{
context <<
Workflow::EnsureSupportForPortableInstall;
}

void InstallMultiplePackages::operator()(Execution::Context& context) const
{
if (m_ensurePackageAgreements)
Expand Down
12 changes: 12 additions & 0 deletions src/AppInstallerCLICore/Workflows/InstallFlow.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ namespace AppInstaller::CLI::Workflow
// Outputs: None
void MsixInstall(Execution::Context& context);

// Runs the flow for installing a Portable package.
// Required Args: None
// Inputs: Installer, InstallerPath
// Outputs: None
void PortableInstall(Execution::Context& context);

// Verifies parameters for install to ensure success.
// Required Args: None
// Inputs:
// Outputs: None
void EnsureSupportForInstall(Execution::Context& context);

// Reports the return code returned by the installer.
// Required Args: None
// Inputs: Manifest, Installer, InstallerResult
Expand Down
Loading