From 6aff1fb2f5e5eeee808efe2e5a7f23711fe738bf Mon Sep 17 00:00:00 2001 From: yao-msft <50888816+yao-msft@users.noreply.github.com> Date: Wed, 20 Jul 2022 16:11:01 -0700 Subject: [PATCH] Add InstallationMetadata to manifests for future deep installation detection (#2350) --- .../v1.2.0/manifest.singleton.1.2.0.json | 6 ++ .../v1.3.0/manifest.installer.1.3.0.json | 63 +++++++++++++++++- .../v1.3.0/manifest.singleton.1.3.0.json | 65 +++++++++++++++++++ .../TestData/ManifestV1_3-Singleton.yaml | 7 ++ .../ManifestV1_3-MultiFile-Installer.yaml | 14 ++++ src/AppInstallerCLITests/YamlManifest.cpp | 19 ++++++ .../Manifest/ManifestCommon.cpp | 21 ++++++ .../Manifest/ManifestYamlPopulator.cpp | 61 +++++++++++++++++ .../Public/winget/ManifestCommon.h | 24 +++++++ .../Public/winget/ManifestInstaller.h | 2 + .../Public/winget/ManifestYamlPopulator.h | 7 ++ 11 files changed, 287 insertions(+), 2 deletions(-) diff --git a/schemas/JSON/manifests/v1.2.0/manifest.singleton.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.singleton.1.2.0.json index 2d568dbd07..e646be86d5 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.singleton.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.singleton.1.2.0.json @@ -606,6 +606,9 @@ "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, + "DisplayInstallWarnings": { + "$ref": "#/definitions/DisplayInstallWarnings" + }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, @@ -822,6 +825,9 @@ "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, + "DisplayInstallWarnings": { + "$ref": "#/definitions/DisplayInstallWarnings" + }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, diff --git a/schemas/JSON/manifests/v1.3.0/manifest.installer.1.3.0.json b/schemas/JSON/manifests/v1.3.0/manifest.installer.1.3.0.json index 963caddbd0..b4549030dd 100644 --- a/schemas/JSON/manifests/v1.3.0/manifest.installer.1.3.0.json +++ b/schemas/JSON/manifests/v1.3.0/manifest.installer.1.3.0.json @@ -103,6 +103,7 @@ "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, + "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, @@ -513,6 +514,58 @@ ], "description": "The installer's elevation requirement" }, + "InstallationMetadata": { + "type": "object", + "title": "InstallationMetadata", + "properties": { + "DefaultInstallLocation": { + "type": [ "string", "null" ], + "minLength": 1, + "maxLength": 2048, + "description": "Represents the default installed package location. Used for deeper installation detection." + }, + "Files": { + "type": [ "array", "null" ], + "uniqueItems": true, + "items": { + "type": "object", + "title": "InstalledFile", + "properties": { + "RelativeFilePath": { + "type": "string", + "minLength": 1, + "maxLength": 2048, + "description": "The relative path to the installed file." + }, + "FileSha256": { + "type": [ "string", "null" ], + "pattern": "^[A-Fa-f0-9]{64}$", + "description": "Optional Sha256 of the installed file." + }, + "FileType": { + "type": [ "string", "null" ], + "enum": [ + "launch", + "uninstall", + "other" + ], + "description": "The optional installed file type. If not specified, the file is treated as other." + }, + "InvocationParameter": { + "type": "string", + "minLength": 1, + "maxLength": 2048, + "description": "Optional parameter for invocable files." + } + }, + "required": [ "RelativeFilePath" ], + "description": "Represents an installed file." + }, + "description": "List of installed files." + } + }, + "description": "Details about the installation. Used for deeper installation detection." + }, "Installer": { "type": "object", "properties": { @@ -624,6 +677,9 @@ }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" + }, + "InstallationMetadata": { + "$ref": "#/definitions/InstallationMetadata" } }, "required": [ @@ -719,6 +775,9 @@ "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, + "DisplayInstallWarnings": { + "$ref": "#/definitions/DisplayInstallWarnings" + }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, @@ -731,8 +790,8 @@ "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, - "DisplayInstallWarnings": { - "$ref": "#/definitions/DisplayInstallWarnings" + "InstallationMetadata": { + "$ref": "#/definitions/InstallationMetadata" }, "Installers": { "type": "array", diff --git a/schemas/JSON/manifests/v1.3.0/manifest.singleton.1.3.0.json b/schemas/JSON/manifests/v1.3.0/manifest.singleton.1.3.0.json index d1f7a65a65..f6c576cca5 100644 --- a/schemas/JSON/manifests/v1.3.0/manifest.singleton.1.3.0.json +++ b/schemas/JSON/manifests/v1.3.0/manifest.singleton.1.3.0.json @@ -145,6 +145,7 @@ "description": "The command alias to be used for calling the package. Only applies to the nested portable package" } }, + "required": [ "RelativeFilePath" ], "description": "A nested installer file contained inside an archive" }, "maxItems": 1024, @@ -554,6 +555,58 @@ ], "description": "The installer's elevation requirement" }, + "InstallationMetadata": { + "type": "object", + "title": "InstallationMetadata", + "properties": { + "DefaultInstallLocation": { + "type": [ "string", "null" ], + "minLength": 1, + "maxLength": 2048, + "description": "Represents the default installed package location. Used for deeper installation detection." + }, + "Files": { + "type": [ "array", "null" ], + "uniqueItems": true, + "items": { + "type": "object", + "title": "InstalledFile", + "properties": { + "RelativeFilePath": { + "type": "string", + "minLength": 1, + "maxLength": 2048, + "description": "The relative path to the installed file." + }, + "FileSha256": { + "type": [ "string", "null" ], + "pattern": "^[A-Fa-f0-9]{64}$", + "description": "Optional Sha256 of the installed file." + }, + "FileType": { + "type": [ "string", "null" ], + "enum": [ + "launch", + "uninstall", + "other" + ], + "description": "The optional installed file type. If not specified, the file is treated as other." + }, + "InvocationParameter": { + "type": "string", + "minLength": 1, + "maxLength": 2048, + "description": "Optional parameter for invocable files." + } + }, + "required": [ "RelativeFilePath" ], + "description": "Represents an installed file." + }, + "description": "List of installed files." + } + }, + "description": "Details about the installation. Used for deeper installation detection." + }, "Installer": { "type": "object", "properties": { @@ -651,6 +704,9 @@ "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, + "DisplayInstallWarnings": { + "$ref": "#/definitions/DisplayInstallWarnings" + }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, @@ -662,6 +718,9 @@ }, "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" + }, + "InstallationMetadata": { + "$ref": "#/definitions/InstallationMetadata" } }, "required": [ @@ -873,6 +932,9 @@ "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, + "DisplayInstallWarnings": { + "$ref": "#/definitions/DisplayInstallWarnings" + }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, @@ -885,6 +947,9 @@ "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, + "InstallationMetadata": { + "$ref": "#/definitions/InstallationMetadata" + }, "Installers": { "type": "array", "items": { diff --git a/src/AppInstallerCLITests/TestData/ManifestV1_3-Singleton.yaml b/src/AppInstallerCLITests/TestData/ManifestV1_3-Singleton.yaml index f28bfc404e..43e452e427 100644 --- a/src/AppInstallerCLITests/TestData/ManifestV1_3-Singleton.yaml +++ b/src/AppInstallerCLITests/TestData/ManifestV1_3-Singleton.yaml @@ -107,6 +107,13 @@ NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias +InstallationMetadata: + DefaultInstallLocation: "%ProgramFiles%\\TestApp" + Files: + - RelativeFilePath: "main.exe" + FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 + FileType: launch + InvocationParameter: "/arg" Installers: - Architecture: x86 diff --git a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_3/ManifestV1_3-MultiFile-Installer.yaml b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_3/ManifestV1_3-MultiFile-Installer.yaml index 87a63c5c64..36ac2a5c0c 100644 --- a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_3/ManifestV1_3-MultiFile-Installer.yaml +++ b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_3/ManifestV1_3-MultiFile-Installer.yaml @@ -78,6 +78,13 @@ NestedInstallerType: msi NestedInstallerFiles: - RelativeFilePath: RelativeFilePath PortableCommandAlias: PortableCommandAlias +InstallationMetadata: + DefaultInstallLocation: "%ProgramFiles%\\TestApp" + Files: + - RelativeFilePath: "main.exe" + FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 + FileType: launch + InvocationParameter: "/arg" Installers: - Architecture: x86 @@ -171,5 +178,12 @@ Installers: PortableCommandAlias: portableAlias1 - RelativeFilePath: relativeFilePath2 PortableCommandAlias: portableAlias2 + InstallationMetadata: + DefaultInstallLocation: "%ProgramFiles%\\TestApp2" + Files: + - RelativeFilePath: "main2.exe" + FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 + FileType: other + InvocationParameter: "/arg2" ManifestType: installer ManifestVersion: 1.3.0 \ No newline at end of file diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp index 99672fdd6e..4e45389c18 100644 --- a/src/AppInstallerCLITests/YamlManifest.cpp +++ b/src/AppInstallerCLITests/YamlManifest.cpp @@ -483,6 +483,12 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(manifest.DefaultInstallerInfo.NestedInstallerFiles.size() == 1); REQUIRE(manifest.DefaultInstallerInfo.NestedInstallerFiles.at(0).RelativeFilePath == "RelativeFilePath"); REQUIRE(manifest.DefaultInstallerInfo.NestedInstallerFiles.at(0).PortableCommandAlias == "PortableCommandAlias"); + REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.DefaultInstallLocation == "%ProgramFiles%\\TestApp"); + REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.size() == 1); + REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.at(0).RelativeFilePath == "main.exe"); + REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); + REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.at(0).FileSha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); + REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.at(0).InvocationParameter == "/arg"); } if (isSingleton) @@ -572,6 +578,13 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes // NestedInstaller metadata should not be populated unless the InstallerType is zip. REQUIRE(installer1.NestedInstallerType == InstallerTypeEnum::Unknown); REQUIRE(installer1.NestedInstallerFiles.size() == 0); + + REQUIRE(installer1.InstallationMetadata.DefaultInstallLocation == "%ProgramFiles%\\TestApp"); + REQUIRE(installer1.InstallationMetadata.Files.size() == 1); + REQUIRE(installer1.InstallationMetadata.Files.at(0).RelativeFilePath == "main.exe"); + REQUIRE(installer1.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); + REQUIRE(installer1.InstallationMetadata.Files.at(0).FileSha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); + REQUIRE(installer1.InstallationMetadata.Files.at(0).InvocationParameter == "/arg"); } if (!isSingleton) @@ -634,6 +647,12 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(installer4.NestedInstallerFiles.at(0).PortableCommandAlias == "portableAlias1"); REQUIRE(installer4.NestedInstallerFiles.at(1).RelativeFilePath == "relativeFilePath2"); REQUIRE(installer4.NestedInstallerFiles.at(1).PortableCommandAlias == "portableAlias2"); + REQUIRE(installer4.InstallationMetadata.DefaultInstallLocation == "%ProgramFiles%\\TestApp2"); + REQUIRE(installer4.InstallationMetadata.Files.size() == 1); + REQUIRE(installer4.InstallationMetadata.Files.at(0).RelativeFilePath == "main2.exe"); + REQUIRE(installer4.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Other); + REQUIRE(installer4.InstallationMetadata.Files.at(0).FileSha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); + REQUIRE(installer4.InstallationMetadata.Files.at(0).InvocationParameter == "/arg2"); } // Localization diff --git a/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp b/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp index 10b59cdad1..41580a79b2 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp @@ -375,6 +375,27 @@ namespace AppInstaller::Manifest return result; } + InstalledFileTypeEnum ConvertToInstalledFileTypeEnum(const std::string& in) + { + std::string inStrLower = Utility::ToLower(in); + InstalledFileTypeEnum result = InstalledFileTypeEnum::Unknown; + + if (inStrLower == "launch") + { + result = InstalledFileTypeEnum::Launch; + } + else if (inStrLower == "uninstall") + { + result = InstalledFileTypeEnum::Uninstall; + } + else if (inStrLower == "other") + { + result = InstalledFileTypeEnum::Other; + } + + return result; + } + std::string_view InstallerTypeToString(InstallerTypeEnum installerType) { switch (installerType) diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp index 1f7e1764b9..94cb160d39 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp @@ -314,6 +314,7 @@ namespace AppInstaller::Manifest { { "NestedInstallerType", [this](const YAML::Node& value)->ValidationErrors { m_p_installer->NestedInstallerType = ConvertToInstallerTypeEnum(value.as()); return {}; } }, { "NestedInstallerFiles", [this](const YAML::Node& value)->ValidationErrors { return ProcessNestedInstallerFilesNode(value); } }, + { "InstallationMetadata", [this](const YAML::Node& value)->ValidationErrors { m_p_installationMetadata = &(m_p_installer->InstallationMetadata); return ValidateAndProcessFields(value, InstallationMetadataFieldInfos); } }, }; std::move(fields_v1_3.begin(), fields_v1_3.end(), std::inserter(result, result.end())); @@ -586,6 +587,40 @@ namespace AppInstaller::Manifest return result; } + std::vector ManifestYamlPopulator::GetInstallationMetadataFieldProcessInfo(const ManifestVer& manifestVersion) + { + std::vector result = {}; + + if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_3 }) + { + result = + { + { "DefaultInstallLocation", [this](const YAML::Node& value)->ValidationErrors { m_p_installationMetadata->DefaultInstallLocation = Utility::Trim(value.as()); return {}; } }, + { "Files", [this](const YAML::Node& value)->ValidationErrors { return ProcessInstallationMetadataFilesNode(value); } }, + }; + } + + return result; + } + + std::vector ManifestYamlPopulator::GetInstallationMetadataFilesFieldProcessInfo(const ManifestVer& manifestVersion) + { + std::vector result = {}; + + if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_3 }) + { + result = + { + { "RelativeFilePath", [this](const YAML::Node& value)->ValidationErrors { m_p_installedFile->RelativeFilePath = Utility::Trim(value.as()); return {}; } }, + { "FileSha256", [this](const YAML::Node& value)->ValidationErrors { m_p_installedFile->FileSha256 = Utility::SHA256::ConvertToBytes(value.as()); return {}; } }, + { "FileType", [this](const YAML::Node& value)->ValidationErrors { m_p_installedFile->FileType = ConvertToInstalledFileTypeEnum(value.as()); return {}; } }, + { "InvocationParameter", [this](const YAML::Node& value)->ValidationErrors { m_p_installedFile->InvocationParameter = Utility::Trim(value.as()); return {}; } }, + }; + } + + return result; + } + ValidationErrors ManifestYamlPopulator::ValidateAndProcessFields( const YAML::Node& rootNode, const std::vector& fieldInfos) @@ -804,6 +839,30 @@ namespace AppInstaller::Manifest return resultErrors; } + std::vector ManifestYamlPopulator::ProcessInstallationMetadataFilesNode(const YAML::Node& installedFilesNode) + { + THROW_HR_IF(E_INVALIDARG, !installedFilesNode.IsSequence()); + + ValidationErrors resultErrors; + std::vector installedFiles; + + for (auto const& entry : installedFilesNode.Sequence()) + { + InstalledFile installedFile; + m_p_installedFile = &installedFile; + auto errors = ValidateAndProcessFields(entry, InstallationMetadataFilesFieldInfos); + std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); + installedFiles.emplace_back(std::move(installedFile)); + } + + if (!installedFiles.empty()) + { + m_p_installationMetadata->Files = installedFiles; + } + + return resultErrors; + } + ValidationErrors ManifestYamlPopulator::PopulateManifestInternal( const YAML::Node& rootNode, Manifest& manifest, @@ -829,6 +888,8 @@ namespace AppInstaller::Manifest AppsAndFeaturesEntryFieldInfos = GetAppsAndFeaturesEntryFieldProcessInfo(manifestVersion); DocumentationFieldInfos = GetDocumentationFieldProcessInfo(manifestVersion); NestedInstallerFileFieldInfos = GetNestedInstallerFileFieldProcessInfo(manifestVersion); + InstallationMetadataFieldInfos = GetInstallationMetadataFieldProcessInfo(manifestVersion); + InstallationMetadataFilesFieldInfos = GetInstallationMetadataFilesFieldProcessInfo(manifestVersion); // Populate root m_p_manifest = &manifest; diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 6eb8d78af0..d213ea7c4a 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -159,6 +159,14 @@ namespace AppInstaller::Manifest Location }; + enum class InstalledFileTypeEnum + { + Unknown, + Launch, + Uninstall, + Other, + }; + enum class ManifestTypeEnum { Singleton, @@ -253,6 +261,20 @@ namespace AppInstaller::Manifest string_t PortableCommandAlias; }; + struct InstalledFile + { + string_t RelativeFilePath; + std::vector FileSha256; + InstalledFileTypeEnum FileType = InstalledFileTypeEnum::Other; + string_t InvocationParameter; + }; + + struct InstallationMetadataInfo + { + string_t DefaultInstallLocation; + std::vector Files; + }; + InstallerTypeEnum ConvertToInstallerTypeEnum(const std::string& in); UpdateBehaviorEnum ConvertToUpdateBehaviorEnum(const std::string& in); @@ -271,6 +293,8 @@ namespace AppInstaller::Manifest ExpectedReturnCodeEnum ConvertToExpectedReturnCodeEnum(const std::string& in); + InstalledFileTypeEnum ConvertToInstalledFileTypeEnum(const std::string& in); + std::string_view InstallerTypeToString(InstallerTypeEnum installerType); std::string_view ScopeToString(ScopeEnum scope); diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h b/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h index ed84cd3eb8..bad29be434 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h @@ -104,5 +104,7 @@ namespace AppInstaller::Manifest ElevationRequirementEnum ElevationRequirement = ElevationRequirementEnum::Unknown; MarketsInfo Markets; + + InstallationMetadataInfo InstallationMetadata; }; } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h b/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h index 53606a7d5e..0f35d253e8 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h @@ -43,6 +43,8 @@ namespace AppInstaller::Manifest std::vector AppsAndFeaturesEntryFieldInfos; std::vector DocumentationFieldInfos; std::vector NestedInstallerFileFieldInfos; + std::vector InstallationMetadataFieldInfos; + std::vector InstallationMetadataFilesFieldInfos; // These pointers are referenced in the processing functions in manifest field process info table. AppInstaller::Manifest::Manifest* m_p_manifest = nullptr; @@ -57,6 +59,8 @@ namespace AppInstaller::Manifest AppInstaller::Manifest::AppsAndFeaturesEntry* m_p_appsAndFeaturesEntry = nullptr; AppInstaller::Manifest::Documentation* m_p_documentation = nullptr; AppInstaller::Manifest::NestedInstallerFile* m_p_nestedInstallerFile = nullptr; + AppInstaller::Manifest::InstallationMetadataInfo* m_p_installationMetadata = nullptr; + AppInstaller::Manifest::InstalledFile* m_p_installedFile = nullptr; // Cache of Installers node and Localization node YAML::Node const* m_p_installersNode = nullptr; @@ -74,6 +78,8 @@ namespace AppInstaller::Manifest std::vector GetAppsAndFeaturesEntryFieldProcessInfo(const ManifestVer& manifestVersion); std::vector GetDocumentationFieldProcessInfo(const ManifestVer& manifestVersion); std::vector GetNestedInstallerFileFieldProcessInfo(const ManifestVer& manifestVersion); + std::vector GetInstallationMetadataFieldProcessInfo(const ManifestVer& manifestVersion); + std::vector GetInstallationMetadataFilesFieldProcessInfo(const ManifestVer& manifestVersion); // This method takes YAML root node and list of manifest field info. // Yaml lib does not support case insensitive search and it allows duplicate keys. If duplicate keys exist, @@ -91,6 +97,7 @@ namespace AppInstaller::Manifest std::vector ProcessExpectedReturnCodesNode(const YAML::Node& returnCodesNode); std::vector ProcessDocumentationsNode(const YAML::Node& documentationsNode); std::vector ProcessNestedInstallerFilesNode(const YAML::Node& nestedInstallerFilesNode); + std::vector ProcessInstallationMetadataFilesNode(const YAML::Node& installedFilesNode); std::vector PopulateManifestInternal( const YAML::Node& rootNode,