Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 18 additions & 13 deletions src/AppInstallerCLICore/Commands/UpgradeCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ namespace AppInstaller::CLI
// either for upgrading or for listing available upgrades.
bool HasArgumentsForMultiplePackages(Execution::Args& execArgs)
{
return execArgs.Contains(Args::Type::All);
return execArgs.Contains(Args::Type::All) ||
execArgs.Contains(Args::Type::IncludeUnknown);
}

// Determines whether there are any arguments only used as options during an upgrade,
Expand Down Expand Up @@ -102,7 +103,7 @@ namespace AppInstaller::CLI
Argument::ForType(Args::Type::AcceptSourceAgreements),
Argument::ForType(Execution::Args::Type::CustomHeader),
Argument{ "all", Argument::NoAlias, Args::Type::All, Resource::String::UpdateAllArgumentDescription, ArgumentType::Flag },
Argument{ "include-unknown", Argument::NoAlias, Args::Type::IncludeUnknown, Resource::String::IncludeUnknownArgumentDescription, ArgumentType::Flag }
Argument{ "include-unknown", Argument::NoAlias, Args::Type::IncludeUnknown, Resource::String::IncludeUnknownArgumentDescription, ArgumentType::Flag },
};
}

Expand Down Expand Up @@ -158,25 +159,29 @@ namespace AppInstaller::CLI

void UpgradeCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const
{
if (execArgs.Contains(Execution::Args::Type::Manifest) &&
if (execArgs.Contains(Execution::Args::Type::Manifest) &&
(HasSearchQueryArguments(execArgs) ||
HasArgumentsForMultiplePackages(execArgs) ||
HasArgumentsForSource(execArgs)))
HasArgumentsForMultiplePackages(execArgs) ||
HasArgumentsForSource(execArgs)))
{
throw CommandException(Resource::String::BothManifestAndSearchQueryProvided);
}


else if (!ShouldListUpgrade(execArgs)
&& !HasSearchQueryArguments(execArgs)
&& (execArgs.Contains(Args::Type::Log) ||
execArgs.Contains(Args::Type::Override) ||
execArgs.Contains(Args::Type::InstallLocation) ||
execArgs.Contains(Args::Type::HashOverride) ||
execArgs.Contains(Args::Type::AcceptPackageAgreements)))
if (!ShouldListUpgrade(execArgs)
&& !HasSearchQueryArguments(execArgs)
&& (execArgs.Contains(Args::Type::Log) ||
execArgs.Contains(Args::Type::Override) ||
execArgs.Contains(Args::Type::InstallLocation) ||
execArgs.Contains(Args::Type::HashOverride) ||
execArgs.Contains(Args::Type::AcceptPackageAgreements)))
{
throw CommandException(Resource::String::InvalidArgumentWithoutQueryError);
}

if (HasArgumentsForSinglePackage(execArgs) && HasArgumentsForMultiplePackages(execArgs))
{
throw CommandException(Resource::String::IncompatibleArgumentsProvided);
}
}

void UpgradeCommand::ExecuteInternal(Execution::Context& context) const
Expand Down
6 changes: 4 additions & 2 deletions src/AppInstallerCLICore/ExecutionArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,11 @@ namespace AppInstaller::CLI::Execution
AdminSettingEnable,
AdminSettingDisable,

// Upgrade Command
All, // Update all installed packages to latest
IncludeUnknown, // Allow upgrades of packages with unknown versions

// Other
All, // Used in Update command to update all installed packages to latest
ListVersions, // Used in Show command to list all available versions of an app
NoVT, // Disable VirtualTerminal outputs
RetroStyle, // Makes progress display as retro
Expand All @@ -91,7 +94,6 @@ namespace AppInstaller::CLI::Execution
DependencySource, // Index source to be queried against for finding dependencies
CustomHeader, // Optional Rest source header
AcceptSourceAgreements, // Accept all source agreements
IncludeUnknown, // Used in Upgrade command to allow upgrades of packages with unknown versions
Wait, // Prompts the user to press any key before exiting

// Used for demonstration purposes
Expand Down
6 changes: 4 additions & 2 deletions src/AppInstallerCLICore/Resources.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(ImportSearchFailed);
WINGET_DEFINE_RESOURCE_STRINGID(ImportSourceNotInstalled);
WINGET_DEFINE_RESOURCE_STRINGID(IncludeUnknownArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(IncompatibleArgumentsProvided);
WINGET_DEFINE_RESOURCE_STRINGID(InstallAndUpgradeCommandsReportDependencies);
WINGET_DEFINE_RESOURCE_STRINGID(InstallArchitectureArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(InstallationAbandoned);
Expand Down Expand Up @@ -372,12 +373,13 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(UnsupportedArgument);
WINGET_DEFINE_RESOURCE_STRINGID(UpdateAllArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(UpdateNotApplicable);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeAvailableForPinned);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeCommandLongDescription);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeCommandShortDescription);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeDifferentInstallTechnology);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeDifferentInstallTechnologyInNewerVersions);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeUnknownCount);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeUnknownCountSingle);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeRequireExplicitCount);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeUnknownVersionCount);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeUnknownVersionExplanation);
WINGET_DEFINE_RESOURCE_STRINGID(Usage);
WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandLongDescription);
Expand Down
109 changes: 71 additions & 38 deletions src/AppInstallerCLICore/Workflows/UpdateFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,32 +44,41 @@ namespace AppInstaller::CLI::Workflow
bool updateFound = false;
bool installedTypeInapplicable = false;

if (!installedVersion.IsUnknown() || context.Args.Contains(Execution::Args::Type::IncludeUnknown))
if (installedVersion.IsUnknown() && !context.Args.Contains(Execution::Args::Type::IncludeUnknown))
{
// The version keys should have already been sorted by version
const auto& versionKeys = package->GetAvailableVersionKeys();
for (const auto& key : versionKeys)
// the package has an unknown version and the user did not request to upgrade it anyway.
if (m_reportUpdateNotFound)
{
// Check Update Version
if (IsUpdateVersionApplicable(installedVersion, Utility::Version(key.Version)))
{
auto packageVersion = package->GetAvailableVersion(key);
auto manifest = packageVersion->GetManifest();
context.Reporter.Info() << Resource::String::UpgradeUnknownVersionExplanation << std::endl;
}

AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
}

// The version keys should have already been sorted by version
const auto& versionKeys = package->GetAvailableVersionKeys();
for (const auto& key : versionKeys)
{
// Check Update Version
if (IsUpdateVersionApplicable(installedVersion, Utility::Version(key.Version)))
{
auto packageVersion = package->GetAvailableVersion(key);
auto manifest = packageVersion->GetManifest();

// Check applicable Installer
auto [installer, inapplicabilities] = manifestComparator.GetPreferredInstaller(manifest);
if (!installer.has_value())
// Check applicable Installer
auto [installer, inapplicabilities] = manifestComparator.GetPreferredInstaller(manifest);
if (!installer.has_value())
{
// If there is at least one installer whose only reason is InstalledType.
auto onlyInstalledType = std::find(inapplicabilities.begin(), inapplicabilities.end(), InapplicabilityFlags::InstalledType);
if (onlyInstalledType != inapplicabilities.end())
{
// If there is at least one installer whose only reason is InstalledType.
auto onlyInstalledType = std::find(inapplicabilities.begin(), inapplicabilities.end(), InapplicabilityFlags::InstalledType);
if (onlyInstalledType != inapplicabilities.end())
{
installedTypeInapplicable = true;
}

continue;
installedTypeInapplicable = true;
}

continue;
}

Logging::Telemetry().LogSelectedInstaller(
static_cast<int>(installer->Arch),
installer->Url,
Expand All @@ -88,25 +97,16 @@ namespace AppInstaller::CLI::Workflow
context.Add<Execution::Data::PackageVersion>(std::move(packageVersion));
context.Add<Execution::Data::Installer>(std::move(installer));

updateFound = true;
break;
}
else
{
// Any following versions are not applicable
break;
}
updateFound = true;
break;
}
}
else
{
// the package has an unknown version and the user did not request to upgrade it anyway.
if (m_reportUpdateNotFound)
else
{
context.Reporter.Info() << Resource::String::UpgradeUnknownVersionExplanation << std::endl;
// Any following versions are not applicable
break;
}
AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
}

if (!updateFound)
{
if (m_reportUpdateNotFound)
Expand Down Expand Up @@ -143,7 +143,8 @@ namespace AppInstaller::CLI::Workflow
const auto& matches = context.Get<Execution::Data::SearchResult>().Matches;
std::vector<std::unique_ptr<Execution::Context>> packagesToInstall;
bool updateAllFoundUpdate = false;
int unknownPackagesCount = 0;
int packagesWithUnknownVersionSkipped = 0;
int packagesThatRequireExplicitSkipped = 0;

for (const auto& match : matches)
{
Expand All @@ -154,15 +155,17 @@ namespace AppInstaller::CLI::Workflow
auto installedVersion = match.Package->GetInstalledVersion();

updateContext.Add<Execution::Data::Package>(match.Package);


// Filter out packages with unknown installed versions
if (context.Args.Contains(Execution::Args::Type::IncludeUnknown))
{
updateContext.Args.AddArg(Execution::Args::Type::IncludeUnknown);
}
else if (Utility::Version(installedVersion->GetProperty(PackageVersionProperty::Version)).IsUnknown())
{
// we don't know what the package's version is and the user didn't ask to upgrade it anyway.
unknownPackagesCount++;
AICLI_LOG(CLI, Info, << "Skipping " << match.Package->GetProperty(PackageProperty::Id) << " as it has unknown installed version");
++packagesWithUnknownVersionSkipped;
continue;
}

Expand All @@ -176,6 +179,24 @@ namespace AppInstaller::CLI::Workflow
continue;
}

// Filter out packages that require explicit upgrades.
// We require explicit upgrades only if the installed version is pinned,
// either because it was manually pinned or because the manifest indicated
// RequireExplicitUpgrade.
// Note that this does not consider whether the update to be installed has
// RequireExplicitUpgrade. While this has the downside of not working with
// packages installed from another source, it ensures consistency with the
// list of available updates (there we don't have the selected installer)
// and at most we will update each package like this once.
auto installedMetadata = updateContext.Get<Execution::Data::InstalledPackageVersion>()->GetMetadata();
auto pinnedState = ConvertToPackagePinnedStateEnum(installedMetadata[PackageVersionMetadata::PinnedState]);
if (pinnedState != PackagePinnedState::NotPinned)
{
AICLI_LOG(CLI, Info, << "Skipping " << match.Package->GetProperty(PackageProperty::Id) << " as it requires explicit upgrade");
++packagesThatRequireExplicitSkipped;
continue;
}

updateAllFoundUpdate = true;

AddToPackagesToInstallIfNotPresent(packagesToInstall, std::move(updateContextPtr));
Expand All @@ -191,5 +212,17 @@ namespace AppInstaller::CLI::Workflow
APPINSTALLER_CLI_ERROR_UPDATE_ALL_HAS_FAILURE,
{ APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE });
}

if (packagesWithUnknownVersionSkipped > 0)
{
AICLI_LOG(CLI, Info, << packagesWithUnknownVersionSkipped << " package(s) skipped due to unknown installed version");
context.Reporter.Info() << packagesWithUnknownVersionSkipped << " " << Resource::String::UpgradeUnknownVersionCount << std::endl;
}

if (packagesThatRequireExplicitSkipped > 0)
{
AICLI_LOG(CLI, Info, << packagesThatRequireExplicitSkipped << " package(s) skipped due to requiring explicit upgrade");
context.Reporter.Info() << packagesThatRequireExplicitSkipped << " " << Resource::String::UpgradeRequireExplicitCount << std::endl;
}
}
}
Loading