winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 9d3d2202ac19b167ad6dff4e38fd80d15ccd51e2
parent d6b2a061265b685541e70c1d9ebfa878cd819776
Author: yao-msft <50888816+yao-msft@users.noreply.github.com>
Date:   Fri,  8 May 2020 12:57:12 -0700

Add ManifestVersion to manifest (#109)


Diffstat:
Msrc/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp | 3++-
Msrc/AppInstallerCLIE2ETests/Constants.cs | 4++--
Msrc/AppInstallerCLIE2ETests/TestData/Manifests/TestBurnInstaller.yaml | 1+
Msrc/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.yaml | 1+
Msrc/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_InapplicableOsVersion.yaml | 1+
Msrc/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_Sha256Mismatch.yaml | 1+
Msrc/AppInstallerCLIE2ETests/TestData/Manifests/TestInnoInstaller.yaml | 1+
Msrc/AppInstallerCLIE2ETests/TestData/Manifests/TestInvalidManifest.yaml | 1+
Msrc/AppInstallerCLIE2ETests/TestData/Manifests/TestMsiInstaller.yaml | 2+-
Msrc/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller.yaml | 2+-
Msrc/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller_SignatureHashMismatch.yaml | 2+-
Msrc/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller_WithSignatureHash.yaml | 2+-
Msrc/AppInstallerCLIE2ETests/TestData/Manifests/TestNullsoftInstaller.yaml | 1+
Msrc/AppInstallerCLIE2ETests/ValidateCommand.cs | 6+++---
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj | 6++++++
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters | 6++++++
Msrc/AppInstallerCLITests/TestData/InstallFlowTest_Exe.yaml | 1+
Msrc/AppInstallerCLITests/TestData/InstallFlowTest_Msix_DownloadFlow.yaml | 4++--
Msrc/AppInstallerCLITests/TestData/InstallFlowTest_Msix_StreamingFlow.yaml | 4++--
Msrc/AppInstallerCLITests/TestData/InstallFlowTest_NoApplicableArchitecture.yaml | 4++--
Msrc/AppInstallerCLITests/TestData/InstallerArgTest_Inno_NoSwitches.yaml | 2+-
Msrc/AppInstallerCLITests/TestData/InstallerArgTest_Inno_WithSwitches.yaml | 1+
Msrc/AppInstallerCLITests/TestData/InstallerArgTest_Msi_NoSwitches.yaml | 1+
Msrc/AppInstallerCLITests/TestData/InstallerArgTest_Msi_WithSwitches.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-ArchInvalid.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-ArchMissing.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-Channel-NotSupported.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-UPPER.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-camelCase.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-lower.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey-DifferentCase.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-IdInvalid.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-IdMissing.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExe-NoSilent.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeInvalid.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeMissing.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-DefaultScope.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-DefaultValues.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-SameLang.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-InstallersMissing.yaml | 1+
Asrc/AppInstallerCLITests/TestData/Manifest-Bad-InvalidManifestVersionValue.yaml | 11+++++++++++
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-NameMissing.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-PublisherMissing.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-Sha256Invalid.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-Sha256Missing.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-SwitchInvalid.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-UnknownProperty.yaml | 1+
Asrc/AppInstallerCLITests/TestData/Manifest-Bad-UnsupportedVersion.yaml | 11+++++++++++
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-UrlInvalid.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-UrlMissing.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-VersionInvalid.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Bad-VersionMissing.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExe-Silent.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExe-SilentRoot.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExeRoot-Silent.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Good-InstallerUniqueness-DiffScope.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Good-Installeruniqueness-DefaultLang.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Good-Installeruniqueness-DiffLangs.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Good-Minimum-InstallerType.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Good-Minimum.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Good-Spaces.yaml | 4++--
Msrc/AppInstallerCLITests/TestData/Manifest-Good-Switches.yaml | 1+
Msrc/AppInstallerCLITests/TestData/Manifest-Good.yaml | 4++--
Msrc/AppInstallerCLITests/Versions.cpp | 18+++++++++++++++---
Msrc/AppInstallerCLITests/YamlManifest.cpp | 6++++--
Msrc/AppInstallerCommonCore/Errors.cpp | 2++
Msrc/AppInstallerCommonCore/Public/AppInstallerErrors.h | 2+-
Msrc/AppInstallerCommonCore/Public/AppInstallerVersions.h | 11++++++++++-
Msrc/AppInstallerCommonCore/Versions.cpp | 48++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerRepositoryCore/Manifest/Manifest.cpp | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/AppInstallerRepositoryCore/Manifest/Manifest.h | 4+++-
Msrc/AppInstallerRepositoryCore/Manifest/ManifestInstaller.cpp | 71+++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/AppInstallerRepositoryCore/Manifest/ManifestInstaller.h | 9+++++++--
Msrc/AppInstallerRepositoryCore/Manifest/ManifestLocalization.cpp | 27+++++++++++++++++++--------
Msrc/AppInstallerRepositoryCore/Manifest/ManifestLocalization.h | 6+++++-
Msrc/AppInstallerRepositoryCore/Manifest/ManifestValidation.cpp | 38+++++++++++++++++++++++++++++++++++---
Msrc/AppInstallerRepositoryCore/Manifest/ManifestValidation.h | 31+++++++++++++++++++++++++++----
84 files changed, 404 insertions(+), 102 deletions(-)

diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp @@ -157,7 +157,8 @@ namespace AppInstaller::CLI::Workflow Logging::Telemetry().LogInstallerFailure(manifest.Id, manifest.Version, manifest.Channel, "ShellExecute", installResult.value()); context.Reporter.Error() << "Installer failed with exit code: " << installResult.value() << std::endl; - if (context.Contains(Execution::Data::LogPath)) + // Show installer log path if exists + if (context.Contains(Execution::Data::LogPath) && std::filesystem::exists(context.Get<Execution::Data::LogPath>())) { context.Reporter.Info() << "Installer log is available at: " << context.Get<Execution::Data::LogPath>().u8string() << std::endl; } diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs @@ -39,9 +39,9 @@ namespace AppInstallerCLIE2ETests public const int ERROR_INVALID_CL_ARGUMENTS = unchecked((int)0x8A150002); public const int ERROR_COMMAND_FAILED = unchecked((int)0x8A150003); public const int ERROR_MANIFEST_FAILED = unchecked((int)0x8A150004); - //public const int ERROR_WORKFLOW_FAILED = unchecked((int)0x8A150005) // Unused, can be repurposed + public const int ERROR_CTRL_SIGNAL_RECEIVED = unchecked((int)0x8A150005); public const int ERROR_SHELLEXEC_INSTALL_FAILED = unchecked((int)0x8A150006); - //public const int ERROR_RUNTIME_ERROR = unchecked((int)0x8A150007) // Unused, can be repurposed + public const int ERROR_UNSUPPORTED_MANIFESTVERSION = unchecked((int)0x8A150007); public const int ERROR_DOWNLOAD_FAILED = unchecked((int)0x8A150008); public const int ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX = unchecked((int)0x8A150009); public const int ERROR_INDEX_INTEGRITY_COMPROMISED = unchecked((int)0x8A15000A); diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestBurnInstaller.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestBurnInstaller.yaml @@ -9,3 +9,4 @@ Installers: InstallerType: burn Switches: InstallLocation: /InstallDir <INSTALLPATH> +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.yaml @@ -15,3 +15,4 @@ Installers: Language: /exeenus Log: /exelog <LOGPATH> InstallLocation: /InstallDir <INSTALLPATH> +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_InapplicableOsVersion.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_InapplicableOsVersion.yaml @@ -16,3 +16,4 @@ Installers: Language: /exeenus Log: /exelog <LOGPATH> InstallLocation: /InstallDir <INSTALLPATH> +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_Sha256Mismatch.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller_Sha256Mismatch.yaml @@ -15,3 +15,4 @@ Installers: Language: /exeenus Log: /exelog <LOGPATH> InstallLocation: /InstallDir <INSTALLPATH> +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInnoInstaller.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInnoInstaller.yaml @@ -9,3 +9,4 @@ Installers: InstallerType: inno Switches: InstallLocation: /InstallDir <INSTALLPATH> +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInvalidManifest.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInvalidManifest.yaml @@ -15,3 +15,4 @@ Installers: Language: /exeenus Log: /exelog <LOGPATH> InstallLocation: /InstallDir <INSTALLPATH> +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsiInstaller.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsiInstaller.yaml @@ -7,4 +7,4 @@ Installers: Url: https://github.com/microsoft/appinstaller-cli/raw/master/src/AppInstallerCLIE2ETests/TestData/AppInstallerTestMsiInstaller.msi Sha256: 18a07fadd84fd66fce094e323a51afba648fd8c2a826573d61b60960d7e35e63 InstallerType: msi - +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller.yaml @@ -7,4 +7,4 @@ Installers: Url: https://github.com/microsoft/appinstaller-cli/raw/master/src/AppInstallerCLIE2ETests/TestData/AppInstallerTestMsixInstaller.msix Sha256: 714c861a52478bfa44228739c500c9429f4e75c7b78d48cac06bfb8f62bad627 InstallerType: msix - +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller_SignatureHashMismatch.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller_SignatureHashMismatch.yaml @@ -8,4 +8,4 @@ Installers: Sha256: 714c861a52478bfa44228739c500c9429f4e75c7b78d48cac06bfb8f62bad626 SignatureSha256: d8a54b79a9c5956df5b338fdaf37f48e56316465defd182cbe64d8e5d3d53d4d InstallerType: msix - +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller_WithSignatureHash.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller_WithSignatureHash.yaml @@ -8,4 +8,4 @@ Installers: Sha256: 714c861a52478bfa44228739c500c9429f4e75c7b78d48cac06bfb8f62bad627 SignatureSha256: d8a54b79a9c5956df5b338fdaf37f48e56316465defd182cbe64d8e5d3d53d4e InstallerType: msix - +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestNullsoftInstaller.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestNullsoftInstaller.yaml @@ -9,3 +9,4 @@ Installers: InstallerType: nullsoft Switches: InstallLocation: /InstallDir <INSTALLPATH> +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/ValidateCommand.cs b/src/AppInstallerCLIE2ETests/ValidateCommand.cs @@ -12,17 +12,17 @@ namespace AppInstallerCLIE2ETests { // Validate a good yaml var result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TestExeInstaller.yaml")); - Assert.AreEqual(result.ExitCode, Constants.ErrorCode.S_OK); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Manifest validation succeeded.")); // Validate invalid yaml result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\TestInvalidManifest.yaml")); - Assert.AreEqual(result.ExitCode, Constants.ErrorCode.S_OK); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); Assert.True(result.StdOut.Contains("Manifest validation failed.")); // Input file not found result = TestCommon.RunAICLICommand("validate", TestCommon.GetTestDataFile("Manifests\\DoesNotExist")); - Assert.AreEqual(result.ExitCode, Constants.ErrorCode.ERROR_FILE_NOT_FOUND); + Assert.AreEqual(Constants.ErrorCode.ERROR_FILE_NOT_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("File does not exist")); } } diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -310,6 +310,9 @@ <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidManifestVersionValue.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> <CopyFileToFolders Include="TestData\Manifest-Bad-NameMissing.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> @@ -328,6 +331,9 @@ <CopyFileToFolders Include="TestData\Manifest-Bad-UnknownProperty.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UnsupportedVersion.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> <CopyFileToFolders Include="TestData\Manifest-Bad-UrlInvalid.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -159,6 +159,9 @@ <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-SameLang.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidManifestVersionValue.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> <CopyFileToFolders Include="TestData\Manifest-Bad-NameMissing.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> @@ -177,6 +180,9 @@ <CopyFileToFolders Include="TestData\Manifest-Bad-UnknownProperty.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UnsupportedVersion.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> <CopyFileToFolders Include="TestData\Manifest-Bad-UrlInvalid.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_Exe.yaml b/src/AppInstallerCLITests/TestData/InstallFlowTest_Exe.yaml @@ -12,3 +12,4 @@ Installers: Url: https://ThisIsNotUsed InstallerType: exe Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_DownloadFlow.yaml b/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_DownloadFlow.yaml @@ -7,4 +7,5 @@ Installers: - Arch: x64 Url: https://github.com/microsoft/msix-packaging/blob/master/src/test/testData/unpack/TestAppxPackage_x64.appx?raw=true InstallerType: msix - Sha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea- \ No newline at end of file + Sha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_StreamingFlow.yaml b/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_StreamingFlow.yaml @@ -8,4 +8,5 @@ Installers: Url: https://github.com/microsoft/msix-packaging/blob/master/src/test/testData/unpack/TestAppxPackage_x64.appx?raw=true InstallerType: msix Sha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea - SignatureSha256: 138781c3e6f635240353f3d14d1d57bdcb89413e49be63b375e6a5d7b93b0d07- \ No newline at end of file + SignatureSha256: 138781c3e6f635240353f3d14d1d57bdcb89413e49be63b375e6a5d7b93b0d07 +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_NoApplicableArchitecture.yaml b/src/AppInstallerCLITests/TestData/InstallFlowTest_NoApplicableArchitecture.yaml @@ -11,4 +11,5 @@ Installers: - Arch: unknown Url: https://ThisIsNotUsed InstallerType: exe - Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B- \ No newline at end of file + Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/InstallerArgTest_Inno_NoSwitches.yaml b/src/AppInstallerCLITests/TestData/InstallerArgTest_Inno_NoSwitches.yaml @@ -8,4 +8,4 @@ Installers: Url: https://ThisIsNotUsed InstallerType: inno Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B - +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/InstallerArgTest_Inno_WithSwitches.yaml b/src/AppInstallerCLITests/TestData/InstallerArgTest_Inno_WithSwitches.yaml @@ -14,3 +14,4 @@ Installers: Silent: /mysilent Log: /mylog="<LOGPATH>" InstallLocation: /myinstalldir="<INSTALLPATH>" +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_NoSwitches.yaml b/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_NoSwitches.yaml @@ -8,3 +8,4 @@ Installers: Url: https://ThisIsNotUsed InstallerType: msi Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_WithSwitches.yaml b/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_WithSwitches.yaml @@ -14,3 +14,4 @@ Installers: Silent: /mysilent Log: /mylog="<LOGPATH>" InstallLocation: /myinstalldir="<INSTALLPATH>" +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-ArchInvalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-ArchInvalid.yaml @@ -8,3 +8,4 @@ Installers: - Arch: NotAnArch Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-ArchMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-ArchMissing.yaml @@ -7,3 +7,4 @@ InstallerType: Zip Installers: - Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-Channel-NotSupported.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-Channel-NotSupported.yaml @@ -9,3 +9,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-UPPER.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-UPPER.yaml @@ -8,3 +8,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-camelCase.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-camelCase.yaml @@ -8,3 +8,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-lower.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-lower.yaml @@ -8,3 +8,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml @@ -9,3 +9,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey-DifferentCase.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey-DifferentCase.yaml @@ -9,3 +9,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey.yaml @@ -9,3 +9,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-IdInvalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-IdInvalid.yaml @@ -8,3 +8,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-IdMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-IdMissing.yaml @@ -7,3 +7,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExe-NoSilent.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExe-NoSilent.yaml @@ -10,3 +10,4 @@ Installers: InstallerType: Exe Switches: Interactive: /i +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml @@ -10,3 +10,4 @@ Installers: Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD InstallerType: Exe +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml @@ -10,3 +10,4 @@ Installers: Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Switches: Interactive: /i +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml @@ -10,3 +10,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeInvalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeInvalid.yaml @@ -8,3 +8,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeMissing.yaml @@ -7,3 +7,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-DefaultScope.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-DefaultScope.yaml @@ -12,3 +12,4 @@ Installers: Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Scope: user +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-DefaultValues.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-DefaultValues.yaml @@ -11,3 +11,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-SameLang.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-SameLang.yaml @@ -13,3 +13,4 @@ Installers: Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Language: en-US +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness.yaml @@ -15,3 +15,4 @@ Installers: Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Language: en-US Scope: user +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallersMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallersMissing.yaml @@ -4,3 +4,4 @@ Name: MSIX SDK Version: 1.7.32 InstallerType: Zip Publisher: Microsoft +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InvalidManifestVersionValue.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InvalidManifestVersionValue.yaml @@ -0,0 +1,11 @@ +# Bad manifest. Unsupported version +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.0.0.0 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 1.1.0-invalid diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-NameMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-NameMissing.yaml @@ -7,3 +7,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-PublisherMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-PublisherMissing.yaml @@ -7,3 +7,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-Sha256Invalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-Sha256Invalid.yaml @@ -8,3 +8,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B6 +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-Sha256Missing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-Sha256Missing.yaml @@ -7,3 +7,4 @@ InstallerType: Zip Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-SwitchInvalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-SwitchInvalid.yaml @@ -10,3 +10,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-UnknownProperty.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-UnknownProperty.yaml @@ -9,3 +9,4 @@ Installers: Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Fake: this is Fake +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-UnsupportedVersion.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-UnsupportedVersion.yaml @@ -0,0 +1,11 @@ +# Bad manifest. Unsupported version +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.0.0.0 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 1000.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-UrlInvalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-UrlInvalid.yaml @@ -8,3 +8,4 @@ Installers: - Arch: x86 Url: NotAUrl Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-UrlMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-UrlMissing.yaml @@ -7,3 +7,4 @@ InstallerType: Zip Installers: - Arch: x86 Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-VersionInvalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-VersionInvalid.yaml @@ -8,3 +8,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-VersionMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-VersionMissing.yaml @@ -7,3 +7,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExe-Silent.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExe-Silent.yaml @@ -11,3 +11,4 @@ Installers: Switches: Silent: /s SilentWithProgress: /s +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExe-SilentRoot.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExe-SilentRoot.yaml @@ -11,3 +11,4 @@ Installers: Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD InstallerType: Exe +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExeRoot-Silent.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExeRoot-Silent.yaml @@ -11,3 +11,4 @@ Installers: Switches: Silent: /s SilentWithProgress: /s +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml @@ -11,3 +11,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerUniqueness-DiffScope.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerUniqueness-DiffScope.yaml @@ -13,3 +13,4 @@ Installers: Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Scope: system +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-Installeruniqueness-DefaultLang.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-Installeruniqueness-DefaultLang.yaml @@ -12,3 +12,4 @@ Installers: Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Language: en-MX +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-Installeruniqueness-DiffLangs.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-Installeruniqueness-DiffLangs.yaml @@ -13,3 +13,4 @@ Installers: Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Language: es-MX +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-Minimum-InstallerType.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-Minimum-InstallerType.yaml @@ -8,3 +8,4 @@ Installers: Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD InstallerType: Zip +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-Minimum.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-Minimum.yaml @@ -8,3 +8,4 @@ Installers: - Arch: x86 Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-Spaces.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-Spaces.yaml @@ -50,4 +50,5 @@ Localization: - Language: es-MX Description: El proyecto MSIX SDK es habilita desarrolladores de diferentes Homepage: https://github.com/microsoft/msix-packaging/es-MX - LicenseUrl: https://github.com/microsoft/msix-packaging/blob/master/LICENSE-es-MX- \ No newline at end of file + LicenseUrl: https://github.com/microsoft/msix-packaging/blob/master/LICENSE-es-MX +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-Switches.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-Switches.yaml @@ -12,3 +12,4 @@ Installers: Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD Switches: Silent: /s +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good.yaml @@ -50,4 +50,5 @@ Localization: - Language: es-MX Description: El proyecto MSIX SDK es habilita desarrolladores de diferentes Homepage: https://github.com/microsoft/msix-packaging/es-MX - LicenseUrl: https://github.com/microsoft/msix-packaging/blob/master/LICENSE-es-MX- \ No newline at end of file + LicenseUrl: https://github.com/microsoft/msix-packaging/blob/master/LICENSE-es-MX +ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/Versions.cpp b/src/AppInstallerCLITests/Versions.cpp @@ -78,7 +78,15 @@ void RequireLessThan(std::string_view a, std::string_view b) Version vB{ std::string(b) }; REQUIRE(vA < vB); - REQUIRE(!(vB < vA)); + REQUIRE_FALSE(vB < vA); + REQUIRE(vA <= vB); + REQUIRE_FALSE(vB <= vA); + REQUIRE(vB > vA); + REQUIRE_FALSE(vA > vB); + REQUIRE(vB >= vA); + REQUIRE_FALSE(vA >= vB); + REQUIRE_FALSE(vA == vB); + REQUIRE(vA != vB); } void RequireEqual(std::string_view a, std::string_view b) @@ -86,8 +94,12 @@ void RequireEqual(std::string_view a, std::string_view b) Version vA{ std::string(a) }; Version vB{ std::string(b) }; - REQUIRE(!(vA < vB)); - REQUIRE(!(vB < vA)); + REQUIRE(vA == vB); + REQUIRE_FALSE(vA != vB); + REQUIRE(vA <= vB); + REQUIRE(vA >= vB); + REQUIRE_FALSE(vA < vB); + REQUIRE_FALSE(vA > vB); } TEST_CASE("VersionCompare", "[versions]") diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp @@ -216,12 +216,14 @@ TEST_CASE("ReadBadManifests", "[ManifestValidation]") { "Manifest-Bad-PublisherMissing.yaml", "Required field missing. Field: Publisher" }, { "Manifest-Bad-Sha256Invalid.yaml", "Invalid field value. Field: Sha256" }, { "Manifest-Bad-Sha256Missing.yaml", "Required field missing. Field: Sha256" }, - { "Manifest-Bad-SwitchInvalid.yaml", "Unknown field. Field: NotASwitch" }, - { "Manifest-Bad-UnknownProperty.yaml", "Unknown field. Field: Fake" }, + { "Manifest-Bad-SwitchInvalid.yaml", "Unknown field. Field: NotASwitch", true }, + { "Manifest-Bad-UnknownProperty.yaml", "Unknown field. Field: Fake", true }, + { "Manifest-Bad-UnsupportedVersion.yaml", "Unsupported ManifestVersion" }, { "Manifest-Bad-UrlInvalid.yaml", "Invalid field value. Field: Url" }, { "Manifest-Bad-UrlMissing.yaml", "Required field missing. Field: Url" }, { "Manifest-Bad-VersionInvalid.yaml", "Invalid field value. Field: Version" }, { "Manifest-Bad-VersionMissing.yaml", "Required field missing. Field: Version" }, + { "Manifest-Bad-InvalidManifestVersionValue.yaml", "Invalid field value. Field: ManifestVersion" }, }; for (auto const& testCase : TestCases) diff --git a/src/AppInstallerCommonCore/Errors.cpp b/src/AppInstallerCommonCore/Errors.cpp @@ -24,6 +24,8 @@ namespace AppInstaller return "Opening manifest failed"; case APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED: return "Running ShellExecute failed"; + case APPINSTALLER_CLI_ERROR_UNSUPPORTED_MANIFESTVERSION: + return "Cannot process manifest. The manifest version is higher than supported. Please update the client."; case APPINSTALLER_CLI_ERROR_DOWNLOAD_FAILED: return "Downloading installer failed"; case APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX: diff --git a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h @@ -16,7 +16,7 @@ #define APPINSTALLER_CLI_ERROR_MANIFEST_FAILED ((HRESULT)0x8A150004) #define APPINSTALLER_CLI_ERROR_CTRL_SIGNAL_RECEIVED ((HRESULT)0x8A150005) #define APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED ((HRESULT)0x8A150006) -//#define APPINSTALLER_CLI_ERROR_RUNTIME_ERROR ((HRESULT)0x8A150007) // Unused, can be repurposed +#define APPINSTALLER_CLI_ERROR_UNSUPPORTED_MANIFESTVERSION ((HRESULT)0x8A150007) #define APPINSTALLER_CLI_ERROR_DOWNLOAD_FAILED ((HRESULT)0x8A150008) #define APPINSTALLER_CLI_ERROR_CANNOT_WRITE_TO_UPLEVEL_INDEX ((HRESULT)0x8A150009) #define APPINSTALLER_CLI_ERROR_INDEX_INTEGRITY_COMPROMISED ((HRESULT)0x8A15000A) diff --git a/src/AppInstallerCommonCore/Public/AppInstallerVersions.h b/src/AppInstallerCommonCore/Public/AppInstallerVersions.h @@ -26,6 +26,8 @@ namespace AppInstaller::Utility // The default characters to split a version string on. constexpr static std::string_view DefaultSplitChars = "."sv; + Version() = default; + Version(const std::string& version, std::string_view splitChars = DefaultSplitChars) : Version(std::string(version), splitChars) {} Version(std::string&& version, std::string_view splitChars = DefaultSplitChars); @@ -34,6 +36,11 @@ namespace AppInstaller::Utility const std::string& ToString() const { return m_version; } bool operator<(const Version& other) const; + bool operator>(const Version& other) const; + bool operator<=(const Version& other) const; + bool operator>=(const Version& other) const; + bool operator==(const Version& other) const; + bool operator!=(const Version& other) const; // An individual version part in between split characters. struct Part @@ -41,6 +48,8 @@ namespace AppInstaller::Utility Part(const std::string& part); bool operator<(const Part& other) const; + bool operator==(const Part& other) const; + bool operator!=(const Part& other) const; uint64_t Integer = 0; std::string Other; @@ -49,7 +58,7 @@ namespace AppInstaller::Utility // Gets the part breakdown for a given version; used for tests. const std::vector<Part>& GetParts() const { return m_parts; } - private: + protected: std::string m_version; std::vector<Part> m_parts; }; diff --git a/src/AppInstallerCommonCore/Versions.cpp b/src/AppInstallerCommonCore/Versions.cpp @@ -63,6 +63,44 @@ namespace AppInstaller::Utility return m_parts.size() < other.m_parts.size(); } + bool Version::operator>(const Version& other) const + { + return other < *this; + } + + bool Version::operator<=(const Version& other) const + { + return !(*this > other); + } + + bool Version::operator>=(const Version& other) const + { + return !(*this < other); + } + + bool Version::operator==(const Version& other) const + { + if (m_parts.size() != other.m_parts.size()) + { + return false; + } + + for (size_t i = 0; i < m_parts.size(); ++i) + { + if (m_parts[i] != other.m_parts[i]) + { + return false; + } + } + + return true; + } + + bool Version::operator!=(const Version& other) const + { + return !(*this == other); + } + Version::Part::Part(const std::string& part) { size_t end = 0; @@ -96,6 +134,16 @@ namespace AppInstaller::Utility return false; } + bool Version::Part::operator==(const Part& other) const + { + return Integer == other.Integer && Other == other.Other; + } + + bool Version::Part::operator!=(const Part& other) const + { + return !(*this == other); + } + bool Channel::operator<(const Channel& other) const { return m_channel < other.m_channel; diff --git a/src/AppInstallerRepositoryCore/Manifest/Manifest.cpp b/src/AppInstallerRepositoryCore/Manifest/Manifest.cpp @@ -39,41 +39,76 @@ namespace AppInstaller::Manifest std::vector<ValidationError> Manifest::PopulateManifestFields(const YAML::Node& rootNode, bool fullValidation) { + // Detect manifest version first to determine expected fields + // Use index to access ManifestVersion directly. If there're duplicates or other general errors, it'll be detected in later + // processing of iterating the whole manifest. + // Todo: make ManifestVersion required when all manifests in our repo have been updated to contain a ManifestVersion + if (rootNode["ManifestVersion"]) + { + auto manifestVersionValue = rootNode["ManifestVersion"].as<std::string>(); + ManifestVersion = ManifestVer(manifestVersionValue, fullValidation); + } + else + { + ManifestVersion = PreviewManifestVersion; + } + + // Check manifest version is supported + if (ManifestVersion.Major() > MaxSupportedMajorVersion) + { + THROW_EXCEPTION_MSG(ManifestException(APPINSTALLER_CLI_ERROR_UNSUPPORTED_MANIFESTVERSION), "Unsupported ManifestVersion: %S", ManifestVersion.ToString().c_str()); + } + + std::vector<ManifestFieldInfo> fieldInfos = + { + { "ManifestVersion", [this](const YAML::Node&) { /* ManifestVersion already processed */ }, false, + // Regex here is to prevent leading 0s in the version, this also keeps consistent with other versions in the manifest + "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){2}$" }, + }; + YAML::Node switchesNode; YAML::Node installersNode; YAML::Node localizationsNode; - const std::vector<ManifestFieldInfo> FieldInfos = - { - { "Id", [this](const YAML::Node& value) { Id = value.as<std::string>(); Utility::Trim(Id); }, true, "^[\\S]+\\.[\\S]+$" }, - { "Name", [this](const YAML::Node& value) { Name = value.as<std::string>(); Utility::Trim(Name); }, true }, - { "Version", [this](const YAML::Node& value) { Version = value.as<std::string>(); Utility::Trim(Version); }, true, - "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$" }, - { "Publisher", [this](const YAML::Node& value) { Publisher = value.as<std::string>(); }, true }, - { "AppMoniker", [this](const YAML::Node& value) { AppMoniker = value.as<std::string>(); Utility::Trim(AppMoniker); } }, - { "Channel", [this](const YAML::Node& value) { Channel = value.as<std::string>(); Utility::Trim(Channel); } }, - { "Author", [this](const YAML::Node& value) { Author = value.as<std::string>(); } }, - { "License", [this](const YAML::Node& value) { License = value.as<std::string>(); } }, - { "MinOSVersion", [this](const YAML::Node& value) { MinOSVersion = value.as<std::string>(); Utility::Trim(MinOSVersion); }, false, - "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$" }, - { "Tags", [this](const YAML::Node& value) { Tags = SplitMultiValueField(value.as<std::string>()); } }, - { "Commands", [this](const YAML::Node& value) { Commands = SplitMultiValueField(value.as<std::string>()); } }, - { "Protocols", [this](const YAML::Node& value) { Protocols = SplitMultiValueField(value.as<std::string>()); } }, - { "FileExtensions", [this](const YAML::Node& value) { FileExtensions = SplitMultiValueField(value.as<std::string>()); } }, - { "InstallerType", [this](const YAML::Node& value) { InstallerType = ManifestInstaller::ConvertToInstallerTypeEnum(value.as<std::string>()); } }, - { "Description", [this](const YAML::Node& value) { Description = value.as<std::string>(); } }, - { "Homepage", [this](const YAML::Node& value) { Homepage = value.as<std::string>(); } }, - { "LicenseUrl", [this](const YAML::Node& value) { LicenseUrl = value.as<std::string>(); } }, - { "Switches", [&](const YAML::Node& value) { switchesNode = value; } }, - { "Installers", [&](const YAML::Node& value) { installersNode = value; }, true }, - { "Localization", [&](const YAML::Node& value) { localizationsNode = value; } }, - }; + // Todo: The FieldInfo can be a table with an entry specifying which version the field is introduced + // so that we can query applicable fields given a ManifestVersion + if (ManifestVersion >= PreviewManifestVersion) + { + // Add preview fields + std::vector<ManifestFieldInfo> previewFieldInfos = + { + { "Id", [this](const YAML::Node& value) { Id = value.as<std::string>(); Utility::Trim(Id); }, true, "^[\\S]+\\.[\\S]+$" }, + { "Name", [this](const YAML::Node& value) { Name = value.as<std::string>(); Utility::Trim(Name); }, true }, + { "Version", [this](const YAML::Node& value) { Version = value.as<std::string>(); Utility::Trim(Version); }, true, + "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$" }, + { "Publisher", [this](const YAML::Node& value) { Publisher = value.as<std::string>(); }, true }, + { "AppMoniker", [this](const YAML::Node& value) { AppMoniker = value.as<std::string>(); Utility::Trim(AppMoniker); } }, + { "Channel", [this](const YAML::Node& value) { Channel = value.as<std::string>(); Utility::Trim(Channel); } }, + { "Author", [this](const YAML::Node& value) { Author = value.as<std::string>(); } }, + { "License", [this](const YAML::Node& value) { License = value.as<std::string>(); } }, + { "MinOSVersion", [this](const YAML::Node& value) { MinOSVersion = value.as<std::string>(); Utility::Trim(MinOSVersion); }, false, + "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$" }, + { "Tags", [this](const YAML::Node& value) { Tags = SplitMultiValueField(value.as<std::string>()); } }, + { "Commands", [this](const YAML::Node& value) { Commands = SplitMultiValueField(value.as<std::string>()); } }, + { "Protocols", [this](const YAML::Node& value) { Protocols = SplitMultiValueField(value.as<std::string>()); } }, + { "FileExtensions", [this](const YAML::Node& value) { FileExtensions = SplitMultiValueField(value.as<std::string>()); } }, + { "InstallerType", [this](const YAML::Node& value) { InstallerType = ManifestInstaller::ConvertToInstallerTypeEnum(value.as<std::string>()); } }, + { "Description", [this](const YAML::Node& value) { Description = value.as<std::string>(); } }, + { "Homepage", [this](const YAML::Node& value) { Homepage = value.as<std::string>(); } }, + { "LicenseUrl", [this](const YAML::Node& value) { LicenseUrl = value.as<std::string>(); } }, + { "Switches", [&](const YAML::Node& value) { switchesNode = value; } }, + { "Installers", [&](const YAML::Node& value) { installersNode = value; }, true }, + { "Localization", [&](const YAML::Node& value) { localizationsNode = value; } }, + }; + + std::move(previewFieldInfos.begin(), previewFieldInfos.end(), std::inserter(fieldInfos, fieldInfos.end())); + } - std::vector<ValidationError> resultErrors = ValidateAndProcessFields(rootNode, FieldInfos, fullValidation); + std::vector<ValidationError> resultErrors = ValidateAndProcessFields(rootNode, fieldInfos, fullValidation); if (!switchesNode.IsNull()) { - auto errors = ManifestInstaller::PopulateSwitchesFields(switchesNode, this->Switches, fullValidation); + auto errors = ManifestInstaller::PopulateSwitchesFields(switchesNode, this->Switches, fullValidation, ManifestVersion); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); } @@ -85,7 +120,7 @@ namespace AppInstaller::Manifest for (std::size_t i = 0; i < installersNode.size(); i++) { YAML::Node installerNode = installersNode[i]; ManifestInstaller installer; - auto errors = installer.PopulateInstallerFields(installerNode, defaultInstaller, fullValidation); + auto errors = installer.PopulateInstallerFields(installerNode, defaultInstaller, fullValidation, ManifestVersion); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); this->Installers.emplace_back(std::move(installer)); } @@ -101,7 +136,7 @@ namespace AppInstaller::Manifest for (std::size_t i = 0; i < localizationsNode.size(); i++) { YAML::Node localizationNode = localizationsNode[i]; ManifestLocalization localization; - auto errors = localization.PopulateLocalizationFields(localizationNode, defaultLocalization, fullValidation); + auto errors = localization.PopulateLocalizationFields(localizationNode, defaultLocalization, fullValidation, ManifestVersion); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); this->Localization.emplace_back(std::move(localization)); } @@ -168,6 +203,11 @@ namespace AppInstaller::Manifest YAML::Node rootNode = YAML::LoadFile(inputFile.u8string()); errors = manifest.PopulateManifestFields(rootNode, fullValidation); } + catch (const ManifestException&) + { + // Prevent ManifestException from being wrapped in another ManifestException + throw; + } catch (const std::exception& e) { THROW_EXCEPTION_MSG(ManifestException(), e.what()); @@ -196,6 +236,11 @@ namespace AppInstaller::Manifest YAML::Node rootNode = YAML::Load(input); errors = manifest.PopulateManifestFields(rootNode, fullValidation); } + catch (const ManifestException&) + { + // Prevent ManifestException from being wrapped in another ManifestException + throw; + } catch (const std::exception& e) { THROW_EXCEPTION_MSG(ManifestException(), e.what()); diff --git a/src/AppInstallerRepositoryCore/Manifest/Manifest.h b/src/AppInstallerRepositoryCore/Manifest/Manifest.h @@ -12,7 +12,7 @@ namespace AppInstaller::Manifest { - // Our representation of the parsed manifest file. + // Representation of the parsed manifest file. struct Manifest { using string_t = Utility::NormalizedString; @@ -59,6 +59,8 @@ namespace AppInstaller::Manifest string_t LicenseUrl; + ManifestVer ManifestVersion; + std::map<ManifestInstaller::InstallerSwitchType, string_t> Switches; std::vector<ManifestInstaller> Installers; diff --git a/src/AppInstallerRepositoryCore/Manifest/ManifestInstaller.cpp b/src/AppInstallerRepositoryCore/Manifest/ManifestInstaller.cpp @@ -6,25 +6,36 @@ namespace AppInstaller::Manifest { - std::vector<ValidationError> ManifestInstaller::PopulateInstallerFields(const YAML::Node& installerNode, const ManifestInstaller& defaultInstaller, bool fullValidation) + std::vector<ValidationError> ManifestInstaller::PopulateInstallerFields( + const YAML::Node& installerNode, + const ManifestInstaller& defaultInstaller, + bool fullValidation, + ManifestVer manifestVersion) { YAML::Node switchesNode; this->InstallerType = defaultInstaller.InstallerType; this->Scope = "user"; - const std::vector<ManifestFieldInfo> InstallerFieldInfos = + std::vector<ManifestFieldInfo> fieldInfos; + + if (manifestVersion >= PreviewManifestVersion) { - { "Arch", [this](const YAML::Node& value) { Arch = Utility::ConvertToArchitectureEnum(value.as<std::string>()); }, true }, - { "Url", [this](const YAML::Node& value) { Url = value.as<std::string>(); }, true }, - { "Sha256", [this](const YAML::Node& value) { Sha256 = Utility::SHA256::ConvertToBytes(value.as<std::string>()); }, true, "^[A-Fa-f0-9]{64}$" }, - { "SignatureSha256", [this](const YAML::Node& value) { SignatureSha256 = Utility::SHA256::ConvertToBytes(value.as<std::string>()); } }, - { "Language", [this](const YAML::Node& value) { Language = value.as<std::string>(); } }, - { "Scope", [this](const YAML::Node& value) { Scope = value.as<std::string>(); } }, - { "InstallerType", [this](const YAML::Node& value) { InstallerType = ConvertToInstallerTypeEnum(value.as<std::string>()); } }, - { "Switches", [&](const YAML::Node& value) { switchesNode = value; } }, - }; - - auto resultErrors = ValidateAndProcessFields(installerNode, InstallerFieldInfos, fullValidation); + std::vector<ManifestFieldInfo> previewFieldInfos = + { + { "Arch", [this](const YAML::Node& value) { Arch = Utility::ConvertToArchitectureEnum(value.as<std::string>()); }, true }, + { "Url", [this](const YAML::Node& value) { Url = value.as<std::string>(); }, true }, + { "Sha256", [this](const YAML::Node& value) { Sha256 = Utility::SHA256::ConvertToBytes(value.as<std::string>()); }, true, "^[A-Fa-f0-9]{64}$" }, + { "SignatureSha256", [this](const YAML::Node& value) { SignatureSha256 = Utility::SHA256::ConvertToBytes(value.as<std::string>()); } }, + { "Language", [this](const YAML::Node& value) { Language = value.as<std::string>(); } }, + { "Scope", [this](const YAML::Node& value) { Scope = value.as<std::string>(); } }, + { "InstallerType", [this](const YAML::Node& value) { InstallerType = ConvertToInstallerTypeEnum(value.as<std::string>()); } }, + { "Switches", [&](const YAML::Node& value) { switchesNode = value; } }, + }; + + std::move(previewFieldInfos.begin(), previewFieldInfos.end(), std::inserter(fieldInfos, fieldInfos.end())); + } + + auto resultErrors = ValidateAndProcessFields(installerNode, fieldInfos, fullValidation); // Populate default known switches this->Switches = GetDefaultKnownSwitches(this->InstallerType); @@ -38,7 +49,7 @@ namespace AppInstaller::Manifest // Override with switches from installer if applicable if (!switchesNode.IsNull()) { - auto errors = PopulateSwitchesFields(switchesNode, this->Switches, fullValidation); + auto errors = PopulateSwitchesFields(switchesNode, this->Switches, fullValidation, manifestVersion); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); } @@ -75,20 +86,28 @@ namespace AppInstaller::Manifest std::vector<ValidationError> ManifestInstaller::PopulateSwitchesFields( const YAML::Node& switchesNode, std::map<InstallerSwitchType, string_t>& switches, - bool fullValidation) + bool fullValidation, + ManifestVer manifestVersion) { - const std::vector<ManifestFieldInfo> SwitchesFieldInfos = + std::vector<ManifestFieldInfo> fieldInfos; + + if (manifestVersion >= PreviewManifestVersion) { - { "Custom", [&](const YAML::Node& value) { switches[InstallerSwitchType::Custom] = value.as<std::string>(); } }, - { "Silent", [&](const YAML::Node& value) { switches[InstallerSwitchType::Silent] = value.as<std::string>(); } }, - { "SilentWithProgress", [&](const YAML::Node& value) { switches[InstallerSwitchType::SilentWithProgress] = value.as<std::string>(); } }, - { "Interactive", [&](const YAML::Node& value) { switches[InstallerSwitchType::Interactive] = value.as<std::string>(); } }, - { "Language", [&](const YAML::Node& value) { switches[InstallerSwitchType::Language] = value.as<std::string>(); } }, - { "Log", [&](const YAML::Node& value) { switches[InstallerSwitchType::Log] = value.as<std::string>(); } }, - { "InstallLocation", [&](const YAML::Node& value) { switches[InstallerSwitchType::InstallLocation] = value.as<std::string>(); } }, - }; - - return ValidateAndProcessFields(switchesNode, SwitchesFieldInfos, fullValidation); + std::vector<ManifestFieldInfo> previewFieldInfos = + { + { "Custom", [&](const YAML::Node& value) { switches[InstallerSwitchType::Custom] = value.as<std::string>(); } }, + { "Silent", [&](const YAML::Node& value) { switches[InstallerSwitchType::Silent] = value.as<std::string>(); } }, + { "SilentWithProgress", [&](const YAML::Node& value) { switches[InstallerSwitchType::SilentWithProgress] = value.as<std::string>(); } }, + { "Interactive", [&](const YAML::Node& value) { switches[InstallerSwitchType::Interactive] = value.as<std::string>(); } }, + { "Language", [&](const YAML::Node& value) { switches[InstallerSwitchType::Language] = value.as<std::string>(); } }, + { "Log", [&](const YAML::Node& value) { switches[InstallerSwitchType::Log] = value.as<std::string>(); } }, + { "InstallLocation", [&](const YAML::Node& value) { switches[InstallerSwitchType::InstallLocation] = value.as<std::string>(); } }, + }; + + std::move(previewFieldInfos.begin(), previewFieldInfos.end(), std::inserter(fieldInfos, fieldInfos.end())); + } + + return ValidateAndProcessFields(switchesNode, fieldInfos, fullValidation); } std::map<ManifestInstaller::InstallerSwitchType, ManifestInstaller::string_t> ManifestInstaller::GetDefaultKnownSwitches(InstallerTypeEnum installerType) diff --git a/src/AppInstallerRepositoryCore/Manifest/ManifestInstaller.h b/src/AppInstallerRepositoryCore/Manifest/ManifestInstaller.h @@ -80,11 +80,16 @@ namespace AppInstaller::Manifest static std::vector<ValidationError> PopulateSwitchesFields( const YAML::Node& switchesNode, std::map<InstallerSwitchType, string_t>& switches, - bool fullValidation); + bool fullValidation, + ManifestVer manifestVersion); // Populates ManifestInstaller // defaultInstaller: if an optional field is not found in the YAML node, the field will be populated with value from defaultInstaller. - std::vector<ValidationError> PopulateInstallerFields(const YAML::Node& installerNode, const ManifestInstaller& defaultInstaller, bool fullValidation); + std::vector<ValidationError> PopulateInstallerFields( + const YAML::Node& installerNode, + const ManifestInstaller& defaultInstaller, + bool fullValidation, + ManifestVer manifestVersion); static std::string InstallerTypeToString(InstallerTypeEnum installerType); }; diff --git a/src/AppInstallerRepositoryCore/Manifest/ManifestLocalization.cpp b/src/AppInstallerRepositoryCore/Manifest/ManifestLocalization.cpp @@ -6,21 +6,32 @@ namespace AppInstaller::Manifest { - std::vector<ValidationError> ManifestLocalization::PopulateLocalizationFields(const YAML::Node& localizationNode, const ManifestLocalization& defaultLocalization, bool fullValidation) + std::vector<ValidationError> ManifestLocalization::PopulateLocalizationFields( + const YAML::Node& localizationNode, + const ManifestLocalization& defaultLocalization, + bool fullValidation, + ManifestVer manifestVersion) { // Populates default values first this->Description = defaultLocalization.Description; this->Homepage = defaultLocalization.Homepage; this->LicenseUrl = defaultLocalization.LicenseUrl; - const std::vector<ManifestFieldInfo> FieldInfos = + std::vector<ManifestFieldInfo> fieldInfos; + + if (manifestVersion >= PreviewManifestVersion) { - { "Language", [this](const YAML::Node& value) { Language = value.as<std::string>(); }, true }, - { "Description", [this](const YAML::Node& value) { Description = value.as<std::string>(); } }, - { "Homepage", [this](const YAML::Node& value) { Homepage = value.as<std::string>(); } }, - { "LicenseUrl", [this](const YAML::Node& value) { LicenseUrl = value.as<std::string>(); } }, - }; + std::vector<ManifestFieldInfo> previewFieldInfos = + { + { "Language", [this](const YAML::Node& value) { Language = value.as<std::string>(); }, true }, + { "Description", [this](const YAML::Node& value) { Description = value.as<std::string>(); } }, + { "Homepage", [this](const YAML::Node& value) { Homepage = value.as<std::string>(); } }, + { "LicenseUrl", [this](const YAML::Node& value) { LicenseUrl = value.as<std::string>(); } }, + }; + + std::move(previewFieldInfos.begin(), previewFieldInfos.end(), std::inserter(fieldInfos, fieldInfos.end())); + } - return ValidateAndProcessFields(localizationNode, FieldInfos, fullValidation); + return ValidateAndProcessFields(localizationNode, fieldInfos, fullValidation); } } diff --git a/src/AppInstallerRepositoryCore/Manifest/ManifestLocalization.h b/src/AppInstallerRepositoryCore/Manifest/ManifestLocalization.h @@ -23,6 +23,10 @@ namespace AppInstaller::Manifest // Populates ManifestLocalization // defaultLocalization: if an optional field is not found in the YAML node, the field will be populated with value from defaultLocalization. - std::vector<ValidationError> PopulateLocalizationFields(const YAML::Node& localizationNode, const ManifestLocalization& defaultLocalization, bool fullValidation); + std::vector<ValidationError> PopulateLocalizationFields( + const YAML::Node& localizationNode, + const ManifestLocalization& defaultLocalization, + bool fullValidation, + ManifestVer manifestVersion); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Manifest/ManifestValidation.cpp b/src/AppInstallerRepositoryCore/Manifest/ManifestValidation.cpp @@ -6,7 +6,10 @@ namespace AppInstaller::Manifest { - std::vector<ValidationError> ValidateAndProcessFields(const YAML::Node& rootNode, const std::vector<ManifestFieldInfo> fieldInfos, bool fullValidation) + std::vector<ValidationError> ValidateAndProcessFields( + const YAML::Node& rootNode, + const std::vector<ManifestFieldInfo> fieldInfos, + bool fullValidation) { std::vector<ValidationError> errors; @@ -77,10 +80,10 @@ namespace AppInstaller::Manifest } else { - // Forward compatibility for future fields + // For full validation, also reports unrecognized fields as warning if (fullValidation) { - errors.emplace_back(ManifestError::FieldUnknown, key, "", keyValuePair.first.Mark().line, keyValuePair.first.Mark().column); + errors.emplace_back(ManifestError::FieldUnknown, key, "", keyValuePair.first.Mark().line, keyValuePair.first.Mark().column, ValidationError::Level::Warning); } } } @@ -96,4 +99,33 @@ namespace AppInstaller::Manifest return errors; } + + ManifestVer::ManifestVer(std::string version, bool fullValidation) : Version(std::move(version), ".") + { + bool validationSuccess = true; + + if (m_parts.size() > 3) + { + validationSuccess = false; + } + else + { + for (size_t i = 0; i < m_parts.size(); i++) + { + if (!m_parts[i].Other.empty() && + (i < 2 || fullValidation)) + { + validationSuccess = false; + break; + } + } + } + + if (!validationSuccess) + { + std::vector<ValidationError> errors; + errors.emplace_back(ManifestError::InvalidFieldValue, "ManifestVersion", m_version); + THROW_EXCEPTION(ManifestException(std::move(errors))); + } + } } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Manifest/ManifestValidation.h b/src/AppInstallerRepositoryCore/Manifest/ManifestValidation.h @@ -6,11 +6,29 @@ #include <functional> #include <wil/result.h> #include <AppInstallerErrors.h> +#include <AppInstallerVersions.h> namespace YAML { class Node; } namespace AppInstaller::Manifest { + // ManifestVer is inherited from Utility::Version and is a more restricted version. + // ManifestVer is used to specify the version of app manifest itself. + // Currently ManifestVer is a 3 part version in the format of [0-65535].[0-65535].[0-65535] + struct ManifestVer : public Utility::Version + { + ManifestVer() = default; + + ManifestVer(std::string version, bool fullValidation); + + uint64_t Major() { return m_parts.size() > 0 ? m_parts[0].Integer : 0; } + uint64_t Minor() { return m_parts.size() > 1 ? m_parts[1].Integer : 0; } + uint64_t Patch() { return m_parts.size() > 2 ? m_parts[2].Integer : 0; } + }; + + static const uint64_t MaxSupportedMajorVersion = 1; + static const ManifestVer PreviewManifestVersion = ManifestVer("0.1.0", false); + namespace ManifestError { const char* const ErrorMessagePrefix = "Manifest Error: "; @@ -76,12 +94,15 @@ namespace AppInstaller::Manifest // Yaml-cpp does not support case insensitive search and it allows duplicate keys. If duplicate keys exist, // the value is undefined. So in this method, we will iterate through the node map and process each individual // pair ourselves. This also helps with generating aggregated error rather than throwing on first failure. - std::vector<ValidationError> ValidateAndProcessFields(const YAML::Node& rootNode, const std::vector<ManifestFieldInfo> fieldInfos, bool fullValidation); + std::vector<ValidationError> ValidateAndProcessFields( + const YAML::Node& rootNode, + const std::vector<ManifestFieldInfo> fieldInfos, + bool fullValidation); struct ManifestException : public wil::ResultException { - ManifestException(std::vector<ValidationError>&& errors = {}) : - m_errors(std::move(errors)), wil::ResultException(APPINSTALLER_CLI_ERROR_MANIFEST_FAILED) + ManifestException(std::vector<ValidationError>&& errors = {}, HRESULT hr = APPINSTALLER_CLI_ERROR_MANIFEST_FAILED) : + m_errors(std::move(errors)), wil::ResultException(hr) { auto p = [&](ValidationError const& e) { return e.ErrorLevel == ValidationError::Level::Error; @@ -90,6 +111,8 @@ namespace AppInstaller::Manifest m_warningOnly = !m_errors.empty() && std::find_if(m_errors.begin(), m_errors.end(), p) == m_errors.end(); } + ManifestException(HRESULT hr) : ManifestException({}, hr) {} + // Error message without wil diagnostic info const std::string& GetManifestErrorMessage() const noexcept { @@ -97,7 +120,7 @@ namespace AppInstaller::Manifest { if (m_errors.empty()) { - // Syntax error, Yaml-cpp error is stored in FailureInfo + // Syntax error, yaml-cpp error is stored in FailureInfo m_manifestErrorMessage = Utility::ConvertToUTF8(GetFailureInfo().pszMessage); } else