From 0f64c0c9b60972133940c8a412b96799f27a4561 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Thu, 3 Mar 2022 11:55:28 -0800 Subject: [PATCH 01/19] add proposed schema changes for 1.2 --- .../v1.2.0/manifest.defaultLocale.1.2.0.json | 28 +++++++++ .../v1.2.0/manifest.installer.1.2.0.json | 61 ++++++++++++++----- .../v1.2.0/manifest.locale.1.2.0.json | 28 +++++++++ 3 files changed, 102 insertions(+), 15 deletions(-) diff --git a/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json index 44c9201d23..d07416dad4 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json @@ -35,6 +35,21 @@ "description": "The agreement URL." } } + }, + "Documentation": { + "type": "object", + "properties": { + "DocumentDescription": { + "type": [ "string", "null" ], + "minLength": 1, + "maxLength": 100, + "description": "The document description." + }, + "DocumentUrl": { + "$ref": "#/definitions/Url", + "description": "The documentation URL." + } + } } }, "type": "object", @@ -154,6 +169,19 @@ "$ref": "#/definitions/Url", "description": "The package release notes url" }, + "InstallationNotes": { + "type": [ "string", "null" ], + "minLength": 1, + "maxLength": 240, + "description": "The notes displayed to the user upon completion of a package installation." + }, + "Documentations": { + "type": [ "array", "null" ], + "items": { + "$ref": "#/definitions/Documentation" + }, + "maxItems": 128 + }, "ManifestType": { "type": "string", "default": "defaultLocale", diff --git a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json index ecc28d4bad..3b22bee50d 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json @@ -46,6 +46,12 @@ "pattern": "^(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}$", "description": "The installer minimum operating system version" }, + "Url": { + "type": [ "string", "null" ], + "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", + "maxLength": 2048, + "description": "Url type" + }, "InstallerType": { "type": [ "string", "null" ], "enum": [ @@ -63,6 +69,17 @@ ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, + "Architecture": { + "type": "string", + "enum": [ + "x86", + "x64", + "arm", + "arm64", + "neutral" + ], + "description": "The installer target architecture" + }, "Scope": { "type": [ "string", "null" ], "enum": [ @@ -178,8 +195,13 @@ "cancelledByUser", "alreadyInstalled", "downgrade", - "blockedByPolicy" + "blockedByPolicy", + "custom" ] + }, + "ReturnResponseUrl": { + "$ref": "#/definitions/Url", + "description": "The return response url to provide additional guidance for expected return codes" } } }, @@ -209,7 +231,7 @@ "type": [ "array", "null" ], "items": { "type": "string", - "pattern": "^[a-z][-a-z0-9\\.\\+]*$", + "pattern": "^[a-z0-9][-a-z0-9\\.\\+]*$", "maxLength": 2048 }, "maxItems": 16, @@ -223,7 +245,7 @@ "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, - "maxItems": 256, + "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, @@ -262,6 +284,12 @@ }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" + }, + "Architecture": { + "$ref": "#/definitions/Architecture" + }, + "Scope": { + "$ref": "#/definitions/Scope" } }, "required": [ "PackageIdentifier" ] @@ -385,6 +413,19 @@ }, "description": "List of OS architectures the installer does not support" }, + "UnsupportedArguments": { + "type": [ "array", "null" ], + "uniqueItems": true, + "items": { + "type": "string", + "title": "UnsupportedArgument", + "enum": [ + "log", + "location" + ] + }, + "description": "List of winget arguments the installer does not support" + }, "AppsAndFeaturesEntry": { "type": "object", "properties": { @@ -449,15 +490,7 @@ "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { - "type": "string", - "enum": [ - "x86", - "x64", - "arm", - "arm64", - "neutral" - ], - "description": "The installer target architecture" + "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" @@ -466,9 +499,7 @@ "$ref": "#/definitions/Scope" }, "InstallerUrl": { - "type": "string", - "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", - "maxLength": 2048, + "$ref": "#/definitions/Url", "description": "The installer Url" }, "InstallerSha256": { diff --git a/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json index 3e1d62366e..8e04035067 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json @@ -35,6 +35,21 @@ "description": "The agreement URL." } } + }, + "Documentation": { + "type": "object", + "properties": { + "DocumentDescription": { + "type": [ "string", "null" ], + "minLength": 1, + "maxLength": 100, + "description": "The document description." + }, + "DocumentUrl": { + "$ref": "#/definitions/Url", + "description": "The documentation URL." + } + } } }, "type": "object", @@ -149,6 +164,19 @@ "$ref": "#/definitions/Url", "description": "The package release notes url" }, + "InstallationNotes": { + "type": [ "string", "null" ], + "minLength": 1, + "maxLength": 240, + "description": "The notes displayed to the user upon completion of a package installation." + }, + "Documentations": { + "type": [ "array", "null" ], + "items": { + "$ref": "#/definitions/Documentation" + }, + "maxItems": 128 + }, "ManifestType": { "type": "string", "default": "locale", From ec39660cfe09f772932e0853c1af7dd096e40279 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 4 Mar 2022 11:30:10 -0800 Subject: [PATCH 02/19] make adjustments to changes --- .../manifests/v1.2.0/manifest.defaultLocale.1.2.0.json | 4 ++++ .../manifests/v1.2.0/manifest.installer.1.2.0.json | 10 ++++------ .../JSON/manifests/v1.2.0/manifest.locale.1.2.0.json | 6 +++++- schemas/JSON/settings/settings.schema.0.2.json | 7 ++++++- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json index d07416dad4..ab75111d32 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json @@ -169,6 +169,10 @@ "$ref": "#/definitions/Url", "description": "The package release notes url" }, + "PurchaseUrl": { + "$ref": "#/definitions/Url", + "description": "The purchase url for acquiring entitlement for the package." + }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, diff --git a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json index 3b22bee50d..1c7d8ecd19 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json @@ -284,12 +284,6 @@ }, "MinimumVersion": { "$ref": "#/definitions/PackageVersion" - }, - "Architecture": { - "$ref": "#/definitions/Architecture" - }, - "Scope": { - "$ref": "#/definitions/Scope" } }, "required": [ "PackageIdentifier" ] @@ -398,6 +392,10 @@ "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, + "DisplayInstallWarnings": { + "type": [ "boolean", "null" ], + "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." + }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, diff --git a/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json index 8e04035067..10812f51fd 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json @@ -43,7 +43,7 @@ "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, - "description": "The document description." + "description": "The description of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", @@ -164,6 +164,10 @@ "$ref": "#/definitions/Url", "description": "The package release notes url" }, + "PurchaseUrl": { + "$ref": "#/definitions/Url", + "description": "The purchase url for acquiring entitlement for the package." + }, "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, diff --git a/schemas/JSON/settings/settings.schema.0.2.json b/schemas/JSON/settings/settings.schema.0.2.json index b839fae7db..9d620b283a 100644 --- a/schemas/JSON/settings/settings.schema.0.2.json +++ b/schemas/JSON/settings/settings.schema.0.2.json @@ -96,7 +96,12 @@ "type": "object", "properties": { "preferences": { "$ref": "#/definitions/InstallPrefReq" }, - "requirements": { "$ref": "#/definitions/InstallPrefReq" } + "requirements": { "$ref": "#/definitions/InstallPrefReq" }, + "ignoreWarnings": { + "description": "Controls whether blocking warning messages shown to the user during an install or upgrade are ignored", + "type": "boolean", + "default": false + } } }, "Telemetry": { From c6eee6777de6876c97529df13abd2b86d8352c27 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 4 Mar 2022 11:34:45 -0800 Subject: [PATCH 03/19] update DefaultLocale --- schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json index ab75111d32..6edb5847c4 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json @@ -43,7 +43,7 @@ "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, - "description": "The document description." + "description": "The description of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", From 71354e2f2d6c4e2229f4042fd176b4be3a507938 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 8 Mar 2022 15:35:29 -0800 Subject: [PATCH 04/19] add initial schema support for 1.2 --- .../v1.2.0/manifest.installer.1.2.0.json | 3 + .../v1.2.0/manifest.singleton.1.2.0.json | 85 +++++++++++++++---- .../TestData/ManifestV1_2-Singleton.yaml | 13 +++ .../ManifestV1_2-MultiFile-Installer.yaml | 6 ++ .../ManifestV1_2-MultiFile-Locale.yaml | 6 +- src/AppInstallerCLITests/YamlManifest.cpp | 37 ++++++++ .../Manifest/ManifestCommon.cpp | 20 +++++ .../Manifest/ManifestValidation.cpp | 8 ++ .../Manifest/ManifestYamlPopulator.cpp | 5 +- .../Public/winget/ManifestCommon.h | 9 ++ .../Public/winget/ManifestInstaller.h | 6 ++ .../Public/winget/ManifestLocalization.h | 15 ++++ .../ManifestSchema.vcxitems.filters | 8 +- 13 files changed, 200 insertions(+), 21 deletions(-) diff --git a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json index 1c7d8ecd19..254c95361f 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json @@ -564,6 +564,9 @@ "RequireExplicitUpgrade": { "$ref": "#/definitions/RequireExplicitUpgrade" }, + "DisplayInstallWarnings": { + "$ref": "#/definitions/DisplayInstallWarnings" + }, "UnsupportedOSArchitectures": { "$ref": "#/definitions/UnsupportedOSArchitectures" }, 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 f4fabe2374..08d4951f40 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 @@ -54,6 +54,21 @@ } } }, + "Documentation": { + "type": "object", + "properties": { + "DocumentDescription": { + "type": [ "string", "null" ], + "minLength": 1, + "maxLength": 100, + "description": "The description of the documentation for providing software guides such as manuals and troubleshooting URLs." + }, + "DocumentUrl": { + "$ref": "#/definitions/Url", + "description": "The documentation URL." + } + } + }, "Channel": { "type": [ "string", "null" ], "minLength": 1, @@ -96,6 +111,17 @@ ], "description": "Enumeration of supported installer types. InstallerType is required in either root level or individual Installer level" }, + "Architecture": { + "type": "string", + "enum": [ + "x86", + "x64", + "arm", + "arm64", + "neutral" + ], + "description": "The installer target architecture" + }, "Scope": { "type": [ "string", "null" ], "enum": [ @@ -211,8 +237,13 @@ "cancelledByUser", "alreadyInstalled", "downgrade", - "blockedByPolicy" + "blockedByPolicy", + "custom" ] + }, + "ReturnResponseUrl": { + "$ref": "#/definitions/Url", + "description": "The return response url to provide additional guidance for expected return codes" } } }, @@ -242,7 +273,7 @@ "type": [ "array", "null" ], "items": { "type": "string", - "pattern": "^[a-z][-a-z0-9\\.\\+]*$", + "pattern": "^[a-z0-9][-a-z0-9\\.\\+]*$", "maxLength": 2048 }, "maxItems": 16, @@ -256,7 +287,7 @@ "pattern": "^[^\\\\/:\\*\\?\"<>\\|\\x01-\\x1f]+$", "maxLength": 64 }, - "maxItems": 256, + "maxItems": 512, "uniqueItems": true, "description": "List of file extensions the package could support" }, @@ -402,6 +433,10 @@ "type": [ "boolean", "null" ], "description": "Indicates whether the installer should be pinned by default from upgrade" }, + "DisplayInstallWarnings": { + "type": [ "boolean", "null" ], + "description": "Indicates whether winget should display a warning message if the install or upgrade is known to interfere with running applications." + }, "UnsupportedOSArchitectures": { "type": [ "array", "null" ], "uniqueItems": true, @@ -417,6 +452,19 @@ }, "description": "List of OS architectures the installer does not support" }, + "UnsupportedArguments": { + "type": [ "array", "null" ], + "uniqueItems": true, + "items": { + "type": "string", + "title": "UnsupportedArgument", + "enum": [ + "log", + "location" + ] + }, + "description": "List of winget arguments the installer does not support" + }, "AppsAndFeaturesEntry": { "type": "object", "properties": { @@ -481,15 +529,7 @@ "$ref": "#/definitions/MinimumOSVersion" }, "Architecture": { - "type": "string", - "enum": [ - "x86", - "x64", - "arm", - "arm64", - "neutral" - ], - "description": "The installer target architecture" + "$ref": "#/definitions/Architecture" }, "InstallerType": { "$ref": "#/definitions/InstallerType" @@ -498,9 +538,7 @@ "$ref": "#/definitions/Scope" }, "InstallerUrl": { - "type": "string", - "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", - "maxLength": 2048, + "$ref": "#/definitions/Url", "description": "The installer Url" }, "InstallerSha256": { @@ -691,6 +729,23 @@ "$ref": "#/definitions/Url", "description": "The package release notes url" }, + "PurchaseUrl": { + "$ref": "#/definitions/Url", + "description": "The purchase url for acquiring entitlement for the package." + }, + "InstallationNotes": { + "type": [ "string", "null" ], + "minLength": 1, + "maxLength": 240, + "description": "The notes displayed to the user upon completion of a package installation." + }, + "Documentations": { + "type": [ "array", "null" ], + "items": { + "$ref": "#/definitions/Documentation" + }, + "maxItems": 128 + }, "Channel": { "$ref": "#/definitions/Channel" }, diff --git a/src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml b/src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml index 67da66159f..5e32320964 100644 --- a/src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml +++ b/src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml @@ -20,6 +20,11 @@ Tags: - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net +PurchaseUrl: https://DefaultPurchaseUrl.com +InstallationNotes: Default installation notes +Documentations: + - DocumentDescription: Default document description + DocumentUrl: https://DefaultDocumentUrl.com Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText @@ -78,6 +83,7 @@ ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true +DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm @@ -146,7 +152,11 @@ Installers: InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false + DisplayInstallWarnings: false ElevationRequirement: elevationRequired + UnsupportedArguments: + - log + - location UnsupportedOSArchitectures: - arm64 Markets: @@ -155,5 +165,8 @@ Installers: ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport + - InstallerReturnCode: 3 + ReturnResponse: custom + ReturnResponseUrl: https://defaultReturnResponseUrl.com ManifestType: singleton ManifestVersion: 1.2.0 diff --git a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Installer.yaml b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Installer.yaml index 73edcd96cd..f862035c8b 100644 --- a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Installer.yaml +++ b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Installer.yaml @@ -54,6 +54,7 @@ ReleaseDate: 2021-01-01 InstallerAbortsTerminal: true InstallLocationRequired: true RequireExplicitUpgrade: true +DisplayInstallWarnings: true ElevationRequirement: elevatesSelf UnsupportedOSArchitectures: - arm @@ -122,6 +123,7 @@ Installers: InstallerAbortsTerminal: false InstallLocationRequired: false RequireExplicitUpgrade: false + DisplayInstallWarnings: false ElevationRequirement: elevationRequired UnsupportedOSArchitectures: - arm64 @@ -142,5 +144,9 @@ Installers: InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 Commands: - standalone + ExpectedReturnCodes: + - InstallerReturnCode: 11 + ReturnResponse: custom + ReturnResponseUrl: https://DefaultReturnResponseUrl.com ManifestType: installer ManifestVersion: 1.2.0 \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Locale.yaml b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Locale.yaml index 7ec285f478..22dcade406 100644 --- a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Locale.yaml +++ b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Locale.yaml @@ -19,10 +19,14 @@ Tags: - "msixsdkUK" ReleaseNotes: Release notes ReleaseNotesUrl: https://ReleaseNotes.net +PurchaseUrl: https://DefaultPurchaseUrl.com +InstallationNotes: Default installation notes Agreements: - AgreementLabel: Label Agreement: Text AgreementUrl: https://AgreementUrl.net - +Documentations: + - DocumentDescription: Default document description + DocumentUrl: https://DefaultDocumentUrl.com ManifestType: locale ManifestVersion: 1.2.0 diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp index 34b3fbec94..3b3f3ec53b 100644 --- a/src/AppInstallerCLITests/YamlManifest.cpp +++ b/src/AppInstallerCLITests/YamlManifest.cpp @@ -395,6 +395,15 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(manifest.DefaultLocalization.Get().at(0).AgreementUrl == "https://DefaultAgreementUrl.net"); } + if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) + { + REQUIRE(manifest.DefaultLocalization.Get() == "https://DefaultPurchaseUrl.com"); + REQUIRE(manifest.DefaultLocalization.Get() == "Default installation notes"); + REQUIRE(manifest.DefaultLocalization.Get().size() == 1); + REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentDescription == "Default document description"); + REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentUrl == "https://DefaultDocumentUrl.com "); + } + REQUIRE(manifest.DefaultInstallerInfo.Locale == "en-US"); REQUIRE(manifest.DefaultInstallerInfo.Platform == std::vector{ PlatformEnum::Desktop, PlatformEnum::Universal }); REQUIRE(manifest.DefaultInstallerInfo.MinOSVersion == "10.0.0.0"); @@ -451,6 +460,14 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(manifest.DefaultInstallerInfo.ExpectedReturnCodes.at(10) == ExpectedReturnCodeEnum::PackageInUse); } + if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) + { + REQUIRE(manifest.DefaultInstallerInfo.DisplayInstallWarnings); + REQUIRE(manifest.DefaultInstallerInfo.UnsupportedArguments.size() == 2); + REQUIRE(manifest.DefaultInstallerInfo.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Log); + REQUIRE(manifest.DefaultInstallerInfo.UnsupportedArguments.at(1) == UnsupportedArgumentEnum::Location); + } + if (isSingleton) { REQUIRE(manifest.Installers.size() == 1); @@ -520,6 +537,13 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(installer1.ExpectedReturnCodes.at(2) == ExpectedReturnCodeEnum::ContactSupport); } + if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) + { + REQUIRE_FALSE(installer1.DisplayInstallWarnings); + REQUIRE(installer1.ExpectedReturnCodes.at(3) == ExpectedReturnCodeEnum::Custom); + REQUIRE(installer1.ReturnResponseUrls.at(3) == "https://defaultReturnResponseUrl.com"); + } + if (!isSingleton) { ManifestInstaller installer2 = manifest.Installers.at(1); @@ -559,6 +583,10 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(installer3.Url == "https://www.microsoft.com/msixsdk/msixsdkx86.exe"); REQUIRE(installer3.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(installer3.Commands == MultiValue{ "standalone" }); + REQUIRE(installer3.ExpectedReturnCodes.size() == 1); + REQUIRE(installer3.ExpectedReturnCodes.at(11) == ExpectedReturnCodeEnum::Custom); + REQUIRE(installer3.ReturnResponseUrls.at(11) == "https://defaultReturnResponseUrl.com"); + REQUIRE_FALSE(installer3.DisplayInstallWarnings); } // Localization @@ -589,6 +617,15 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(localization1.Get().at(0).AgreementText == "Text"); REQUIRE(localization1.Get().at(0).AgreementUrl == "https://AgreementUrl.net"); } + + if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) + { + REQUIRE(localization1.Get() == "https://DefaultPurchaseUrl.com"); + REQUIRE(localization1.Get() == "Default installation notes"); + REQUIRE(localization1.Get().size() == 1); + REQUIRE(localization1.Get().at(0).DocumentDescription == "Default document description"); + REQUIRE(localization1.Get().at(0).DocumentUrl == "https://DefaultDocumentUrl.com "); + } } } diff --git a/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp b/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp index 8ef0c2bcb0..f097147d90 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp @@ -254,6 +254,22 @@ namespace AppInstaller::Manifest return result; } + UnsupportedArgumentEnum ConvertToUnsupportedArgumentEnum(const std::string& in) + { + UnsupportedArgumentEnum result = UnsupportedArgumentEnum::Unknown; + + if (Utility::CaseInsensitiveEquals(in, "log")) + { + result = UnsupportedArgumentEnum::Log; + } + else if (Utility::CaseInsensitiveEquals(in, "location")) + { + result = UnsupportedArgumentEnum::Location; + } + + return result; + } + ManifestTypeEnum ConvertToManifestTypeEnum(const std::string& in) { if (in == "singleton") @@ -351,6 +367,10 @@ namespace AppInstaller::Manifest { result = ExpectedReturnCodeEnum::BlockedByPolicy; } + else if (inStrLower == "custom") + { + result = ExpectedReturnCodeEnum::Custom; + } return result; } diff --git a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp index 560aa1cb3e..253a1c3fb4 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp @@ -96,6 +96,14 @@ namespace AppInstaller::Manifest resultErrors.emplace_back(ManifestError::InvalidFieldValue, "UpdateBehavior"); } + for (const auto& unsupportedArgument : installer.UnsupportedArguments) + { + if (unsupportedArgument == UnsupportedArgumentEnum::Unknown) + { + resultErrors.emplace_back(ManifestError::InvalidFieldValue, "UnsupportedArguments"); + } + } + // Validate system reference strings if they are set at the installer level // Allow PackageFamilyName to be declared with non msix installers to support nested installer scenarios after manifest version 1.1 if (manifest.ManifestVersion <= ManifestVer{ s_ManifestVersionV1_1 } && !installer.PackageFamilyName.empty() && !DoesInstallerTypeUsePackageFamilyName(installer.InstallerType)) diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp index 0146b53fce..23671a39b7 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp @@ -652,6 +652,7 @@ namespace AppInstaller::Manifest ValidationErrors resultErrors; std::map returnCodes; + std::map returnResponseUrls; for (auto const& entry : returnCodesNode.Sequence()) { @@ -660,13 +661,15 @@ namespace AppInstaller::Manifest auto errors = ValidateAndProcessFields(entry, ExpectedReturnCodesFieldInfos); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); - if (!returnCodes.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponse }).second) + if (!returnCodes.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponse }).second && + !returnResponseUrls.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponseUrl }).second) { resultErrors.emplace_back(ManifestError::DuplicateReturnCodeEntry); } } m_p_installer->ExpectedReturnCodes = returnCodes; + m_p_installer->ReturnResponseUrls = returnResponseUrls; return resultErrors; } diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 25f8ec2a16..b8d7897dd4 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -130,12 +130,14 @@ bool HasExtension(std::string_view extension) const; AlreadyInstalled, Downgrade, BlockedByPolicy, + Custom }; struct ExpectedReturnCode { DWORD InstallerReturnCode; ExpectedReturnCodeEnum ReturnResponse; + string_t ReturnResponseUrl; }; enum class PlatformEnum @@ -153,6 +155,13 @@ bool HasExtension(std::string_view extension) const; ElevatesSelf, }; + enum class UnsupportedArgumentEnum + { + Unknown, + Log, + Location + }; + enum class ManifestTypeEnum { Singleton, diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h b/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h index 25f637db71..51114fe268 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h @@ -53,6 +53,8 @@ namespace AppInstaller::Manifest std::map ExpectedReturnCodes; + std::map ReturnResponseUrls; + UpdateBehaviorEnum UpdateBehavior = UpdateBehaviorEnum::Install; std::vector Commands; @@ -83,6 +85,10 @@ namespace AppInstaller::Manifest bool RequireExplicitUpgrade = false; + bool DisplayInstallWarnings = false; + + std::vector UnsupportedArguments; + std::vector UnsupportedOSArchitectures; std::vector AppsAndFeaturesEntries; diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestLocalization.h b/src/AppInstallerCommonCore/Public/winget/ManifestLocalization.h index 9cf7740236..6972e635f7 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestLocalization.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestLocalization.h @@ -26,8 +26,11 @@ namespace AppInstaller::Manifest Description, Tags, Agreements, + Documentations, ReleaseNotes, ReleaseNotesUrl, + PurchaseUrl, + InstallationNotes, Max }; @@ -38,6 +41,12 @@ namespace AppInstaller::Manifest string_t AgreementUrl; }; + struct Documentation + { + string_t DocumentDescription; + string_t DocumentUrl; + }; + namespace details { template @@ -58,6 +67,12 @@ namespace AppInstaller::Manifest using value_t = std::vector; }; + template <> + struct LocalizationMapping + { + using value_t = std::vector; + }; + // Used to deduce the LocalizationVariant type; making a variant that includes std::monostate and all LocalizationMapping types. template inline auto Deduce(std::index_sequence) { return std::variant(I)>::value_t...>{}; } diff --git a/src/ManifestSchema/ManifestSchema.vcxitems.filters b/src/ManifestSchema/ManifestSchema.vcxitems.filters index cc98f45d3f..4093375ba0 100644 --- a/src/ManifestSchema/ManifestSchema.vcxitems.filters +++ b/src/ManifestSchema/ManifestSchema.vcxitems.filters @@ -15,7 +15,7 @@ {fb3524f4-5541-468d-a33c-ddcdad90484d} - + @@ -58,6 +58,9 @@ schema\v1.1.0 + + schema\v1.2.0 + schema\v1.2.0 @@ -70,8 +73,5 @@ schema\v1.2.0 - - schema\v1.2.0 - \ No newline at end of file From 9ddec45445713798b98e07a0e738e4aa52c34d8e Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 14 Mar 2022 18:52:12 -0700 Subject: [PATCH 05/19] fix build errors --- src/AppInstallerCommonCore/Public/winget/ManifestCommon.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index b8d7897dd4..6be7be2ac7 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -135,9 +135,10 @@ bool HasExtension(std::string_view extension) const; struct ExpectedReturnCode { - DWORD InstallerReturnCode; - ExpectedReturnCodeEnum ReturnResponse; - string_t ReturnResponseUrl; + DWORD InstallerReturnCode{}; + ExpectedReturnCodeEnum ReturnResponse{}; + string_t ReturnResponseUrl{}; + ExpectedReturnCode() {}; }; enum class PlatformEnum From 5470c1c8cf0bfc7394aa201df2f9a03cf4006447 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 16 Mar 2022 18:23:29 -0700 Subject: [PATCH 06/19] add tests for 1.2 schema --- .../v1.2.0/manifest.installer.1.2.0.json | 3 + .../JSON/settings/settings.schema.0.2.json | 28 +++++- src/AppInstallerCLICore/Argument.cpp | 8 ++ src/AppInstallerCLICore/ExecutionArgs.h | 4 + src/AppInstallerCLICore/Resources.h | 4 + .../Shared/Strings/en-us/winget.resw | 12 +++ .../AppInstallerCLITests.vcxproj | 9 ++ .../AppInstallerCLITests.vcxproj.filters | 9 ++ ...Bad-DuplicateReturnCode-ExpectedCodes.yaml | 8 +- ...erTypePortable-InvalidAppsAndFeatures.yaml | 42 +++++++++ ...InstallerTypePortable-InvalidCommands.yaml | 26 ++++++ ...ad-InstallerTypePortable-InvalidScope.yaml | 22 +++++ .../TestData/ManifestV1_2-Singleton.yaml | 4 + .../ManifestV1_2-MultiFile-DefaultLocale.yaml | 6 +- .../ManifestV1_2-MultiFile-Installer.yaml | 10 ++- src/AppInstallerCLITests/YamlManifest.cpp | 7 +- .../ExperimentalFeature.cpp | 4 + .../Manifest/ManifestSchemaValidation.cpp | 1 + .../Manifest/ManifestValidation.cpp | 18 ++++ .../Manifest/ManifestYamlPopulator.cpp | 87 ++++++++++++++++++- .../Public/winget/ExperimentalFeature.h | 1 + .../Public/winget/ManifestCommon.h | 2 + .../Public/winget/ManifestValidation.h | 3 + .../Public/winget/ManifestYamlPopulator.h | 4 + .../Public/winget/UserSettings.h | 8 ++ src/AppInstallerCommonCore/UserSettings.cpp | 4 + 26 files changed, 323 insertions(+), 11 deletions(-) create mode 100644 src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidAppsAndFeatures.yaml create mode 100644 src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidCommands.yaml create mode 100644 src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidScope.yaml diff --git a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json index 254c95361f..fcb8dd58d3 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json @@ -673,6 +673,9 @@ "ElevationRequirement": { "$ref": "#/definitions/ElevationRequirement" }, + "DisplayInstallWarnings": { + "$ref": "#/definitions/DisplayInstallWarnings" + }, "Installers": { "type": "array", "items": { diff --git a/schemas/JSON/settings/settings.schema.0.2.json b/schemas/JSON/settings/settings.schema.0.2.json index 9d620b283a..a43101de28 100644 --- a/schemas/JSON/settings/settings.schema.0.2.json +++ b/schemas/JSON/settings/settings.schema.0.2.json @@ -100,7 +100,28 @@ "ignoreWarnings": { "description": "Controls whether blocking warning messages shown to the user during an install or upgrade are ignored", "type": "boolean", - "default": false + "default": false + }, + "PortableAppUserRoot": { + "description": "The default root directory where packages are installed to under User scope. Applies to the portable installer type.", + "type": "string", + "default": "%LOCALAPPDATA%/WinGet/Microsoft/Packages/" + }, + "PortableAppMachineRoot": { + "description": "The default root directory where packages are installed to under Machine scope. Applies to the portable installer type.", + "type": "string", + "default": "%PROGRAMFILES%/WinGet/Packages/" + } + } + }, + "UninstallBehavior": { + "description": "Uninstall settings", + "type": "object", + "properties": { + "purge": { + "description": "Controls whether the default behavior for uninstall should remove all files and directories relevant to this package. Only applies to the portable installerType.", + "type": "boolean", + "default": false } } }, @@ -161,6 +182,11 @@ "description": "Enable use of MSI APIs rather than msiexec for MSI installs", "type": "boolean", "default": false + }, + "portableInstall": { + "description": "Enable support for installing portable packages.", + "type": "boolean", + "default": false } } } diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index d7cf9dd0ec..28fa6150fc 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -86,6 +86,14 @@ namespace AppInstaller::CLI return Argument{ "accept-source-agreements", NoAlias, Args::Type::AcceptSourceAgreements, Resource::String::AcceptSourceAgreementsArgumentDescription, ArgumentType::Flag }; case Args::Type::ExperimentalArg: return Argument{ "arg", NoAlias, Args::Type::ExperimentalArg, Resource::String::ExperimentalArgumentDescription, ArgumentType::Flag, ExperimentalFeature::Feature::ExperimentalArg }; + case Args::Type::Rename: + return Argument{ "rename", NoAlias, Args::Type::Rename, Resource::String::RenameArgumentDescription, ArgumentType::Positional, false }; + case Args::Type::Purge: + return Argument{ "purge", NoAlias, Args::Type::Purge, Resource::String::PurgeArgumentDescription, ArgumentType::Flag, false }; + case Args::Type::Preserve: + return Argument{ "preserve", NoAlias, Args::Type::Preserve, Resource::String::PreserveArgumentDescription, ArgumentType::Flag, false }; + case Args::Type::Wait: + return Argument{ "wait", NoAlias, Args::Type::Wait, Resource::String::WaitArgumentDescription, ArgumentType::Flag, false }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h index f5d231c6af..f656dab696 100644 --- a/src/AppInstallerCLICore/ExecutionArgs.h +++ b/src/AppInstallerCLICore/ExecutionArgs.h @@ -86,6 +86,10 @@ namespace AppInstaller::CLI::Execution CustomHeader, // Optional Rest source header AcceptSourceAgreements, // Accept all source agreements IncludeUnknown, // Used in Upgrade command to allow upgrades of packages with unknown versions + Rename, // Renames the file of the executable. Only applies to the portable installerType + Purge, // + Preserve, // + Wait, // Used for demonstration purposes ExperimentalArg, diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 5f355bdf8e..81f41ea833 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -206,11 +206,14 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(PoliciesPolicy); WINGET_DEFINE_RESOURCE_STRINGID(PoliciesState); WINGET_DEFINE_RESOURCE_STRINGID(PositionArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(PreserveArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(PrivacyStatement); WINGET_DEFINE_RESOURCE_STRINGID(PromptOptionNo); WINGET_DEFINE_RESOURCE_STRINGID(PromptOptionYes); + WINGET_DEFINE_RESOURCE_STRINGID(PurgeArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(QueryArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(RainbowArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(RenameArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ReportIdentityFound); WINGET_DEFINE_RESOURCE_STRINGID(RequiredArgError); WINGET_DEFINE_RESOURCE_STRINGID(RetroArgumentDescription); @@ -358,6 +361,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(VerifyPathFailedNotExist); WINGET_DEFINE_RESOURCE_STRINGID(VersionArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(VersionsArgumentDescription); + WINGET_DEFINE_RESOURCE_STRINGID(WaitArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(WindowsFeaturesDependencies); WINGET_DEFINE_RESOURCE_STRINGID(WindowsLibrariesDependencies); WINGET_DEFINE_RESOURCE_STRINGID(WindowsStoreTerms); diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 61492b9498..666d9f27f4 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1275,4 +1275,16 @@ Please specify one of them using the `--source` option to proceed. System Architecture + + Retains all files and directories created by the package (portable) + + + Deletes all files and directories created by the package (portable) + + + The value to rename the executable file (portable) + + + Prompts the user to press any key before exiting + \ No newline at end of file diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index 7b0dd7f277..18adf844b0 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -377,6 +377,15 @@ true + + true + + + true + + + true + true diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index 19328780f6..082ea229e5 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -258,6 +258,15 @@ TestData + + TestData + + + TestData + + + TestData + TestData diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml index 20ad197fa4..18dca33e1c 100644 --- a/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml +++ b/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml @@ -13,9 +13,9 @@ Installers: InstallerType: exe InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B ExpectedReturnCodes: - - InstallerReturnCode: 1 - ReturnResponse: PackageInUse - - InstallerReturnCode: 1 - ReturnResponse: InstallInProgress + - InstallerReturnCode: 1 + ReturnResponse: packageInUse + - InstallerReturnCode: 1 + ReturnResponse: installInProgress ManifestType: singleton ManifestVersion: 1.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidAppsAndFeatures.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidAppsAndFeatures.yaml new file mode 100644 index 0000000000..e4b828d6c5 --- /dev/null +++ b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidAppsAndFeatures.yaml @@ -0,0 +1,42 @@ +# Bad manifest. Installer Type portable can only have zero or one AppsAndFeatureEntry defined. +PackageIdentifier: TestInstaller.WithLicenseAgreement +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test Installer +Publisher: Microsoft Corporation +Moniker: AICLITestExe +License: Test +ShortDescription: TestInstallerWithLicenseAgreement +AppsAndFeaturesEntries: + - DisplayName: DisplayName1 + DisplayVersion: DisplayVersion1 + Publisher: Publisher1 + ProductCode: ProductCode1 + UpgradeCode: UpgradeCode1 + - DisplayName: DisplayName2 + DisplayVersion: DisplayVersion2 + Publisher: Publisher2 + ProductCode: ProductCode2 + UpgradeCode: UpgradeCode2 +Installers: + - Architecture: x64 + InstallerUrl: https://ThisIsNotUsed + InstallerType: portable + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + - Architecture: x86 + InstallerUrl: https://ThisIsNotUsed2 + InstallerType: portable + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + AppsAndFeaturesEntries: + - DisplayName: DisplayName1 + DisplayVersion: DisplayVersion1 + Publisher: Publisher1 + ProductCode: ProductCode1 + UpgradeCode: UpgradeCode1 + - DisplayName: DisplayName2 + DisplayVersion: DisplayVersion2 + Publisher: Publisher2 + ProductCode: ProductCode2 + UpgradeCode: UpgradeCode2 +ManifestType: singleton +ManifestVersion: 1.2.0 \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidCommands.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidCommands.yaml new file mode 100644 index 0000000000..c4d4baf19d --- /dev/null +++ b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidCommands.yaml @@ -0,0 +1,26 @@ +# Bad manifest. Installer Type portable can only have one or zero commands defined. +PackageIdentifier: TestInstaller.WithLicenseAgreement +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test Installer +Publisher: Microsoft Corporation +Moniker: AICLITestExe +License: Test +ShortDescription: TestInstallerWithLicenseAgreement +Commands: + - Command1 + - Command2 +Installers: + - Architecture: x64 + InstallerUrl: https://ThisIsNotUsed + InstallerType: portable + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + - Architecture: x86 + InstallerUrl: https://ThisIsNotUsed2 + InstallerType: portable + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + Commands: + - Command1 + - Command2 +ManifestType: singleton +ManifestVersion: 1.2.0 \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidScope.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidScope.yaml new file mode 100644 index 0000000000..7bc8bbda87 --- /dev/null +++ b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidScope.yaml @@ -0,0 +1,22 @@ +# Bad manifest. Installer Type portable cannot have a defined scope. +PackageIdentifier: TestInstaller.WithLicenseAgreement +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test Installer +Publisher: Microsoft Corporation +Moniker: AICLITestExe +License: Test +ShortDescription: TestInstallerWithLicenseAgreement +Scope: User +Installers: + - Architecture: x64 + InstallerUrl: https://ThisIsNotUsed + InstallerType: portable + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + Scope: Machine + - Architecture: x86 + InstallerUrl: https://ThisIsNotUsed2 + InstallerType: portable + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B +ManifestType: singleton +ManifestVersion: 1.2.0 \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml b/src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml index 5e32320964..984c458cd7 100644 --- a/src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml +++ b/src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml @@ -100,6 +100,10 @@ Markets: ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse + ReturnResponseUrl: https://DefaultReturnResponseUrl.com +UnsupportedArguments: + - log + - location Installers: - Architecture: x86 diff --git a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-DefaultLocale.yaml b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-DefaultLocale.yaml index 42f0126921..3c9e9945fa 100644 --- a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-DefaultLocale.yaml +++ b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-DefaultLocale.yaml @@ -12,6 +12,8 @@ License: MIT License LicenseUrl: https://www.microsoft.com/msixsdk/license Copyright: Copyright Microsoft Corporation CopyrightUrl: https://www.microsoft.com/msixsdk/copyright +PurchaseUrl: https://DefaultPurchaseUrl.com +InstallationNotes: "Default installation notes" ShortDescription: This is MSIX SDK Description: The MSIX SDK project is an effort to enable developers Moniker: msixsdk @@ -20,10 +22,12 @@ Tags: - "msixsdk" ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net +Documentations: + - DocumentDescription: Default document description + DocumentUrl: https://DefaultDocumentUrl.com Agreements: - AgreementLabel: DefaultLabel Agreement: DefaultText AgreementUrl: https://DefaultAgreementUrl.net - ManifestType: defaultLocale ManifestVersion: 1.2.0 diff --git a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Installer.yaml b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Installer.yaml index f862035c8b..83b64f0fd3 100644 --- a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Installer.yaml +++ b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Installer.yaml @@ -71,6 +71,10 @@ Markets: ExpectedReturnCodes: - InstallerReturnCode: 10 ReturnResponse: packageInUse + ReturnResponseUrl: https://DefaultReturnResponseUrl.com +UnsupportedArguments: + - log + - location Installers: - Architecture: x86 @@ -133,6 +137,9 @@ Installers: ExpectedReturnCodes: - InstallerReturnCode: 2 ReturnResponse: contactSupport + - InstallerReturnCode: 3 + ReturnResponse: custom + ReturnResponseUrl: https://defaultReturnResponseUrl.com - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe @@ -142,11 +149,12 @@ Installers: InstallerType: portable InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.exe InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 + DisplayInstallWarnings: false Commands: - standalone ExpectedReturnCodes: - InstallerReturnCode: 11 ReturnResponse: custom - ReturnResponseUrl: https://DefaultReturnResponseUrl.com + ReturnResponseUrl: https://defaultReturnResponseUrl.com ManifestType: installer ManifestVersion: 1.2.0 \ No newline at end of file diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp index 3b3f3ec53b..128c2710ff 100644 --- a/src/AppInstallerCLITests/YamlManifest.cpp +++ b/src/AppInstallerCLITests/YamlManifest.cpp @@ -254,6 +254,9 @@ TEST_CASE("ReadBadManifests", "[ManifestValidation]") { "Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml", "Silent and SilentWithProgress switches are not specified for InstallerType exe.", true }, { "Manifest-Bad-InstallerTypeInvalid.yaml", "Invalid field value. Field: InstallerType" }, { "Manifest-Bad-InstallerTypeMissing.yaml", "Invalid field value. Field: InstallerType" }, + { "Manifest-Bad-InstallerTypePortable-InvalidAppsAndFeatures.yaml", "Only zero or one entry for Apps and Features can be specified for InstallerType portable." }, + { "Manifest-Bad-InstallerTypePortable-InvalidCommands.yaml", "Only zero or one value for Commands can be specified for InstallerType portable." }, + { "Manifest-Bad-InstallerTypePortable-InvalidScope.yaml", "Scope is not supported for InstallerType portable." }, { "Manifest-Bad-InstallerUniqueness.yaml", "Duplicate installer entry found." }, { "Manifest-Bad-InstallerUniqueness-DefaultScope.yaml", "Duplicate installer entry found." }, { "Manifest-Bad-InstallerUniqueness-DefaultValues.yaml", "Duplicate installer entry found." }, @@ -401,7 +404,7 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(manifest.DefaultLocalization.Get() == "Default installation notes"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentDescription == "Default document description"); - REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentUrl == "https://DefaultDocumentUrl.com "); + REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentUrl == "https://DefaultDocumentUrl.com"); } REQUIRE(manifest.DefaultInstallerInfo.Locale == "en-US"); @@ -624,7 +627,7 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(localization1.Get() == "Default installation notes"); REQUIRE(localization1.Get().size() == 1); REQUIRE(localization1.Get().at(0).DocumentDescription == "Default document description"); - REQUIRE(localization1.Get().at(0).DocumentUrl == "https://DefaultDocumentUrl.com "); + REQUIRE(localization1.Get().at(0).DocumentUrl == "https://DefaultDocumentUrl.com"); } } } diff --git a/src/AppInstallerCommonCore/ExperimentalFeature.cpp b/src/AppInstallerCommonCore/ExperimentalFeature.cpp index fa428ed99f..f845cc3366 100644 --- a/src/AppInstallerCommonCore/ExperimentalFeature.cpp +++ b/src/AppInstallerCommonCore/ExperimentalFeature.cpp @@ -42,6 +42,8 @@ namespace AppInstaller::Settings return userSettings.Get(); case ExperimentalFeature::Feature::DirectMSI: return userSettings.Get(); + case ExperimentalFeature::Feature::PortableInstall: + return userSettings.Get(); default: THROW_HR(E_UNEXPECTED); } @@ -73,6 +75,8 @@ namespace AppInstaller::Settings return ExperimentalFeature{ "Show Dependencies Information", "dependencies", "https://aka.ms/winget-settings", Feature::Dependencies }; case Feature::DirectMSI: return ExperimentalFeature{ "Direct MSI Installation", "directMSI", "https://aka.ms/winget-settings", Feature::DirectMSI }; + case Feature::PortableInstall: + return ExperimentalFeature{ "Portable Installation", "portableInstall", "https://aka.ms/winget-settings", Feature::PortableInstall }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCommonCore/Manifest/ManifestSchemaValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestSchemaValidation.cpp index b501a85bec..24f18346ac 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestSchemaValidation.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestSchemaValidation.cpp @@ -29,6 +29,7 @@ namespace AppInstaller::Manifest::YamlParser { "InstallerAbortsTerminal"sv, YamlScalarType::Bool }, { "InstallLocationRequired"sv, YamlScalarType::Bool }, { "RequireExplicitUpgrade"sv, YamlScalarType::Bool }, + { "DisplayInstallWarnings"sv, YamlScalarType::Bool }, { "InstallerReturnCode"sv, YamlScalarType::Int }, }; diff --git a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp index 253a1c3fb4..7208bf74cb 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp @@ -161,6 +161,24 @@ namespace AppInstaller::Manifest resultErrors.emplace_back(ManifestError::ExeInstallerMissingSilentSwitches, ValidationError::Level::Warning); } + if (installer.InstallerType == InstallerTypeEnum::Portable) + { + if ((manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.size() > 1 && installer.AppsAndFeaturesEntries.size() == 0) || + installer.AppsAndFeaturesEntries.size() > 1) + { + resultErrors.emplace_back(ManifestError::ExceededAppsAndFeaturesEntryLimit); + } + if ((manifest.DefaultInstallerInfo.Commands.size() > 1 && installer.Commands.size() == 0) || + installer.Commands.size() > 1) + { + resultErrors.emplace_back(ManifestError::ExceededCommandsLimit); + } + if (manifest.DefaultInstallerInfo.Scope != ScopeEnum::Unknown || installer.Scope != ScopeEnum::Unknown) + { + resultErrors.emplace_back(ManifestError::ScopeNotSupported, ValidationError::Level::Warning); + } + } + // Check empty string before calling IsValidUrl to avoid duplicate error reporting. if (!installer.Url.empty() && IsValidURL(NULL, Utility::ConvertToUTF16(installer.Url).c_str(), 0) == S_FALSE) { diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp index 23671a39b7..d24aa33187 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp @@ -115,6 +115,20 @@ namespace AppInstaller::Manifest return result; } + + std::vector ProcessUnsupportedArgumentsSequenceNode(const YAML::Node& node) + { + THROW_HR_IF(E_INVALIDARG, !node.IsSequence()); + + std::vector result; + + for (auto const& entry : node.Sequence()) + { + result.emplace_back(ConvertToUnsupportedArgumentEnum(entry.as())); + } + + return result; + } } std::vector ManifestYamlPopulator::GetRootFieldProcessInfo(const ManifestVer& manifestVersion) @@ -281,6 +295,17 @@ namespace AppInstaller::Manifest std::move(fields_v1_1.begin(), fields_v1_1.end(), std::inserter(result, result.end())); } + + if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_2 }) + { + std::vector fields_v1_2 = + { + { "UnsupportedArguments", [this](const YAML::Node& value)->ValidationErrors { m_p_installer->UnsupportedArguments = ProcessUnsupportedArgumentsSequenceNode(value); return {}; } }, + { "DisplayInstallWarnings", [this](const YAML::Node& value)->ValidationErrors { m_p_installer->DisplayInstallWarnings = value.as(); return {}; } }, + }; + + std::move(fields_v1_2.begin(), fields_v1_2.end(), std::inserter(result, result.end())); + } } return result; @@ -324,6 +349,11 @@ namespace AppInstaller::Manifest result.emplace_back("ReturnResponse", [this](const YAML::Node& value)->ValidationErrors { m_p_expectedReturnCode->ReturnResponse = ConvertToExpectedReturnCodeEnum(value.as()); return {}; }); } + if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_2 }) + { + result.emplace_back("ReturnResponseUrl", [this](const YAML::Node& value)->ValidationErrors { m_p_expectedReturnCode->ReturnResponseUrl = value.as(); return {}; }); + } + return result; } @@ -399,6 +429,18 @@ namespace AppInstaller::Manifest std::move(fields_v1_1.begin(), fields_v1_1.end(), std::inserter(result, result.end())); } + + if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_2 }) + { + std::vector fields_v1_2 = + { + { "PurchaseUrl", [this](const YAML::Node& value)->ValidationErrors { m_p_localization->Add(value.as()); return {}; } }, + { "InstallationNotes", [this](const YAML::Node& value)->ValidationErrors { m_p_localization->Add(value.as()); return {}; } }, + { "Documentations", [this](const YAML::Node& value)->ValidationErrors { return ProcessDocumentationsNode(value); }, true }, + }; + + std::move(fields_v1_2.begin(), fields_v1_2.end(), std::inserter(result, result.end())); + } } return result; @@ -500,6 +542,22 @@ namespace AppInstaller::Manifest return result; } + std::vector ManifestYamlPopulator::GetDocumentationFieldProcessInfo(const ManifestVer& manifestVersion) + { + std::vector result = {}; + + if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_2 }) + { + result = + { + { "DocumentDescription", [this](const YAML::Node& value)->ValidationErrors { m_p_documentation->DocumentDescription = Utility::Trim(value.as()); return {}; } }, + { "DocumentUrl", [this](const YAML::Node& value)->ValidationErrors { m_p_documentation->DocumentUrl = Utility::Trim(value.as()); return {}; } }, + }; + } + + return result; + } + ValidationErrors ManifestYamlPopulator::ValidateAndProcessFields( const YAML::Node& rootNode, const std::vector& fieldInfos) @@ -660,9 +718,9 @@ namespace AppInstaller::Manifest m_p_expectedReturnCode = &returnCode; auto errors = ValidateAndProcessFields(entry, ExpectedReturnCodesFieldInfos); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); + returnResponseUrls.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponseUrl }).second; - if (!returnCodes.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponse }).second && - !returnResponseUrls.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponseUrl }).second) + if (!returnCodes.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponse }).second) { resultErrors.emplace_back(ManifestError::DuplicateReturnCodeEntry); } @@ -674,6 +732,30 @@ namespace AppInstaller::Manifest return resultErrors; } + ValidationErrors ManifestYamlPopulator::ProcessDocumentationsNode(const YAML::Node& documentationsNode) + { + THROW_HR_IF(E_INVALIDARG, !documentationsNode.IsSequence()); + + ValidationErrors resultErrors; + std::vector documentations; + + for (auto const& entry : documentationsNode.Sequence()) + { + Documentation documentation; + m_p_documentation = &documentation; + auto errors = ValidateAndProcessFields(entry, DocumentationFieldInfos); + std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); + documentations.emplace_back(std::move(documentation)); + } + + if (!documentations.empty()) + { + m_p_localization->Add(std::move(documentations)); + } + + return resultErrors; + } + ValidationErrors ManifestYamlPopulator::PopulateManifestInternal( const YAML::Node& rootNode, Manifest& manifest, @@ -697,6 +779,7 @@ namespace AppInstaller::Manifest AgreementFieldInfos = GetAgreementFieldProcessInfo(manifestVersion); MarketsFieldInfos = GetMarketsFieldProcessInfo(manifestVersion); AppsAndFeaturesEntryFieldInfos = GetAppsAndFeaturesEntryFieldProcessInfo(manifestVersion); + DocumentationFieldInfos = GetDocumentationFieldProcessInfo(manifestVersion); // Populate root m_p_manifest = &manifest; diff --git a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h index 3276375a43..8ba08e06f0 100644 --- a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h +++ b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h @@ -23,6 +23,7 @@ namespace AppInstaller::Settings Dependencies = 0x1, // Before making DirectMSI non-experimental, it should be part of manifest validation. DirectMSI = 0x2, + PortableInstall = 0x3, Max, // This MUST always be after all experimental features // Features listed after Max will not be shown with the features command diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 6be7be2ac7..49f895d59c 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -256,6 +256,8 @@ bool HasExtension(std::string_view extension) const; ElevationRequirementEnum ConvertToElevationRequirementEnum(const std::string& in); + UnsupportedArgumentEnum ConvertToUnsupportedArgumentEnum(const std::string& in); + ManifestTypeEnum ConvertToManifestTypeEnum(const std::string& in); ExpectedReturnCodeEnum ConvertToExpectedReturnCodeEnum(const std::string& in); diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestValidation.h b/src/AppInstallerCommonCore/Public/winget/ManifestValidation.h index be9267abee..4ca543eee0 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestValidation.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestValidation.h @@ -48,6 +48,9 @@ namespace AppInstaller::Manifest const char* const MissingManifestDependenciesNode = "Dependency not found: "; const char* const NoSuitableMinVersion = "No Suitable Minimum Version: "; const char* const FoundLoop = "Loop found."; + const char* const ExceededAppsAndFeaturesEntryLimit = "Only zero or one entry for Apps and Features can be specified for InstallerType portable."; + const char* const ExceededCommandsLimit = "Only zero or one value for Commands can be specified for InstallerType portable."; + const char* const ScopeNotSupported = "Scope is not supported for InstallerType portable."; } struct ValidationError diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h b/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h index bde55834fa..16323d7464 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h @@ -41,6 +41,7 @@ namespace AppInstaller::Manifest std::vector AgreementFieldInfos; std::vector MarketsFieldInfos; std::vector AppsAndFeaturesEntryFieldInfos; + std::vector DocumentationFieldInfos; // These pointers are referenced in the processing functions in manifest field process info table. AppInstaller::Manifest::Manifest* m_p_manifest = nullptr; @@ -53,6 +54,7 @@ namespace AppInstaller::Manifest AppInstaller::Manifest::Agreement* m_p_agreement = nullptr; AppInstaller::Manifest::MarketsInfo* m_p_markets = nullptr; AppInstaller::Manifest::AppsAndFeaturesEntry* m_p_appsAndFeaturesEntry = nullptr; + AppInstaller::Manifest::Documentation* m_p_documentation = nullptr; // Cache of Installers node and Localization node YAML::Node const* m_p_installersNode = nullptr; @@ -68,6 +70,7 @@ namespace AppInstaller::Manifest std::vector GetAgreementFieldProcessInfo(const ManifestVer& manifestVersion); std::vector GetMarketsFieldProcessInfo(const ManifestVer& manifestVersion); std::vector GetAppsAndFeaturesEntryFieldProcessInfo(const ManifestVer& manifestVersion); + std::vector GetDocumentationFieldProcessInfo(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, @@ -83,6 +86,7 @@ namespace AppInstaller::Manifest std::vector ProcessMarketsNode(const YAML::Node& marketsNode); std::vector ProcessAppsAndFeaturesEntriesNode(const YAML::Node& appsAndFeaturesEntriesNode); std::vector ProcessExpectedReturnCodesNode(const YAML::Node& returnCodesNode); + std::vector ProcessDocumentationsNode(const YAML::Node& documentations); std::vector PopulateManifestInternal( const YAML::Node& rootNode, diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index 709a471b70..168e8a46a6 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -71,6 +71,7 @@ namespace AppInstaller::Settings EFExperimentalCmd, EFExperimentalArg, EFDependencies, + EFPortableInstall, TelemetryDisable, InstallScopePreference, InstallScopeRequirement, @@ -83,6 +84,9 @@ namespace AppInstaller::Settings EFDirectMSI, EnableSelfInitiatedMinidump, LoggingLevelPreference, + PortableAppUserRoot, + PortableAppMachineRoot, + UninstallPurge, Max }; @@ -130,7 +134,11 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::NetworkDOProgressTimeoutInSeconds, uint32_t, std::chrono::seconds, 60s, ".network.doProgressTimeoutInSeconds"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocalePreference, std::vector, std::vector, {}, ".installBehavior.preferences.locale"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocaleRequirement, std::vector, std::vector, {}, ".installBehavior.requirements.locale"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppUserRoot, std::string, std::string, {}, ".installBehavior.portableAppUserRoot"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppMachineRoot, std::string, std::string, {}, ".installBehavior.portableAppMachineRoot"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::UninstallPurge, bool, bool, false, ".uninstallBehavior.purge"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFDirectMSI, bool, bool, false, ".experimentalFeatures.directMSI"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::EFPortableInstall, bool, bool, false, ".experimentalFeatures.portableInstall"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EnableSelfInitiatedMinidump, bool, bool, false, ".debugging.enableSelfInitiatedMinidump"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingLevelPreference, std::string, Logging::Level, Logging::Level::Info, ".logging.level"sv); diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 110f2efe98..1f1f46cb00 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -228,9 +228,13 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(EFExperimentalCmd) WINGET_VALIDATE_PASS_THROUGH(EFExperimentalArg) WINGET_VALIDATE_PASS_THROUGH(EFDependencies) + WINGET_VALIDATE_PASS_THROUGH(EFPortableInstall) WINGET_VALIDATE_PASS_THROUGH(TelemetryDisable) WINGET_VALIDATE_PASS_THROUGH(EFDirectMSI) WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump) + WINGET_VALIDATE_PASS_THROUGH(PortableAppUserRoot) + WINGET_VALIDATE_PASS_THROUGH(PortableAppMachineRoot) + WINGET_VALIDATE_PASS_THROUGH(UninstallPurge) WINGET_VALIDATE_SIGNATURE(InstallArchitecturePreference) { From e5e0664d531e031ce141db492b33343d6df1515b Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Thu, 17 Mar 2022 10:51:46 -0700 Subject: [PATCH 07/19] finalize schema changes --- .../JSON/settings/settings.schema.0.2.json | 2 +- src/AppInstallerCLICore/ExecutionArgs.h | 6 +++--- ...ad-InstallerTypePortable-InvalidScope.yaml | 2 +- .../Manifest/ManifestYamlPopulator.cpp | 3 ++- .../Public/winget/UserSettings.h | 4 +++- src/AppInstallerCommonCore/UserSettings.cpp | 21 +++++++++++++++++-- 6 files changed, 29 insertions(+), 9 deletions(-) diff --git a/schemas/JSON/settings/settings.schema.0.2.json b/schemas/JSON/settings/settings.schema.0.2.json index a43101de28..50bef5ebfb 100644 --- a/schemas/JSON/settings/settings.schema.0.2.json +++ b/schemas/JSON/settings/settings.schema.0.2.json @@ -105,7 +105,7 @@ "PortableAppUserRoot": { "description": "The default root directory where packages are installed to under User scope. Applies to the portable installer type.", "type": "string", - "default": "%LOCALAPPDATA%/WinGet/Microsoft/Packages/" + "default": "%LOCALAPPDATA%/Microsoft/WinGet/Packages/" }, "PortableAppMachineRoot": { "description": "The default root directory where packages are installed to under Machine scope. Applies to the portable installer type.", diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h index f656dab696..a6be53b866 100644 --- a/src/AppInstallerCLICore/ExecutionArgs.h +++ b/src/AppInstallerCLICore/ExecutionArgs.h @@ -87,9 +87,9 @@ namespace AppInstaller::CLI::Execution AcceptSourceAgreements, // Accept all source agreements IncludeUnknown, // Used in Upgrade command to allow upgrades of packages with unknown versions Rename, // Renames the file of the executable. Only applies to the portable installerType - Purge, // - Preserve, // - Wait, + Purge, // Removes all files and directories related to a package during an uninstall. Only applies to the portable installerType. + Preserve, // Retains any files and directories created by the portable exe. + Wait, // Prompts the user to press any key before exiting. // Used for demonstration purposes ExperimentalArg, diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidScope.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidScope.yaml index 7bc8bbda87..d2bdf93279 100644 --- a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidScope.yaml +++ b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypePortable-InvalidScope.yaml @@ -1,4 +1,4 @@ -# Bad manifest. Installer Type portable cannot have a defined scope. +# Bad manifest. Installer Type portable does not support scope and should show a warning. PackageIdentifier: TestInstaller.WithLicenseAgreement PackageVersion: 1.0.0.0 PackageLocale: en-US diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp index d24aa33187..7c93ca424b 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp @@ -718,12 +718,13 @@ namespace AppInstaller::Manifest m_p_expectedReturnCode = &returnCode; auto errors = ValidateAndProcessFields(entry, ExpectedReturnCodesFieldInfos); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); - returnResponseUrls.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponseUrl }).second; if (!returnCodes.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponse }).second) { resultErrors.emplace_back(ManifestError::DuplicateReturnCodeEntry); } + + returnResponseUrls.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponseUrl }).second; } m_p_installer->ExpectedReturnCodes = returnCodes; diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index 168e8a46a6..a5f5e29559 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -84,6 +84,7 @@ namespace AppInstaller::Settings EFDirectMSI, EnableSelfInitiatedMinidump, LoggingLevelPreference, + InstallIgnoreWarnings, PortableAppUserRoot, PortableAppMachineRoot, UninstallPurge, @@ -125,6 +126,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalCmd, bool, bool, false, ".experimentalFeatures.experimentalCmd"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalArg, bool, bool, false, ".experimentalFeatures.experimentalArg"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFDependencies, bool, bool, false, ".experimentalFeatures.dependencies"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::EFPortableInstall, bool, bool, false, ".experimentalFeatures.portableInstall"sv); SETTINGMAPPING_SPECIALIZATION(Setting::TelemetryDisable, bool, bool, false, ".telemetry.disable"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallArchitecturePreference, std::vector, std::vector, {}, ".installBehavior.preferences.architectures"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallArchitectureRequirement, std::vector, std::vector, {}, ".installBehavior.requirements.architectures"sv); @@ -134,11 +136,11 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::NetworkDOProgressTimeoutInSeconds, uint32_t, std::chrono::seconds, 60s, ".network.doProgressTimeoutInSeconds"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocalePreference, std::vector, std::vector, {}, ".installBehavior.preferences.locale"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocaleRequirement, std::vector, std::vector, {}, ".installBehavior.requirements.locale"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::InstallIgnoreWarnings, bool, bool, false, ".installBehavior.ignoreWarnings"sv); SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppUserRoot, std::string, std::string, {}, ".installBehavior.portableAppUserRoot"sv); SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppMachineRoot, std::string, std::string, {}, ".installBehavior.portableAppMachineRoot"sv); SETTINGMAPPING_SPECIALIZATION(Setting::UninstallPurge, bool, bool, false, ".uninstallBehavior.purge"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFDirectMSI, bool, bool, false, ".experimentalFeatures.directMSI"sv); - SETTINGMAPPING_SPECIALIZATION(Setting::EFPortableInstall, bool, bool, false, ".experimentalFeatures.portableInstall"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EnableSelfInitiatedMinidump, bool, bool, false, ".debugging.enableSelfInitiatedMinidump"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingLevelPreference, std::string, Logging::Level, Logging::Level::Info, ".logging.level"sv); diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 1f1f46cb00..fd288c3423 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -232,8 +232,7 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(TelemetryDisable) WINGET_VALIDATE_PASS_THROUGH(EFDirectMSI) WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump) - WINGET_VALIDATE_PASS_THROUGH(PortableAppUserRoot) - WINGET_VALIDATE_PASS_THROUGH(PortableAppMachineRoot) + WINGET_VALIDATE_PASS_THROUGH(InstallIgnoreWarnings) WINGET_VALIDATE_PASS_THROUGH(UninstallPurge) WINGET_VALIDATE_SIGNATURE(InstallArchitecturePreference) @@ -295,6 +294,24 @@ namespace AppInstaller::Settings return SettingMapping::Validate(value); } + WINGET_VALIDATE_SIGNATURE(PortableAppUserRoot) + { + if (!std::filesystem::exists(value)) + { + if (!std::filesystem::create_directories(value)) + { + return {}; + } + } + + return value; + } + + WINGET_VALIDATE_SIGNATURE(PortableAppMachineRoot) + { + return SettingMapping::Validate(value); + } + WINGET_VALIDATE_SIGNATURE(NetworkDownloader) { static constexpr std::string_view s_downloader_default = "default"; From d2d50bd8f6aa8b456567361b2acd92294ba1edd1 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Thu, 17 Mar 2022 10:55:10 -0700 Subject: [PATCH 08/19] update spelling --- .github/actions/spelling/expect.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index e34f3bc6de..80ef7edf3c 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -287,6 +287,7 @@ powertoys pri processthreads productcode +PROGRAMFILES pscustomobject pseudocode psm From 9dd5764e3873cd8fe748c468145c28821e0108af Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 18 Mar 2022 13:50:38 -0700 Subject: [PATCH 09/19] increase documentation limit to 256 and fix grammar --- .../JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json | 2 +- schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json | 2 +- schemas/JSON/manifests/v1.2.0/manifest.singleton.1.2.0.json | 2 +- src/AppInstallerCLITests/YamlManifest.cpp | 4 ++-- src/AppInstallerCommonCore/Public/winget/ManifestValidation.h | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json index 6edb5847c4..b100a1d372 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json @@ -184,7 +184,7 @@ "items": { "$ref": "#/definitions/Documentation" }, - "maxItems": 128 + "maxItems": 256 }, "ManifestType": { "type": "string", diff --git a/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json index 10812f51fd..a5e3601440 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json @@ -179,7 +179,7 @@ "items": { "$ref": "#/definitions/Documentation" }, - "maxItems": 128 + "maxItems": 256 }, "ManifestType": { "type": "string", 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 08d4951f40..64bd1bae18 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 @@ -744,7 +744,7 @@ "items": { "$ref": "#/definitions/Documentation" }, - "maxItems": 128 + "maxItems": 256 }, "Channel": { "$ref": "#/definitions/Channel" diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp index 128c2710ff..990c35c163 100644 --- a/src/AppInstallerCLITests/YamlManifest.cpp +++ b/src/AppInstallerCLITests/YamlManifest.cpp @@ -254,8 +254,8 @@ TEST_CASE("ReadBadManifests", "[ManifestValidation]") { "Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml", "Silent and SilentWithProgress switches are not specified for InstallerType exe.", true }, { "Manifest-Bad-InstallerTypeInvalid.yaml", "Invalid field value. Field: InstallerType" }, { "Manifest-Bad-InstallerTypeMissing.yaml", "Invalid field value. Field: InstallerType" }, - { "Manifest-Bad-InstallerTypePortable-InvalidAppsAndFeatures.yaml", "Only zero or one entry for Apps and Features can be specified for InstallerType portable." }, - { "Manifest-Bad-InstallerTypePortable-InvalidCommands.yaml", "Only zero or one value for Commands can be specified for InstallerType portable." }, + { "Manifest-Bad-InstallerTypePortable-InvalidAppsAndFeatures.yaml", "Only zero or one entry for Apps and Features may be specified for InstallerType portable." }, + { "Manifest-Bad-InstallerTypePortable-InvalidCommands.yaml", "Only zero or one value for Commands may be specified for InstallerType portable." }, { "Manifest-Bad-InstallerTypePortable-InvalidScope.yaml", "Scope is not supported for InstallerType portable." }, { "Manifest-Bad-InstallerUniqueness.yaml", "Duplicate installer entry found." }, { "Manifest-Bad-InstallerUniqueness-DefaultScope.yaml", "Duplicate installer entry found." }, diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestValidation.h b/src/AppInstallerCommonCore/Public/winget/ManifestValidation.h index 4ca543eee0..1d6dff5cc5 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestValidation.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestValidation.h @@ -48,8 +48,8 @@ namespace AppInstaller::Manifest const char* const MissingManifestDependenciesNode = "Dependency not found: "; const char* const NoSuitableMinVersion = "No Suitable Minimum Version: "; const char* const FoundLoop = "Loop found."; - const char* const ExceededAppsAndFeaturesEntryLimit = "Only zero or one entry for Apps and Features can be specified for InstallerType portable."; - const char* const ExceededCommandsLimit = "Only zero or one value for Commands can be specified for InstallerType portable."; + const char* const ExceededAppsAndFeaturesEntryLimit = "Only zero or one entry for Apps and Features may be specified for InstallerType portable."; + const char* const ExceededCommandsLimit = "Only zero or one value for Commands may be specified for InstallerType portable."; const char* const ScopeNotSupported = "Scope is not supported for InstallerType portable."; } From e8919c163820803f7c3004929db9df0b89fa4abd Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 28 Mar 2022 12:10:53 -0700 Subject: [PATCH 10/19] edit bit flags and move dir creation of portable app dir --- .../Public/AppInstallerRuntime.h | 6 ++ .../Public/winget/ExperimentalFeature.h | 4 +- src/AppInstallerCommonCore/Runtime.cpp | 63 +++++++++++++++++++ src/AppInstallerCommonCore/UserSettings.cpp | 21 +------ 4 files changed, 74 insertions(+), 20 deletions(-) diff --git a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h index fdf9ec48bb..b399c6b9aa 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h @@ -47,6 +47,12 @@ namespace AppInstaller::Runtime SecureSettings, // The value of %USERPROFILE%. UserProfile, + // The location where portable packages are installed to with user scope. + PortableAppUserRoot, + // The location where portable packages are installed to with machine scope (x64). + PortableAppMachineRootX64, + // The location where portable packages are installed to with machine scope (x86). + PortableAppMachineRootX86, }; // Gets the path to the requested location. diff --git a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h index 8ba08e06f0..3201db348b 100644 --- a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h +++ b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h @@ -23,8 +23,8 @@ namespace AppInstaller::Settings Dependencies = 0x1, // Before making DirectMSI non-experimental, it should be part of manifest validation. DirectMSI = 0x2, - PortableInstall = 0x3, - Max, // This MUST always be after all experimental features + PortableInstall = 0x4, + Max = 0x8, // This MUST always be after all experimental features // Features listed after Max will not be shown with the features command // This can be used to hide highly experimental features (or these example ones) diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index 8958a33f51..9bc3f36f29 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -4,6 +4,7 @@ #include #include "Public/AppInstallerRuntime.h" #include "Public/AppInstallerStrings.h" +#include "Public/winget/UserSettings.h" #include @@ -12,6 +13,7 @@ namespace AppInstaller::Runtime { using namespace Utility; + using namespace Settings; namespace { @@ -22,6 +24,7 @@ namespace AppInstaller::Runtime constexpr std::string_view s_SecureSettings_Base = "Microsoft/WinGet"sv; constexpr std::string_view s_SecureSettings_UserRelative = "settings"sv; constexpr std::string_view s_SecureSettings_Relative_Unpackaged = "win"sv; + constexpr std::string_view s_DefaultPackagesDirectory = "Packages"sv; #ifndef WINGET_DISABLE_FOR_FUZZING constexpr std::string_view s_SecureSettings_Relative_Packaged = "pkg"sv; #endif @@ -309,6 +312,36 @@ namespace AppInstaller::Runtime result = GetKnownFolderPath(FOLDERID_Profile); create = false; break; + case PathName::PortableAppUserRoot: + result = Settings::User().Get(); + if (result.empty()) + { + result /= GetKnownFolderPath(FOLDERID_LocalAppData); + result /= s_SecureSettings_Base; + result /= s_DefaultPackagesDirectory; + } + create = true; + break; + case PathName::PortableAppMachineRootX64: + result = Settings::User().Get(); + if (result.empty()) + { + result /= GetKnownFolderPath(FOLDERID_ProgramFiles); + result /= s_DefaultTempDirectory; + result /= s_DefaultPackagesDirectory; + } + create = true; + break; + case PathName::PortableAppMachineRootX86: + result = Settings::User().Get(); + if (result.empty()) + { + result /= GetKnownFolderPath(FOLDERID_ProgramFilesX86); + result /= s_DefaultTempDirectory; + result /= s_DefaultPackagesDirectory; + } + create = true; + break; default: THROW_HR(E_UNEXPECTED); } @@ -349,6 +382,36 @@ namespace AppInstaller::Runtime result = GetKnownFolderPath(FOLDERID_Profile); create = false; break; + case PathName::PortableAppUserRoot: + result = Settings::User().Get(); + if (result.empty()) + { + result /= GetKnownFolderPath(FOLDERID_LocalAppData); + result /= s_SecureSettings_Base; + result /= s_DefaultPackagesDirectory; + } + create = true; + break; + case PathName::PortableAppMachineRootX64: + result = Settings::User().Get(); + if (result.empty()) + { + result /= GetKnownFolderPath(FOLDERID_ProgramFiles); + result /= s_DefaultTempDirectory; + result /= s_DefaultPackagesDirectory; + } + create = true; + break; + case PathName::PortableAppMachineRootX86: + result = Settings::User().Get(); + if (result.empty()) + { + result /= GetKnownFolderPath(FOLDERID_ProgramFilesX86); + result /= s_DefaultTempDirectory; + result /= s_DefaultPackagesDirectory; + } + create = true; + break; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index fd288c3423..704ffcbc09 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -234,6 +234,9 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump) WINGET_VALIDATE_PASS_THROUGH(InstallIgnoreWarnings) WINGET_VALIDATE_PASS_THROUGH(UninstallPurge) + WINGET_VALIDATE_PASS_THROUGH(PortableAppUserRoot) + WINGET_VALIDATE_PASS_THROUGH(PortableAppMachineRoot) + WINGET_VALIDATE_SIGNATURE(InstallArchitecturePreference) { @@ -294,24 +297,6 @@ namespace AppInstaller::Settings return SettingMapping::Validate(value); } - WINGET_VALIDATE_SIGNATURE(PortableAppUserRoot) - { - if (!std::filesystem::exists(value)) - { - if (!std::filesystem::create_directories(value)) - { - return {}; - } - } - - return value; - } - - WINGET_VALIDATE_SIGNATURE(PortableAppMachineRoot) - { - return SettingMapping::Validate(value); - } - WINGET_VALIDATE_SIGNATURE(NetworkDownloader) { static constexpr std::string_view s_downloader_default = "default"; From 520c4b88b644969381d93155c338b8c4ca76ac90 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Mon, 28 Mar 2022 15:42:17 -0700 Subject: [PATCH 11/19] add experimental feature description for portableinstall --- doc/Settings.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/Settings.md b/doc/Settings.md index b732139c61..bbb658db17 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -171,3 +171,15 @@ Experimental feature with the aim of managing dependencies, as of now it only sh "dependencies": true }, ``` + +### PortableInstall + +This feature enables the Windows Package Manager to install Portable/Standalone packages. You can enable the feature as shown below. + +>Note: This feature is currently not supported yet. Once we add support for this feature in the near future, this setting will be in place to allow users to try out installing Portable/Standalone apps. + +```json + "experimentalFeatures": { + "portableInstall": true + }, +``` From f7013f3f8839df71ad10e6937fd9b728c60abcf2 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Tue, 29 Mar 2022 13:14:57 -0700 Subject: [PATCH 12/19] address initial feedback --- .github/actions/spelling/allow.txt | 1 + .github/actions/spelling/expect.txt | 1 - .../manifests/v1.2.0/manifest.defaultLocale.1.2.0.json | 6 +++--- .../JSON/manifests/v1.2.0/manifest.singleton.1.2.0.json | 6 +++--- schemas/JSON/settings/settings.schema.0.2.json | 4 ++-- src/AppInstallerCLICore/ExecutionArgs.h | 8 +++++--- .../Manifest/ManifestYamlPopulator.cpp | 2 +- src/AppInstallerCommonCore/Public/winget/ManifestCommon.h | 3 +-- .../Public/winget/ManifestLocalization.h | 2 +- src/AppInstallerCommonCore/Public/winget/UserSettings.h | 4 ++-- src/AppInstallerCommonCore/UserSettings.cpp | 2 +- 11 files changed, 20 insertions(+), 19 deletions(-) diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index 92342b3c0e..eed1c5fb75 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -390,6 +390,7 @@ PRIMARYKEY prioritization PRODUCTNAME PRODUCTVERSION +PROGRAMFILES PROGRESSONLY promptrestart PROPERTYDUMP diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 80ef7edf3c..e34f3bc6de 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -287,7 +287,6 @@ powertoys pri processthreads productcode -PROGRAMFILES pscustomobject pseudocode psm diff --git a/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json index b100a1d372..c230b9883b 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.defaultLocale.1.2.0.json @@ -39,11 +39,11 @@ "Documentation": { "type": "object", "properties": { - "DocumentDescription": { + "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, - "description": "The description of the documentation for providing software guides such as manuals and troubleshooting URLs." + "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", @@ -176,7 +176,7 @@ "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, - "maxLength": 240, + "maxLength": 256, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { 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 64bd1bae18..d2bb27d117 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 @@ -57,11 +57,11 @@ "Documentation": { "type": "object", "properties": { - "DocumentDescription": { + "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, - "description": "The description of the documentation for providing software guides such as manuals and troubleshooting URLs." + "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", @@ -736,7 +736,7 @@ "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, - "maxLength": 240, + "maxLength": 256, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { diff --git a/schemas/JSON/settings/settings.schema.0.2.json b/schemas/JSON/settings/settings.schema.0.2.json index 50bef5ebfb..72061aebc1 100644 --- a/schemas/JSON/settings/settings.schema.0.2.json +++ b/schemas/JSON/settings/settings.schema.0.2.json @@ -118,8 +118,8 @@ "description": "Uninstall settings", "type": "object", "properties": { - "purge": { - "description": "Controls whether the default behavior for uninstall should remove all files and directories relevant to this package. Only applies to the portable installerType.", + "purgePortableApp": { + "description": "Controls whether the default behavior for uninstall removes all files and directories relevant to this package. Only applies to the portable installerType.", "type": "boolean", "default": false } diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h index a6be53b866..c958633204 100644 --- a/src/AppInstallerCLICore/ExecutionArgs.h +++ b/src/AppInstallerCLICore/ExecutionArgs.h @@ -41,6 +41,11 @@ namespace AppInstaller::CLI::Execution InstallArchitecture, HashOverride, // Ignore hash mismatches AcceptPackageAgreements, // Accept all license agreements for packages + Rename, // Renames the file of the executable. Only applies to the portable installerType + + // Uninstall behavior + Purge, // Removes all files and directories related to a package during an uninstall. Only applies to the portable installerType. + Preserve, // Retains any files and directories created by the portable exe. //Source Command SourceName, @@ -86,9 +91,6 @@ namespace AppInstaller::CLI::Execution CustomHeader, // Optional Rest source header AcceptSourceAgreements, // Accept all source agreements IncludeUnknown, // Used in Upgrade command to allow upgrades of packages with unknown versions - Rename, // Renames the file of the executable. Only applies to the portable installerType - Purge, // Removes all files and directories related to a package during an uninstall. Only applies to the portable installerType. - Preserve, // Retains any files and directories created by the portable exe. Wait, // Prompts the user to press any key before exiting. // Used for demonstration purposes diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp index 7c93ca424b..7061112f1b 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp @@ -550,7 +550,7 @@ namespace AppInstaller::Manifest { result = { - { "DocumentDescription", [this](const YAML::Node& value)->ValidationErrors { m_p_documentation->DocumentDescription = Utility::Trim(value.as()); return {}; } }, + { "DocumentDescription", [this](const YAML::Node& value)->ValidationErrors { m_p_documentation->DocumentLabel = Utility::Trim(value.as()); return {}; } }, { "DocumentUrl", [this](const YAML::Node& value)->ValidationErrors { m_p_documentation->DocumentUrl = Utility::Trim(value.as()); return {}; } }, }; } diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 49f895d59c..9743d98474 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -130,7 +130,7 @@ bool HasExtension(std::string_view extension) const; AlreadyInstalled, Downgrade, BlockedByPolicy, - Custom + Custom, }; struct ExpectedReturnCode @@ -138,7 +138,6 @@ bool HasExtension(std::string_view extension) const; DWORD InstallerReturnCode{}; ExpectedReturnCodeEnum ReturnResponse{}; string_t ReturnResponseUrl{}; - ExpectedReturnCode() {}; }; enum class PlatformEnum diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestLocalization.h b/src/AppInstallerCommonCore/Public/winget/ManifestLocalization.h index 6972e635f7..590de1fa43 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestLocalization.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestLocalization.h @@ -43,7 +43,7 @@ namespace AppInstaller::Manifest struct Documentation { - string_t DocumentDescription; + string_t DocumentLabel; string_t DocumentUrl; }; diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index a5f5e29559..63e5f3e458 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -87,7 +87,7 @@ namespace AppInstaller::Settings InstallIgnoreWarnings, PortableAppUserRoot, PortableAppMachineRoot, - UninstallPurge, + UninstallPurgePortableApp, Max }; @@ -139,7 +139,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::InstallIgnoreWarnings, bool, bool, false, ".installBehavior.ignoreWarnings"sv); SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppUserRoot, std::string, std::string, {}, ".installBehavior.portableAppUserRoot"sv); SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppMachineRoot, std::string, std::string, {}, ".installBehavior.portableAppMachineRoot"sv); - SETTINGMAPPING_SPECIALIZATION(Setting::UninstallPurge, bool, bool, false, ".uninstallBehavior.purge"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::UninstallPurgePortableApp, bool, bool, false, ".uninstallBehavior.purge"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFDirectMSI, bool, bool, false, ".experimentalFeatures.directMSI"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EnableSelfInitiatedMinidump, bool, bool, false, ".debugging.enableSelfInitiatedMinidump"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingLevelPreference, std::string, Logging::Level, Logging::Level::Info, ".logging.level"sv); diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 704ffcbc09..547ee33ede 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -233,7 +233,7 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(EFDirectMSI) WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump) WINGET_VALIDATE_PASS_THROUGH(InstallIgnoreWarnings) - WINGET_VALIDATE_PASS_THROUGH(UninstallPurge) + WINGET_VALIDATE_PASS_THROUGH(UninstallPurgePortableApp) WINGET_VALIDATE_PASS_THROUGH(PortableAppUserRoot) WINGET_VALIDATE_PASS_THROUGH(PortableAppMachineRoot) From efcec3e9381a970d89585c56f02494ae9d837da9 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 30 Mar 2022 13:33:29 -0700 Subject: [PATCH 13/19] resolve PR feedback --- .../v1.2.0/manifest.installer.1.2.0.json | 5 ++- .../v1.2.0/manifest.locale.1.2.0.json | 2 +- .../v1.2.0/manifest.singleton.1.2.0.json | 5 ++- .../Workflows/InstallFlow.cpp | 4 +- .../RestInterface_1_1.cpp | 2 +- .../TestData/ManifestV1_2-Singleton.yaml | 2 +- .../ManifestV1_2-MultiFile-DefaultLocale.yaml | 2 +- .../ManifestV1_2-MultiFile-Locale.yaml | 2 +- src/AppInstallerCLITests/YamlManifest.cpp | 18 ++++----- .../Manifest/ManifestValidation.cpp | 16 ++------ .../Manifest/ManifestYamlPopulator.cpp | 16 +++----- .../Public/winget/ExperimentalFeature.h | 2 +- .../Public/winget/ManifestCommon.h | 14 +++---- .../Public/winget/ManifestInstaller.h | 8 +++- src/AppInstallerCommonCore/Runtime.cpp | 40 ++++++++++--------- src/AppInstallerCommonCore/UserSettings.cpp | 1 - .../1_1/Json/ManifestDeserializer_1_1.cpp | 4 +- 17 files changed, 70 insertions(+), 73 deletions(-) diff --git a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json index fcb8dd58d3..c69789b5c5 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json @@ -497,7 +497,10 @@ "$ref": "#/definitions/Scope" }, "InstallerUrl": { - "$ref": "#/definitions/Url", + "allOf": [ + { "$ref": "#/definitions/Url" }, + { "not": { "type": "null" } } + ], "description": "The installer Url" }, "InstallerSha256": { diff --git a/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json index a5e3601440..2fb772241a 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json @@ -39,7 +39,7 @@ "Documentation": { "type": "object", "properties": { - "DocumentDescription": { + "DocumentLabel": { "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, 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 d2bb27d117..a3acca096e 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 @@ -538,7 +538,10 @@ "$ref": "#/definitions/Scope" }, "InstallerUrl": { - "$ref": "#/definitions/Url", + "allOf": [ + { "$ref": "#/definitions/Url" }, + { "not": { "type": "null" } } + ], "description": "The installer Url" }, "InstallerSha256": { diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 093d58e485..f92ec85ea3 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -344,9 +344,9 @@ namespace AppInstaller::CLI::Workflow // Show a specific message if we can identify the return code const auto& expectedReturnCodes = context.Get()->ExpectedReturnCodes; auto expectedReturnCodeItr = expectedReturnCodes.find(installResult); - if (expectedReturnCodeItr != expectedReturnCodes.end() && expectedReturnCodeItr->second != ExpectedReturnCodeEnum::Unknown) + if (expectedReturnCodeItr != expectedReturnCodes.end() && expectedReturnCodeItr->second.ReturnResponseEnum != ExpectedReturnCodeEnum::Unknown) { - auto returnCode = ExpectedReturnCode::GetExpectedReturnCode(expectedReturnCodeItr->second); + auto returnCode = ExpectedReturnCode::GetExpectedReturnCode(expectedReturnCodeItr->second.ReturnResponseEnum); context.Reporter.Error() << returnCode.Message << std::endl; AICLI_TERMINATE_CONTEXT(returnCode.HResult); } diff --git a/src/AppInstallerCLITests/RestInterface_1_1.cpp b/src/AppInstallerCLITests/RestInterface_1_1.cpp index d63b0d1573..df62a2e742 100644 --- a/src/AppInstallerCLITests/RestInterface_1_1.cpp +++ b/src/AppInstallerCLITests/RestInterface_1_1.cpp @@ -297,7 +297,7 @@ namespace REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(actualInstaller.Markets.AllowedMarkets.size() == 1); REQUIRE(actualInstaller.Markets.AllowedMarkets.at(0) == "US"); - REQUIRE(actualInstaller.ExpectedReturnCodes.at(3) == ExpectedReturnCodeEnum::InstallInProgress); + REQUIRE(actualInstaller.ExpectedReturnCodes.at(3).ReturnResponseEnum == ExpectedReturnCodeEnum::InstallInProgress); } }; } diff --git a/src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml b/src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml index 984c458cd7..2c424f06a7 100644 --- a/src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml +++ b/src/AppInstallerCLITests/TestData/ManifestV1_2-Singleton.yaml @@ -23,7 +23,7 @@ ReleaseNotesUrl: https://DefaultReleaseNotes.net PurchaseUrl: https://DefaultPurchaseUrl.com InstallationNotes: Default installation notes Documentations: - - DocumentDescription: Default document description + - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Agreements: - AgreementLabel: DefaultLabel diff --git a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-DefaultLocale.yaml b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-DefaultLocale.yaml index 3c9e9945fa..7809e22450 100644 --- a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-DefaultLocale.yaml +++ b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-DefaultLocale.yaml @@ -23,7 +23,7 @@ Tags: ReleaseNotes: Default release notes ReleaseNotesUrl: https://DefaultReleaseNotes.net Documentations: - - DocumentDescription: Default document description + - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com Agreements: - AgreementLabel: DefaultLabel diff --git a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Locale.yaml b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Locale.yaml index 22dcade406..44b77d9696 100644 --- a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Locale.yaml +++ b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_2/ManifestV1_2-MultiFile-Locale.yaml @@ -26,7 +26,7 @@ Agreements: Agreement: Text AgreementUrl: https://AgreementUrl.net Documentations: - - DocumentDescription: Default document description + - DocumentLabel: Default document label DocumentUrl: https://DefaultDocumentUrl.com ManifestType: locale ManifestVersion: 1.2.0 diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp index 990c35c163..eac4c782fe 100644 --- a/src/AppInstallerCLITests/YamlManifest.cpp +++ b/src/AppInstallerCLITests/YamlManifest.cpp @@ -403,7 +403,7 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(manifest.DefaultLocalization.Get() == "https://DefaultPurchaseUrl.com"); REQUIRE(manifest.DefaultLocalization.Get() == "Default installation notes"); REQUIRE(manifest.DefaultLocalization.Get().size() == 1); - REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentDescription == "Default document description"); + REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentLabel == "Default document label"); REQUIRE(manifest.DefaultLocalization.Get().at(0).DocumentUrl == "https://DefaultDocumentUrl.com"); } @@ -460,7 +460,7 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(manifest.DefaultInstallerInfo.Markets.AllowedMarkets.size() == 1); REQUIRE(manifest.DefaultInstallerInfo.Markets.AllowedMarkets.at(0) == "US"); REQUIRE(manifest.DefaultInstallerInfo.ExpectedReturnCodes.size() == 1); - REQUIRE(manifest.DefaultInstallerInfo.ExpectedReturnCodes.at(10) == ExpectedReturnCodeEnum::PackageInUse); + REQUIRE(manifest.DefaultInstallerInfo.ExpectedReturnCodes.at(10).ReturnResponseEnum == ExpectedReturnCodeEnum::PackageInUse); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) @@ -537,14 +537,14 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(installer1.Markets.AllowedMarkets.size() == 0); REQUIRE(installer1.Markets.ExcludedMarkets.size() == 1); REQUIRE(installer1.Markets.ExcludedMarkets.at(0) == "US"); - REQUIRE(installer1.ExpectedReturnCodes.at(2) == ExpectedReturnCodeEnum::ContactSupport); + REQUIRE(installer1.ExpectedReturnCodes.at(2).ReturnResponseEnum == ExpectedReturnCodeEnum::ContactSupport); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) { REQUIRE_FALSE(installer1.DisplayInstallWarnings); - REQUIRE(installer1.ExpectedReturnCodes.at(3) == ExpectedReturnCodeEnum::Custom); - REQUIRE(installer1.ReturnResponseUrls.at(3) == "https://defaultReturnResponseUrl.com"); + REQUIRE(installer1.ExpectedReturnCodes.at(3).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); + REQUIRE(installer1.ExpectedReturnCodes.at(3).ReturnResponseUrl == "https://defaultReturnResponseUrl.com"); } if (!isSingleton) @@ -575,7 +575,7 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(installer2.Markets.AllowedMarkets.size() == 1); REQUIRE(installer2.Markets.AllowedMarkets.at(0) == "US"); REQUIRE(installer2.ExpectedReturnCodes.size() == 1); - REQUIRE(installer2.ExpectedReturnCodes.at(10) == ExpectedReturnCodeEnum::PackageInUse); + REQUIRE(installer2.ExpectedReturnCodes.at(10).ReturnResponseEnum == ExpectedReturnCodeEnum::PackageInUse); } if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) @@ -587,8 +587,8 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(installer3.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); REQUIRE(installer3.Commands == MultiValue{ "standalone" }); REQUIRE(installer3.ExpectedReturnCodes.size() == 1); - REQUIRE(installer3.ExpectedReturnCodes.at(11) == ExpectedReturnCodeEnum::Custom); - REQUIRE(installer3.ReturnResponseUrls.at(11) == "https://defaultReturnResponseUrl.com"); + REQUIRE(installer3.ExpectedReturnCodes.at(11).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); + REQUIRE(installer3.ExpectedReturnCodes.at(11).ReturnResponseUrl == "https://defaultReturnResponseUrl.com"); REQUIRE_FALSE(installer3.DisplayInstallWarnings); } @@ -626,7 +626,7 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(localization1.Get() == "https://DefaultPurchaseUrl.com"); REQUIRE(localization1.Get() == "Default installation notes"); REQUIRE(localization1.Get().size() == 1); - REQUIRE(localization1.Get().at(0).DocumentDescription == "Default document description"); + REQUIRE(localization1.Get().at(0).DocumentLabel == "Default document label"); REQUIRE(localization1.Get().at(0).DocumentUrl == "https://DefaultDocumentUrl.com"); } } diff --git a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp index 7208bf74cb..d1c3957f00 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp @@ -96,14 +96,6 @@ namespace AppInstaller::Manifest resultErrors.emplace_back(ManifestError::InvalidFieldValue, "UpdateBehavior"); } - for (const auto& unsupportedArgument : installer.UnsupportedArguments) - { - if (unsupportedArgument == UnsupportedArgumentEnum::Unknown) - { - resultErrors.emplace_back(ManifestError::InvalidFieldValue, "UnsupportedArguments"); - } - } - // Validate system reference strings if they are set at the installer level // Allow PackageFamilyName to be declared with non msix installers to support nested installer scenarios after manifest version 1.1 if (manifest.ManifestVersion <= ManifestVer{ s_ManifestVersionV1_1 } && !installer.PackageFamilyName.empty() && !DoesInstallerTypeUsePackageFamilyName(installer.InstallerType)) @@ -163,17 +155,15 @@ namespace AppInstaller::Manifest if (installer.InstallerType == InstallerTypeEnum::Portable) { - if ((manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.size() > 1 && installer.AppsAndFeaturesEntries.size() == 0) || - installer.AppsAndFeaturesEntries.size() > 1) + if (installer.AppsAndFeaturesEntries.size() > 1) { resultErrors.emplace_back(ManifestError::ExceededAppsAndFeaturesEntryLimit); } - if ((manifest.DefaultInstallerInfo.Commands.size() > 1 && installer.Commands.size() == 0) || - installer.Commands.size() > 1) + if (installer.Commands.size() > 1) { resultErrors.emplace_back(ManifestError::ExceededCommandsLimit); } - if (manifest.DefaultInstallerInfo.Scope != ScopeEnum::Unknown || installer.Scope != ScopeEnum::Unknown) + if (installer.Scope != ScopeEnum::Unknown) { resultErrors.emplace_back(ManifestError::ScopeNotSupported, ValidationError::Level::Warning); } diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp index 7061112f1b..6751593448 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp @@ -7,6 +7,7 @@ namespace AppInstaller::Manifest { using ValidationErrors = std::vector; + using ExpectedReturnCodeInfo = AppInstaller::Manifest::ManifestInstaller::ExpectedReturnCodeInfo; namespace { @@ -436,7 +437,7 @@ namespace AppInstaller::Manifest { { "PurchaseUrl", [this](const YAML::Node& value)->ValidationErrors { m_p_localization->Add(value.as()); return {}; } }, { "InstallationNotes", [this](const YAML::Node& value)->ValidationErrors { m_p_localization->Add(value.as()); return {}; } }, - { "Documentations", [this](const YAML::Node& value)->ValidationErrors { return ProcessDocumentationsNode(value); }, true }, + { "Documentations", [this](const YAML::Node& value)->ValidationErrors { return ProcessDocumentationsNode(value); }}, }; std::move(fields_v1_2.begin(), fields_v1_2.end(), std::inserter(result, result.end())); @@ -550,7 +551,7 @@ namespace AppInstaller::Manifest { result = { - { "DocumentDescription", [this](const YAML::Node& value)->ValidationErrors { m_p_documentation->DocumentLabel = Utility::Trim(value.as()); return {}; } }, + { "DocumentLabel", [this](const YAML::Node& value)->ValidationErrors { m_p_documentation->DocumentLabel = Utility::Trim(value.as()); return {}; } }, { "DocumentUrl", [this](const YAML::Node& value)->ValidationErrors { m_p_documentation->DocumentUrl = Utility::Trim(value.as()); return {}; } }, }; } @@ -709,8 +710,7 @@ namespace AppInstaller::Manifest THROW_HR_IF(E_INVALIDARG, !returnCodesNode.IsSequence()); ValidationErrors resultErrors; - std::map returnCodes; - std::map returnResponseUrls; + std::map returnCodes; for (auto const& entry : returnCodesNode.Sequence()) { @@ -718,17 +718,13 @@ namespace AppInstaller::Manifest m_p_expectedReturnCode = &returnCode; auto errors = ValidateAndProcessFields(entry, ExpectedReturnCodesFieldInfos); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); - - if (!returnCodes.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponse }).second) + if (!returnCodes.insert({ returnCode.InstallerReturnCode, ExpectedReturnCodeInfo{ returnCode.ReturnResponse, returnCode.ReturnResponseUrl} }).second) { resultErrors.emplace_back(ManifestError::DuplicateReturnCodeEntry); } - - returnResponseUrls.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponseUrl }).second; } m_p_installer->ExpectedReturnCodes = returnCodes; - m_p_installer->ReturnResponseUrls = returnResponseUrls; return resultErrors; } @@ -848,7 +844,7 @@ namespace AppInstaller::Manifest if (installer.ExpectedReturnCodes.find(defaultReturnCode.first) == installer.ExpectedReturnCodes.end() && std::find(installer.InstallerSuccessCodes.begin(), installer.InstallerSuccessCodes.end(), defaultReturnCode.first) == installer.InstallerSuccessCodes.end()) { - installer.ExpectedReturnCodes[defaultReturnCode.first] = defaultReturnCode.second; + installer.ExpectedReturnCodes[defaultReturnCode.first].ReturnResponseEnum = defaultReturnCode.second; } } diff --git a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h index 3201db348b..2984adcf5a 100644 --- a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h +++ b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h @@ -24,7 +24,7 @@ namespace AppInstaller::Settings // Before making DirectMSI non-experimental, it should be part of manifest validation. DirectMSI = 0x2, PortableInstall = 0x4, - Max = 0x8, // This MUST always be after all experimental features + Max, // This MUST always be after all experimental features // Features listed after Max will not be shown with the features command // This can be used to hide highly experimental features (or these example ones) diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 9743d98474..fd6e99a318 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -133,13 +133,6 @@ bool HasExtension(std::string_view extension) const; Custom, }; - struct ExpectedReturnCode - { - DWORD InstallerReturnCode{}; - ExpectedReturnCodeEnum ReturnResponse{}; - string_t ReturnResponseUrl{}; - }; - enum class PlatformEnum { Unknown, @@ -181,6 +174,13 @@ bool HasExtension(std::string_view extension) const; External }; + struct ExpectedReturnCode + { + DWORD InstallerReturnCode = 0; + ExpectedReturnCodeEnum ReturnResponse = ExpectedReturnCodeEnum::Unknown; + string_t ReturnResponseUrl; + }; + struct Dependency { DependencyType Type; diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h b/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h index 51114fe268..d02a3847c7 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h @@ -51,9 +51,13 @@ namespace AppInstaller::Manifest std::vector InstallerSuccessCodes; - std::map ExpectedReturnCodes; + struct ExpectedReturnCodeInfo + { + ExpectedReturnCodeEnum ReturnResponseEnum = ExpectedReturnCodeEnum::Unknown; + string_t ReturnResponseUrl; + }; - std::map ReturnResponseUrls; + std::map ExpectedReturnCodes; UpdateBehaviorEnum UpdateBehavior = UpdateBehaviorEnum::Install; diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index 9bc3f36f29..ee23dc8d47 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -24,7 +24,9 @@ namespace AppInstaller::Runtime constexpr std::string_view s_SecureSettings_Base = "Microsoft/WinGet"sv; constexpr std::string_view s_SecureSettings_UserRelative = "settings"sv; constexpr std::string_view s_SecureSettings_Relative_Unpackaged = "win"sv; - constexpr std::string_view s_DefaultPackagesDirectory = "Packages"sv; + constexpr std::string_view s_PortableAppUserRoot = "Microsoft/WinGet"sv; + constexpr std::string_view s_PortableAppMachineRoot = "WinGet"sv; + constexpr std::string_view s_PackagesDirectory = "Packages"sv; #ifndef WINGET_DISABLE_FOR_FUZZING constexpr std::string_view s_SecureSettings_Relative_Packaged = "pkg"sv; #endif @@ -316,9 +318,9 @@ namespace AppInstaller::Runtime result = Settings::User().Get(); if (result.empty()) { - result /= GetKnownFolderPath(FOLDERID_LocalAppData); - result /= s_SecureSettings_Base; - result /= s_DefaultPackagesDirectory; + result = GetKnownFolderPath(FOLDERID_LocalAppData); + result /= s_PortableAppUserRoot; + result /= s_PackagesDirectory; } create = true; break; @@ -326,9 +328,9 @@ namespace AppInstaller::Runtime result = Settings::User().Get(); if (result.empty()) { - result /= GetKnownFolderPath(FOLDERID_ProgramFiles); - result /= s_DefaultTempDirectory; - result /= s_DefaultPackagesDirectory; + result = GetKnownFolderPath(FOLDERID_ProgramFilesX64); + result /= s_PortableAppMachineRoot; + result /= s_PackagesDirectory; } create = true; break; @@ -336,9 +338,9 @@ namespace AppInstaller::Runtime result = Settings::User().Get(); if (result.empty()) { - result /= GetKnownFolderPath(FOLDERID_ProgramFilesX86); - result /= s_DefaultTempDirectory; - result /= s_DefaultPackagesDirectory; + result = GetKnownFolderPath(FOLDERID_ProgramFilesX86); + result /= s_PortableAppMachineRoot; + result /= s_PackagesDirectory; } create = true; break; @@ -386,9 +388,9 @@ namespace AppInstaller::Runtime result = Settings::User().Get(); if (result.empty()) { - result /= GetKnownFolderPath(FOLDERID_LocalAppData); - result /= s_SecureSettings_Base; - result /= s_DefaultPackagesDirectory; + result = GetKnownFolderPath(FOLDERID_LocalAppData); + result /= s_PortableAppUserRoot; + result /= s_PackagesDirectory; } create = true; break; @@ -396,9 +398,9 @@ namespace AppInstaller::Runtime result = Settings::User().Get(); if (result.empty()) { - result /= GetKnownFolderPath(FOLDERID_ProgramFiles); - result /= s_DefaultTempDirectory; - result /= s_DefaultPackagesDirectory; + result = GetKnownFolderPath(FOLDERID_ProgramFilesX64); + result /= s_PortableAppMachineRoot; + result /= s_PackagesDirectory; } create = true; break; @@ -406,9 +408,9 @@ namespace AppInstaller::Runtime result = Settings::User().Get(); if (result.empty()) { - result /= GetKnownFolderPath(FOLDERID_ProgramFilesX86); - result /= s_DefaultTempDirectory; - result /= s_DefaultPackagesDirectory; + result = GetKnownFolderPath(FOLDERID_ProgramFilesX86); + result /= s_PortableAppMachineRoot; + result /= s_PackagesDirectory; } create = true; break; diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 547ee33ede..3713b5d795 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -237,7 +237,6 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(PortableAppUserRoot) WINGET_VALIDATE_PASS_THROUGH(PortableAppMachineRoot) - WINGET_VALIDATE_SIGNATURE(InstallArchitecturePreference) { std::vector archs; diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp index 2ea1be4bab..94d1ac6d33 100644 --- a/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp +++ b/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp @@ -139,7 +139,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_1::Json // Only add when it is valid if (installerReturnCode != 0 && returnResponse != ExpectedReturnCodeEnum::Unknown) { - if (!installer.ExpectedReturnCodes.insert({ installerReturnCode, returnResponse }).second) + if (!installer.ExpectedReturnCodes.insert({ installerReturnCode, AppInstaller::Manifest::ManifestInstaller::ExpectedReturnCodeInfo{returnResponse} }).second) { AICLI_LOG(Repo, Error, << "Expected return codes cannot have repeated value."); return {}; @@ -155,7 +155,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_1::Json if (installer.ExpectedReturnCodes.find(defaultReturnCode.first) == installer.ExpectedReturnCodes.end() && std::find(installer.InstallerSuccessCodes.begin(), installer.InstallerSuccessCodes.end(), defaultReturnCode.first) == installer.InstallerSuccessCodes.end()) { - installer.ExpectedReturnCodes[defaultReturnCode.first] = defaultReturnCode.second; + installer.ExpectedReturnCodes[defaultReturnCode.first].ReturnResponseEnum = defaultReturnCode.second; } } } From b7e2e1313eac9e568acbd7bc1f8b5dcecdbf91fd Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 30 Mar 2022 14:03:07 -0700 Subject: [PATCH 14/19] revert settings md --- doc/Settings.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/doc/Settings.md b/doc/Settings.md index bbb658db17..b732139c61 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -171,15 +171,3 @@ Experimental feature with the aim of managing dependencies, as of now it only sh "dependencies": true }, ``` - -### PortableInstall - -This feature enables the Windows Package Manager to install Portable/Standalone packages. You can enable the feature as shown below. - ->Note: This feature is currently not supported yet. Once we add support for this feature in the near future, this setting will be in place to allow users to try out installing Portable/Standalone apps. - -```json - "experimentalFeatures": { - "portableInstall": true - }, -``` From 734a6ce542aacc7420b0cd24e6c79b5c9f356407 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 30 Mar 2022 15:09:54 -0700 Subject: [PATCH 15/19] remove protocol regex --- schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json | 1 - schemas/JSON/manifests/v1.2.0/manifest.singleton.1.2.0.json | 1 - 2 files changed, 2 deletions(-) diff --git a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json index c69789b5c5..2f1860eb70 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json @@ -231,7 +231,6 @@ "type": [ "array", "null" ], "items": { "type": "string", - "pattern": "^[a-z0-9][-a-z0-9\\.\\+]*$", "maxLength": 2048 }, "maxItems": 16, 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 a3acca096e..ac5747917d 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 @@ -273,7 +273,6 @@ "type": [ "array", "null" ], "items": { "type": "string", - "pattern": "^[a-z0-9][-a-z0-9\\.\\+]*$", "maxLength": 2048 }, "maxItems": 16, From d1405077023adb719f9812f706610a726e9e29cc Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Thu, 31 Mar 2022 16:50:56 -0700 Subject: [PATCH 16/19] resolve PR comments --- .../manifests/v1.2.0/manifest.installer.1.2.0.json | 7 +++---- .../manifests/v1.2.0/manifest.locale.1.2.0.json | 4 ++-- .../manifests/v1.2.0/manifest.singleton.1.2.0.json | 7 +++---- .../Manifest/ManifestYamlPopulator.cpp | 2 +- .../Public/winget/UserSettings.h | 2 +- src/AppInstallerCommonCore/Runtime.cpp | 14 +++++++------- .../Schema/1_1/Json/ManifestDeserializer_1_1.cpp | 2 +- 7 files changed, 18 insertions(+), 20 deletions(-) diff --git a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json index 2f1860eb70..51f976d3c0 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.installer.1.2.0.json @@ -496,10 +496,9 @@ "$ref": "#/definitions/Scope" }, "InstallerUrl": { - "allOf": [ - { "$ref": "#/definitions/Url" }, - { "not": { "type": "null" } } - ], + "type": "string", + "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", + "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { diff --git a/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json b/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json index 2fb772241a..7e644746f3 100644 --- a/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json +++ b/schemas/JSON/manifests/v1.2.0/manifest.locale.1.2.0.json @@ -43,7 +43,7 @@ "type": [ "string", "null" ], "minLength": 1, "maxLength": 100, - "description": "The description of the documentation for providing software guides such as manuals and troubleshooting URLs." + "description": "The label of the documentation for providing software guides such as manuals and troubleshooting URLs." }, "DocumentUrl": { "$ref": "#/definitions/Url", @@ -171,7 +171,7 @@ "InstallationNotes": { "type": [ "string", "null" ], "minLength": 1, - "maxLength": 240, + "maxLength": 256, "description": "The notes displayed to the user upon completion of a package installation." }, "Documentations": { 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 ac5747917d..216705a623 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 @@ -537,10 +537,9 @@ "$ref": "#/definitions/Scope" }, "InstallerUrl": { - "allOf": [ - { "$ref": "#/definitions/Url" }, - { "not": { "type": "null" } } - ], + "type": "string", + "pattern": "^([Hh][Tt][Tt][Pp][Ss]?)://.+$", + "maxLength": 2048, "description": "The installer Url" }, "InstallerSha256": { diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp index 6751593448..2cacce455c 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp @@ -718,7 +718,7 @@ namespace AppInstaller::Manifest m_p_expectedReturnCode = &returnCode; auto errors = ValidateAndProcessFields(entry, ExpectedReturnCodesFieldInfos); std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); - if (!returnCodes.insert({ returnCode.InstallerReturnCode, ExpectedReturnCodeInfo{ returnCode.ReturnResponse, returnCode.ReturnResponseUrl} }).second) + if (!returnCodes.insert({ returnCode.InstallerReturnCode, {returnCode.ReturnResponse, returnCode.ReturnResponseUrl} }).second) { resultErrors.emplace_back(ManifestError::DuplicateReturnCodeEntry); } diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index 63e5f3e458..3501f6a966 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -139,7 +139,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::InstallIgnoreWarnings, bool, bool, false, ".installBehavior.ignoreWarnings"sv); SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppUserRoot, std::string, std::string, {}, ".installBehavior.portableAppUserRoot"sv); SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppMachineRoot, std::string, std::string, {}, ".installBehavior.portableAppMachineRoot"sv); - SETTINGMAPPING_SPECIALIZATION(Setting::UninstallPurgePortableApp, bool, bool, false, ".uninstallBehavior.purge"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::UninstallPurgePortableApp, bool, bool, false, ".uninstallBehavior.purgePortableApp"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFDirectMSI, bool, bool, false, ".experimentalFeatures.directMSI"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EnableSelfInitiatedMinidump, bool, bool, false, ".debugging.enableSelfInitiatedMinidump"sv); SETTINGMAPPING_SPECIALIZATION(Setting::LoggingLevelPreference, std::string, Logging::Level, Logging::Level::Info, ".logging.level"sv); diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp index ee23dc8d47..8adf34826f 100644 --- a/src/AppInstallerCommonCore/Runtime.cpp +++ b/src/AppInstallerCommonCore/Runtime.cpp @@ -26,7 +26,7 @@ namespace AppInstaller::Runtime constexpr std::string_view s_SecureSettings_Relative_Unpackaged = "win"sv; constexpr std::string_view s_PortableAppUserRoot = "Microsoft/WinGet"sv; constexpr std::string_view s_PortableAppMachineRoot = "WinGet"sv; - constexpr std::string_view s_PackagesDirectory = "Packages"sv; + constexpr std::string_view s_PortablePackagesDirectory = "Packages"sv; #ifndef WINGET_DISABLE_FOR_FUZZING constexpr std::string_view s_SecureSettings_Relative_Packaged = "pkg"sv; #endif @@ -320,7 +320,7 @@ namespace AppInstaller::Runtime { result = GetKnownFolderPath(FOLDERID_LocalAppData); result /= s_PortableAppUserRoot; - result /= s_PackagesDirectory; + result /= s_PortablePackagesDirectory; } create = true; break; @@ -330,7 +330,7 @@ namespace AppInstaller::Runtime { result = GetKnownFolderPath(FOLDERID_ProgramFilesX64); result /= s_PortableAppMachineRoot; - result /= s_PackagesDirectory; + result /= s_PortablePackagesDirectory; } create = true; break; @@ -340,7 +340,7 @@ namespace AppInstaller::Runtime { result = GetKnownFolderPath(FOLDERID_ProgramFilesX86); result /= s_PortableAppMachineRoot; - result /= s_PackagesDirectory; + result /= s_PortablePackagesDirectory; } create = true; break; @@ -390,7 +390,7 @@ namespace AppInstaller::Runtime { result = GetKnownFolderPath(FOLDERID_LocalAppData); result /= s_PortableAppUserRoot; - result /= s_PackagesDirectory; + result /= s_PortablePackagesDirectory; } create = true; break; @@ -400,7 +400,7 @@ namespace AppInstaller::Runtime { result = GetKnownFolderPath(FOLDERID_ProgramFilesX64); result /= s_PortableAppMachineRoot; - result /= s_PackagesDirectory; + result /= s_PortablePackagesDirectory; } create = true; break; @@ -410,7 +410,7 @@ namespace AppInstaller::Runtime { result = GetKnownFolderPath(FOLDERID_ProgramFilesX86); result /= s_PortableAppMachineRoot; - result /= s_PackagesDirectory; + result /= s_PortablePackagesDirectory; } create = true; break; diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp index 94d1ac6d33..152533f3ea 100644 --- a/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp +++ b/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp @@ -139,7 +139,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_1::Json // Only add when it is valid if (installerReturnCode != 0 && returnResponse != ExpectedReturnCodeEnum::Unknown) { - if (!installer.ExpectedReturnCodes.insert({ installerReturnCode, AppInstaller::Manifest::ManifestInstaller::ExpectedReturnCodeInfo{returnResponse} }).second) + if (!installer.ExpectedReturnCodes.insert({ installerReturnCode, {returnResponse, ""} }).second) { AICLI_LOG(Repo, Error, << "Expected return codes cannot have repeated value."); return {}; From 74c89ddc869fb037219e3ee1258e0b1f3f46c5f4 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 1 Apr 2022 13:04:35 -0700 Subject: [PATCH 17/19] change portableAppRoot to path type --- .../Shared/Strings/en-us/winget.resw | 2 +- src/AppInstallerCLITests/UserSettings.cpp | 13 +++++++++++++ .../Public/winget/UserSettings.h | 4 ++-- src/AppInstallerCommonCore/UserSettings.cpp | 18 ++++++++++++++++-- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 666d9f27f4..8f78790a05 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1279,7 +1279,7 @@ Please specify one of them using the `--source` option to proceed. Retains all files and directories created by the package (portable) - Deletes all files and directories created by the package (portable) + Deletes all files and directories in the package directory (portable) The value to rename the executable file (portable) diff --git a/src/AppInstallerCLITests/UserSettings.cpp b/src/AppInstallerCLITests/UserSettings.cpp index dd3ed57d6a..4edcb04ef5 100644 --- a/src/AppInstallerCLITests/UserSettings.cpp +++ b/src/AppInstallerCLITests/UserSettings.cpp @@ -413,3 +413,16 @@ TEST_CASE("SettingsExperimentalCmd", "[settings]") REQUIRE(userSettingTest.GetWarnings().size() == 0); } } + +TEST_CASE("SettingsPortableAppRoot", "[settings]") +{ + SECTION("Relative path") + { + std::string_view json = R"({ "installBehavior": { "portableAppUserRoot": %LOCALAPPDATA%/Portable/Root } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get().empty()); + REQUIRE(userSettingTest.GetWarnings().size() == 1); + } +} diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index 3501f6a966..5c6898e0ec 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -137,8 +137,8 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocalePreference, std::vector, std::vector, {}, ".installBehavior.preferences.locale"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocaleRequirement, std::vector, std::vector, {}, ".installBehavior.requirements.locale"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallIgnoreWarnings, bool, bool, false, ".installBehavior.ignoreWarnings"sv); - SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppUserRoot, std::string, std::string, {}, ".installBehavior.portableAppUserRoot"sv); - SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppMachineRoot, std::string, std::string, {}, ".installBehavior.portableAppMachineRoot"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppUserRoot, std::string, std::filesystem::path, {}, ".installBehavior.portableAppUserRoot"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppMachineRoot, std::string, std::filesystem::path, {}, ".installBehavior.portableAppMachineRoot"sv); SETTINGMAPPING_SPECIALIZATION(Setting::UninstallPurgePortableApp, bool, bool, false, ".uninstallBehavior.purgePortableApp"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFDirectMSI, bool, bool, false, ".experimentalFeatures.directMSI"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EnableSelfInitiatedMinidump, bool, bool, false, ".debugging.enableSelfInitiatedMinidump"sv); diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 3713b5d795..4edf7495dd 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -234,8 +234,22 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump) WINGET_VALIDATE_PASS_THROUGH(InstallIgnoreWarnings) WINGET_VALIDATE_PASS_THROUGH(UninstallPurgePortableApp) - WINGET_VALIDATE_PASS_THROUGH(PortableAppUserRoot) - WINGET_VALIDATE_PASS_THROUGH(PortableAppMachineRoot) + + WINGET_VALIDATE_SIGNATURE(PortableAppUserRoot) + { + std::filesystem::path root = { value }; + if (!root.is_absolute()) + { + return {}; + } + + return root; + } + + WINGET_VALIDATE_SIGNATURE(PortableAppMachineRoot) + { + return SettingMapping::Validate(value); + } WINGET_VALIDATE_SIGNATURE(InstallArchitecturePreference) { From 223a169b84cf15f6d802fe8c2223651a053011ed Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 1 Apr 2022 13:06:30 -0700 Subject: [PATCH 18/19] spacing --- .../Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp index 152533f3ea..9b66883c07 100644 --- a/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp +++ b/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp @@ -139,7 +139,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_1::Json // Only add when it is valid if (installerReturnCode != 0 && returnResponse != ExpectedReturnCodeEnum::Unknown) { - if (!installer.ExpectedReturnCodes.insert({ installerReturnCode, {returnResponse, ""} }).second) + if (!installer.ExpectedReturnCodes.insert({ installerReturnCode, { returnResponse, "" } }).second) { AICLI_LOG(Repo, Error, << "Expected return codes cannot have repeated value."); return {}; From 93a409c2a3377966c0a470ca1b1de427287f2eb5 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Fri, 1 Apr 2022 13:18:21 -0700 Subject: [PATCH 19/19] convert to utf16 path --- src/AppInstallerCommonCore/UserSettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 4edf7495dd..36f40aabe3 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -237,7 +237,7 @@ namespace AppInstaller::Settings WINGET_VALIDATE_SIGNATURE(PortableAppUserRoot) { - std::filesystem::path root = { value }; + std::filesystem::path root = ConvertToUTF16(value); if (!root.is_absolute()) { return {};