Skip to content

Commit 3556a3e

Browse files
authored
Dscv3 package resource (#5395)
## Change Add a new DSC v3 resource: `package`. This largely follows the design of the v2 resource, but uses the v3 paradigm for existence. Other minor changes: - Adds a `StdErrLogger` and attaches it during resource execution. This currently writes all logs at `Error` or higher to `std::cerr`, allowing `dsc.exe` to receive (and pass on) failure information. - Uses the type of the standard input handle to determine if it should be read from (will not read JSON input from the interactive console, only piped input). - Adds support for localized schema generation for resources. - Moves `ManifestComparator` to common for re-use. ## Validation Added E2E tests to invoke the package resource in a variety of situations.
1 parent 990e1a6 commit 3556a3e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2155
-384
lines changed

src/AppInstallerCLICore/AppInstallerCLICore.vcxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@
305305
<ClInclude Include="Commands\DscCommand.h" />
306306
<ClInclude Include="Commands\DscCommandBase.h" />
307307
<ClInclude Include="Commands\DscComposableObject.h" />
308+
<ClInclude Include="Commands\DscPackageResource.h" />
308309
<ClInclude Include="Commands\DscTestFileResource.h" />
309310
<ClInclude Include="Commands\DscTestJsonResource.h" />
310311
<ClInclude Include="Commands\ErrorCommand.h" />
@@ -369,7 +370,6 @@
369370
<ClInclude Include="Workflows\SettingsFlow.h" />
370371
<ClInclude Include="Workflows\ShellExecuteInstallerHandler.h" />
371372
<ClInclude Include="Workflows\InstallFlow.h" />
372-
<ClInclude Include="Workflows\ManifestComparator.h" />
373373
<ClInclude Include="Workflows\ResumeFlow.h" />
374374
<ClInclude Include="Workflows\ShowFlow.h" />
375375
<ClInclude Include="Workflows\SourceFlow.h" />
@@ -391,6 +391,7 @@
391391
<ClCompile Include="Commands\DscCommand.cpp" />
392392
<ClCompile Include="Commands\DscCommandBase.cpp" />
393393
<ClCompile Include="Commands\DscComposableObject.cpp" />
394+
<ClCompile Include="Commands\DscPackageResource.cpp" />
394395
<ClCompile Include="Commands\DscTestFileResource.cpp" />
395396
<ClCompile Include="Commands\DscTestJsonResource.cpp" />
396397
<ClCompile Include="Commands\ErrorCommand.cpp" />
@@ -456,7 +457,6 @@
456457
<ClCompile Include="Workflows\SettingsFlow.cpp" />
457458
<ClCompile Include="Workflows\ShellExecuteInstallerHandler.cpp" />
458459
<ClCompile Include="Workflows\InstallFlow.cpp" />
459-
<ClCompile Include="Workflows\ManifestComparator.cpp" />
460460
<ClCompile Include="Workflows\ResumeFlow.cpp" />
461461
<ClCompile Include="Workflows\ShowFlow.cpp" />
462462
<ClCompile Include="Workflows\SourceFlow.cpp" />

src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,6 @@
4747
<ClInclude Include="Workflows\InstallFlow.h">
4848
<Filter>Workflows</Filter>
4949
</ClInclude>
50-
<ClInclude Include="Workflows\ManifestComparator.h">
51-
<Filter>Workflows</Filter>
52-
</ClInclude>
5350
<ClInclude Include="Workflows\ShellExecuteInstallerHandler.h">
5451
<Filter>Workflows</Filter>
5552
</ClInclude>
@@ -284,6 +281,9 @@
284281
<ClInclude Include="Commands\DscTestJsonResource.h">
285282
<Filter>Commands\Configuration</Filter>
286283
</ClInclude>
284+
<ClInclude Include="Commands\DscPackageResource.h">
285+
<Filter>Commands\Configuration</Filter>
286+
</ClInclude>
287287
</ItemGroup>
288288
<ItemGroup>
289289
<ClCompile Include="pch.cpp">
@@ -301,9 +301,6 @@
301301
<ClCompile Include="Core.cpp">
302302
<Filter>Source Files</Filter>
303303
</ClCompile>
304-
<ClCompile Include="Workflows\ManifestComparator.cpp">
305-
<Filter>Workflows</Filter>
306-
</ClCompile>
307304
<ClCompile Include="Workflows\ShellExecuteInstallerHandler.cpp">
308305
<Filter>Workflows</Filter>
309306
</ClCompile>
@@ -535,6 +532,9 @@
535532
<ClCompile Include="Commands\DscTestJsonResource.cpp">
536533
<Filter>Commands\Configuration</Filter>
537534
</ClCompile>
535+
<ClCompile Include="Commands\DscPackageResource.cpp">
536+
<Filter>Commands\Configuration</Filter>
537+
</ClCompile>
538538
</ItemGroup>
539539
<ItemGroup>
540540
<None Include="PropertySheet.props" />

src/AppInstallerCLICore/Argument.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ namespace AppInstaller::CLI
289289
return { type, "force"_liv, ArgTypeCategory::CopyFlagToSubContext };
290290
case Execution::Args::Type::OutputFile:
291291
return { type, "output"_liv, 'o' };
292+
case Execution::Args::Type::Correlation:
293+
return { type, "correlation"_liv };
292294

293295
case Execution::Args::Type::DependencySource:
294296
return { type, "dependency-source"_liv, ArgTypeCategory::ExtendedSource };
@@ -468,6 +470,8 @@ namespace AppInstaller::CLI
468470
return Argument{ type, Resource::String::NoProxyArgumentDescription, ArgumentType::Flag, TogglePolicy::Policy::ProxyCommandLineOptions, BoolAdminSetting::ProxyCommandLineOptions };
469471
case Args::Type::Family:
470472
return Argument{ type, Resource::String::FontFamilyNameArgumentDescription, ArgumentType::Positional, false };
473+
case Args::Type::Correlation:
474+
return Argument{ type, Resource::String::CorrelationArgumentDescription, ArgumentType::Standard, Argument::Visibility::Hidden };
471475
default:
472476
THROW_HR(E_UNEXPECTED);
473477
}
@@ -486,6 +490,7 @@ namespace AppInstaller::CLI
486490
args.emplace_back(Args::Type::DisableInteractivity, Resource::String::DisableInteractivityArgumentDescription, ArgumentType::Flag, false);
487491
args.push_back(ForType(Args::Type::Proxy));
488492
args.push_back(ForType(Args::Type::NoProxy));
493+
args.push_back(ForType(Args::Type::Correlation));
489494
}
490495

491496
std::string Argument::GetUsageString() const

src/AppInstallerCLICore/Commands/DscCommand.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33
#include "pch.h"
44
#include "DscCommand.h"
5+
#include "DscPackageResource.h"
56

67
#ifndef AICLI_DISABLE_TEST_HOOKS
78
#include "DscTestFileResource.h"
@@ -13,6 +14,7 @@ namespace AppInstaller::CLI
1314
std::vector<std::unique_ptr<Command>> DscCommand::GetCommands() const
1415
{
1516
return InitializeFromMoveOnly<std::vector<std::unique_ptr<Command>>>({
17+
std::make_unique<DscPackageResource>(FullName()),
1618
#ifndef AICLI_DISABLE_TEST_HOOKS
1719
std::make_unique<DscTestFileResource>(FullName()),
1820
std::make_unique<DscTestJsonResource>(FullName()),

src/AppInstallerCLICore/Commands/DscCommandBase.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "DscCommandBase.h"
55
#include "DscCommand.h"
66
#include <winget/Runtime.h>
7+
#include <winget/StdErrLogger.h>
78

89
#define WINGET_DSC_FUNCTION_FOREACH(_macro_) \
910
_macro_(Get); \
@@ -201,8 +202,7 @@ namespace AppInstaller::CLI
201202
void DscCommandBase::ExecuteInternal(Execution::Context& context) const
202203
{
203204
context.Reporter.SetChannel(Execution::Reporter::Channel::Json);
204-
205-
// TODO: Consider adding a stderr logger
205+
Logging::StdErrLogger::Add();
206206

207207
#define WINGET_DSC_FUNCTION_ARGUMENT(_function_) \
208208
if (context.Args.Contains(Execution::Args::Type::DscResourceFunction ## _function_)) \
@@ -266,18 +266,33 @@ namespace AppInstaller::CLI
266266

267267
#undef WINGET_DSC_FUNCTION_METHOD
268268

269-
std::optional<Json::Value> DscCommandBase::GetJsonFromInput(Execution::Context& context) const
269+
std::optional<Json::Value> DscCommandBase::GetJsonFromInput(Execution::Context& context, bool terminateContextOnError) const
270270
{
271-
Json::Value result;
272-
Json::CharReaderBuilder builder;
273-
Json::String errors;
274-
if (!Json::parseFromStream(builder, context.Reporter.RawInputStream(), &result, &errors))
271+
// Don't attempt to read from an interactive stream as this will just block
272+
if (!context.Reporter.InputStreamIsInteractive())
275273
{
274+
AICLI_LOG(CLI, Verbose, << "Reading Json from input stream...");
275+
276+
Json::Value result;
277+
Json::CharReaderBuilder builder;
278+
Json::String errors;
279+
if (Json::parseFromStream(builder, context.Reporter.RawInputStream(), &result, &errors))
280+
{
281+
AICLI_LOG(CLI, Info, << "Json from input stream:\n" << Json::writeString(Json::StreamWriterBuilder{}, result));
282+
return result;
283+
}
284+
276285
AICLI_LOG(CLI, Error, << "Failed to read input JSON: " << errors);
277-
AICLI_TERMINATE_CONTEXT_RETURN(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, std::nullopt);
278286
}
279287

280-
return result;
288+
if (terminateContextOnError)
289+
{
290+
AICLI_TERMINATE_CONTEXT_RETURN(APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE, std::nullopt);
291+
}
292+
else
293+
{
294+
return std::nullopt;
295+
}
281296
}
282297

283298
void DscCommandBase::WriteJsonOutputLine(Execution::Context& context, const Json::Value& value) const

src/AppInstallerCLICore/Commands/DscCommandBase.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ namespace AppInstaller::CLI
9494
virtual void ResourceFunctionManifest(Execution::Context& context) const;
9595

9696
// Parses a JSON object from stdin.
97-
std::optional<Json::Value> GetJsonFromInput(Execution::Context& context) const;
97+
std::optional<Json::Value> GetJsonFromInput(Execution::Context& context, bool terminateContextOnError = true) const;
9898

9999
// Writes the value to the context output.
100100
void WriteJsonOutputLine(Execution::Context& context, const Json::Value& value) const;

src/AppInstallerCLICore/Commands/DscComposableObject.cpp

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,32 @@
55

66
namespace AppInstaller::CLI
77
{
8+
namespace
9+
{
10+
std::string GetTypeString(Json::ValueType type)
11+
{
12+
switch (type)
13+
{
14+
case Json::nullValue:
15+
return "null";
16+
case Json::intValue:
17+
case Json::uintValue:
18+
case Json::realValue:
19+
return "number";
20+
case Json::stringValue:
21+
return "string";
22+
case Json::booleanValue:
23+
return "boolean";
24+
case Json::arrayValue:
25+
return "array";
26+
case Json::objectValue:
27+
return "object";
28+
default:
29+
THROW_HR(E_UNEXPECTED);
30+
}
31+
}
32+
}
33+
834
namespace details
935
{
1036
const Json::Value* GetProperty(const Json::Value& object, std::string_view name)
@@ -32,7 +58,14 @@ namespace AppInstaller::CLI
3258
return result;
3359
}
3460

35-
void AddPropertySchema(Json::Value& object, std::string_view name, DscComposablePropertyFlag flags, std::string_view type, std::string_view description)
61+
void AddPropertySchema(
62+
Json::Value& object,
63+
std::string_view name,
64+
DscComposablePropertyFlag flags,
65+
Json::ValueType type,
66+
std::string_view description,
67+
const std::vector<std::string>& enumValues,
68+
const std::optional<std::string>& defaultValue)
3669
{
3770
Json::Value& propertiesObject = object["properties"];
3871

@@ -45,13 +78,30 @@ namespace AppInstaller::CLI
4578

4679
Json::Value property{ Json::ValueType::objectValue };
4780

48-
if (!type.empty())
81+
if (type != Json::ValueType::objectValue)
4982
{
50-
property["type"] = std::string{ type };
83+
property["type"] = GetTypeString(type);
5184
}
5285

5386
property["description"] = std::string{ description };
5487

88+
if (!enumValues.empty())
89+
{
90+
Json::Value enumArray{ Json::ValueType::arrayValue };
91+
92+
for (const std::string& enumValue : enumValues)
93+
{
94+
enumArray.append(enumValue);
95+
}
96+
97+
property["enum"] = std::move(enumArray);
98+
}
99+
100+
if (defaultValue)
101+
{
102+
property["default"] = defaultValue.value();
103+
}
104+
55105
propertiesObject[nameString] = std::move(property);
56106

57107
if (WI_IsFlagSet(flags, DscComposablePropertyFlag::Required))

0 commit comments

Comments
 (0)