Skip to content

Commit

Permalink
Download command (#3376)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryfu-msft committed Jul 15, 2023
1 parent d637f0e commit fb3650a
Show file tree
Hide file tree
Showing 75 changed files with 1,876 additions and 136 deletions.
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
},
```
1 change: 1 addition & 0 deletions doc/windows/package-manager/winget/returnCodes.md
Expand Up @@ -118,6 +118,7 @@ ms.localizationpriority: medium
| 0x8A150068 | -1978335128 | APPINSTALLER_CLI_ERROR_PACKAGE_IS_PINNED | The package has a pin that prevents upgrade. |
| 0x8A150069 | -1978335127 | APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB | The package currently installed is the stub package |
| 0x8A15006A | -1978335126 | APPINSTALLER_CLI_ERROR_APPTERMINATION_RECEIVED | Application shutdown signal received |
| 0x8A15006B | -1978335125 | APPINSTALLER_CLI_ERROR_DOWNLOAD_DEPENDENCIES | Failed to download package dependencies. |

## Install errors.

Expand Down
18 changes: 17 additions & 1 deletion schemas/JSON/settings/settings.schema.0.2.json
Expand Up @@ -136,7 +136,18 @@
"purgePortablePackage": {
"description": "Controls whether the default behavior for uninstall removes all files and directories relevant to this package. Only applies to the portable installerType.",
"type": "boolean",
"default": false
"default": false
}
}
},
"DownloadBehavior": {
"description": "Download settings",
"type": "object",
"properties": {
"defaultDownloadDirectory": {
"description": "The default directory where installers are downloaded to.",
"type": "string",
"default": "%USERPROFILE%/Downloads/"
}
}
},
Expand Down Expand Up @@ -213,6 +224,11 @@
"description": "Enable support for configuration",
"type": "boolean",
"default": false
},
"download": {
"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 @@ -358,6 +358,7 @@
<ClInclude Include="Commands\ConfigureTestCommand.h" />
<ClInclude Include="Commands\ConfigureValidateCommand.h" />
<ClInclude Include="Commands\DebugCommand.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 @@ -425,6 +426,7 @@
<ClCompile Include="Commands\ConfigureTestCommand.cpp" />
<ClCompile Include="Commands\ConfigureValidateCommand.cpp" />
<ClCompile Include="Commands\DebugCommand.cpp" />
<ClCompile Include="Commands\DownloadCommand.cpp" />
<ClCompile Include="Commands\ImportCommand.cpp" />
<ClCompile Include="Commands\PinCommand.cpp" />
<ClCompile Include="Commands\TestCommand.cpp" />
Expand Down
6 changes: 6 additions & 0 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
Expand Up @@ -224,6 +224,9 @@
<ClInclude Include="Commands\TestCommand.h">
<Filter>Commands</Filter>
</ClInclude>
<ClInclude Include="Commands\DownloadCommand.h">
<Filter>Commands</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
Expand Down Expand Up @@ -415,6 +418,9 @@
<ClCompile Include="Commands\TestCommand.cpp">
<Filter>Commands</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 };
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
7 changes: 5 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,17 @@ namespace AppInstaller::CLI
Workflow::EnsureApplicableInstaller <<
Workflow::ReportIdentityAndInstallationDisclaimer <<
Workflow::ShowPromptsForSinglePackage(/* ensureAcceptance */ true) <<
Workflow::ManageDependencies << // TODO: Separate handling dependencies from download flow.
Workflow::SetDownloadDirectory <<
Workflow::DownloadPackageDependencies <<
Workflow::DownloadInstaller;
}

// IMPORTANT: To use this command, the caller should have already executed the COMDownloadCommand
void COMInstallCommand::ExecuteInternal(Context& context) const
{
context <<
Workflow::ReverifyInstallerHash <<
Workflow::InstallDependencies <<
Workflow::ReverifyInstallerHash <<
Workflow::InstallPackageInstaller;
}

Expand Down
91 changes: 91 additions & 0 deletions src/AppInstallerCLICore/Commands/DownloadCommand.cpp
@@ -0,0 +1,91 @@
// 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/PromptFlow.h"
#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::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::InstallerDownloadOnly);

if (context.Args.Contains(Execution::Args::Type::Manifest))
{
context <<
Workflow::ReportExecutionStage(ExecutionStage::Discovery) <<
Workflow::GetManifestFromArg;
}
else
{
context <<
Workflow::ReportExecutionStage(ExecutionStage::Discovery) <<
Workflow::OpenSource() <<
Workflow::SearchSourceForSingle <<
Workflow::HandleSearchResultFailures <<
Workflow::EnsureOneMatchFromSearchResult(OperationType::Download) <<
Workflow::GetManifestFromPackage(false);
}

context <<
Workflow::SetDownloadDirectory <<
Workflow::SelectInstaller <<
Workflow::EnsureApplicableInstaller <<
Workflow::ReportIdentityAndInstallationDisclaimer <<
Workflow::ShowPromptsForSinglePackage(/* ensureAcceptance */ true) <<
Workflow::DownloadPackageDependencies <<
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;
};
}
4 changes: 2 additions & 2 deletions src/AppInstallerCLICore/Commands/InstallCommand.cpp
Expand Up @@ -132,8 +132,8 @@ namespace AppInstaller::CLI
Workflow::GetMultiSearchRequests <<
Workflow::SearchSubContextsForSingle() <<
Workflow::ReportExecutionStage(Workflow::ExecutionStage::Execution) <<
Workflow::InstallMultiplePackages(
Resource::String::InstallAndUpgradeCommandsReportDependencies,
Workflow::ProcessMultiplePackages(
Resource::String::PackageRequiresDependencies,
APPINSTALLER_CLI_ERROR_MULTIPLE_INSTALL_FAILED);
}
else
Expand Down
3 changes: 3 additions & 0 deletions src/AppInstallerCLICore/Commands/RootCommand.cpp
Expand Up @@ -23,6 +23,7 @@
#include "ConfigureCommand.h"
#include "DebugCommand.h"
#include "TestCommand.h"
#include "DownloadCommand.h"

#include "Resources.h"
#include "TableOutput.h"
Expand Down Expand Up @@ -140,6 +141,7 @@ namespace AppInstaller::CLI
keyDirectories.OutputLine({ Resource::LocString{ Resource::String::PortableRootUser }, Runtime::GetPathTo(Runtime::PathName::PortablePackageUserRoot, true).u8string() });
keyDirectories.OutputLine({ Resource::LocString{ Resource::String::PortableRoot }, Runtime::GetPathTo(Runtime::PathName::PortablePackageMachineRoot, true).u8string() });
keyDirectories.OutputLine({ Resource::LocString{ Resource::String::PortableRoot86 }, Runtime::GetPathTo(Runtime::PathName::PortablePackageMachineRootX86, true).u8string() });
keyDirectories.OutputLine({ Resource::LocString{ Resource::String::InstallerDownloads }, Runtime::GetPathTo(Runtime::PathName::UserProfileDownloads, true).u8string() });
keyDirectories.Complete();
context.Reporter.Info() << std::endl;
}
Expand Down Expand Up @@ -177,6 +179,7 @@ namespace AppInstaller::CLI
std::make_unique<ImportCommand>(FullName()),
std::make_unique<PinCommand>(FullName()),
std::make_unique<ConfigureCommand>(FullName()),
std::make_unique<DownloadCommand>(FullName()),
#if _DEBUG
std::make_unique<DebugCommand>(FullName()),
#endif
Expand Down
4 changes: 2 additions & 2 deletions src/AppInstallerCLICore/Commands/UpgradeCommand.cpp
Expand Up @@ -202,8 +202,8 @@ namespace AppInstaller::CLI
Workflow::GetMultiSearchRequests <<
Workflow::SearchSubContextsForSingle(OperationType::Upgrade) <<
Workflow::ReportExecutionStage(Workflow::ExecutionStage::Execution) <<
Workflow::InstallMultiplePackages(
Resource::String::InstallAndUpgradeCommandsReportDependencies,
Workflow::ProcessMultiplePackages(
Resource::String::PackageRequiresDependencies,
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

0 comments on commit fb3650a

Please sign in to comment.