diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt
index 01ea0ee6ff..63f59bbe5c 100644
--- a/.github/actions/spelling/allow.txt
+++ b/.github/actions/spelling/allow.txt
@@ -378,6 +378,7 @@ UNICODESTRING
uninstalling
Unmarshal
unskipped
+unstash
untimes
updatefile
updatemanifest
diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt
index cd1f377635..4387a0a726 100644
--- a/.github/actions/spelling/expect.txt
+++ b/.github/actions/spelling/expect.txt
@@ -325,6 +325,7 @@ maxvalue
maybenull
MBH
MBs
+mcr
mday
mdmp
mdmpto
diff --git a/schemas/JSON/manifests/latest/manifest.defaultLocale.latest.json b/schemas/JSON/manifests/latest/manifest.defaultLocale.latest.json
index 5d96d684e8..dd9e967b82 100644
--- a/schemas/JSON/manifests/latest/manifest.defaultLocale.latest.json
+++ b/schemas/JSON/manifests/latest/manifest.defaultLocale.latest.json
@@ -1,7 +1,7 @@
{
- "$id": "https://aka.ms/winget-manifest.defaultlocale.1.12.0.schema.json",
+ "$id": "https://aka.ms/winget-manifest.defaultlocale.1.28.0.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
- "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.12.0",
+ "description": "A representation of a multiple-file manifest representing a default app metadata in the OWC. v1.28.0",
"definitions": {
"Url": {
"type": [ "string", "null" ],
@@ -261,7 +261,7 @@
},
"ManifestVersion": {
"type": "string",
- "default": "1.12.0",
+ "default": "1.28.0",
"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])){2}$",
"description": "The manifest syntax version"
}
diff --git a/schemas/JSON/manifests/latest/manifest.installer.latest.json b/schemas/JSON/manifests/latest/manifest.installer.latest.json
index 2f634646d9..5442e77897 100644
--- a/schemas/JSON/manifests/latest/manifest.installer.latest.json
+++ b/schemas/JSON/manifests/latest/manifest.installer.latest.json
@@ -1,7 +1,7 @@
{
- "$id": "https://aka.ms/winget-manifest.installer.1.12.0.schema.json",
+ "$id": "https://aka.ms/winget-manifest.installer.1.28.0.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
- "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.12.0",
+ "description": "A representation of a single-file manifest representing an app installers in the OWC. v1.28.0",
"definitions": {
"PackageIdentifier": {
"type": "string",
@@ -639,6 +639,78 @@
],
"description": "The authentication requirement for downloading the installer."
},
+ "DesiredStateConfiguration": {
+ "type": [ "object", "null" ],
+ "description": "References to desired state configuration (DSC) resources that are related to the package.",
+ "properties": {
+ "PowerShell": {
+ "type": [ "array", "null" ],
+ "description": "Contains data about DSC resources that are contained in PowerShell modules.",
+ "uniqueItems": true,
+ "maxItems": 16,
+ "items": {
+ "type": "object",
+ "title": "PowerShell DSC Module Item",
+ "properties": {
+ "RepositoryUrl": {
+ "$ref": "#/definitions/Url"
+ },
+ "ModuleName": {
+ "type": "string",
+ "description": "The name of the module containing resources.",
+ "$comment": "From nuget package id, although PowerShell convention is slightly more strict: https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu1017",
+ "pattern": "^\\w+([.-]\\w+)*$",
+ "maxLength": 100
+ },
+ "Resources": {
+ "type": "array",
+ "description": "The resources contained within the module.",
+ "maxItems": 64,
+ "items": {
+ "type": "object",
+ "title": "PowerShell DSC Resource Item",
+ "properties": {
+ "Name": {
+ "type": "string",
+ "description": "The name of the resource.",
+ "$comment": "Needs to be an identifier in the various languages (MOF, PS class name), could not find any direct description.",
+ "pattern": "^[A-Za-z][-_A-Za-z0-9]*$",
+ "maxLength": 100
+ }
+ }
+ }
+ }
+ },
+ "required": [ "RepositoryUrl", "ModuleName", "Resources" ]
+ }
+ },
+ "DSCv3": {
+ "type": [ "object", "null" ],
+ "description": "Contains data about DSC resources that are contained in the package using the DSC v3 specification.",
+ "properties": {
+ "Resources": {
+ "type": "array",
+ "description": "The resources contained within the package.",
+ "maxItems": 128,
+ "items": {
+ "type": "object",
+ "title": "DSCv3 Resource Item",
+ "properties": {
+ "Type": {
+ "type": "string",
+ "description": "The name of the resource.",
+ "$comment": "Pulled from DSCv3 definition; matches `Publisher.Product.Component/ResourceName` where the Product and Component are optional.",
+ "pattern": "^\\w+(\\.\\w+){0,2}\\/\\w+$",
+ "maxLength": 256
+ }
+ }
+ }
+ }
+ },
+ "required": [ "Resources" ]
+ }
+ }
+ },
"Installer": {
"type": "object",
"properties": {
@@ -765,6 +837,9 @@
},
"Authentication": {
"$ref": "#/definitions/Authentication"
+ },
+ "DesiredStateConfiguration": {
+ "$ref": "#/definitions/DesiredStateConfiguration"
}
},
"required": [
@@ -890,6 +965,9 @@
"Authentication": {
"$ref": "#/definitions/Authentication"
},
+ "DesiredStateConfiguration": {
+ "$ref": "#/definitions/DesiredStateConfiguration"
+ },
"Installers": {
"type": "array",
"items": {
@@ -906,7 +984,7 @@
},
"ManifestVersion": {
"type": "string",
- "default": "1.12.0",
+ "default": "1.28.0",
"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])){2}$",
"description": "The manifest syntax version"
}
diff --git a/schemas/JSON/manifests/latest/manifest.locale.latest.json b/schemas/JSON/manifests/latest/manifest.locale.latest.json
index f21e130f61..e381fb7381 100644
--- a/schemas/JSON/manifests/latest/manifest.locale.latest.json
+++ b/schemas/JSON/manifests/latest/manifest.locale.latest.json
@@ -1,7 +1,7 @@
{
- "$id": "https://aka.ms/winget-manifest.locale.1.12.0.schema.json",
+ "$id": "https://aka.ms/winget-manifest.locale.1.28.0.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
- "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.12.0",
+ "description": "A representation of a multiple-file manifest representing app metadata in other locale in the OWC. v1.28.0",
"definitions": {
"Url": {
"type": [ "string", "null" ],
@@ -256,7 +256,7 @@
},
"ManifestVersion": {
"type": "string",
- "default": "1.12.0",
+ "default": "1.28.0",
"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])){2}$",
"description": "The manifest syntax version"
}
diff --git a/schemas/JSON/manifests/latest/manifest.singleton.latest.json b/schemas/JSON/manifests/latest/manifest.singleton.latest.json
index 9a44532d61..413f523c22 100644
--- a/schemas/JSON/manifests/latest/manifest.singleton.latest.json
+++ b/schemas/JSON/manifests/latest/manifest.singleton.latest.json
@@ -1,7 +1,7 @@
{
- "$id": "https://aka.ms/winget-manifest.singleton.1.12.0.schema.json",
+ "$id": "https://aka.ms/winget-manifest.singleton.1.28.0.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
- "description": "A representation of a single-file manifest representing an app in the OWC. v1.12.0",
+ "description": "A representation of a single-file manifest representing an app in the OWC. v1.28.0",
"definitions": {
"PackageIdentifier": {
"type": "string",
@@ -740,6 +740,78 @@
],
"description": "The authentication requirement for downloading the installer."
},
+ "DesiredStateConfiguration": {
+ "type": [ "object", "null" ],
+ "description": "References to desired state configuration (DSC) resources that are related to the package.",
+ "properties": {
+ "PowerShell": {
+ "type": [ "array", "null" ],
+ "description": "Contains data about DSC resources that are contained in PowerShell modules.",
+ "uniqueItems": true,
+ "maxItems": 16,
+ "items": {
+ "type": "object",
+ "title": "PowerShell DSC Module Item",
+ "properties": {
+ "RepositoryUrl": {
+ "$ref": "#/definitions/Url"
+ },
+ "ModuleName": {
+ "type": "string",
+ "description": "The name of the module containing resources.",
+ "$comment": "From nuget package id, although PowerShell convention is slightly more strict: https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu1017",
+ "pattern": "^\\w+([.-]\\w+)*$",
+ "maxLength": 100
+ },
+ "Resources": {
+ "type": "array",
+ "description": "The resources contained within the module.",
+ "maxItems": 64,
+ "items": {
+ "type": "object",
+ "title": "PowerShell DSC Resource Item",
+ "properties": {
+ "Name": {
+ "type": "string",
+ "description": "The name of the resource.",
+ "$comment": "Needs to be an identifier in the various languages (MOF, PS class name), could not find any direct description.",
+ "pattern": "^[A-Za-z][-_A-Za-z0-9]*$",
+ "maxLength": 100
+ }
+ }
+ }
+ }
+ },
+ "required": [ "RepositoryUrl", "ModuleName", "Resources" ]
+ }
+ },
+ "DSCv3": {
+ "type": [ "object", "null" ],
+ "description": "Contains data about DSC resources that are contained in the package using the DSC v3 specification.",
+ "properties": {
+ "Resources": {
+ "type": "array",
+ "description": "The resources contained within the package.",
+ "maxItems": 128,
+ "items": {
+ "type": "object",
+ "title": "DSCv3 Resource Item",
+ "properties": {
+ "Type": {
+ "type": "string",
+ "description": "The name of the resource.",
+ "$comment": "Pulled from DSCv3 definition; matches `Publisher.Product.Component/ResourceName` where the Product and Component are optional.",
+ "pattern": "^\\w+(\\.\\w+){0,2}\\/\\w+$",
+ "maxLength": 256
+ }
+ }
+ }
+ }
+ },
+ "required": [ "Resources" ]
+ }
+ }
+ },
"Installer": {
"type": "object",
"properties": {
@@ -866,6 +938,9 @@
},
"Authentication": {
"$ref": "#/definitions/Authentication"
+ },
+ "DesiredStateConfiguration": {
+ "$ref": "#/definitions/DesiredStateConfiguration"
}
},
"required": [
@@ -1114,6 +1189,9 @@
"Authentication": {
"$ref": "#/definitions/Authentication"
},
+ "DesiredStateConfiguration": {
+ "$ref": "#/definitions/DesiredStateConfiguration"
+ },
"Installers": {
"type": "array",
"items": {
@@ -1130,7 +1208,7 @@
},
"ManifestVersion": {
"type": "string",
- "default": "1.12.0",
+ "default": "1.28.0",
"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])){2}$",
"description": "The manifest syntax version"
}
diff --git a/schemas/JSON/manifests/latest/manifest.version.latest.json b/schemas/JSON/manifests/latest/manifest.version.latest.json
index 2e4a2c8671..7d36364257 100644
--- a/schemas/JSON/manifests/latest/manifest.version.latest.json
+++ b/schemas/JSON/manifests/latest/manifest.version.latest.json
@@ -1,7 +1,7 @@
{
- "$id": "https://aka.ms/winget-manifest.version.1.12.0.schema.json",
+ "$id": "https://aka.ms/winget-manifest.version.1.28.0.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
- "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.12.0",
+ "description": "A representation of a multi-file manifest representing an app version in the OWC. v1.28.0",
"type": "object",
"properties": {
"PackageIdentifier": {
@@ -31,7 +31,7 @@
},
"ManifestVersion": {
"type": "string",
- "default": "1.12.0",
+ "default": "1.28.0",
"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])){2}$",
"description": "The manifest syntax version"
}
diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
index 11148bc2ef..e2d439a275 100644
--- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
+++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
@@ -1055,6 +1055,24 @@
true
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ true
+
diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters
index c63e833b68..643382697d 100644
--- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters
+++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters
@@ -61,6 +61,9 @@
{6bd68492-3f96-4023-af35-63249cd18158}
+
+ {0e5f9ff4-6f22-46eb-8538-1c560a008f06}
+
@@ -1113,5 +1116,29 @@
TestData
+
+ TestData\MultiFileManifestV1_28
+
+
+ TestData\MultiFileManifestV1_28
+
+
+ TestData\MultiFileManifestV1_28
+
+
+ TestData\MultiFileManifestV1_28
+
+
+ TestData
+
+
+ TestData
+
+
+ TestData
+
+
+ TestData
+
\ No newline at end of file
diff --git a/src/AppInstallerCLITests/TestData/ManifestV1_28-PowerShellDSC.yaml b/src/AppInstallerCLITests/TestData/ManifestV1_28-PowerShellDSC.yaml
new file mode 100644
index 0000000000..a9630d5bf2
--- /dev/null
+++ b/src/AppInstallerCLITests/TestData/ManifestV1_28-PowerShellDSC.yaml
@@ -0,0 +1,226 @@
+# yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.28.0.schema.json
+
+PackageIdentifier: microsoft.msixsdk
+PackageVersion: 1.7.32
+PackageLocale: en-US
+Publisher: Microsoft
+PublisherUrl: https://www.microsoft.com
+PublisherSupportUrl: https://www.microsoft.com/support
+PrivacyUrl: https://www.microsoft.com/privacy
+Author: Microsoft
+PackageName: MSIX SDK
+PackageUrl: https://www.microsoft.com/msixsdk/home
+License: MIT License
+LicenseUrl: https://www.microsoft.com/msixsdk/license
+Copyright: Copyright Microsoft Corporation
+CopyrightUrl: https://www.microsoft.com/msixsdk/copyright
+ShortDescription: This is MSIX SDK
+Description: The MSIX SDK project is an effort to enable developers
+Moniker: msixsdk
+Tags:
+ - "appxsdk"
+ - "msixsdk"
+ReleaseNotes: Default release notes
+ReleaseNotesUrl: https://DefaultReleaseNotes.net
+PurchaseUrl: https://DefaultPurchaseUrl.com
+InstallationNotes: Default installation notes
+Documentations:
+ - DocumentLabel: Default document label
+ DocumentUrl: https://DefaultDocumentUrl.com
+Icons:
+ - IconUrl: https://testIcon-en-US
+ IconFileType: ico
+ IconResolution: custom
+ IconTheme: default
+ IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123
+Agreements:
+ - AgreementLabel: DefaultLabel
+ Agreement: DefaultText
+ AgreementUrl: https://DefaultAgreementUrl.net
+InstallerLocale: en-US
+Platform:
+ - Windows.Desktop
+ - Windows.Universal
+MinimumOSVersion: 10.0.0.0
+InstallerType: exe
+Scope: machine
+InstallModes:
+ - interactive
+ - silent
+ - silentWithProgress
+InstallerSwitches:
+ Custom: /custom
+ SilentWithProgress: /silentwithprogress
+ Silent: /silence
+ Interactive: /interactive
+ Log: /log=
+ InstallLocation: /dir=
+ Upgrade: /upgrade
+ Repair: /repair
+InstallerSuccessCodes:
+ - 1
+ - 0x80070005
+UpgradeBehavior: uninstallPrevious
+RepairBehavior: modify
+Commands:
+ - makemsix
+ - makeappx
+Protocols:
+ - protocol1
+ - protocol2
+FileExtensions:
+ - appx
+ - msix
+ - appxbundle
+ - msixbundle
+Dependencies:
+ WindowsFeatures:
+ - IIS
+ WindowsLibraries:
+ - VC Runtime
+ PackageDependencies:
+ - PackageIdentifier: Microsoft.MsixSdkDep
+ MinimumVersion: 1.0.0
+ ExternalDependencies:
+ - Outside dependencies
+Capabilities:
+ - internetClient
+RestrictedCapabilities:
+ - runFullTrust
+PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe
+ProductCode: "{Foo}"
+ReleaseDate: 2021-01-01
+InstallerAbortsTerminal: true
+InstallLocationRequired: true
+RequireExplicitUpgrade: true
+DisplayInstallWarnings: true
+ElevationRequirement: elevatesSelf
+UnsupportedOSArchitectures:
+ - arm
+AppsAndFeaturesEntries:
+ - DisplayName: DisplayName
+ DisplayVersion: DisplayVersion
+ Publisher: Publisher
+ ProductCode: ProductCode
+ UpgradeCode: UpgradeCode
+ InstallerType: exe
+Markets:
+ AllowedMarkets:
+ - US
+ExpectedReturnCodes:
+ - InstallerReturnCode: 10
+ ReturnResponse: packageInUse
+ ReturnResponseUrl: https://DefaultReturnResponseUrl.com
+UnsupportedArguments:
+ - log
+NestedInstallerType: msi
+NestedInstallerFiles:
+ - RelativeFilePath: RelativeFilePath
+ PortableCommandAlias: PortableCommandAlias
+InstallationMetadata:
+ DefaultInstallLocation: "%ProgramFiles%\\TestApp"
+ Files:
+ - RelativeFilePath: "main.exe"
+ FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ FileType: launch
+ InvocationParameter: "/arg"
+ DisplayName: "DisplayName"
+DownloadCommandProhibited: true
+ArchiveBinariesDependOnPath: true
+DesiredStateConfiguration:
+ PowerShell:
+ - RepositoryUrl: https://www.powershellgallery.com/api/v2
+ ModuleName: Microsoft.WinGet.DSC
+ Resources:
+ - Name: WinGetUserSettings
+ - Name: WinGetAdminSettings
+ - Name: WinGetSource
+ - Name: WinGetPackageManager
+ - Name: WinGetPackage
+ - RepositoryUrl: https://mcr.microsoft.com/
+ ModuleName: Microsoft.WinGet.DSC
+ Resources:
+ - Name: WinGetUserSettings
+ - Name: WinGetAdminSettings
+ - Name: WinGetSource
+ - Name: WinGetPackageManager
+ - Name: WinGetPackage
+ DSCv3:
+ Resources:
+ - Type: Microsoft.WinGet/AdminSettings
+ - Type: Microsoft.WinGet/Package
+ - Type: Microsoft.WinGet/Source
+ - Type: Microsoft.WinGet/UserSettingsFile
+
+Installers:
+ - Architecture: x86
+ InstallerLocale: en-GB
+ Platform:
+ - Windows.Desktop
+ MinimumOSVersion: 10.0.1.0
+ InstallerType: msix
+ InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix
+ InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ Scope: user
+ InstallModes:
+ - interactive
+ InstallerSwitches:
+ Custom: /c
+ SilentWithProgress: /sp
+ Silent: /s
+ Interactive: /i
+ Log: /l=
+ InstallLocation: /d=
+ Upgrade: /u
+ Repair: /r
+ UpgradeBehavior: install
+ Commands:
+ - makemsixPreview
+ - makeappxPreview
+ Protocols:
+ - protocol1preview
+ - protocol2preview
+ FileExtensions:
+ - appxbundle
+ - msixbundle
+ - appx
+ - msix
+ Dependencies:
+ WindowsFeatures:
+ - PreviewIIS
+ WindowsLibraries:
+ - Preview VC Runtime
+ PackageDependencies:
+ - PackageIdentifier: Microsoft.MsixSdkDepPreview
+ MinimumVersion: 1.0.0
+ ExternalDependencies:
+ - Preview Outside dependencies
+ PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe
+ Capabilities:
+ - internetClientPreview
+ RestrictedCapabilities:
+ - runFullTrustPreview
+ ReleaseDate: 2021-02-02
+ InstallerAbortsTerminal: false
+ InstallLocationRequired: false
+ RequireExplicitUpgrade: false
+ DisplayInstallWarnings: false
+ ElevationRequirement: elevationRequired
+ UnsupportedArguments:
+ - location
+ UnsupportedOSArchitectures:
+ - arm64
+ Markets:
+ ExcludedMarkets:
+ - "US"
+ ExpectedReturnCodes:
+ - InstallerReturnCode: 2
+ ReturnResponse: contactSupport
+ - InstallerReturnCode: 3
+ ReturnResponse: custom
+ ReturnResponseUrl: https://defaultReturnResponseUrl.com
+ DownloadCommandProhibited: false
+ ArchiveBinariesDependOnPath: false
+ManifestType: singleton
+ManifestVersion: 1.28.0
diff --git a/src/AppInstallerCLITests/TestData/ManifestV1_28-Singleton.yaml b/src/AppInstallerCLITests/TestData/ManifestV1_28-Singleton.yaml
new file mode 100644
index 0000000000..1de1249422
--- /dev/null
+++ b/src/AppInstallerCLITests/TestData/ManifestV1_28-Singleton.yaml
@@ -0,0 +1,209 @@
+# yaml-language-server: $schema=https://aka.ms/winget-manifest.singleton.1.28.0.schema.json
+
+PackageIdentifier: microsoft.msixsdk
+PackageVersion: 1.7.32
+PackageLocale: en-US
+Publisher: Microsoft
+PublisherUrl: https://www.microsoft.com
+PublisherSupportUrl: https://www.microsoft.com/support
+PrivacyUrl: https://www.microsoft.com/privacy
+Author: Microsoft
+PackageName: MSIX SDK
+PackageUrl: https://www.microsoft.com/msixsdk/home
+License: MIT License
+LicenseUrl: https://www.microsoft.com/msixsdk/license
+Copyright: Copyright Microsoft Corporation
+CopyrightUrl: https://www.microsoft.com/msixsdk/copyright
+ShortDescription: This is MSIX SDK
+Description: The MSIX SDK project is an effort to enable developers
+Moniker: msixsdk
+Tags:
+ - "appxsdk"
+ - "msixsdk"
+ReleaseNotes: Default release notes
+ReleaseNotesUrl: https://DefaultReleaseNotes.net
+PurchaseUrl: https://DefaultPurchaseUrl.com
+InstallationNotes: Default installation notes
+Documentations:
+ - DocumentLabel: Default document label
+ DocumentUrl: https://DefaultDocumentUrl.com
+Icons:
+ - IconUrl: https://testIcon-en-US
+ IconFileType: ico
+ IconResolution: custom
+ IconTheme: default
+ IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123
+Agreements:
+ - AgreementLabel: DefaultLabel
+ Agreement: DefaultText
+ AgreementUrl: https://DefaultAgreementUrl.net
+InstallerLocale: en-US
+Platform:
+ - Windows.Desktop
+ - Windows.Universal
+MinimumOSVersion: 10.0.0.0
+InstallerType: exe
+Scope: machine
+InstallModes:
+ - interactive
+ - silent
+ - silentWithProgress
+InstallerSwitches:
+ Custom: /custom
+ SilentWithProgress: /silentwithprogress
+ Silent: /silence
+ Interactive: /interactive
+ Log: /log=
+ InstallLocation: /dir=
+ Upgrade: /upgrade
+ Repair: /repair
+InstallerSuccessCodes:
+ - 1
+ - 0x80070005
+UpgradeBehavior: uninstallPrevious
+RepairBehavior: modify
+Commands:
+ - makemsix
+ - makeappx
+Protocols:
+ - protocol1
+ - protocol2
+FileExtensions:
+ - appx
+ - msix
+ - appxbundle
+ - msixbundle
+Dependencies:
+ WindowsFeatures:
+ - IIS
+ WindowsLibraries:
+ - VC Runtime
+ PackageDependencies:
+ - PackageIdentifier: Microsoft.MsixSdkDep
+ MinimumVersion: 1.0.0
+ ExternalDependencies:
+ - Outside dependencies
+Capabilities:
+ - internetClient
+RestrictedCapabilities:
+ - runFullTrust
+PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe
+ProductCode: "{Foo}"
+ReleaseDate: 2021-01-01
+InstallerAbortsTerminal: true
+InstallLocationRequired: true
+RequireExplicitUpgrade: true
+DisplayInstallWarnings: true
+ElevationRequirement: elevatesSelf
+UnsupportedOSArchitectures:
+ - arm
+AppsAndFeaturesEntries:
+ - DisplayName: DisplayName
+ DisplayVersion: DisplayVersion
+ Publisher: Publisher
+ ProductCode: ProductCode
+ UpgradeCode: UpgradeCode
+ InstallerType: exe
+Markets:
+ AllowedMarkets:
+ - US
+ExpectedReturnCodes:
+ - InstallerReturnCode: 10
+ ReturnResponse: packageInUse
+ ReturnResponseUrl: https://DefaultReturnResponseUrl.com
+UnsupportedArguments:
+ - log
+NestedInstallerType: msi
+NestedInstallerFiles:
+ - RelativeFilePath: RelativeFilePath
+ PortableCommandAlias: PortableCommandAlias
+InstallationMetadata:
+ DefaultInstallLocation: "%ProgramFiles%\\TestApp"
+ Files:
+ - RelativeFilePath: "main.exe"
+ FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ FileType: launch
+ InvocationParameter: "/arg"
+ DisplayName: "DisplayName"
+DownloadCommandProhibited: true
+ArchiveBinariesDependOnPath: true
+DesiredStateConfiguration:
+ DSCv3:
+ Resources:
+ - Type: Microsoft.WinGet/AdminSettings
+ - Type: Microsoft.WinGet/Package
+ - Type: Microsoft.WinGet/Source
+ - Type: Microsoft.WinGet/UserSettingsFile
+
+Installers:
+ - Architecture: x86
+ InstallerLocale: en-GB
+ Platform:
+ - Windows.Desktop
+ MinimumOSVersion: 10.0.1.0
+ InstallerType: msix
+ InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix
+ InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ Scope: user
+ InstallModes:
+ - interactive
+ InstallerSwitches:
+ Custom: /c
+ SilentWithProgress: /sp
+ Silent: /s
+ Interactive: /i
+ Log: /l=
+ InstallLocation: /d=
+ Upgrade: /u
+ Repair: /r
+ UpgradeBehavior: install
+ Commands:
+ - makemsixPreview
+ - makeappxPreview
+ Protocols:
+ - protocol1preview
+ - protocol2preview
+ FileExtensions:
+ - appxbundle
+ - msixbundle
+ - appx
+ - msix
+ Dependencies:
+ WindowsFeatures:
+ - PreviewIIS
+ WindowsLibraries:
+ - Preview VC Runtime
+ PackageDependencies:
+ - PackageIdentifier: Microsoft.MsixSdkDepPreview
+ MinimumVersion: 1.0.0
+ ExternalDependencies:
+ - Preview Outside dependencies
+ PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe
+ Capabilities:
+ - internetClientPreview
+ RestrictedCapabilities:
+ - runFullTrustPreview
+ ReleaseDate: 2021-02-02
+ InstallerAbortsTerminal: false
+ InstallLocationRequired: false
+ RequireExplicitUpgrade: false
+ DisplayInstallWarnings: false
+ ElevationRequirement: elevationRequired
+ UnsupportedArguments:
+ - location
+ UnsupportedOSArchitectures:
+ - arm64
+ Markets:
+ ExcludedMarkets:
+ - "US"
+ ExpectedReturnCodes:
+ - InstallerReturnCode: 2
+ ReturnResponse: contactSupport
+ - InstallerReturnCode: 3
+ ReturnResponse: custom
+ ReturnResponseUrl: https://defaultReturnResponseUrl.com
+ DownloadCommandProhibited: false
+ ArchiveBinariesDependOnPath: false
+ManifestType: singleton
+ManifestVersion: 1.28.0
diff --git a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-DefaultLocale.yaml b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-DefaultLocale.yaml
new file mode 100644
index 0000000000..2741bf0d26
--- /dev/null
+++ b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-DefaultLocale.yaml
@@ -0,0 +1,41 @@
+# yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.28.0.schema.json
+
+PackageIdentifier: microsoft.msixsdk
+PackageVersion: 1.7.32
+PackageLocale: en-US
+Publisher: Microsoft
+PublisherUrl: https://www.microsoft.com
+PublisherSupportUrl: https://www.microsoft.com/support
+PrivacyUrl: https://www.microsoft.com/privacy
+Author: Microsoft
+PackageName: MSIX SDK
+PackageUrl: https://www.microsoft.com/msixsdk/home
+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
+Tags:
+ - "appxsdk"
+ - "msixsdk"
+ReleaseNotes: Default release notes
+ReleaseNotesUrl: https://DefaultReleaseNotes.net
+Documentations:
+ - DocumentLabel: Default document label
+ DocumentUrl: https://DefaultDocumentUrl.com
+Icons:
+ - IconUrl: https://testIcon-en-US
+ IconFileType: ico
+ IconResolution: custom
+ IconTheme: default
+ IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123
+Agreements:
+ - AgreementLabel: DefaultLabel
+ Agreement: DefaultText
+ AgreementUrl: https://DefaultAgreementUrl.net
+ManifestType: defaultLocale
+ManifestVersion: 1.28.0
diff --git a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-Installer.yaml b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-Installer.yaml
new file mode 100644
index 0000000000..353d04cc90
--- /dev/null
+++ b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-Installer.yaml
@@ -0,0 +1,241 @@
+# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.28.0.schema.json
+
+PackageIdentifier: microsoft.msixsdk
+PackageVersion: 1.7.32
+InstallerLocale: en-US
+Platform:
+ - Windows.Desktop
+ - Windows.Universal
+MinimumOSVersion: 10.0.0.0
+InstallerType: exe
+Scope: machine
+InstallModes:
+ - interactive
+ - silent
+ - silentWithProgress
+InstallerSwitches:
+ Custom: /custom
+ SilentWithProgress: /silentwithprogress
+ Silent: /silence
+ Interactive: /interactive
+ Log: /log=
+ InstallLocation: /dir=
+ Upgrade: /upgrade
+ Repair: /repair
+InstallerSuccessCodes:
+ - 1
+ - 0x80070005
+UpgradeBehavior: uninstallPrevious
+RepairBehavior: modify
+Commands:
+ - makemsix
+ - makeappx
+Protocols:
+ - protocol1
+ - protocol2
+FileExtensions:
+ - appx
+ - msix
+ - appxbundle
+ - msixbundle
+Dependencies:
+ WindowsFeatures:
+ - IIS
+ WindowsLibraries:
+ - VC Runtime
+ PackageDependencies:
+ - PackageIdentifier: Microsoft.MsixSdkDep
+ MinimumVersion: 1.0.0
+ ExternalDependencies:
+ - Outside dependencies
+Capabilities:
+ - internetClient
+RestrictedCapabilities:
+ - runFullTrust
+PackageFamilyName: Microsoft.DesktopAppInstaller_8wekyb3d8bbwe
+ProductCode: "{Foo}"
+ReleaseDate: 2021-01-01
+InstallerAbortsTerminal: true
+InstallLocationRequired: true
+RequireExplicitUpgrade: true
+DisplayInstallWarnings: true
+ElevationRequirement: elevatesSelf
+UnsupportedOSArchitectures:
+ - arm
+AppsAndFeaturesEntries:
+ - DisplayName: DisplayName
+ DisplayVersion: DisplayVersion
+ Publisher: Publisher
+ ProductCode: ProductCode
+ UpgradeCode: UpgradeCode
+ InstallerType: exe
+Markets:
+ AllowedMarkets:
+ - "US"
+ExpectedReturnCodes:
+ - InstallerReturnCode: 10
+ ReturnResponse: packageInUse
+ ReturnResponseUrl: https://DefaultReturnResponseUrl.com
+UnsupportedArguments:
+ - log
+NestedInstallerType: msi
+NestedInstallerFiles:
+ - RelativeFilePath: RelativeFilePath
+ PortableCommandAlias: PortableCommandAlias
+InstallationMetadata:
+ DefaultInstallLocation: "%ProgramFiles%\\TestApp"
+ Files:
+ - RelativeFilePath: "main.exe"
+ FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ FileType: launch
+ InvocationParameter: "/arg"
+ DisplayName: "DisplayName"
+DownloadCommandProhibited: true
+ArchiveBinariesDependOnPath: true
+DesiredStateConfiguration:
+ DSCv3:
+ Resources:
+ - Type: Microsoft.WinGet/AdminSettings
+ - Type: Microsoft.WinGet/Package
+ - Type: Microsoft.WinGet/Source
+ - Type: Microsoft.WinGet/UserSettingsFile
+
+Installers:
+ - Architecture: x86
+ InstallerLocale: en-GB
+ Platform:
+ - Windows.Desktop
+ MinimumOSVersion: 10.0.1.0
+ InstallerType: msix
+ InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx86.msix
+ InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ SignatureSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ Scope: user
+ InstallModes:
+ - interactive
+ InstallerSwitches:
+ Custom: /c
+ SilentWithProgress: /sp
+ Silent: /s
+ Interactive: /i
+ Log: /l=
+ InstallLocation: /d=
+ Upgrade: /u
+ Repair: /r
+ UpgradeBehavior: install
+ Commands:
+ - makemsixPreview
+ - makeappxPreview
+ Protocols:
+ - protocol1preview
+ - protocol2preview
+ FileExtensions:
+ - appxbundle
+ - msixbundle
+ - appx
+ - msix
+ Dependencies:
+ WindowsFeatures:
+ - PreviewIIS
+ WindowsLibraries:
+ - Preview VC Runtime
+ PackageDependencies:
+ - PackageIdentifier: Microsoft.MsixSdkDepPreview
+ MinimumVersion: 1.0.0
+ ExternalDependencies:
+ - Preview Outside dependencies
+ PackageFamilyName: Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe
+ Capabilities:
+ - internetClientPreview
+ RestrictedCapabilities:
+ - runFullTrustPreview
+ ReleaseDate: 2021-02-02
+ InstallerAbortsTerminal: false
+ InstallLocationRequired: false
+ RequireExplicitUpgrade: false
+ DisplayInstallWarnings: false
+ ElevationRequirement: elevationRequired
+ UnsupportedOSArchitectures:
+ - arm64
+ Markets:
+ ExcludedMarkets:
+ - "US"
+ ExpectedReturnCodes:
+ - InstallerReturnCode: 2
+ ReturnResponse: contactSupport
+ - InstallerReturnCode: 3
+ ReturnResponse: custom
+ ReturnResponseUrl: https://defaultReturnResponseUrl.com
+ UnsupportedArguments:
+ - location
+ DownloadCommandProhibited: false
+ ArchiveBinariesDependOnPath: false
+ - Architecture: x64
+ InstallerType: exe
+ InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe
+ InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ ProductCode: "{Bar}"
+ InstallerSwitches:
+ Repair: /r
+ UpgradeBehavior: deny
+ RepairBehavior: uninstaller
+ DesiredStateConfiguration:
+ DSCv3:
+ Resources:
+ - Type: None/None
+ - Architecture: x86
+ 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
+ DesiredStateConfiguration:
+ DSCv3:
+ - Architecture: x64
+ InstallerType: zip
+ InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe
+ InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ NestedInstallerType: portable
+ NestedInstallerFiles:
+ - RelativeFilePath: relativeFilePath1
+ PortableCommandAlias: portableAlias1
+ - RelativeFilePath: relativeFilePath2
+ PortableCommandAlias: portableAlias2
+ InstallationMetadata:
+ DefaultInstallLocation: "%ProgramFiles%\\TestApp2"
+ Files:
+ - RelativeFilePath: "main2.exe"
+ FileSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ FileType: other
+ InvocationParameter: "/arg2"
+ DisplayName: "DisplayName2"
+ ArchiveBinariesDependOnPath: true
+ - Architecture: x64
+ InstallerType: burn
+ InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe
+ InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ ProductCode: "{Bar}"
+ UpgradeBehavior: deny
+ RepairBehavior: modify
+ - Architecture: neutral
+ InstallerType: zip
+ InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe
+ InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ NestedInstallerType: font
+ NestedInstallerFiles:
+ - RelativeFilePath: relativeFilePath1.otf
+ - RelativeFilePath: relativeFilePath2.ttf
+ - RelativeFilePath: relativeFilePath3.fnt
+ - RelativeFilePath: relativeFilePath4.ttc
+ - RelativeFilePath: relativeFilePath5.otc
+ - Architecture: neutral
+ InstallerType: font
+ InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe
+ InstallerSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82
+ManifestType: installer
+ManifestVersion: 1.28.0
diff --git a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-Locale.yaml b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-Locale.yaml
new file mode 100644
index 0000000000..1306ccd6b4
--- /dev/null
+++ b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-Locale.yaml
@@ -0,0 +1,40 @@
+# yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.28.0.schema.json
+
+PackageIdentifier: microsoft.msixsdk
+PackageVersion: 1.7.32
+PackageLocale: en-GB
+Publisher: Microsoft UK
+PublisherUrl: https://www.microsoft.com/UK
+PublisherSupportUrl: https://www.microsoft.com/support/UK
+PrivacyUrl: https://www.microsoft.com/privacy/UK
+Author: Microsoft UK
+PackageName: MSIX SDK UK
+PackageUrl: https://www.microsoft.com/msixsdk/home/UK
+License: MIT License UK
+LicenseUrl: https://www.microsoft.com/msixsdk/license/UK
+Copyright: Copyright Microsoft Corporation UK
+CopyrightUrl: https://www.microsoft.com/msixsdk/copyright/UK
+ShortDescription: This is MSIX SDK UK
+Description: The MSIX SDK project is an effort to enable developers UK
+Tags:
+ - "appxsdkUK"
+ - "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:
+ - DocumentLabel: Default document label
+ DocumentUrl: https://DefaultDocumentUrl.com
+Icons:
+ - IconUrl: https://localeTestIcon-en-GB
+ IconFileType: png
+ IconResolution: 32x32
+ IconTheme: light
+ IconSha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321
+ManifestType: locale
+ManifestVersion: 1.28.0
diff --git a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-Version.yaml b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-Version.yaml
new file mode 100644
index 0000000000..d7b510407b
--- /dev/null
+++ b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_28/ManifestV1_28-MultiFile-Version.yaml
@@ -0,0 +1,7 @@
+# yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.28.0.schema.json
+
+PackageIdentifier: microsoft.msixsdk
+PackageVersion: 1.7.32
+DefaultLocale: en-US
+ManifestType: version
+ManifestVersion: 1.28.0
diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp
index fac1eee06e..4c5021887e 100644
--- a/src/AppInstallerCLITests/YamlManifest.cpp
+++ b/src/AppInstallerCLITests/YamlManifest.cpp
@@ -3,6 +3,7 @@
#include "pch.h"
#include "TestCommon.h"
#include
+#include
#include
#include
#include
@@ -122,6 +123,29 @@ namespace
}
}
+ void RequireContainerInfoPresent(const std::vector& containers, const DesiredStateConfigurationContainerInfo& info)
+ {
+ INFO("Looking for container info: " << AppInstaller::ToIntegral(info.Type) << " - " << info.RepositoryURL << " - " << info.ModuleName);
+
+ for (const auto& container : containers)
+ {
+ if (container.Type == info.Type && container.RepositoryURL == info.RepositoryURL && container.ModuleName == info.ModuleName)
+ {
+ REQUIRE(container.Resources.size() == info.Resources.size());
+ for (const auto& resource : info.Resources)
+ {
+ INFO("Looking for resource: " << resource.Name);
+ bool foundResource = std::any_of(container.Resources.begin(), container.Resources.end(), [&](const auto& a) { return a.Name == resource.Name; });
+ REQUIRE(foundResource);
+ }
+
+ return;
+ }
+ }
+
+ FAIL("Did not find a matching container.");
+ }
+
void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, ManifestVer manifestVer = { s_ManifestVersionV1 }, bool isExported = false)
{
REQUIRE(manifest.Id == "microsoft.msixsdk");
@@ -181,7 +205,7 @@ namespace
REQUIRE(manifest.DefaultInstallerInfo.Scope == ScopeEnum::Machine);
REQUIRE(manifest.DefaultInstallerInfo.InstallModes == std::vector{ InstallModeEnum::Interactive, InstallModeEnum::Silent, InstallModeEnum::SilentWithProgress });
- auto defaultSwitches = manifest.DefaultInstallerInfo.Switches;
+ const auto& defaultSwitches = manifest.DefaultInstallerInfo.Switches;
REQUIRE(defaultSwitches.at(InstallerSwitchType::Custom) == "/custom");
REQUIRE(defaultSwitches.at(InstallerSwitchType::SilentWithProgress) == "/silentwithprogress");
REQUIRE(defaultSwitches.at(InstallerSwitchType::Silent) == "/silence");
@@ -196,7 +220,7 @@ namespace
REQUIRE(manifest.DefaultInstallerInfo.Protocols == MultiValue{ "protocol1", "protocol2" });
REQUIRE(manifest.DefaultInstallerInfo.FileExtensions == MultiValue{ "appx", "msix", "appxbundle", "msixbundle" });
- auto dependencies = manifest.DefaultInstallerInfo.Dependencies;
+ const auto& dependencies = manifest.DefaultInstallerInfo.Dependencies;
REQUIRE(dependencies.HasExactDependency(DependencyType::WindowsFeature, "IIS"));
REQUIRE(dependencies.HasExactDependency(DependencyType::WindowsLibrary, "VC Runtime"));
REQUIRE(dependencies.HasExactDependency(DependencyType::Package, "Microsoft.MsixSdkDep", "1.0.0"));
@@ -296,7 +320,7 @@ namespace
}
}
- ManifestInstaller installer1 = manifest.Installers.at(0);
+ const ManifestInstaller& installer1 = manifest.Installers.at(0);
REQUIRE(installer1.Arch == Architecture::X86);
REQUIRE(installer1.Locale == "en-GB");
REQUIRE(installer1.Platform == std::vector{ PlatformEnum::Desktop });
@@ -308,7 +332,7 @@ namespace
REQUIRE(installer1.Scope == ScopeEnum::User);
REQUIRE(installer1.InstallModes == std::vector{ InstallModeEnum::Interactive });
- auto installer1Switches = installer1.Switches;
+ const auto& installer1Switches = installer1.Switches;
REQUIRE(installer1Switches.at(InstallerSwitchType::Custom) == "/c");
REQUIRE(installer1Switches.at(InstallerSwitchType::SilentWithProgress) == "/sp");
REQUIRE(installer1Switches.at(InstallerSwitchType::Silent) == "/s");
@@ -322,7 +346,7 @@ namespace
REQUIRE(installer1.Protocols == MultiValue{ "protocol1preview", "protocol2preview" });
REQUIRE(installer1.FileExtensions == MultiValue{ "appxbundle", "msixbundle", "appx", "msix" });
- auto installer1Dependencies = installer1.Dependencies;
+ const auto& installer1Dependencies = installer1.Dependencies;
REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::WindowsFeature, "PreviewIIS"));
REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "Preview VC Runtime"));
REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::Package, "Microsoft.MsixSdkDepPreview", "1.0.0"));
@@ -389,11 +413,22 @@ namespace
REQUIRE_FALSE(installer1.ArchiveBinariesDependOnPath);
}
+ if (manifestVer >= ManifestVer{ s_ManifestVersionV1_28 })
+ {
+ auto containers = &manifest.DefaultInstallerInfo.DesiredStateConfiguration;
+ if (isExported)
+ {
+ containers = &manifest.Installers[0].DesiredStateConfiguration;
+ }
+
+ RequireContainerInfoPresent(*containers, { { { "Microsoft.WinGet/AdminSettings" }, { "Microsoft.WinGet/Package" }, { "Microsoft.WinGet/Source" }, { "Microsoft.WinGet/UserSettingsFile" } } });
+ }
+
if (!isSingleton)
{
if (!isExported)
{
- ManifestInstaller installer2 = manifest.Installers.at(1);
+ const ManifestInstaller& installer2 = manifest.Installers.at(1);
REQUIRE(installer2.BaseInstallerType == InstallerTypeEnum::Exe);
REQUIRE(installer2.Arch == Architecture::X64);
REQUIRE(installer2.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe");
@@ -424,7 +459,7 @@ namespace
if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 })
{
- ManifestInstaller installer3 = manifest.Installers.at(2);
+ const ManifestInstaller& installer3 = manifest.Installers.at(2);
REQUIRE(installer3.BaseInstallerType == InstallerTypeEnum::Portable);
REQUIRE(installer3.Arch == Architecture::X86);
REQUIRE(installer3.Url == "https://www.microsoft.com/msixsdk/msixsdkx86.exe");
@@ -440,7 +475,7 @@ namespace
if (manifestVer >= ManifestVer{ s_ManifestVersionV1_4 })
{
- ManifestInstaller installer4 = manifest.Installers.at(3);
+ const ManifestInstaller& installer4 = manifest.Installers.at(3);
REQUIRE(installer4.BaseInstallerType == InstallerTypeEnum::Zip);
REQUIRE(installer4.Arch == Architecture::X64);
REQUIRE(installer4.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe");
@@ -472,7 +507,7 @@ namespace
REQUIRE(installer2.RepairBehavior == RepairBehaviorEnum::Uninstaller);
REQUIRE(installer2.Switches.at(InstallerSwitchType::Repair) == "/r");
- ManifestInstaller installer5 = manifest.Installers.at(4);
+ const ManifestInstaller& installer5 = manifest.Installers.at(4);
REQUIRE(installer5.BaseInstallerType == InstallerTypeEnum::Burn);
REQUIRE(installer5.Arch == Architecture::X64);
REQUIRE(installer5.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe");
@@ -484,13 +519,13 @@ namespace
if (manifestVer >= ManifestVer{ s_ManifestVersionV1_9 })
{
- ManifestInstaller installer4 = manifest.Installers.at(3);
+ const ManifestInstaller& installer4 = manifest.Installers.at(3);
REQUIRE(installer4.ArchiveBinariesDependOnPath);
}
if (manifestVer >= ManifestVer{ s_ManifestVersionV1_12 })
{
- ManifestInstaller installer6 = manifest.Installers.at(5);
+ const ManifestInstaller& installer6 = manifest.Installers.at(5);
REQUIRE(installer6.BaseInstallerType == InstallerTypeEnum::Zip);
REQUIRE(installer6.Arch == Architecture::Neutral);
REQUIRE(installer6.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe");
@@ -503,17 +538,23 @@ namespace
REQUIRE(installer6.NestedInstallerFiles.at(3).RelativeFilePath == "relativeFilePath4.ttc");
REQUIRE(installer6.NestedInstallerFiles.at(4).RelativeFilePath == "relativeFilePath5.otc");
- ManifestInstaller installer7 = manifest.Installers.at(6);
+ const ManifestInstaller& installer7 = manifest.Installers.at(6);
REQUIRE(installer7.BaseInstallerType == InstallerTypeEnum::Font);
REQUIRE(installer7.Arch == Architecture::Neutral);
REQUIRE(installer7.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe");
REQUIRE(installer7.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82"));
}
+
+ if (manifestVer >= ManifestVer{ s_ManifestVersionV1_28 })
+ {
+ RequireContainerInfoPresent(manifest.Installers[1].DesiredStateConfiguration, { { { "None/None" } } });
+ REQUIRE(manifest.Installers[2].DesiredStateConfiguration.size() == 0);
+ }
}
// Localization
REQUIRE(manifest.Localizations.size() == 1);
- ManifestLocalization localization1 = manifest.Localizations.at(0);
+ const ManifestLocalization& localization1 = manifest.Localizations.at(0);
REQUIRE(localization1.Locale == "en-GB");
REQUIRE(localization1.Get() == "Microsoft UK");
REQUIRE(localization1.Get() == "https://www.microsoft.com/UK");
@@ -944,260 +985,56 @@ TEST_CASE("ManifestVersionExtensions", "[ManifestValidation]")
REQUIRE(ManifestVer("1.0.0-msstore.2-other"sv).HasExtension("msstore"));
}
-TEST_CASE("ValidateV1GoodManifestAndVerifyContents", "[ManifestValidation]")
-{
- ManifestValidateOption validateOption;
- validateOption.FullValidation = true;
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption);
- VerifyV1ManifestContent(singletonManifest, true);
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1-MultiFile-Version.yaml",
- "ManifestV1-MultiFile-Installer.yaml",
- "ManifestV1-MultiFile-DefaultLocale.yaml",
- "ManifestV1-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- TempFile mergedManifestFile{ "merged.yaml" };
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile);
- VerifyV1ManifestContent(multiFileManifest, false);
-
- // Read from merged manifest should have the same content as multi file manifest
- Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile);
- VerifyV1ManifestContent(mergedManifest, false);
-}
-
-TEST_CASE("ValidateV1_1GoodManifestAndVerifyContents", "[ManifestValidation]")
-{
- ManifestValidateOption validateOption;
- validateOption.FullValidation = true;
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_1-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption);
- VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_1 });
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_1-MultiFile-Version.yaml",
- "ManifestV1_1-MultiFile-Installer.yaml",
- "ManifestV1_1-MultiFile-DefaultLocale.yaml",
- "ManifestV1_1-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- TempFile mergedManifestFile{ "merged.yaml" };
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile);
- VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_1 });
-
- // Read from merged manifest should have the same content as multi file manifest
- Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile);
- VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_1 });
-}
-
-TEST_CASE("ValidateV1_2GoodManifestAndVerifyContents", "[ManifestValidation]")
-{
- ManifestValidateOption validateOption;
- validateOption.FullValidation = true;
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_2-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption);
- VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_2 });
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_2-MultiFile-Version.yaml",
- "ManifestV1_2-MultiFile-Installer.yaml",
- "ManifestV1_2-MultiFile-DefaultLocale.yaml",
- "ManifestV1_2-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- TempFile mergedManifestFile{ "merged.yaml" };
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile);
- VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_2 });
-
- // Read from merged manifest should have the same content as multi file manifest
- Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile);
- VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_2 });
-}
-
-TEST_CASE("ValidateV1_4GoodManifestAndVerifyContents", "[ManifestValidation]")
+void ValidateGoodManifestAndVerifyContents(const std::vector& singleton, const std::vector& multiFiles, std::string_view version)
{
ManifestValidateOption validateOption;
validateOption.FullValidation = true;
TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_4-Singleton.yaml" }, singletonDirectory);
+ CopyTestDataFilesToFolder(singleton, singletonDirectory);
Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption);
- VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_4 });
+ VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ version });
TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_4-MultiFile-Version.yaml",
- "ManifestV1_4-MultiFile-Installer.yaml",
- "ManifestV1_4-MultiFile-DefaultLocale.yaml",
- "ManifestV1_4-MultiFile-Locale.yaml" }, multiFileDirectory);
+ CopyTestDataFilesToFolder(multiFiles, multiFileDirectory);
TempFile mergedManifestFile{ "merged.yaml" };
Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile);
- VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_4 });
+ VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ version });
// Read from merged manifest should have the same content as multi file manifest
Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile);
- VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_4 });
+ VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ version });
}
-TEST_CASE("ValidateV1_5GoodManifestAndVerifyContents", "[ManifestValidation]")
-{
- ManifestValidateOption validateOption;
- validateOption.FullValidation = true;
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_5-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption);
- VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_5 });
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_5-MultiFile-Version.yaml",
- "ManifestV1_5-MultiFile-Installer.yaml",
- "ManifestV1_5-MultiFile-DefaultLocale.yaml",
- "ManifestV1_5-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- TempFile mergedManifestFile{ "merged.yaml" };
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile);
- VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_5 });
-
- // Read from merged manifest should have the same content as multi file manifest
- Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile);
- VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_5 });
+#define WINGET_VALIDATE_GOOD_MANIFEST_VERSION(_version_) TEST_CASE("ValidateGoodManifestAndVerifyContents_" #_version_ , "[ManifestValidation][ManifestVersionValidation]") \
+{ \
+ ValidateGoodManifestAndVerifyContents( \
+ { "ManifestV" #_version_ "-Singleton.yaml" }, \
+ { \
+ "ManifestV" #_version_ "-MultiFile-Version.yaml", \
+ "ManifestV" #_version_ "-MultiFile-Installer.yaml", \
+ "ManifestV" #_version_ "-MultiFile-DefaultLocale.yaml", \
+ "ManifestV" #_version_ "-MultiFile-Locale.yaml" \
+ }, \
+ s_ManifestVersionV ## _version_); \
}
-TEST_CASE("ValidateV1_6GoodManifestAndVerifyContents", "[ManifestValidation]")
+WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1)
+WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_1)
+WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_2)
+WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_4)
+WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_5)
+WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_6)
+WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_7)
+WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_9)
+WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_10)
+WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_12)
+WINGET_VALIDATE_GOOD_MANIFEST_VERSION(1_28)
+
+void WriteSingletonManifestAndVerifyContents(const std::vector& singleton, const std::vector& multiFiles, std::string_view version)
{
- ManifestValidateOption validateOption;
- validateOption.FullValidation = true;
TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_6-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption);
- VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_6 });
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_6-MultiFile-Version.yaml",
- "ManifestV1_6-MultiFile-Installer.yaml",
- "ManifestV1_6-MultiFile-DefaultLocale.yaml",
- "ManifestV1_6-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- TempFile mergedManifestFile{ "merged.yaml" };
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile);
- VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_6 });
-
- // Read from merged manifest should have the same content as multi file manifest
- Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile);
- VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_6 });
-}
-
-TEST_CASE("ValidateV1_7GoodManifestAndVerifyContents", "[ManifestValidation]")
-{
- ManifestValidateOption validateOption;
- validateOption.FullValidation = true;
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_7-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption);
- VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_7 });
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_7-MultiFile-Version.yaml",
- "ManifestV1_7-MultiFile-Installer.yaml",
- "ManifestV1_7-MultiFile-DefaultLocale.yaml",
- "ManifestV1_7-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- TempFile mergedManifestFile{ "merged.yaml" };
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile);
- VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_7 });
-
- // Read from merged manifest should have the same content as multi file manifest
- Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile);
- VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_7 });
-}
-
-TEST_CASE("ValidateV1_9GoodManifestAndVerifyContents", "[ManifestValidation]")
-{
- ManifestValidateOption validateOption;
- validateOption.FullValidation = true;
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_9-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption);
- VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_9 });
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_9-MultiFile-Version.yaml",
- "ManifestV1_9-MultiFile-Installer.yaml",
- "ManifestV1_9-MultiFile-DefaultLocale.yaml",
- "ManifestV1_9-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- TempFile mergedManifestFile{ "merged.yaml" };
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile);
- VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_9 });
-
- // Read from merged manifest should have the same content as multi file manifest
- Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile);
- VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_9 });
-}
-
-TEST_CASE("ValidateV1_10GoodManifestAndVerifyContents", "[ManifestValidation]")
-{
- ManifestValidateOption validateOption;
- validateOption.FullValidation = true;
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_10-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption);
- VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_10 });
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_10-MultiFile-Version.yaml",
- "ManifestV1_10-MultiFile-Installer.yaml",
- "ManifestV1_10-MultiFile-DefaultLocale.yaml",
- "ManifestV1_10-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- TempFile mergedManifestFile{ "merged.yaml" };
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile);
- VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_10 });
-
- // Read from merged manifest should have the same content as multi file manifest
- Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile);
- VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_10 });
-}
-
-TEST_CASE("ValidateV1_12GoodManifestAndVerifyContents", "[ManifestValidation]")
-{
- ManifestValidateOption validateOption;
- validateOption.FullValidation = true;
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_12-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption);
- VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_12 });
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_12-MultiFile-Version.yaml",
- "ManifestV1_12-MultiFile-Installer.yaml",
- "ManifestV1_12-MultiFile-DefaultLocale.yaml",
- "ManifestV1_12-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- TempFile mergedManifestFile{ "merged.yaml" };
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile);
- VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_12 });
-
- // Read from merged manifest should have the same content as multi file manifest
- Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile);
- VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_12 });
-}
-
-TEST_CASE("WriteV1SingletonManifestAndVerifyContents", "[ManifestCreation]")
-{
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_1-Singleton.yaml" }, singletonDirectory);
+ CopyTestDataFilesToFolder(singleton, singletonDirectory);
Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory);
TempDirectory exportedSingletonDirectory{ "exportedSingleton" };
@@ -1206,14 +1043,10 @@ TEST_CASE("WriteV1SingletonManifestAndVerifyContents", "[ManifestCreation]")
REQUIRE(std::filesystem::exists(generatedSingletonManifestPath));
Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory);
- VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1 }, true);
+ VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ version }, true);
TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1-MultiFile-Version.yaml",
- "ManifestV1-MultiFile-Installer.yaml",
- "ManifestV1-MultiFile-DefaultLocale.yaml",
- "ManifestV1-MultiFile-Locale.yaml" }, multiFileDirectory);
+ CopyTestDataFilesToFolder(multiFiles, multiFileDirectory);
Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory);
TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" };
@@ -1222,266 +1055,49 @@ TEST_CASE("WriteV1SingletonManifestAndVerifyContents", "[ManifestCreation]")
REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath));
Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory);
- VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1 }, true);
+ VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ version }, true);
}
-TEST_CASE("WriteV1_1SingletonManifestAndVerifyContents", "[ManifestCreation]")
-{
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_1-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory);
-
- TempDirectory exportedSingletonDirectory{ "exportedSingleton" };
- std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml";
- YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedSingletonManifestPath));
- Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory);
- VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_1 }, true);
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_1-MultiFile-Version.yaml",
- "ManifestV1_1-MultiFile-Installer.yaml",
- "ManifestV1_1-MultiFile-DefaultLocale.yaml",
- "ManifestV1_1-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory);
- TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" };
- std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml";
- YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath));
- Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory);
- VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_1 }, true);
+#define WINGET_WRITE_VERIFY_MANIFEST_VERSION(_version_) TEST_CASE("WriteSingletonManifestAndVerifyContents_" #_version_ , "[ManifestCreation][ManifestVersionCreation]") \
+{ \
+ WriteSingletonManifestAndVerifyContents( \
+ { "ManifestV" #_version_ "-Singleton.yaml" }, \
+ { \
+ "ManifestV" #_version_ "-MultiFile-Version.yaml", \
+ "ManifestV" #_version_ "-MultiFile-Installer.yaml", \
+ "ManifestV" #_version_ "-MultiFile-DefaultLocale.yaml", \
+ "ManifestV" #_version_ "-MultiFile-Locale.yaml" \
+ }, \
+ s_ManifestVersionV ## _version_); \
}
-TEST_CASE("WriteV1_2SingletonManifestAndVerifyContents", "[ManifestCreation]")
-{
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_2-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory);
-
- TempDirectory exportedSingletonDirectory{ "exportedSingleton" };
- std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml";
- YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedSingletonManifestPath));
- Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory);
- VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_2 }, true);
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_2-MultiFile-Version.yaml",
- "ManifestV1_2-MultiFile-Installer.yaml",
- "ManifestV1_2-MultiFile-DefaultLocale.yaml",
- "ManifestV1_2-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory);
- TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" };
- std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml";
- YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath));
- Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory);
- VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_2 }, true);
-}
-
-TEST_CASE("WriteV1_4SingletonManifestAndVerifyContents", "[ManifestCreation]")
-{
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_4-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory);
-
- TempDirectory exportedSingletonDirectory{ "exportedSingleton" };
- std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml";
- YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedSingletonManifestPath));
- Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory);
- VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_4 }, true);
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_4-MultiFile-Version.yaml",
- "ManifestV1_4-MultiFile-Installer.yaml",
- "ManifestV1_4-MultiFile-DefaultLocale.yaml",
- "ManifestV1_4-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory);
- TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" };
- std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml";
- YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath));
- Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory);
- VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_4 }, true);
-}
-
-TEST_CASE("WriteV1_5SingletonManifestAndVerifyContents", "[ManifestCreation]")
-{
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_5-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory);
-
- TempDirectory exportedSingletonDirectory{ "exportedSingleton" };
- std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml";
- YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedSingletonManifestPath));
- Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory);
- VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_5 }, true);
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_5-MultiFile-Version.yaml",
- "ManifestV1_5-MultiFile-Installer.yaml",
- "ManifestV1_5-MultiFile-DefaultLocale.yaml",
- "ManifestV1_5-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory);
- TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" };
- std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml";
- YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath));
- Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory);
- VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_5 }, true);
-}
-
-TEST_CASE("WriteV1_6SingletonManifestAndVerifyContents", "[ManifestCreation]")
-{
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_6-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory);
-
- TempDirectory exportedSingletonDirectory{ "exportedSingleton" };
- std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml";
- YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedSingletonManifestPath));
- Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory);
- VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_6 }, true);
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_6-MultiFile-Version.yaml",
- "ManifestV1_6-MultiFile-Installer.yaml",
- "ManifestV1_6-MultiFile-DefaultLocale.yaml",
- "ManifestV1_6-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory);
- TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" };
- std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml";
- YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath));
- Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory);
- VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_6 }, true);
-}
-
-TEST_CASE("WriteV1_7SingletonManifestAndVerifyContents", "[ManifestCreation]")
-{
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_7-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory);
-
- TempDirectory exportedSingletonDirectory{ "exportedSingleton" };
- std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml";
- YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedSingletonManifestPath));
- Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory);
- VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_7 }, true);
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_7-MultiFile-Version.yaml",
- "ManifestV1_7-MultiFile-Installer.yaml",
- "ManifestV1_7-MultiFile-DefaultLocale.yaml",
- "ManifestV1_7-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory);
- TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" };
- std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml";
- YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath));
- Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory);
- VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_7 }, true);
-}
-
-TEST_CASE("WriteV1_9SingletonManifestAndVerifyContents", "[ManifestCreation]")
-{
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_9-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory);
-
- TempDirectory exportedSingletonDirectory{ "exportedSingleton" };
- std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml";
- YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedSingletonManifestPath));
- Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory);
- VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_9 }, true);
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_9-MultiFile-Version.yaml",
- "ManifestV1_9-MultiFile-Installer.yaml",
- "ManifestV1_9-MultiFile-DefaultLocale.yaml",
- "ManifestV1_9-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory);
- TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" };
- std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml";
- YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath));
- Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory);
- VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_9 }, true);
-}
-
-TEST_CASE("WriteV1_10SingletonManifestAndVerifyContents", "[ManifestCreation]")
-{
- TempDirectory singletonDirectory{ "SingletonManifest" };
- CopyTestDataFilesToFolder({ "ManifestV1_10-Singleton.yaml" }, singletonDirectory);
- Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory);
-
- TempDirectory exportedSingletonDirectory{ "exportedSingleton" };
- std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml";
- YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedSingletonManifestPath));
- Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory);
- VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_10 }, true);
-
- TempDirectory multiFileDirectory{ "MultiFileManifest" };
- CopyTestDataFilesToFolder({
- "ManifestV1_10-MultiFile-Version.yaml",
- "ManifestV1_10-MultiFile-Installer.yaml",
- "ManifestV1_10-MultiFile-DefaultLocale.yaml",
- "ManifestV1_10-MultiFile-Locale.yaml" }, multiFileDirectory);
-
- Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory);
- TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" };
- std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml";
- YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath);
-
- REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath));
- Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory);
- VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_10 }, true);
-}
+WINGET_WRITE_VERIFY_MANIFEST_VERSION(1)
+WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_1)
+WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_2)
+WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_4)
+WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_5)
+WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_6)
+WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_7)
+WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_9)
+WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_10)
+WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_12)
+WINGET_WRITE_VERIFY_MANIFEST_VERSION(1_28)
// Since Authentication is not supported in community repo and will cause manifest validation failure,
// we are not adding Authentication in v1_10 manifests. Instead a separate test is created for Authentication.
-TEST_CASE("ReadWriteValidateV1_10ManifestWithInstallerAuthentication", "[ManifestValidation]")
+TEST_CASE("ReadWriteValidateV1_10ManifestWithInstallerAuthentication", "[ManifestCreation][ManifestVersionCreation]")
{
// Read manifest
TempDirectory testDirectory{ "TestManifest" };
CopyTestDataFilesToFolder({ "ManifestV1_10-InstallerAuthentication.yaml" }, testDirectory);
Manifest testManifest = YamlParser::CreateFromPath(testDirectory);
+ // Validate schema
+ ManifestValidateOption validateOption;
+ validateOption.SchemaValidationOnly = true;
+ validateOption.ThrowOnWarning = true;
+ YamlParser::CreateFromPath(testDirectory, validateOption);
+
// Verify content
REQUIRE(testManifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ s_ManifestVersionV1_10 });
REQUIRE(testManifest.DefaultInstallerInfo.AuthInfo.Type == AppInstaller::Authentication::AuthenticationType::MicrosoftEntraId);
@@ -1508,7 +1124,7 @@ TEST_CASE("ReadWriteValidateV1_10ManifestWithInstallerAuthentication", "[Manifes
// Read back and validate content
REQUIRE(std::filesystem::exists(exportedManifestPath));
Manifest exportedManifest = YamlParser::CreateFromPath(exportedDirectory);
- REQUIRE(testManifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ s_ManifestVersionV1_10 });
+ REQUIRE(exportedManifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ s_ManifestVersionV1_10 });
REQUIRE(exportedManifest.Installers.size() == 1);
REQUIRE(exportedManifest.Installers[0].AuthInfo.Type == AppInstaller::Authentication::AuthenticationType::MicrosoftEntraIdForAzureBlobStorage);
REQUIRE(exportedManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo);
@@ -1516,6 +1132,55 @@ TEST_CASE("ReadWriteValidateV1_10ManifestWithInstallerAuthentication", "[Manifes
REQUIRE(exportedManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo->Scope.empty());
}
+// PowerShell DSC is not supported in the community repo and will cause manifest validation failure.
+TEST_CASE("ReadWriteValidateV1_28ManifestWithPowerShellDSC", "[ManifestCreation][ManifestVersionCreation]")
+{
+ // Read manifest
+ TempDirectory testDirectory{ "TestManifest" };
+ CopyTestDataFilesToFolder({ "ManifestV1_28-PowerShellDSC.yaml" }, testDirectory);
+ Manifest testManifest = YamlParser::CreateFromPath(testDirectory);
+
+ // Validate schema
+ ManifestValidateOption validateOption;
+ validateOption.SchemaValidationOnly = true;
+ validateOption.ThrowOnWarning = true;
+ YamlParser::CreateFromPath(testDirectory, validateOption);
+
+ // TODO: Update ValidateManifest
+ // TODO: Update this test with similar validations
+
+ // Verify content
+ REQUIRE(testManifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ s_ManifestVersionV1_28 });
+ REQUIRE(testManifest.Installers.size() == 1);
+ REQUIRE(testManifest.Installers[0].DesiredStateConfiguration.size() == 3);
+
+ RequireContainerInfoPresent(testManifest.Installers[0].DesiredStateConfiguration, { "https://www.powershellgallery.com/api/v2", "Microsoft.WinGet.DSC", { { "WinGetUserSettings" }, { "WinGetAdminSettings" }, { "WinGetSource" }, { "WinGetPackageManager" }, { "WinGetPackage" } } });
+ RequireContainerInfoPresent(testManifest.Installers[0].DesiredStateConfiguration, { "https://mcr.microsoft.com/", "Microsoft.WinGet.DSC", { { "WinGetUserSettings" }, { "WinGetAdminSettings" }, { "WinGetSource" }, { "WinGetPackageManager" }, { "WinGetPackage" } } });
+ RequireContainerInfoPresent(testManifest.Installers[0].DesiredStateConfiguration, { { { "Microsoft.WinGet/AdminSettings" }, { "Microsoft.WinGet/Package" }, { "Microsoft.WinGet/Source" }, { "Microsoft.WinGet/UserSettingsFile" } } });
+
+ // Manifest Validation. Only error is "PowerShell not supported".
+ auto errors = ValidateManifest(testManifest, true);
+ REQUIRE(errors.size() == 1);
+ REQUIRE(errors[0].GetErrorMessage() == "Field is not supported.");
+ REQUIRE(errors[0].Context == "DesiredStateConfiguration.PowerShell");
+
+ // Write manifest
+ TempDirectory exportedDirectory{ "ExportedManifest" };
+ std::filesystem::path exportedManifestPath = exportedDirectory.GetPath() / "ExportedManifest.yaml";
+ YamlWriter::OutputYamlFile(testManifest, testManifest.Installers[0], exportedManifestPath);
+
+ // Read back and validate content
+ REQUIRE(std::filesystem::exists(exportedManifestPath));
+ Manifest exportedManifest = YamlParser::CreateFromPath(exportedDirectory);
+ REQUIRE(exportedManifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ s_ManifestVersionV1_28 });
+ REQUIRE(exportedManifest.Installers.size() == 1);
+ REQUIRE(exportedManifest.Installers[0].DesiredStateConfiguration.size() == 3);
+
+ RequireContainerInfoPresent(exportedManifest.Installers[0].DesiredStateConfiguration, { "https://www.powershellgallery.com/api/v2", "Microsoft.WinGet.DSC", { { "WinGetUserSettings" }, { "WinGetAdminSettings" }, { "WinGetSource" }, { "WinGetPackageManager" }, { "WinGetPackage" } } });
+ RequireContainerInfoPresent(exportedManifest.Installers[0].DesiredStateConfiguration, { "https://mcr.microsoft.com/", "Microsoft.WinGet.DSC", { { "WinGetUserSettings" }, { "WinGetAdminSettings" }, { "WinGetSource" }, { "WinGetPackageManager" }, { "WinGetPackage" } } });
+ RequireContainerInfoPresent(exportedManifest.Installers[0].DesiredStateConfiguration, { { { "Microsoft.WinGet/AdminSettings" }, { "Microsoft.WinGet/Package" }, { "Microsoft.WinGet/Source" }, { "Microsoft.WinGet/UserSettingsFile" } } });
+}
+
TEST_CASE("WriteManifestWithMultipleLocale", "[ManifestCreation]")
{
Manifest multiLocaleManifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-MultiLocale.yaml"));
diff --git a/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp b/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp
index 544c82e36c..82454b1847 100644
--- a/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp
+++ b/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp
@@ -1188,7 +1188,7 @@ namespace AppInstaller::Manifest
}
// for testing purposes
- bool DependencyList::HasExactDependency(DependencyType type, const string_t& id, const string_t& minVersion)
+ bool DependencyList::HasExactDependency(DependencyType type, const string_t& id, const string_t& minVersion) const
{
for (const auto& dependency : m_dependencies)
{
@@ -1207,7 +1207,7 @@ namespace AppInstaller::Manifest
return false;
}
- size_t DependencyList::Size()
+ size_t DependencyList::Size() const
{
return m_dependencies.size();
}
diff --git a/src/AppInstallerCommonCore/Manifest/ManifestSchemaValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestSchemaValidation.cpp
index 54ff6bbdb8..878afd0f7d 100644
--- a/src/AppInstallerCommonCore/Manifest/ManifestSchemaValidation.cpp
+++ b/src/AppInstallerCommonCore/Manifest/ManifestSchemaValidation.cpp
@@ -272,7 +272,17 @@ namespace AppInstaller::Manifest::YamlParser
int idx = MANIFESTSCHEMA_NO_RESOURCE;
std::map resourceMap;
- if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_12 })
+ if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_28 })
+ {
+ resourceMap = {
+ { ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_28_SINGLETON },
+ { ManifestTypeEnum::Version, IDX_MANIFEST_SCHEMA_V1_28_VERSION },
+ { ManifestTypeEnum::Installer, IDX_MANIFEST_SCHEMA_V1_28_INSTALLER },
+ { ManifestTypeEnum::DefaultLocale, IDX_MANIFEST_SCHEMA_V1_28_DEFAULTLOCALE },
+ { ManifestTypeEnum::Locale, IDX_MANIFEST_SCHEMA_V1_28_LOCALE },
+ };
+ }
+ else if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_12 })
{
resourceMap = {
{ ManifestTypeEnum::Singleton, IDX_MANIFEST_SCHEMA_V1_12_SINGLETON },
diff --git a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp
index c9f87c516a..50ef2ad2a5 100644
--- a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp
+++ b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp
@@ -90,6 +90,7 @@ namespace AppInstaller::Manifest
return ErrorIdToMessageMap;
}
}
+
std::vector ValidateManifest(const Manifest& manifest, bool fullValidation)
{
std::vector resultErrors;
@@ -435,6 +436,19 @@ namespace AppInstaller::Manifest
resultErrors.emplace_back(ManifestError::InvalidFieldValue, "Authentication");
}
}
+
+ if (fullValidation)
+ {
+ for (const auto& container : installer.DesiredStateConfiguration)
+ {
+ if (container.Type == DesiredStateConfigurationContainerType::PowerShell)
+ {
+ // PowerShell DSC is not supported in community repo.
+ resultErrors.emplace_back(ManifestError::FieldNotSupported, "DesiredStateConfiguration.PowerShell");
+ break;
+ }
+ }
+ }
}
// Validate localizations
diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp
index aef14781b6..4fb2f2dcb7 100644
--- a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp
+++ b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp
@@ -398,6 +398,22 @@ namespace AppInstaller::Manifest
std::move(fields_v1_10.begin(), fields_v1_10.end(), std::inserter(result, result.end()));
}
+
+ if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_28 })
+ {
+ std::vector fields_v1_28 =
+ {
+ { "DesiredStateConfiguration", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors
+ {
+ auto* installer = GetManifestInstallerPtr(v);
+ installer->DesiredStateConfiguration.clear();
+ return ValidateAndProcessFields(value, DesiredStateConfigurationFieldInfos, VariantManifestPtr(&(installer->DesiredStateConfiguration)));
+ }
+ },
+ };
+
+ std::move(fields_v1_28.begin(), fields_v1_28.end(), std::inserter(result, result.end()));
+ }
}
return result;
@@ -808,6 +824,90 @@ namespace AppInstaller::Manifest
return result;
}
+ std::vector ManifestYamlPopulator::GetDesiredStateConfigurationFieldInfos()
+ {
+ std::vector result = {};
+
+ if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_28 })
+ {
+ result =
+ {
+ { "PowerShell", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessDSC_PowerShellModuleNode(value, variant_ptr>(v)); } },
+ { "DSCv3", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors
+ {
+ auto* variantValue = variant_ptr>(v);
+ variantValue->emplace_back(DesiredStateConfigurationContainerType::DSCv3);
+ return ValidateAndProcessFields(value, DesiredStateConfigurationDSCv3FieldInfos, VariantManifestPtr(&variantValue->back()));
+ }
+ },
+ };
+ }
+
+ return result;
+ }
+
+ std::vector ManifestYamlPopulator::GetDesiredStateConfigurationPowerShellModuleFieldInfos()
+ {
+ std::vector result = {};
+
+ if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_28 })
+ {
+ result =
+ {
+ { "RepositoryUrl", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->RepositoryURL = Utility::Trim(value.as()); return {}; } },
+ { "ModuleName", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->ModuleName = Utility::Trim(value.as()); return {}; } },
+ { "Resources", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessDSC_PowerShellResourcesNode(value, variant_ptr(v)); } },
+ };
+ }
+
+ return result;
+ }
+
+ std::vector ManifestYamlPopulator::GetDesiredStateConfigurationPowerShellResourceFieldInfos()
+ {
+ std::vector result = {};
+
+ if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_28 })
+ {
+ result =
+ {
+ { "Name", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Name = Utility::Trim(value.as()); return {}; } },
+ };
+ }
+
+ return result;
+ }
+
+ std::vector ManifestYamlPopulator::GetDesiredStateConfigurationDSCv3FieldInfos()
+ {
+ std::vector result = {};
+
+ if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_28 })
+ {
+ result =
+ {
+ { "Resources", [this](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { return ProcessDSCv3ResourcesNode(value, variant_ptr(v)); } },
+ };
+ }
+
+ return result;
+ }
+
+ std::vector ManifestYamlPopulator::GetDesiredStateConfigurationDSCv3ResourceFieldInfos()
+ {
+ std::vector result = {};
+
+ if (m_manifestVersion.get() >= ManifestVer{ s_ManifestVersionV1_28 })
+ {
+ result =
+ {
+ { "Type", [](const YAML::Node& value, const VariantManifestPtr& v)->ValidationErrors { variant_ptr(v)->Name = Utility::Trim(value.as()); return {}; } },
+ };
+ }
+
+ return result;
+ }
+
ValidationErrors ManifestYamlPopulator::ValidateAndProcessFields(
const YAML::Node& rootNode,
const std::vector& fieldInfos,
@@ -1084,6 +1184,55 @@ namespace AppInstaller::Manifest
return resultErrors;
}
+ std::vector ManifestYamlPopulator::ProcessDSC_PowerShellModuleNode(const YAML::Node& node, std::vector* containers)
+ {
+ THROW_HR_IF(E_INVALIDARG, !node.IsSequence());
+
+ ValidationErrors resultErrors;
+
+ for (auto const& entry : node.Sequence())
+ {
+ auto& containerInfo = containers->emplace_back(DesiredStateConfigurationContainerType::PowerShell);
+ auto errors = ValidateAndProcessFields(entry, DesiredStateConfigurationPowerShellModuleFieldInfos, VariantManifestPtr(&containerInfo));
+ std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end()));
+ }
+
+ return resultErrors;
+ }
+
+ std::vector ManifestYamlPopulator::ProcessDSC_PowerShellResourcesNode(const YAML::Node& node, DesiredStateConfigurationContainerInfo* container)
+ {
+ THROW_HR_IF(E_INVALIDARG, !node.IsSequence());
+
+ ValidationErrors resultErrors;
+
+ for (auto const& entry : node.Sequence())
+ {
+ auto& resourceInfo = container->Resources.emplace_back();
+ auto errors = ValidateAndProcessFields(entry, DesiredStateConfigurationPowerShellResourceFieldInfos, VariantManifestPtr(&resourceInfo));
+ std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end()));
+ }
+
+ return resultErrors;
+ }
+
+ std::vector ManifestYamlPopulator::ProcessDSCv3ResourcesNode(const YAML::Node& node, DesiredStateConfigurationContainerInfo* container)
+ {
+ THROW_HR_IF(E_INVALIDARG, !node.IsSequence());
+
+ ValidationErrors resultErrors;
+
+ for (auto const& entry : node.Sequence())
+ {
+ auto& resourceInfo = container->Resources.emplace_back();
+ auto errors = ValidateAndProcessFields(entry, DesiredStateConfigurationDSCv3ResourceFieldInfos, VariantManifestPtr(&resourceInfo));
+ std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end()));
+ }
+
+ return resultErrors;
+ }
+
+
ManifestYamlPopulator::ManifestYamlPopulator(YAML::Node& rootNode, Manifest& manifest, const ManifestVer& manifestVersion, ManifestValidateOption validateOption) :
m_rootNode(rootNode), m_manifest(manifest), m_manifestVersion(manifestVersion), m_validateOption(validateOption)
{
@@ -1114,6 +1263,11 @@ namespace AppInstaller::Manifest
InstallationMetadataFilesFieldInfos = GetInstallationMetadataFilesFieldProcessInfo();
AuthenticationFieldInfos = GetAuthenticationFieldInfos();
MicrosoftEntraIdAuthenticationInfoFieldInfos = GetMicrosoftEntraIdAuthenticationInfoFieldInfos();
+ DesiredStateConfigurationFieldInfos = GetDesiredStateConfigurationFieldInfos();
+ DesiredStateConfigurationPowerShellModuleFieldInfos = GetDesiredStateConfigurationPowerShellModuleFieldInfos();
+ DesiredStateConfigurationPowerShellResourceFieldInfos = GetDesiredStateConfigurationPowerShellResourceFieldInfos();
+ DesiredStateConfigurationDSCv3FieldInfos = GetDesiredStateConfigurationDSCv3FieldInfos();
+ DesiredStateConfigurationDSCv3ResourceFieldInfos = GetDesiredStateConfigurationDSCv3ResourceFieldInfos();
resultErrors = ValidateAndProcessFields(rootNode, RootFieldInfos, VariantManifestPtr(&(m_manifest.get())));
@@ -1127,15 +1281,22 @@ namespace AppInstaller::Manifest
{
ManifestInstaller installer = m_manifest.get().DefaultInstallerInfo;
+#define WINGET_STASH_INSTALLER_PROPERTY(_property_,_clear_) \
+ auto stashed ## _property_ = std::move(installer. _property_); \
+ installer. _property_ . _clear_ ();
+
+#define WINGET_UNSTASH_INSTALLER_PROPERTY(_property_) \
+ installer. _property_ = std::move(stashed ## _property_);
+
// Clear these defaults as PackageFamilyName, ProductCode, AppsAndFeaturesEntries need to be copied based on InstallerType
- installer.PackageFamilyName.clear();
- installer.ProductCode.clear();
- installer.AppsAndFeaturesEntries.clear();
- // Clear dependencies as installer overrides root dependencies
- installer.Dependencies.Clear();
+ WINGET_STASH_INSTALLER_PROPERTY(PackageFamilyName, clear);
+ WINGET_STASH_INSTALLER_PROPERTY(ProductCode, clear);
+ WINGET_STASH_INSTALLER_PROPERTY(AppsAndFeaturesEntries, clear);
+ // Clear dependencies as installer specific overrides root
+ WINGET_STASH_INSTALLER_PROPERTY(Dependencies, Clear);
// Clear nested installers as it should only be copied for zip installerType.
installer.NestedInstallerType = InstallerTypeEnum::Unknown;
- installer.NestedInstallerFiles.clear();
+ WINGET_STASH_INSTALLER_PROPERTY(NestedInstallerFiles, clear);
auto errors = ValidateAndProcessFields(entry, InstallerFieldInfos, VariantManifestPtr(&installer));
std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end()));
@@ -1145,7 +1306,7 @@ namespace AppInstaller::Manifest
{
if (installer.NestedInstallerFiles.empty())
{
- installer.NestedInstallerFiles = m_manifest.get().DefaultInstallerInfo.NestedInstallerFiles;
+ WINGET_UNSTASH_INSTALLER_PROPERTY(NestedInstallerFiles);
}
if (installer.NestedInstallerType == InstallerTypeEnum::Unknown)
@@ -1157,25 +1318,25 @@ namespace AppInstaller::Manifest
// Copy in system reference strings from the root if not set in the installer and appropriate
if (installer.AppsAndFeaturesEntries.empty() && DoesInstallerTypeWriteAppsAndFeaturesEntry(installer.EffectiveInstallerType()))
{
- installer.AppsAndFeaturesEntries = m_manifest.get().DefaultInstallerInfo.AppsAndFeaturesEntries;
+ WINGET_UNSTASH_INSTALLER_PROPERTY(AppsAndFeaturesEntries);
}
if (installer.PackageFamilyName.empty() &&
(DoesInstallerTypeUsePackageFamilyName(installer.EffectiveInstallerType()) ||
DoAnyAppsAndFeaturesEntriesUsePackageFamilyName(installer.AppsAndFeaturesEntries)))
{
- installer.PackageFamilyName = m_manifest.get().DefaultInstallerInfo.PackageFamilyName;
+ WINGET_UNSTASH_INSTALLER_PROPERTY(PackageFamilyName);
}
if (installer.ProductCode.empty() && DoesInstallerTypeUseProductCode(installer.EffectiveInstallerType()))
{
- installer.ProductCode = m_manifest.get().DefaultInstallerInfo.ProductCode;
+ WINGET_UNSTASH_INSTALLER_PROPERTY(ProductCode);
}
// If there are no dependencies on installer use default ones
if (!installer.Dependencies.HasAny())
{
- installer.Dependencies = m_manifest.get().DefaultInstallerInfo.Dependencies;
+ WINGET_UNSTASH_INSTALLER_PROPERTY(Dependencies);
}
// Populate installer default switches if not exists
diff --git a/src/AppInstallerCommonCore/Manifest/YamlWriter.cpp b/src/AppInstallerCommonCore/Manifest/YamlWriter.cpp
index 4483fbdaba..ad2e381a60 100644
--- a/src/AppInstallerCommonCore/Manifest/YamlWriter.cpp
+++ b/src/AppInstallerCommonCore/Manifest/YamlWriter.cpp
@@ -73,6 +73,14 @@ namespace AppInstaller::Manifest::YamlWriter
constexpr std::string_view MicrosoftEntraIdAuthenticationInfo = "MicrosoftEntraIdAuthenticationInfo"sv;
constexpr std::string_view MicrosoftEntraIdResource = "Resource"sv;
constexpr std::string_view MicrosoftEntraIdScope = "Scope"sv;
+ constexpr std::string_view DesiredStateConfiguration = "DesiredStateConfiguration"sv;
+ constexpr std::string_view DesiredStateConfigurationResources = "Resources"sv;
+ constexpr std::string_view DesiredStateConfigurationPowerShell = "PowerShell"sv;
+ constexpr std::string_view DesiredStateConfigurationPowerShellRepositoryURL = "RepositoryUrl"sv;
+ constexpr std::string_view DesiredStateConfigurationPowerShellModuleName = "ModuleName"sv;
+ constexpr std::string_view DesiredStateConfigurationPowerShellResourceName = "Name"sv;
+ constexpr std::string_view DesiredStateConfigurationDSCv3 = "DSCv3"sv;
+ constexpr std::string_view DesiredStateConfigurationDSCv3ResourceType = "Type"sv;
// Installer switches
constexpr std::string_view InstallerSwitches = "InstallerSwitches"sv;
@@ -584,6 +592,89 @@ namespace AppInstaller::Manifest::YamlWriter
out << YAML::EndSeq;
}
+ out << YAML::EndMap;
+ }
+
+ void ProcessDesiredStateConfiguration(YAML::Emitter& out, const std::vector& containers)
+ {
+ if (containers.empty())
+ {
+ return;
+ }
+
+ out << YAML::Key << DesiredStateConfiguration;
+ out << YAML::BeginMap;
+
+ // Process PowerShell containers
+ bool firstPowerShellContainer = true;
+ for (const auto& container : containers)
+ {
+ if (container.Type == DesiredStateConfigurationContainerType::PowerShell)
+ {
+ if (firstPowerShellContainer)
+ {
+ out << YAML::Key << DesiredStateConfigurationPowerShell;
+ out << YAML::BeginSeq;
+ firstPowerShellContainer = false;
+ }
+
+ out << YAML::BeginMap;
+
+ WRITE_PROPERTY(out, DesiredStateConfigurationPowerShellRepositoryURL, container.RepositoryURL);
+ WRITE_PROPERTY(out, DesiredStateConfigurationPowerShellModuleName, container.ModuleName);
+
+ out << YAML::Key << DesiredStateConfigurationResources;
+ out << YAML::BeginSeq;
+
+ for (const auto& resource : container.Resources)
+ {
+ out << YAML::BeginMap;
+ WRITE_PROPERTY(out, DesiredStateConfigurationPowerShellResourceName, resource.Name);
+ out << YAML::EndMap;
+ }
+
+ out << YAML::EndSeq;
+ out << YAML::EndMap;
+ }
+ }
+
+ if (!firstPowerShellContainer)
+ {
+ out << YAML::EndSeq;
+ }
+
+ // Process DSCv3 container
+ bool firstDSCv3Container = true;
+ for (const auto& container : containers)
+ {
+ if (container.Type == DesiredStateConfigurationContainerType::DSCv3)
+ {
+ if (firstDSCv3Container)
+ {
+ out << YAML::Key << DesiredStateConfigurationDSCv3;
+ out << YAML::BeginMap;
+ firstDSCv3Container = false;
+ }
+ else
+ {
+ THROW_HR_MSG(E_INVALIDARG, "Cannot have multiple DSCv3 containers.");
+ }
+
+ out << YAML::Key << DesiredStateConfigurationResources;
+ out << YAML::BeginSeq;
+
+ for (const auto& resource : container.Resources)
+ {
+ out << YAML::BeginMap;
+ WRITE_PROPERTY(out, DesiredStateConfigurationDSCv3ResourceType, resource.Name);
+ out << YAML::EndMap;
+ }
+
+ out << YAML::EndSeq;
+ out << YAML::EndMap;
+ }
+ }
+
out << YAML::EndMap;
}
@@ -632,6 +723,7 @@ namespace AppInstaller::Manifest::YamlWriter
ProcessUnsupportedArguments(out, installer.UnsupportedArguments);
ProcessUnsupportedOSArchitecture(out, installer.UnsupportedOSArchitectures);
ProcessAuthentication(out, installer.AuthInfo);
+ ProcessDesiredStateConfiguration(out, installer.DesiredStateConfiguration);
}
void ProcessInstaller(YAML::Emitter& out, const ManifestInstaller& installer)
diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h
index 894139d2e6..96b7affb01 100644
--- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h
+++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h
@@ -7,6 +7,7 @@
#include