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

Download command #3376

Merged
merged 37 commits into from Jul 15, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
8b43d55
save work
Jun 2, 2023
d178be5
Merge branch 'master' of https://github.com/ryfu-msft/winget-cli into…
Jun 8, 2023
8557576
initial implementation for download command
Jun 9, 2023
99ea470
remove context flag, add installerType argument
Jun 13, 2023
7c413a9
save work for com and e2e tests
Jun 15, 2023
32dab46
add E2E tests
Jun 15, 2023
c019ba7
save work
Jun 16, 2023
ed07786
remove force arg
Jun 16, 2023
89b1f07
save work
Jun 20, 2023
33f06bb
save work
Jun 22, 2023
0b27a2e
cleanup com and add tests
Jun 22, 2023
c315bd3
resolve merge conflicts
Jun 22, 2023
bf8a107
fix out of proc com registration
Jun 22, 2023
5fa8f61
resolve merge conflicts
Jun 23, 2023
d78ed8d
fix tests and refactor
Jun 26, 2023
dedb403
revert comment change
Jun 26, 2023
a458ddb
refresh path variable
Jun 26, 2023
1444fc2
try again
Jun 26, 2023
23465bd
try again diagnostic
Jun 27, 2023
8bda4bb
use packagemanagersettings for inproc
Jun 27, 2023
51f6b56
manually set experimental feature for inproc com
Jun 27, 2023
3d47c21
try again
Jun 27, 2023
be14705
address initial comments
Jun 28, 2023
8b849e4
remove experimental feature
Jun 28, 2023
bd8c1ed
fix line spacing
Jun 28, 2023
a7a9eee
revert and fix logs
Jun 28, 2023
64dd740
configure settings at start
Jun 29, 2023
5afd9a2
fix COM E2E test and revert pipeline yml
Jun 29, 2023
790dae7
change locale test manifest
Jun 29, 2023
14474b8
resolve merge conflicts
Jun 29, 2023
fbb8224
fix locale test
Jun 30, 2023
05757f8
address PR feedback
Jul 8, 2023
43b76b3
resolve ambiguous reference
Jul 10, 2023
5ec8894
fix hang
Jul 10, 2023
24e7573
resolve merge conflicts
Jul 10, 2023
4613a22
address final comments
Jul 15, 2023
78e68db
move installer download check
Jul 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions doc/Settings.md
Expand Up @@ -281,4 +281,15 @@ You can enable the feature as shown below.
"experimentalFeatures": {
"windowsFeature": true
},
```

### download

This feature enables the download command. This command allows users to download the installers of a specified package.
You can enable the feature as shown below.

```json
"experimentalFeatures": {
"download": true
},
```
5 changes: 5 additions & 0 deletions schemas/JSON/settings/settings.schema.0.2.json
Expand Up @@ -213,6 +213,11 @@
"description": "Enable support for configuration",
"type": "boolean",
"default": false
},
"downloadCommand": {
ryfu-msft marked this conversation as resolved.
Show resolved Hide resolved
"description": "Enable support for the download command",
"type": "boolean",
"default": false
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
Expand Up @@ -345,6 +345,7 @@
<ClInclude Include="Commands\ConfigureShowCommand.h" />
<ClInclude Include="Commands\ConfigureTestCommand.h" />
<ClInclude Include="Commands\ConfigureValidateCommand.h" />
<ClInclude Include="Commands\DownloadCommand.h" />
<ClInclude Include="Commands\ExperimentalCommand.h" />
<ClInclude Include="Commands\ExportCommand.h" />
<ClInclude Include="Commands\ImportCommand.h" />
Expand Down Expand Up @@ -410,6 +411,7 @@
<ClCompile Include="Commands\ConfigureShowCommand.cpp" />
<ClCompile Include="Commands\ConfigureTestCommand.cpp" />
<ClCompile Include="Commands\ConfigureValidateCommand.cpp" />
<ClCompile Include="Commands\DownloadCommand.cpp" />
<ClCompile Include="Commands\ImportCommand.cpp" />
<ClCompile Include="Commands\PinCommand.cpp" />
<ClCompile Include="ConfigurationContext.cpp" />
Expand Down
6 changes: 6 additions & 0 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
Expand Up @@ -218,6 +218,9 @@
<ClInclude Include="ConfigurationSetProcessorFactoryRemoting.h">
<Filter>Workflows</Filter>
</ClInclude>
<ClInclude Include="Commands\DownloadCommand.h">
<Filter>Commands</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
Expand Down Expand Up @@ -403,6 +406,9 @@
<ClCompile Include="ConfigurationSetProcessorFactoryRemoting.cpp">
<Filter>Workflows</Filter>
</ClCompile>
<ClInclude Include="Commands\DownloadCommand.cpp">
<Filter>Commands</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
Expand Down
13 changes: 11 additions & 2 deletions src/AppInstallerCLICore/Argument.cpp
Expand Up @@ -77,6 +77,8 @@ namespace AppInstaller::CLI
return { type, "scope"_liv, ArgTypeCategory::InstallerSelection | ArgTypeCategory::CopyValueToSubContext };
case Execution::Args::Type::InstallArchitecture:
return { type, "architecture"_liv, 'a', ArgTypeCategory::InstallerSelection | ArgTypeCategory::CopyValueToSubContext };
case Execution::Args::Type::InstallerType:
return { type, "installer-type"_liv, ArgTypeCategory::InstallerSelection };
ryfu-msft marked this conversation as resolved.
Show resolved Hide resolved
case Execution::Args::Type::HashOverride:
return { type, "ignore-security-hash"_liv, ArgTypeCategory::InstallerBehavior | ArgTypeCategory::CopyFlagToSubContext };
case Execution::Args::Type::IgnoreLocalArchiveMalwareScan:
Expand Down Expand Up @@ -164,7 +166,6 @@ namespace AppInstaller::CLI
case Execution::Args::Type::Upgrade:
return { type, "upgrade-available"_liv};


// Pin command
case Execution::Args::Type::GatedVersion:
return { type, "version"_liv, 'v', ArgTypeCategory::None, ArgTypeExclusiveSet::PinType };
Expand All @@ -183,6 +184,10 @@ namespace AppInstaller::CLI
case Execution::Args::Type::ConfigurationDisable:
return { type, "disable"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::StubType };

// Download command
case Execution::Args::Type::DownloadDirectory:
return { type, "download-directory"_liv, 'd', ArgTypeCategory::None };

// Common arguments
case Execution::Args::Type::NoVT:
return { type, "no-vt"_liv, ArgTypeCategory::None, ArgTypeExclusiveSet::ProgressBarOption };
Expand Down Expand Up @@ -271,7 +276,7 @@ namespace AppInstaller::CLI
case Args::Type::Locale:
return Argument{ type, Resource::String::LocaleArgumentDescription, ArgumentType::Standard };
case Args::Type::InstallArchitecture:
return Argument{ type, Resource::String::InstallArchitectureArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help };
return Argument{ type, Resource::String::ArchitectureArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help };
case Args::Type::Log:
return Argument{ type, Resource::String::LogArgumentDescription, ArgumentType::Standard };
case Args::Type::CustomSwitches:
Expand Down Expand Up @@ -336,6 +341,10 @@ namespace AppInstaller::CLI
return Argument{ type, Resource::String::UninstallPreviousArgumentDescription, ArgumentType::Flag, Argument::Visibility::Help };
case Args::Type::Force:
return Argument{ type, Resource::String::ForceArgumentDescription, ArgumentType::Flag, false };
case Args::Type::DownloadDirectory:
return Argument{ type, Resource::String::DownloadDirectoryArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help, false };
case Args::Type::InstallerType:
return Argument{ type, Resource::String::InstallerTypeArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help, false };
default:
THROW_HR(E_UNEXPECTED);
}
Expand Down
9 changes: 9 additions & 0 deletions src/AppInstallerCLICore/Command.cpp
Expand Up @@ -720,6 +720,15 @@ namespace AppInstaller::CLI
}
}

if (execArgs.Contains(Execution::Args::Type::InstallerType))
{
Manifest::InstallerTypeEnum selectedInstallerType = Manifest::ConvertToInstallerTypeEnum(std::string(execArgs.GetArg(Execution::Args::Type::InstallerType)));
if (selectedInstallerType == Manifest::InstallerTypeEnum::Unknown)
{
throw CommandException(Resource::String::InvalidArgumentValueErrorWithoutValidValues(Argument::ForType(Execution::Args::Type::InstallerType).Name()));
}
}

Argument::ValidateExclusiveArguments(execArgs);

ValidateArgumentsInternal(execArgs);
Expand Down
11 changes: 9 additions & 2 deletions src/AppInstallerCLICore/Commands/COMCommand.cpp
Expand Up @@ -7,6 +7,7 @@
#include "Workflows/PromptFlow.h"
#include "Workflows/UninstallFlow.h"
#include "Workflows/WorkflowBase.h"
#include "Workflows/DependenciesFlow.h"

namespace AppInstaller::CLI
{
Expand All @@ -24,15 +25,21 @@ namespace AppInstaller::CLI
Workflow::EnsureApplicableInstaller <<
Workflow::ReportIdentityAndInstallationDisclaimer <<
Workflow::ShowPromptsForSinglePackage(/* ensureAcceptance */ true) <<
Workflow::ManageDependencies << // TODO: Separate handling dependencies from download flow.
Workflow::SetDownloadDirectory <<
Workflow::DownloadPackageDependencies(/* includeInstalledPackages */ WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::DownloadInstallerOnly)) <<
Workflow::DownloadInstaller;
}

// IMPORTANT: To use this command, the caller should have already executed the COMDownloadCommand
void COMInstallCommand::ExecuteInternal(Context& context) const
{
// We don't build dependency graph as that should have already been built from the COMDownloadCommand.
context <<
Workflow::ReverifyInstallerHash <<
Workflow::GetDependenciesFromInstaller <<
Workflow::ReportDependencies(Resource::String::InstallAndUpgradeCommandsReportDependencies) <<
Workflow::EnableWindowsFeaturesDependencies <<
Workflow::ProcessMultiplePackages(Resource::String::InstallAndUpgradeCommandsReportDependencies, APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES, {}, false, true, true) <<
Workflow::ReverifyInstallerHash <<
Workflow::InstallPackageInstaller;
}

Expand Down
106 changes: 106 additions & 0 deletions src/AppInstallerCLICore/Commands/DownloadCommand.cpp
@@ -0,0 +1,106 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "pch.h"
#include "DownloadCommand.h"
#include "Workflows/DownloadFlow.h"
#include "Workflows/InstallFlow.h"
#include "Workflows/MultiQueryFlow.h"
#include "Workflows/PromptFlow.h"
ryfu-msft marked this conversation as resolved.
Show resolved Hide resolved
#include "Resources.h"
#include <AppInstallerRuntime.h>

namespace AppInstaller::CLI
{
using namespace AppInstaller::CLI::Execution;
using namespace AppInstaller::CLI::Workflow;
using namespace AppInstaller::Utility::literals;

std::vector<Argument> DownloadCommand::GetArguments() const
{
return {
Argument::ForType(Args::Type::Query),
Argument::ForType(Args::Type::DownloadDirectory),
Argument::ForType(Args::Type::Manifest),
Argument::ForType(Args::Type::Id),
Argument::ForType(Args::Type::Name),
Argument::ForType(Args::Type::Moniker),
Argument::ForType(Args::Type::Version),
Argument::ForType(Args::Type::Channel),
Argument::ForType(Args::Type::Source),
Argument{ Args::Type::InstallScope, Resource::String::InstallScopeDescription, ArgumentType::Standard, Argument::Visibility::Help },
Argument::ForType(Args::Type::InstallArchitecture),
Argument::ForType(Args::Type::InstallerType),
Argument::ForType(Args::Type::Exact),
Argument::ForType(Args::Type::Locale),
Argument::ForType(Args::Type::Log),
ryfu-msft marked this conversation as resolved.
Show resolved Hide resolved
Argument::ForType(Args::Type::HashOverride),
Argument::ForType(Args::Type::SkipDependencies),
Argument::ForType(Execution::Args::Type::AcceptPackageAgreements),
Argument::ForType(Execution::Args::Type::AcceptSourceAgreements),
};
}

Resource::LocString DownloadCommand::ShortDescription() const
{
return { Resource::String::DownloadCommandShortDescription };
}

Resource::LocString DownloadCommand::LongDescription() const
{
return { Resource::String::DownloadCommandLongDescription };
}

Utility::LocIndView DownloadCommand::HelpLink() const
{
return "https://aka.ms/winget-command-download"_liv;
}

void DownloadCommand::ValidateArgumentsInternal(Args& execArgs) const
{
Argument::ValidateCommonArguments(execArgs);
}

void DownloadCommand::ExecuteInternal(Context& context) const
{
context.SetFlags(AppInstaller::CLI::Execution::ContextFlag::DownloadInstallerOnly);

if (context.Args.Contains(Execution::Args::Type::Manifest))
{
context <<
Workflow::ReportExecutionStage(ExecutionStage::Discovery) <<
Workflow::GetManifestFromArg;
}
else
{
context <<
Workflow::ReportExecutionStage(ExecutionStage::Discovery) <<
Workflow::OpenSource();

if (!context.Args.Contains(Execution::Args::Type::Force))
{
context <<
Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed, false, Repository::CompositeSearchBehavior::AvailablePackages);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need the installed source for download command? Including the installed source will trigger complex correlation logic. And it does not help with the download.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed installed source since the download command should consider both installed and uninstalled packages.

}

context <<
Workflow::SearchSourceForSingle <<
Workflow::HandleSearchResultFailures <<
Workflow::EnsureOneMatchFromSearchResult(OperationType::Download) <<
Workflow::GetManifestFromPackage(false);
}

if (context.IsTerminated())
{
return;
ryfu-msft marked this conversation as resolved.
Show resolved Hide resolved
}

context <<
Workflow::SetDownloadDirectory <<
Workflow::SelectInstaller <<
Workflow::EnsureApplicableInstaller <<
ryfu-msft marked this conversation as resolved.
Show resolved Hide resolved
Workflow::ReportIdentityAndInstallationDisclaimer <<
ryfu-msft marked this conversation as resolved.
Show resolved Hide resolved
Workflow::ShowPromptsForSinglePackage(/* ensureAcceptance */ true) <<
ryfu-msft marked this conversation as resolved.
Show resolved Hide resolved
Workflow::DownloadPackageDependencies(/* includeInstalledPackages */ true) <<
Workflow::DownloadInstaller;
}
}
23 changes: 23 additions & 0 deletions src/AppInstallerCLICore/Commands/DownloadCommand.h
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "Command.h"

namespace AppInstaller::CLI
{
struct DownloadCommand final : public Command
{
DownloadCommand(std::string_view parent) : Command("download", {} /* aliases */, parent, Settings::ExperimentalFeature::Feature::Download) {}

std::vector<Argument> GetArguments() const override;

Resource::LocString ShortDescription() const override;
Resource::LocString LongDescription() const override;

Utility::LocIndView HelpLink() const override;

protected:
void ValidateArgumentsInternal(Execution::Args& execArgs) const override;
void ExecuteInternal(Execution::Context& context) const override;
};
}
2 changes: 1 addition & 1 deletion src/AppInstallerCLICore/Commands/InstallCommand.cpp
Expand Up @@ -132,7 +132,7 @@ namespace AppInstaller::CLI
Workflow::GetMultiSearchRequests <<
Workflow::SearchSubContextsForSingle() <<
Workflow::ReportExecutionStage(Workflow::ExecutionStage::Execution) <<
Workflow::InstallMultiplePackages(
Workflow::ProcessMultiplePackages(
Resource::String::InstallAndUpgradeCommandsReportDependencies,
APPINSTALLER_CLI_ERROR_MULTIPLE_INSTALL_FAILED);
}
Expand Down
2 changes: 2 additions & 0 deletions src/AppInstallerCLICore/Commands/RootCommand.cpp
Expand Up @@ -21,6 +21,7 @@
#include "ImportCommand.h"
#include "PinCommand.h"
#include "ConfigureCommand.h"
#include "DownloadCommand.h"

#include "Resources.h"
#include "TableOutput.h"
Expand Down Expand Up @@ -175,6 +176,7 @@ namespace AppInstaller::CLI
std::make_unique<ImportCommand>(FullName()),
std::make_unique<PinCommand>(FullName()),
std::make_unique<ConfigureCommand>(FullName()),
std::make_unique<DownloadCommand>(FullName()),
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/AppInstallerCLICore/Commands/UpgradeCommand.cpp
Expand Up @@ -202,7 +202,7 @@ namespace AppInstaller::CLI
Workflow::GetMultiSearchRequests <<
Workflow::SearchSubContextsForSingle(OperationType::Upgrade) <<
Workflow::ReportExecutionStage(Workflow::ExecutionStage::Execution) <<
Workflow::InstallMultiplePackages(
Workflow::ProcessMultiplePackages(
Resource::String::InstallAndUpgradeCommandsReportDependencies,
APPINSTALLER_CLI_ERROR_MULTIPLE_INSTALL_FAILED);
}
Expand Down
8 changes: 8 additions & 0 deletions src/AppInstallerCLICore/ContextOrchestrator.cpp
Expand Up @@ -362,6 +362,7 @@ namespace AppInstaller::CLI::Execution
case PackageOperationType::Install: return "root:install"sv;
case PackageOperationType::Upgrade: return "root:upgrade"sv;
case PackageOperationType::Uninstall: return "root:uninstall"sv;
case PackageOperationType::Download: return "root:download"sv;
default: return "unknown";
}
}
Expand All @@ -386,4 +387,11 @@ namespace AppInstaller::CLI::Execution
std::unique_ptr<OrchestratorQueueItem> item = std::make_unique<OrchestratorQueueItem>(OrchestratorQueueItemId(std::move(packageId), std::move(sourceId)), std::move(context), PackageOperationType::Search);
return item;
}

std::unique_ptr<OrchestratorQueueItem> OrchestratorQueueItemFactory::CreateItemForDownload(std::wstring packageId, std::wstring sourceId, std::unique_ptr<COMContext> context)
{
std::unique_ptr<OrchestratorQueueItem> item = std::make_unique<OrchestratorQueueItem>(OrchestratorQueueItemId(std::move(packageId), std::move(sourceId)), std::move(context), PackageOperationType::Download);
item->AddCommand(std::make_unique<::AppInstaller::CLI::COMDownloadCommand>(RootCommand::CommandName));
return item;
}
}
3 changes: 3 additions & 0 deletions src/AppInstallerCLICore/ContextOrchestrator.h
Expand Up @@ -46,6 +46,7 @@ namespace AppInstaller::CLI::Execution
Install,
Upgrade,
Uninstall,
Download,
};

struct OrchestratorQueueItem
Expand Down Expand Up @@ -98,6 +99,8 @@ namespace AppInstaller::CLI::Execution
static std::unique_ptr<OrchestratorQueueItem> CreateItemForUninstall(std::wstring packageId, std::wstring sourceId, std::unique_ptr<COMContext> context);
// Create queue item for finding existing entry from the orchestrator queue
static std::unique_ptr<OrchestratorQueueItem> CreateItemForSearch(std::wstring packageId, std::wstring sourceId, std::unique_ptr<COMContext> context);
// Create queue item for download
static std::unique_ptr<OrchestratorQueueItem> CreateItemForDownload(std::wstring packageId, std::wstring sourceId, std::unique_ptr<COMContext> context);
};

struct ContextOrchestrator
Expand Down
6 changes: 5 additions & 1 deletion src/AppInstallerCLICore/ExecutionArgs.h
Expand Up @@ -41,11 +41,12 @@ namespace AppInstaller::CLI::Execution
InstallLocation,
InstallScope,
InstallArchitecture,
InstallerType,
HashOverride, // Ignore hash mismatches
SkipDependencies, // Skip dependencies
IgnoreLocalArchiveMalwareScan, // Ignore the local malware scan on archive files
AcceptPackageAgreements, // Accept all license agreements for packages
Rename, // Renames the file of the executable. Only applies to the portable installerType
Rename, // Renames the file of the executable
NoUpgrade, // Install flow should not try to convert to upgrade flow upon finding existing installed version

// Uninstall behavior
Expand Down Expand Up @@ -80,6 +81,9 @@ namespace AppInstaller::CLI::Execution
IgnoreUnavailable,
IgnoreVersions,

// Download Command
DownloadDirectory,

// Setting Command
AdminSettingEnable,
AdminSettingDisable,
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/ExecutionContext.h
Expand Up @@ -65,6 +65,7 @@ namespace AppInstaller::CLI::Execution
ShowSearchResultsOnPartialFailure = 0x10,
DisableInteractivity = 0x40,
BypassIsStoreClientBlockedPolicyCheck = 0x80,
DownloadInstallerOnly = 0x100,
};

DEFINE_ENUM_FLAG_OPERATORS(ContextFlag);
Expand Down