Implement V1 manifest and schema validation#740
Conversation
| !Runtime::IsCurrentOSVersionGreaterThanOrEqual(Version(manifest.MinOSVersion))) | ||
| { | ||
| context.Reporter.Error() << Resource::String::InstallationRequiresHigherWindows << ' ' << manifest.MinOSVersion << std::endl; | ||
| AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION)); |
There was a problem hiding this comment.
I understand the motivation to remove this, but it does mean that we will not message the reason for failure to the user (or ourselves for the failure code). Maybe it is upcoming in my reading of the PR, but we need a way to message the failure state of no applicable installers. #Resolved
There was a problem hiding this comment.
Yes, the MinOSVersion check is moved to installer selection logic since in the new world each installer has its own MinOSVersion. It will be part of the No_Applicable_Installer error
#Resolved
There was a problem hiding this comment.
But I think we need to do a better job of messaging the reason that there are not applicable installers. Some of that burden could be placed on search to filter them out before we even attempt to install, but I prefer to show everything and have the user be informed that we don't have their flavor of installer rather than we don't even know what they are talking about.
It might be more complex, and thus not piled on to this PR, but I think we should attempt to give some reason as to why there are no applicable installers. Imagine:
for each installer
reason[i] = GetAllReasonsWhyNotApplicable(installer)
if (AndAllReasons() != 0)
ReportReasons(AndAllReasons())
So if all installers require a newer OS, we say that. If they are all ARM, we can say that they are for a different processor architecture. If one required a newer OS and one is ARM, we say both somehow.
In reply to: 571311595 [](ancestors = 571311595)
There was a problem hiding this comment.
I'll add a todo for better message regarding installer selection
In reply to: 571318031 [](ancestors = 571318031,571311595)
| if (isOSVersionSatisfied1 && !isOSVersionSatisfied2) | ||
| { | ||
| return true; | ||
| } |
There was a problem hiding this comment.
Let's re-write the loop to check IsInstallerApplicable first before ever calling IsInstallerBetterMatch. That way we don't need to duplicate these kinds of boolean checks. It ends up making this function just the arch > arch comparison. Not sure why I didn't think of it when I rewrote this a while back. #Resolved
| { | ||
| const auto& manifest = context.Get<Execution::Data::Manifest>(); | ||
|
|
||
| // Channel is moved to installer level, get all channels from all installers |
There was a problem hiding this comment.
// Channel is moved to installer level, get all channels from all installers [](start = 8, length = 76)
Channel is not an installer level item. It is a part of the version. If the schema has this change, I obviously missed it in review. #Resolved
|
|
||
| context << | ||
| VerifyFile(Execution::Args::Type::Manifest) << | ||
| VerifyPath(Execution::Args::Type::Manifest) << |
There was a problem hiding this comment.
VerifyPath [](start = 12, length = 10)
This one does not seem like it should have changed, winget install -m a_directory will now work? #Resolved
There was a problem hiding this comment.
It will work if the directory contains only one manifest(either split manifest files or a singleton inside). The input will be checked in ValidateInput method in manifest parsing
In reply to: 571315313 [](ancestors = 571315313)
| var result = TestCommon.RunAICLICommand("hash", TestCommon.GetTestDataFile("DoesNot.Exist")); | ||
| Assert.AreEqual(Constants.ErrorCode.ERROR_FILE_NOT_FOUND, result.ExitCode); | ||
| Assert.True(result.StdOut.Contains("File does not exist")); | ||
| Assert.True(result.StdOut.Contains("Path does not exist")); |
There was a problem hiding this comment.
Path [](start = 48, length = 4)
This one should not have changed. #Resolved
| @@ -0,0 +1,23 @@ | |||
| PackageIdentifier: microsoft.msixsdk | |||
There was a problem hiding this comment.
These split manifests should be in a directory for organization and testing. Was it complicating the copy on build? #Resolved
| } | ||
| } | ||
|
|
||
| std::string LoadResourceAsString(PCWSTR resourceModuleName, PCWSTR resourceName, PCWSTR resourceType) |
There was a problem hiding this comment.
PCWSTR [](start = 37, length = 6)
std::wstring_view #Resolved
There was a problem hiding this comment.
Although I suppose that doesn't require null termination, so it is suspect. We should probably add a utility type that can handle this for us.
In reply to: 571321711 [](ancestors = 571321711)
There was a problem hiding this comment.
per offline chat, we decided to remove the resourceModuleName parameter, leave the other 2 PCWSTR as they are because that's what MAKEINTRESOURCE produces and later consumed by resource related apis
In reply to: 571322386 [](ancestors = 571322386,571321711)
|
|
||
| std::string LoadResourceAsString(PCWSTR resourceModuleName, PCWSTR resourceName, PCWSTR resourceType) | ||
| { | ||
| HMODULE resourceModule = GetModuleHandle(resourceModuleName); |
There was a problem hiding this comment.
GetModuleHandle [](start = 33, length = 15)
If you are going to allow arbitrary modules here, you should use the proper, thread safe GetModuleHandleEx and a wil::unique_hmodule to hold it. #Resolved
| // - DefaultLocale matches in version manifest and defaultLocale manifest | ||
| // - Validate manifest type correctness | ||
| // - Allowed file type in multi file manifest: version, installer, defaultLocale, locale | ||
| // - Allowed file type in multi file manifest: preview manifest, merged and singleton |
There was a problem hiding this comment.
multi [](start = 36, length = 5)
single #Resolved
| template <Localization L> | ||
| struct LocalizationMapping | ||
| { | ||
| // value_t type specifies the type of this data |
There was a problem hiding this comment.
// value_t type specifies the type of this data [](start = 12, length = 47)
Since almost everything is going to be string_t, you could define it here in the unspecialized definition. Then one only needs to specify the very few exceptions to the rule (1 right now). #Resolved
| // fullValidation: Bool to set if manifest creation should perform extra validation that client does not need. | ||
| // e.g. Channel should be null. Client code does not need this check to work properly. | ||
| // throwOnWarning: Bool to indicate if an exception should be thrown with only warnings detected in the manifest. | ||
| // resourceDll: Binary where schemas are embedded, or nullptr if they are embedded in the binary that created the process |
There was a problem hiding this comment.
// resourceDll: Binary where schemas are embedded, or nullptr if they are embedded in the binary that created the process [](start = 4, length = 124)
This seems like it is only marginally useful for some negative test case. Isn't it possible to determine the module that the current code is executing in? #Resolved
There was a problem hiding this comment.
It's mainly for WinGetUtil.dll, I cannot find a simple way to detect the code is being called by wingetutil(or you prefer a bool like isFromWinGetUtil)?
In reply to: 571327922 [](ancestors = 571327922)
There was a problem hiding this comment.
Either use GetModuleHandleEx or https://devblogs.microsoft.com/oldnewthing/20041025-00
In reply to: 571330356 [](ancestors = 571330356,571327922)
| @@ -76,9 +76,11 @@ extern "C" | |||
| // Validates a given manifest. Returns a bool for validation result and | |||
| // a string representing validation errors if validation failed. | |||
| WINGET_UTIL_API WinGetValidateManifest( | |||
There was a problem hiding this comment.
WinGetValidateManifest [](start = 20, length = 22)
This will need to be a new function and the old one will need to continue to work with it's existing signature. #Resolved
| WINGET_STRING_OUT* message) try | ||
| WINGET_STRING_OUT* message, | ||
| WINGET_STRING mergedManifestPath, | ||
| BOOL isPartialManifest) try |
There was a problem hiding this comment.
isPartialManifest [](start = 13, length = 17)
We need to know the expected type, or return it, or the service code will need to parse the manifest enough to answer the question of "Does this file name pattern match the type in the manifest?".
Rather than a BOOL here, we should take in an enum with values for single file, split, sub-component of a split. It could even be an in-out where we also let them give us "don't know" and we give them back the answer. #Resolved
Misspellings found, please review:
To accept these changes, run the following commands from this repository on this branch |
Change
Validation
Note: After this change, we might need to revisit and relax restrictions in some of v1 manifest schemas as I found around 300+ manifests in current winget-pkgs will fail in new restrictions imposed by V1 manifest schemas
Microsoft Reviewers: Open in CodeFlow