Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 2 additions & 0 deletions src/AppInstallerCLICore/Command.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ namespace AppInstaller::CLI
Command(name, aliases, parent, visibility, Settings::ExperimentalFeature::Feature::None) {}
Command(std::string_view name, std::string_view parent, Settings::ExperimentalFeature::Feature feature) :
Command(name, {}, parent, Command::Visibility::Show, feature) {}
Command(std::string_view name, std::string_view parent, Settings::ExperimentalFeature::Feature feature, CommandOutputFlags outputFlags) :
Command(name, {}, parent, Command::Visibility::Show, feature, Settings::TogglePolicy::Policy::None, outputFlags) {}
Command(std::string_view name, std::vector<std::string_view> aliases, std::string_view parent, Settings::ExperimentalFeature::Feature feature) :
Command(name, aliases, parent, Command::Visibility::Show, feature) {}
Command(std::string_view name, std::vector<std::string_view> aliases, std::string_view parent, Settings::TogglePolicy::Policy groupPolicy) :
Expand Down
54 changes: 53 additions & 1 deletion src/AppInstallerCLICore/Commands/DscCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,22 @@

namespace AppInstaller::CLI
{
namespace
{
Argument GetOutputFileArgument()
{
return { Execution::Args::Type::OutputFile, Resource::String::OutputDirectoryArgumentDescription, ArgumentType::Standard };
}
}

DscCommand::DscCommand(std::string_view parent) :
Command(StaticName(), parent)
{
}

std::vector<std::unique_ptr<Command>> DscCommand::GetCommands() const
{
// These should all derive from DscCommandBase
return InitializeFromMoveOnly<std::vector<std::unique_ptr<Command>>>({
std::make_unique<DscPackageResource>(FullName()),
std::make_unique<DscSourceResource>(FullName()),
Expand All @@ -26,6 +40,16 @@ namespace AppInstaller::CLI
});
}

std::vector<Argument> DscCommand::GetArguments() const
{
std::vector<Argument> result;

result.emplace_back(Execution::Args::Type::DscResourceFunctionManifest, Resource::String::DscResourceFunctionDescriptionManifest, ArgumentType::Flag);
result.emplace_back(GetOutputFileArgument());

return result;
}

Resource::LocString DscCommand::ShortDescription() const
{
return { Resource::String::DscCommandShortDescription };
Expand All @@ -43,6 +67,34 @@ namespace AppInstaller::CLI

void DscCommand::ExecuteInternal(Execution::Context& context) const
{
OutputHelp(context.Reporter);
if (context.Args.Contains(Execution::Args::Type::DscResourceFunctionManifest))
{
std::filesystem::path outputDirectory{ Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::OutputFile)) };
std::filesystem::create_directories(outputDirectory);

std::string filePrefix = Utility::ToLower(DscCommandBase::ModuleName());

for (const auto& command : GetCommands())
{
DscCommandBase* commandBase = static_cast<DscCommandBase*>(command.get());

std::filesystem::path outputPath = outputDirectory;
outputPath /= std::string{ filePrefix }.append(".").append(commandBase->Name()).append(".dsc.resource.json");
commandBase->WriteManifest(context, outputPath);
}
}
else
{
OutputHelp(context.Reporter);
}
}

void DscCommand::ValidateArgumentsInternal(Execution::Args& args) const
{
if (args.Contains(Execution::Args::Type::DscResourceFunctionManifest) &&
!args.Contains(Execution::Args::Type::OutputFile))
{
throw CommandException(Resource::String::RequiredArgError(GetOutputFileArgument().Name()));
}
}
}
4 changes: 3 additions & 1 deletion src/AppInstallerCLICore/Commands/DscCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ namespace AppInstaller::CLI
{
struct DscCommand final : public Command
{
DscCommand(std::string_view parent) : Command(StaticName(), parent, Settings::ExperimentalFeature::Feature::ConfigurationDSCv3) {}
DscCommand(std::string_view parent);

static constexpr std::string_view StaticName() { return "dscv3"sv; };

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

Resource::LocString ShortDescription() const override;
Resource::LocString LongDescription() const override;
Expand All @@ -21,5 +22,6 @@ namespace AppInstaller::CLI

protected:
void ExecuteInternal(Execution::Context& context) const override;
void ValidateArgumentsInternal(Execution::Args& args) const override;
};
}
31 changes: 24 additions & 7 deletions src/AppInstallerCLICore/Commands/DscCommandBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ namespace AppInstaller::CLI
{
namespace
{
constexpr std::string_view s_WingetModuleName = "Microsoft.WinGet"sv;

std::string GetFunctionManifestString(DscFunctions function)
{
THROW_HR_IF(E_INVALIDARG, !WI_IsSingleFlagSet(function));
Expand Down Expand Up @@ -167,7 +165,7 @@ namespace AppInstaller::CLI
}

DscCommandBase::DscCommandBase(std::string_view parent, std::string_view resourceName, DscResourceKind kind, DscFunctions functions, DscFunctionModifiers modifiers) :
Command(resourceName, parent, CommandOutputFlags::IgnoreSettingsWarnings), m_kind(kind), m_functions(functions), m_modifiers(modifiers)
Command(resourceName, parent, Settings::ExperimentalFeature::Feature::ConfigurationDSCv3, CommandOutputFlags::IgnoreSettingsWarnings), m_kind(kind), m_functions(functions), m_modifiers(modifiers)
{
// Limits on current implementation
THROW_HR_IF(E_NOTIMPL, kind != DscResourceKind::Resource);
Expand Down Expand Up @@ -224,14 +222,23 @@ namespace AppInstaller::CLI

WINGET_DSC_FUNCTION_FOREACH(WINGET_DSC_FUNCTION_METHOD);

void DscCommandBase::ResourceFunctionManifest(Execution::Context& context) const
std::string_view DscCommandBase::ModuleName()
{
#ifndef AICLI_DISABLE_TEST_HOOKS
return "Microsoft.WinGet.Dev"sv;
Comment thread
JohnMcPMS marked this conversation as resolved.
Outdated
#else
return "Microsoft.WinGet"sv;
#endif
}

void DscCommandBase::WriteManifest(Execution::Context& context, const std::filesystem::path& filePath) const
{
Json::Value json{ Json::ValueType::objectValue };

// TODO: Move to release schema when released (there should be an aka.ms link as well, but it wasn't active yet)
//json["$schema"] = "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/v3/bundled/resource/manifest.json";
json["$schema"] = "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json";
json["type"] = std::string{ s_WingetModuleName } + '/' + ResourceType();
json["type"] = std::string{ ModuleName() } + '/' + ResourceType();
json["description"] = LongDescription().get();
json["version"] = Runtime::GetClientVersion().get();

Expand All @@ -253,9 +260,9 @@ namespace AppInstaller::CLI
writerBuilder.settings_["indentation"] = " ";
std::string jsonString = Json::writeString(writerBuilder, json);

if (context.Args.Contains(Execution::Args::Type::OutputFile))
if (!filePath.empty())
{
std::ofstream stream{ Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::OutputFile)), std::ios::binary };
std::ofstream stream{ filePath, std::ios::binary };
stream.write(jsonString.c_str(), jsonString.length());
}
else
Expand All @@ -264,6 +271,16 @@ namespace AppInstaller::CLI
}
}

void DscCommandBase::ResourceFunctionManifest(Execution::Context& context) const
{
std::filesystem::path path;
if (context.Args.Contains(Execution::Args::Type::OutputFile))
{
path = std::filesystem::path{ Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::OutputFile)) };
}
WriteManifest(context, path);
}

#undef WINGET_DSC_FUNCTION_METHOD

std::optional<Json::Value> DscCommandBase::GetJsonFromInput(Execution::Context& context, bool terminateContextOnError) const
Expand Down
6 changes: 6 additions & 0 deletions src/AppInstallerCLICore/Commands/DscCommandBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ namespace AppInstaller::CLI

Utility::LocIndView HelpLink() const override;

static std::string_view ModuleName();

// Writes the manifest for the command to the file path.
// If the path is empty, writes the manifest to the output stream.
void WriteManifest(Execution::Context& context, const std::filesystem::path& filePath) const;

protected:
void ExecuteInternal(Execution::Context& context) const override;

Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/Resources.h
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(OpenSourceFailedNoMatchHelp);
WINGET_DEFINE_RESOURCE_STRINGID(OpenSourceFailedNoSourceDefined);
WINGET_DEFINE_RESOURCE_STRINGID(Options);
WINGET_DEFINE_RESOURCE_STRINGID(OutputDirectoryArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(OutputFileArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(OverrideArgumentDescription);
WINGET_DEFINE_RESOURCE_STRINGID(OverwritingExistingFileAtMessage);
Expand Down
2 changes: 1 addition & 1 deletion src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ namespace AppInstaller::CLI::Workflow
};

static const std::wstring s_PackageSettingsExclusionList[] = {
L"Microsoft.WinGet/", L"Microsoft.DSC.Debug/", L"Microsoft.DSC/", L"Microsoft.DSC.Transitional/", L"Microsoft.Windows/RebootPending",
L"Microsoft.WinGet/", L"Microsoft.WinGet.Dev/", L"Microsoft.DSC.Debug/", L"Microsoft.DSC/", L"Microsoft.DSC.Transitional/", L"Microsoft.Windows/RebootPending",
L"Microsoft.Windows/Registry", L"Microsoft.Windows/WMI", L"Microsoft.Windows/WindowsPowerShell", L"Microsoft/OSInfo"
};

Expand Down
4 changes: 2 additions & 2 deletions src/AppInstallerCLIE2ETests/ConfigureCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -330,12 +330,12 @@ public void DSCv3_Export()
var exportDir = TestCommon.GetRandomTestDir();
var exportFile = Path.Combine(exportDir, "exported.yml");

result = TestCommon.RunAICLICommand("test config-export-units", $"-o {exportFile} --resource Microsoft.WinGet/TestJSON --verbose");
result = TestCommon.RunAICLICommand("test config-export-units", $"-o {exportFile} --resource Microsoft.WinGet.Dev/TestJSON --verbose");
Assert.AreEqual(0, result.ExitCode);

Assert.True(File.Exists(exportFile));
string exportText = File.ReadAllText(exportFile);
Assert.True(exportText.Contains("Microsoft.WinGet/TestJSON"));
Assert.True(exportText.Contains("Microsoft.WinGet.Dev/TestJSON"));
Assert.True(exportText.Contains(propertyName1));
Assert.True(exportText.Contains(propertyName2));
Assert.True(exportText.Contains(propertyValue1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ metadata:
processor: dscv3
resources:
- name: Test File
type: Microsoft.WinGet/TestFile
type: Microsoft.WinGet.Dev/TestFile
metadata:
description: Description 1.
properties:
Expand Down
10 changes: 10 additions & 0 deletions src/AppInstallerCLIPackage/AppInstallerCLIPackage.wapproj
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,12 @@
<PropertyGroup Condition="'$(Platform)'=='x86'">
<ConfigServerRid>win-x86</ConfigServerRid>
</PropertyGroup>
<Target Name="WinGetGenerateDSCv3Manifests" BeforeTargets="WinGetIncludeAdditionalFilesInPackage">
<Message Importance="normal" Text="Generating DSCv3 manifests..." />
<Copy SourceFiles="$(SolutionDir)\$(PlatformTarget)\$(Configuration)\WindowsPackageManager\WindowsPackageManager.dll" DestinationFolder="$(SolutionDir)\$(PlatformTarget)\$(Configuration)\AppInstallerCLI" SkipUnchangedFiles="true" />
<Exec Command="$(SolutionDir)\$(PlatformTarget)\$(Configuration)\AppInstallerCLI\winget.exe dscv3 --manifest -o $(SolutionDir)\$(PlatformTarget)\$(Configuration)\AppInstallerCLI\DSCv3Manifests" />
<Message Importance="normal" Text="DSCv3 manifests generated." />
</Target>
<Target Name="WinGetIncludeAdditionalFilesInPackage" AfterTargets="_ComputeAppxPackagePayload">
<PropertyGroup>
<WinGetAdditionalPackageFileRoot>$(SolutionDir)</WinGetAdditionalPackageFileRoot>
Expand All @@ -244,6 +250,10 @@
<PackagePath>ExternalModules</PackagePath>
<Recurse>true</Recurse>
</WinGetAdditionalPackageFile>
<WinGetAdditionalPackageFile Include="$(SolutionDir)\$(PlatformTarget)\$(Configuration)\AppInstallerCLI\DSCv3Manifests\*">
<PackagePath>.</PackagePath>
<Recurse>true</Recurse>
</WinGetAdditionalPackageFile>
</ItemGroup>
<Error Condition="!Exists('%(WinGetAdditionalPackageFile.Identity)')" Text="%(WinGetAdditionalPackageFile.Identity) was not found" />
<!-- Single (non-recursive) file items -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3345,4 +3345,7 @@ Please specify one of them using the --source option to proceed.</value>
<data name="ConfigurationExportFailedToGetUnitProcessors" xml:space="preserve">
<value>Failed to get unit processors. Individual package settings will not be exported.</value>
</data>
</root>
<data name="OutputDirectoryArgumentDescription" xml:space="preserve">
<value>Directory where the results are to be written</value>
</data>
</root>
Loading