Support/Package/Schema/Eigenverft.Manifested.Sandbox.Package.DefinitionSchema.Wire1_3.ps1
|
<#
Eigenverft.Manifested.Sandbox.Package.DefinitionSchema.Wire1_3 Validators and runtime projection for package definition schemaVersion 1.3. #> function Get-PackageObjectPropertyValue { [CmdletBinding()] param( [AllowNull()] [psobject]$InputObject, [Parameter(Mandatory = $true)] [string]$Name ) if (-not $InputObject -or -not $InputObject.PSObject.Properties[$Name]) { return $null } return $InputObject.$Name } function Test-PackageObjectHasProperty { [CmdletBinding()] [OutputType([bool])] param( [AllowNull()] [psobject]$InputObject, [Parameter(Mandatory = $true)] [string]$Name ) return ($InputObject -and $InputObject.PSObject.Properties[$Name]) } function Get-PackagePresenceDiscoveryEntryPoints { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$Definition, [Parameter(Mandatory = $true)] [ValidateSet('commands', 'apps')] [string]$ToolKind, [switch]$ExposedOnly ) if (-not (Test-PackageObjectHasProperty -InputObject $Definition -Name 'presenceDiscovery') -or -not (Test-PackageObjectHasProperty -InputObject $Definition.presenceDiscovery -Name $ToolKind)) { return @() } $exposedPropertyName = if ([string]::Equals($ToolKind, 'commands', [System.StringComparison]::Ordinal)) { 'exposeCommand' } else { 'exposeApp' } return @( foreach ($entryPoint in @($Definition.presenceDiscovery.$ToolKind)) { if ($null -eq $entryPoint) { continue } if ($ExposedOnly -and ( -not $entryPoint.PSObject.Properties[$exposedPropertyName] -or -not [bool]$entryPoint.$exposedPropertyName)) { continue } $entryPoint } ) } function Get-PackagePresenceDiscoveryEntryPoint { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$Definition, [Parameter(Mandatory = $true)] [ValidateSet('commands', 'apps')] [string]$ToolKind, [Parameter(Mandatory = $true)] [string]$Name, [switch]$ExposedOnly ) foreach ($entryPoint in @(Get-PackagePresenceDiscoveryEntryPoints -Definition $Definition -ToolKind $ToolKind -ExposedOnly:$ExposedOnly)) { if ([string]::Equals([string]$entryPoint.name, $Name, [System.StringComparison]::OrdinalIgnoreCase)) { return $entryPoint } } return $null } function Resolve-PackagePresenceEntryPointPath { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$EntryPoint, [Parameter(Mandatory = $true)] [string]$InstallDirectory ) return (Join-Path $InstallDirectory (([string]$EntryPoint.relativePath) -replace '/', '\')) } function Resolve-PackagePresenceToolPath { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$Definition, [Parameter(Mandatory = $true)] [ValidateSet('commands', 'apps')] [string]$ToolKind, [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [string]$InstallDirectory ) $entryPoint = Get-PackagePresenceDiscoveryEntryPoint -Definition $Definition -ToolKind $ToolKind -Name $Name if (-not $entryPoint) { return $null } return (Resolve-PackagePresenceEntryPointPath -EntryPoint $entryPoint -InstallDirectory $InstallDirectory) } function Assert-PackageDefinitionNoRetiredNestedProperty_1_3 { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$DefinitionId, [AllowNull()] [psobject]$InputObject, [Parameter(Mandatory = $true)] [string]$PropertyName, [Parameter(Mandatory = $true)] [string]$PropertyPath, [Parameter(Mandatory = $true)] [string]$ReplacementPath ) if ($InputObject -and $InputObject.PSObject.Properties[$PropertyName]) { throw "Package definition '$DefinitionId' still uses retired schemaVersion 1.1 property '$PropertyPath'. Use '$ReplacementPath'." } } function Assert-PackageArtifactTrustMetadata_1_3 { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$DefinitionId, [Parameter(Mandatory = $true)] [string]$Version, [Parameter(Mandatory = $true)] [string]$TargetId, [AllowNull()] [psobject]$Artifact ) if (-not $Artifact) { return } foreach ($retiredProperty in @('autoUpdateSupported', 'integrity', 'authenticode')) { if ($Artifact.PSObject.Properties[$retiredProperty]) { throw "Package definition '$DefinitionId' version '$Version' artifact '$TargetId' still uses retired packageFile.$retiredProperty. Use artifact contentHash or publisherSignature metadata." } } if ($Artifact.PSObject.Properties['contentHash']) { $contentHash = $Artifact.contentHash if (-not $contentHash -or -not $contentHash.PSObject.Properties['algorithm'] -or [string]::IsNullOrWhiteSpace([string]$contentHash.algorithm)) { throw "Package definition '$DefinitionId' version '$Version' artifact '$TargetId' defines packageFile.contentHash without algorithm." } if (-not [string]::Equals([string]$contentHash.algorithm, 'sha256', [System.StringComparison]::OrdinalIgnoreCase)) { throw "Package definition '$DefinitionId' version '$Version' artifact '$TargetId' uses unsupported packageFile.contentHash algorithm '$($contentHash.algorithm)'. Use sha256." } if (-not $contentHash.PSObject.Properties['value'] -or [string]::IsNullOrWhiteSpace([string]$contentHash.value)) { throw "Package definition '$DefinitionId' version '$Version' artifact '$TargetId' defines packageFile.contentHash without value." } } if ($Artifact.PSObject.Properties['publisherSignature']) { $publisherSignature = $Artifact.publisherSignature if (-not $publisherSignature -or -not $publisherSignature.PSObject.Properties['kind'] -or [string]::IsNullOrWhiteSpace([string]$publisherSignature.kind)) { throw "Package definition '$DefinitionId' version '$Version' artifact '$TargetId' defines packageFile.publisherSignature without kind." } if (-not [string]::Equals([string]$publisherSignature.kind, 'authenticode', [System.StringComparison]::OrdinalIgnoreCase)) { throw "Package definition '$DefinitionId' version '$Version' artifact '$TargetId' uses unsupported packageFile.publisherSignature kind '$($publisherSignature.kind)'. Use authenticode." } if (-not $publisherSignature.PSObject.Properties['requireValid']) { throw "Package definition '$DefinitionId' version '$Version' artifact '$TargetId' defines packageFile.publisherSignature without requireValid." } if (-not $publisherSignature.PSObject.Properties['subjectContains'] -or [string]::IsNullOrWhiteSpace([string]$publisherSignature.subjectContains)) { throw "Package definition '$DefinitionId' version '$Version' artifact '$TargetId' defines packageFile.publisherSignature without subjectContains." } } } function Assert-PackagePresenceRequirementFlags_1_3 { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$DefinitionId, [Parameter(Mandatory = $true)] [string]$PropertyPath, [Parameter(Mandatory = $true)] [psobject]$Require ) $requiredRequirements = @('files', 'directories', 'commands', 'apps', 'metadataFiles', 'signatures', 'fileDetails', 'registry') foreach ($required in @($requiredRequirements)) { if (-not $Require.PSObject.Properties[$required]) { throw "Package definition '$DefinitionId' requires '$PropertyPath.require.$required'." } if ($Require.$required -isnot [bool]) { throw "Package definition '$DefinitionId' field '$PropertyPath.require.$required' must be boolean." } } } function Assert-PackageRemovedOperation_1_3 { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$DefinitionId, [Parameter(Mandatory = $true)] [psobject]$RemovedOperation ) if (-not $RemovedOperation.PSObject.Properties['policy']) { throw "Package definition '$DefinitionId' is missing packageOperations.removed.policy." } if (-not $RemovedOperation.PSObject.Properties['operation']) { throw "Package definition '$DefinitionId' is missing packageOperations.removed.operation." } if (-not $RemovedOperation.PSObject.Properties['absenceVerification']) { throw "Package definition '$DefinitionId' is missing packageOperations.removed.absenceVerification." } if (-not $RemovedOperation.PSObject.Properties['postRemoveCleanup']) { throw "Package definition '$DefinitionId' is missing packageOperations.removed.postRemoveCleanup." } $policy = $RemovedOperation.policy foreach ($requiredPolicyProperty in @('whenNotInInventory', 'allowedInventoryOwnershipKinds', 'allowUntrackedExternalRemoval', 'removeDependencies')) { if (-not $policy.PSObject.Properties[$requiredPolicyProperty]) { throw "Package definition '$DefinitionId' is missing packageOperations.removed.policy.$requiredPolicyProperty." } } if (-not [string]::Equals([string]$policy.whenNotInInventory, 'succeed', [System.StringComparison]::OrdinalIgnoreCase) -and -not [string]::Equals([string]$policy.whenNotInInventory, 'fail', [System.StringComparison]::OrdinalIgnoreCase)) { throw "Package definition '$DefinitionId' uses unsupported packageOperations.removed.policy.whenNotInInventory value '$($policy.whenNotInInventory)'." } foreach ($kind in @($policy.allowedInventoryOwnershipKinds)) { if ([string]::IsNullOrWhiteSpace([string]$kind)) { throw "Package definition '$DefinitionId' has empty packageOperations.removed.policy.allowedInventoryOwnershipKinds entry." } if (-not [string]::Equals([string]$kind, 'PackageInstalled', [System.StringComparison]::OrdinalIgnoreCase) -and -not [string]::Equals([string]$kind, 'PackageApplied', [System.StringComparison]::OrdinalIgnoreCase) -and -not [string]::Equals([string]$kind, 'AdoptedExternal', [System.StringComparison]::OrdinalIgnoreCase)) { throw "Package definition '$DefinitionId' uses unsupported packageOperations.removed.policy.allowedInventoryOwnershipKinds value '$kind'." } } if ($policy.allowUntrackedExternalRemoval -isnot [bool]) { throw "Package definition '$DefinitionId' requires packageOperations.removed.policy.allowUntrackedExternalRemoval to be boolean." } if ($policy.removeDependencies -isnot [bool]) { throw "Package definition '$DefinitionId' requires packageOperations.removed.policy.removeDependencies to be boolean." } $operation = $RemovedOperation.operation if (-not $operation.PSObject.Properties['kind']) { throw "Package definition '$DefinitionId' is missing packageOperations.removed.operation.kind." } $operationKind = [string]$operation.kind switch ($operationKind) { 'deleteInstallDirectory' { if (-not $operation.PSObject.Properties['pathSource'] -or -not [string]::Equals([string]$operation.pathSource, 'inventory.installDirectory', [System.StringComparison]::OrdinalIgnoreCase)) { throw "Package definition '$DefinitionId' removed.operation.kind 'deleteInstallDirectory' requires pathSource = 'inventory.installDirectory'." } } 'nsisUninstaller' { foreach ($required in @('commandSource', 'commandArguments', 'elevation', 'timeoutSec', 'successExitCodes', 'restartExitCodes', 'uiMode')) { if (-not $operation.PSObject.Properties[$required]) { throw "Package definition '$DefinitionId' missing packageOperations.removed.operation.$required." } } if (-not $operation.commandSource.PSObject.Properties['use'] -or -not [string]::Equals([string]$operation.commandSource.use, 'existingInstallDiscovery', [System.StringComparison]::OrdinalIgnoreCase)) { throw "Package definition '$DefinitionId' packageOperations.removed.operation.commandSource.use must be 'existingInstallDiscovery'." } if (-not $operation.commandSource.PSObject.Properties['searchLocationId'] -or [string]::IsNullOrWhiteSpace([string]$operation.commandSource.searchLocationId)) { throw "Package definition '$DefinitionId' packageOperations.removed.operation.commandSource.searchLocationId is missing." } if (-not $operation.commandSource.PSObject.Properties['registryValueOrder'] -or @($operation.commandSource.registryValueOrder).Count -eq 0) { throw "Package definition '$DefinitionId' packageOperations.removed.operation.commandSource.registryValueOrder is missing." } foreach ($registryValue in @($operation.commandSource.registryValueOrder)) { if (-not [string]::Equals([string]$registryValue, 'QuietUninstallString', [System.StringComparison]::OrdinalIgnoreCase) -and -not [string]::Equals([string]$registryValue, 'UninstallString', [System.StringComparison]::OrdinalIgnoreCase)) { throw "Package definition '$DefinitionId' packageOperations.removed.operation.commandSource.registryValueOrder contains unsupported value '$registryValue'." } } if (($operation.timeoutSec -isnot [int] -and $operation.timeoutSec -isnot [long]) -or $operation.timeoutSec -le 0) { throw "Package definition '$DefinitionId' packageOperations.removed.operation.timeoutSec must be a positive integer." } if (-not ($operation.successExitCodes -is [array])) { throw "Package definition '$DefinitionId' packageOperations.removed.operation.successExitCodes must be an array." } if (-not ($operation.restartExitCodes -is [array])) { throw "Package definition '$DefinitionId' packageOperations.removed.operation.restartExitCodes must be an array." } } 'none' { # no operation-specific fields required. } default { throw "Package definition '$DefinitionId' uses unsupported packageOperations.removed.operation.kind '$operationKind'." } } $absence = $RemovedOperation.absenceVerification if (-not $absence.PSObject.Properties['use'] -or -not [string]::Equals([string]$absence.use, 'presenceDiscovery', [System.StringComparison]::OrdinalIgnoreCase)) { throw "Package definition '$DefinitionId' requires packageOperations.removed.absenceVerification.use = 'presenceDiscovery'." } if (-not $absence.PSObject.Properties['require']) { throw "Package definition '$DefinitionId' is missing packageOperations.removed.absenceVerification.require." } Assert-PackagePresenceRequirementFlags_1_3 -DefinitionId $DefinitionId -PropertyPath 'packageOperations.removed.absenceVerification' -Require $absence.require $postRemoveCleanup = $RemovedOperation.postRemoveCleanup foreach ($requiredPost in @('packageInventoryRecord', 'generatedShims', 'pathEntries', 'workDirectories')) { if (-not $postRemoveCleanup.PSObject.Properties[$requiredPost]) { throw "Package definition '$DefinitionId' requires packageOperations.removed.postRemoveCleanup.$requiredPost." } if ($postRemoveCleanup.$requiredPost -isnot [bool]) { throw "Package definition '$DefinitionId' packageOperations.removed.postRemoveCleanup.$requiredPost must be boolean." } } } function Assert-PackageDefinitionSchema_1_3 { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$DefinitionDocumentInfo, [Parameter(Mandatory = $true)] [string]$DefinitionId, [string]$DefinitionRepositoryId = (Get-PackageDefaultRepositoryId) ) $definition = $DefinitionDocumentInfo.Document foreach ($retiredProperty in @('releases', 'providedTools', 'shared', 'releaseDefaults', 'installedStateDiscovery', 'installedStateCheck', 'existingInstallPolicy')) { if ($definition.PSObject.Properties[$retiredProperty]) { throw "Package definition '$($DefinitionDocumentInfo.Path)' still uses retired schemaVersion 1.1 property '$retiredProperty'." } } foreach ($requiredProperty in @('schemaVersion', 'id', 'display', 'dependencies', 'artifacts', 'presenceDiscovery', 'existingInstallDiscovery', 'packageOperations')) { if (-not $definition.PSObject.Properties[$requiredProperty]) { throw "Package definition '$($DefinitionDocumentInfo.Path)' is missing required schemaVersion 1.3 property '$requiredProperty'." } } if (-not [string]::Equals([string]$definition.id, $DefinitionId, [System.StringComparison]::OrdinalIgnoreCase)) { throw "Package definition id '$($definition.id)' does not match expected id '$DefinitionId'." } if (-not $definition.artifacts.PSObject.Properties['targets']) { throw "Package definition '$DefinitionId' is missing required artifacts.targets array." } if (-not $definition.artifacts.PSObject.Properties['releases']) { throw "Package definition '$DefinitionId' is missing required artifacts.releases array." } $targetIds = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase) $targetsById = @{} foreach ($target in @($definition.artifacts.targets)) { if (-not $target.PSObject.Properties['id'] -or [string]::IsNullOrWhiteSpace([string]$target.id)) { throw "Package definition '$DefinitionId' has artifact target without id." } if (-not $targetIds.Add([string]$target.id)) { throw "Package definition '$DefinitionId' has duplicate artifact target id '$($target.id)'." } $targetsById[[string]$target.id] = $target foreach ($requiredTargetProperty in @('releaseTrack', 'artifactDistributionVariant', 'constraints', 'versionSelection')) { if (-not $target.PSObject.Properties[$requiredTargetProperty]) { throw "Package definition '$DefinitionId' artifact target '$($target.id)' is missing '$requiredTargetProperty'." } } if (-not [string]::Equals([string]$target.versionSelection.strategy, 'latestByVersion', [System.StringComparison]::OrdinalIgnoreCase)) { throw "Package definition '$DefinitionId' artifact target '$($target.id)' uses unsupported versionSelection.strategy '$($target.versionSelection.strategy)'. Use latestByVersion." } } $dependencies = if (Test-PackageObjectHasProperty -InputObject $definition -Name 'dependencies') { @($definition.dependencies) } else { @() } foreach ($dependency in @($dependencies)) { if ($null -eq $dependency) { continue } if (-not $dependency.PSObject.Properties['repositoryId'] -or [string]::IsNullOrWhiteSpace([string]$dependency.repositoryId)) { throw "Package definition '$DefinitionId' has dependency without repositoryId." } if (-not $dependency.PSObject.Properties['definitionId'] -or [string]::IsNullOrWhiteSpace([string]$dependency.definitionId)) { throw "Package definition '$DefinitionId' has dependency without definitionId." } } $sharedOperation = if ($definition.packageOperations.PSObject.Properties['policy']) { $definition.packageOperations.policy } else { $null } $ownershipPolicy = if ($sharedOperation -and $sharedOperation.PSObject.Properties['ownershipPolicy']) { $sharedOperation.ownershipPolicy } else { $null } Assert-PackageDefinitionNoRetiredNestedProperty_1_3 -DefinitionId $DefinitionId -InputObject $ownershipPolicy -PropertyName 'requireManagedOwnership' -PropertyPath 'packageOperations.policy.ownershipPolicy.requireManagedOwnership' -ReplacementPath 'packageOperations.policy.ownershipPolicy.requirePackageOwnership' $assignedOperation = if ($definition.packageOperations.PSObject.Properties['assigned']) { $definition.packageOperations.assigned } else { $null } $assignedInstall = if ($assignedOperation -and $assignedOperation.PSObject.Properties['install']) { $assignedOperation.install } else { $null } Assert-PackageDefinitionNoRetiredNestedProperty_1_3 -DefinitionId $DefinitionId -InputObject $assignedOperation -PropertyName 'managerDependency' -PropertyPath 'packageOperations.assigned.managerDependency' -ReplacementPath 'dependencies plus packageOperations.assigned.installerCommand' Assert-PackageDefinitionNoRetiredNestedProperty_1_3 -DefinitionId $DefinitionId -InputObject $assignedInstall -PropertyName 'managerDependency' -PropertyPath 'packageOperations.assigned.managerDependency' -ReplacementPath 'dependencies plus packageOperations.assigned.installerCommand' Assert-PackageDefinitionNoRetiredNestedProperty_1_3 -DefinitionId $DefinitionId -InputObject $assignedOperation -PropertyName 'managerKind' -PropertyPath 'packageOperations.assigned.managerKind' -ReplacementPath 'packageOperations.assigned.kind = npmGlobalPackage' Assert-PackageDefinitionNoRetiredNestedProperty_1_3 -DefinitionId $DefinitionId -InputObject $assignedInstall -PropertyName 'managerKind' -PropertyPath 'packageOperations.assigned.managerKind' -ReplacementPath 'packageOperations.assigned.kind = npmGlobalPackage' if (-not $definition.packageOperations.PSObject.Properties['removed']) { throw "Package definition '$DefinitionId' is missing required packageOperations.removed." } Assert-PackageRemovedOperation_1_3 -DefinitionId $DefinitionId -RemovedOperation $definition.packageOperations.removed if (-not $definition.artifacts.sources) { throw "Package definition '$DefinitionId' is missing artifacts.sources map." } foreach ($sourceProperty in @($definition.artifacts.sources.PSObject.Properties)) { $source = $sourceProperty.Value if (-not $source.PSObject.Properties['kind'] -or [string]::IsNullOrWhiteSpace([string]$source.kind)) { throw "Package definition '$DefinitionId' artifacts source '$($sourceProperty.Name)' is missing kind." } } foreach ($versionEntry in @($definition.artifacts.releases)) { if ($versionEntry.PSObject.Properties['artifactsByTarget']) { throw "Package definition '$DefinitionId' release '$($versionEntry.version)' still uses retired property 'artifactsByTarget'." } Assert-PackageDefinitionNoRetiredNestedProperty_1_3 -DefinitionId $DefinitionId -InputObject $versionEntry -PropertyName 'artifactsByTarget' -PropertyPath 'artifacts.releases[].artifactsByTarget' -ReplacementPath 'targetArtifacts' if (-not $versionEntry.PSObject.Properties['version'] -or [string]::IsNullOrWhiteSpace([string]$versionEntry.version)) { throw "Package definition '$DefinitionId' has release entry without version." } if (-not $versionEntry.PSObject.Properties['releaseTracks'] -or $null -eq $versionEntry.releaseTracks) { throw "Package definition '$DefinitionId' release '$($versionEntry.version)' is missing releaseTracks." } if (-not $versionEntry.PSObject.Properties['targetArtifacts'] -or $null -eq $versionEntry.targetArtifacts) { throw "Package definition '$DefinitionId' release '$($versionEntry.version)' is missing targetArtifacts." } $releaseUpstream = if ($versionEntry.PSObject.Properties['upstreamRelease']) { $versionEntry.upstreamRelease } else { $null } foreach ($artifactProperty in @($versionEntry.targetArtifacts.PSObject.Properties)) { if (-not $targetIds.Contains([string]$artifactProperty.Name)) { throw "Package definition '$DefinitionId' release '$($versionEntry.version)' references unknown artifact target '$($artifactProperty.Name)'." } $artifact = $artifactProperty.Value if (-not $artifact -or -not $artifact.PSObject.Properties['artifactId'] -or [string]::IsNullOrWhiteSpace([string]$artifact.artifactId)) { throw "Package definition '$DefinitionId' release '$($versionEntry.version)' artifact '$($artifactProperty.Name)' is missing artifactId." } Assert-PackageArtifactTrustMetadata_1_3 -DefinitionId $DefinitionId -Version ([string]$versionEntry.version) -TargetId ([string]$artifactProperty.Name) -Artifact $artifact $artifactAcquisitionCandidates = if ($artifact.PSObject.Properties['acquisitionCandidates']) { @($artifact.acquisitionCandidates) } else { @() } if (-not $artifactAcquisitionCandidates -and $targetsById[[string]$artifactProperty.Name] -and $targetsById[[string]$artifactProperty.Name].PSObject.Properties['acquisitionCandidates']) { $artifactAcquisitionCandidates = @($targetsById[[string]$artifactProperty.Name].acquisitionCandidates) } foreach ($candidate in @($artifactAcquisitionCandidates)) { if ($candidate.PSObject.Properties['priority']) { throw "Package definition '$DefinitionId' release '$($versionEntry.version)' artifact '$($artifactProperty.Name)' still uses retired acquisitionCandidate property 'priority'. Use searchOrder." } if (-not [string]::Equals([string]$candidate.kind, 'download', [System.StringComparison]::OrdinalIgnoreCase)) { continue } if (-not (Test-PackageObjectHasProperty -InputObject $definition.artifacts.sources -Name ([string]$candidate.sourceId))) { throw "Package definition '$DefinitionId' release '$($versionEntry.version)' artifact '$($artifactProperty.Name)' references unknown artifacts source '$($candidate.sourceId)'." } $candidateSource = Get-PackageObjectPropertyValue -InputObject $definition.artifacts.sources -Name ([string]$candidate.sourceId) if ($candidateSource -and [string]::Equals([string]$candidateSource.kind, 'githubRelease', [System.StringComparison]::OrdinalIgnoreCase)) { if (-not $releaseUpstream -or -not $releaseUpstream.PSObject.Properties['sourceId'] -or [string]::IsNullOrWhiteSpace([string]$releaseUpstream.sourceId) -or -not [string]::Equals([string]$releaseUpstream.sourceId, [string]$candidate.sourceId, [System.StringComparison]::OrdinalIgnoreCase) -or -not $releaseUpstream.PSObject.Properties['releaseTag'] -or [string]::IsNullOrWhiteSpace([string]$releaseUpstream.releaseTag)) { throw "Package definition '$DefinitionId' release '$($versionEntry.version)' artifact '$($artifactProperty.Name)' requires releaseTag because candidate '$($candidate.sourceId)' uses GitHub release." } } } } } $exposedCommands = @(Get-PackagePresenceDiscoveryEntryPoints -Definition $definition -ToolKind 'commands' -ExposedOnly) $assigned = $definition.packageOperations.assigned if ($assigned.PSObject.Properties['pathRegistration'] -and $assigned.pathRegistration.PSObject.Properties['source'] -and [string]::Equals([string]$assigned.pathRegistration.source.kind, 'shim', [System.StringComparison]::OrdinalIgnoreCase)) { if (-not [string]::Equals([string]$assigned.pathRegistration.source.use, 'presenceDiscovery.commands', [System.StringComparison]::Ordinal)) { throw "Package definition '$DefinitionId' pathRegistration.source kind 'shim' requires use='presenceDiscovery.commands'." } if ($exposedCommands.Count -eq 0) { throw "Package definition '$DefinitionId' uses shim PATH registration but has no exposed presenceDiscovery.commands." } } } function Get-PackageArtifactForTarget { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$VersionEntry, [Parameter(Mandatory = $true)] [string]$TargetId ) foreach ($property in @($VersionEntry.targetArtifacts.PSObject.Properties)) { if ([string]::Equals([string]$property.Name, $TargetId, [System.StringComparison]::OrdinalIgnoreCase)) { return $property.Value } } return $null } function Resolve-PackageTargetArtifactText { [CmdletBinding()] param( [AllowNull()] [string]$Text, [Parameter(Mandatory = $true)] [psobject]$ArtifactTarget, [Parameter(Mandatory = $true)] [psobject]$VersionEntry, [AllowNull()] [psobject]$UpstreamRelease ) if ($null -eq $Text) { return $null } return Resolve-TemplateText -Text $Text -Tokens @{ version = [string]$VersionEntry.version releaseTag = if ($UpstreamRelease -and $UpstreamRelease.PSObject.Properties['releaseTag']) { [string]$UpstreamRelease.releaseTag } else { $null } releaseTrack = [string]$ArtifactTarget.releaseTrack artifactDistributionVariant = [string]$ArtifactTarget.artifactDistributionVariant } } function New-PackageReadinessFromPresenceDiscovery { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$Definition, [Parameter(Mandatory = $true)] [psobject]$Assigned ) $require = if ($Assigned.PSObject.Properties['readyStateCheck'] -and $Assigned.readyStateCheck.PSObject.Properties['require']) { $Assigned.readyStateCheck.require } else { [pscustomobject]@{} } $discovery = $Definition.presenceDiscovery $commandChecks = New-Object System.Collections.Generic.List[object] if ($require.PSObject.Properties['commands'] -and [bool]$require.commands) { foreach ($command in @($discovery.commands)) { foreach ($stateCheck in @($command.stateChecks)) { if ($null -eq $stateCheck) { continue } $check = ConvertTo-PackageObject -InputObject $stateCheck $check | Add-Member -MemberType NoteProperty -Name 'entryPoint' -Value ([string]$command.name) -Force $commandChecks.Add($check) | Out-Null } } } return [pscustomobject]@{ files = if ($require.PSObject.Properties['files'] -and [bool]$require.files) { @($discovery.files) } else { @() } directories = if ($require.PSObject.Properties['directories'] -and [bool]$require.directories) { @($discovery.directories) } else { @() } commandChecks = @($commandChecks.ToArray()) metadataFiles = if ($require.PSObject.Properties['metadataFiles'] -and [bool]$require.metadataFiles) { @($discovery.metadataFiles) } else { @() } signatures = if ($require.PSObject.Properties['signatures'] -and [bool]$require.signatures) { @($discovery.signatures) } else { @() } fileDetails = if ($require.PSObject.Properties['fileDetails'] -and [bool]$require.fileDetails) { @($discovery.fileDetails) } else { @() } registryChecks = if ($require.PSObject.Properties['registry'] -and [bool]$require.registry) { @($discovery.registry) } else { @() } } } function Resolve-PackageEffectivePackage_1_3 { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$PackageConfig ) $definition = $PackageConfig.Definition $releaseTrack = if ([string]::IsNullOrWhiteSpace([string]$PackageConfig.ReleaseTrack)) { 'none' } else { [string]$PackageConfig.ReleaseTrack } $matchState = New-Object System.Collections.Generic.List[object] foreach ($target in @($definition.artifacts.targets)) { $constraints = $target.constraints $osConstraints = if ($constraints.PSObject.Properties['os']) { @($constraints.os) } else { @() } $cpuConstraints = if ($constraints.PSObject.Properties['cpu']) { @($constraints.cpu) } else { @() } if (-not [string]::Equals([string]$target.releaseTrack, $releaseTrack, [System.StringComparison]::OrdinalIgnoreCase) -or -not (Test-PackageConstraintSetMatch -Values $osConstraints -ActualValue $PackageConfig.Platform) -or -not (Test-PackageConstraintSetMatch -Values $cpuConstraints -ActualValue $PackageConfig.Architecture)) { continue } foreach ($versionEntry in @($definition.artifacts.releases)) { $releaseTracks = if ($versionEntry.PSObject.Properties['releaseTracks']) { @($versionEntry.releaseTracks) } else { @() } $versionIsInTrack = $false foreach ($releaseTrackName in @($releaseTracks)) { if ([string]::Equals([string]$releaseTrackName, [string]$target.releaseTrack, [System.StringComparison]::OrdinalIgnoreCase)) { $versionIsInTrack = $true break } } if (-not $versionIsInTrack) { continue } $artifact = Get-PackageArtifactForTarget -VersionEntry $versionEntry -TargetId ([string]$target.id) if ($artifact) { $matchState.Add([pscustomobject]@{ ArtifactTarget = $target VersionEntry = $versionEntry Artifact = $artifact SortVersion = ConvertTo-PackageVersion -VersionText ([string]$versionEntry.version) }) | Out-Null } } } if ($matchState.Count -eq 0) { throw "No Package target/release entry matched platform '$($PackageConfig.Platform)', architecture '$($PackageConfig.Architecture)', and releaseTrack '$releaseTrack'." } $selected = @($matchState.ToArray()) | Sort-Object -Descending -Property SortVersion | Select-Object -First 1 $target = $selected.ArtifactTarget $versionEntry = $selected.VersionEntry $artifact = $selected.Artifact $rawAssigned = ConvertTo-PackageObject -InputObject $definition.packageOperations.assigned $assigned = $rawAssigned $rawAssignedInstall = if ($rawAssigned -is [psobject] -and $rawAssigned.PSObject.Properties['install']) { $rawAssigned.install } else { $null } if ($rawAssignedInstall -is [psobject]) { $flattenedAssigned = [ordered]@{} foreach ($property in @($rawAssigned.PSObject.Properties)) { $flattenedAssigned[$property.Name] = $property.Value } foreach ($property in @($rawAssignedInstall.PSObject.Properties)) { if (-not $flattenedAssigned.Contains($property.Name)) { $flattenedAssigned[$property.Name] = $property.Value } } $assigned = [pscustomobject]$flattenedAssigned } $upstreamRelease = if ($versionEntry.PSObject.Properties['upstreamRelease']) { $versionEntry.upstreamRelease } else { $null } $fileName = if ($artifact.PSObject.Properties['fileName'] -and -not [string]::IsNullOrWhiteSpace([string]$artifact.fileName)) { [string]$artifact.fileName } elseif ($target.PSObject.Properties['fileNameTemplate']) { Resolve-PackageTargetArtifactText -Text ([string]$target.fileNameTemplate) -ArtifactTarget $target -VersionEntry $versionEntry -UpstreamRelease $upstreamRelease } else { $null } $packageFile = $null if (-not [string]::IsNullOrWhiteSpace($fileName) -or $artifact.PSObject.Properties['contentHash'] -or $artifact.PSObject.Properties['publisherSignature']) { $packageFile = [ordered]@{} if (-not [string]::IsNullOrWhiteSpace($fileName)) { $packageFile.fileName = $fileName } if ($artifact.PSObject.Properties['contentHash']) { $packageFile.contentHash = ConvertTo-PackageObject -InputObject $artifact.contentHash } if ($artifact.PSObject.Properties['publisherSignature']) { $packageFile.publisherSignature = ConvertTo-PackageObject -InputObject $artifact.publisherSignature } } $artifactAcquisitionCandidates = if ($artifact.PSObject.Properties['acquisitionCandidates']) { @($artifact.acquisitionCandidates) } else { @() } if (-not $artifactAcquisitionCandidates -and $target.PSObject.Properties['acquisitionCandidates']) { $artifactAcquisitionCandidates = @($target.acquisitionCandidates) } $artifactSourcePath = if ($artifact.PSObject.Properties['sourcePath']) { Resolve-PackageTargetArtifactText -Text ([string]$artifact.sourcePath) -ArtifactTarget $target -VersionEntry $versionEntry -UpstreamRelease $upstreamRelease } else { $null } $acquisitionCandidates = @( foreach ($source in @($artifactAcquisitionCandidates)) { $candidate = ConvertTo-PackageObject -InputObject $source if ([string]::Equals([string]$candidate.kind, 'download', [System.StringComparison]::OrdinalIgnoreCase)) { if ($candidate.PSObject.Properties['sourcePath'] -and -not [string]::IsNullOrWhiteSpace([string]$candidate.sourcePath)) { $candidate.sourcePath = Resolve-PackageTargetArtifactText -Text ([string]$candidate.sourcePath) -ArtifactTarget $target -VersionEntry $versionEntry -UpstreamRelease $upstreamRelease } elseif (-not [string]::IsNullOrWhiteSpace($artifactSourcePath)) { $candidate | Add-Member -MemberType NoteProperty -Name 'sourcePath' -Value $artifactSourcePath -Force } } $candidate } ) $packageId = if ($artifact.PSObject.Properties['artifactId'] -and -not [string]::IsNullOrWhiteSpace([string]$artifact.artifactId)) { [string]$artifact.artifactId } else { '{0}-{1}-{2}' -f [string]$definition.id, [string]$target.id, [string]$versionEntry.version } return [pscustomobject]@{ id = $packageId artifactId = [string]$artifact.artifactId version = [string]$versionEntry.version releaseTag = if ($upstreamRelease -and $upstreamRelease.PSObject.Properties['releaseTag']) { [string]$upstreamRelease.releaseTag } else { $null } releaseTrack = [string]$target.releaseTrack artifactDistributionVariant = [string]$target.artifactDistributionVariant artifactTargetId = [string]$target.id constraints = ConvertTo-PackageObject -InputObject $target.constraints packageFile = if ($packageFile) { [pscustomobject]$packageFile } else { $null } upstreamRelease = ConvertTo-PackageObject -InputObject $upstreamRelease acquisitionCandidates = @($acquisitionCandidates | Sort-Object -Property @{ Expression = { if ($_.PSObject.Properties['searchOrder']) { [int]$_.searchOrder } else { [int]::MaxValue } } }) compatibility = ConvertTo-PackageObject -InputObject $definition.packageOperations.policy.compatibility presenceDiscovery = ConvertTo-PackageObject -InputObject $definition.presenceDiscovery existingInstallDiscovery = ConvertTo-PackageObject -InputObject $definition.existingInstallDiscovery ownershipPolicy = ConvertTo-PackageObject -InputObject $definition.packageOperations.policy.ownershipPolicy assigned = $assigned removed = ConvertTo-PackageObject -InputObject $definition.packageOperations.removed readiness = New-PackageReadinessFromPresenceDiscovery -Definition $definition -Assigned $assigned } } |