functions/Get-ExtensionDependencies.ps1
# <copyright file="Get-ExtensionDependencies.ps1" company="Endjin Limited"> # Copyright (c) Endjin Limited. All rights reserved. # </copyright> function Get-ExtensionDependencies { <# .SYNOPSIS Retrieves the dependencies of a given extension by reading its module manifest. .DESCRIPTION Retrieves the dependencies of a given extension by reading its module manifest and falling-back to the legacy definition method in a `dependencies.psd1` file. .PARAMETER Extension The metadata object for the extension we are resolving dependencies for. .INPUTS None. You can't pipe objects to Get-ExtensionDependencies. .OUTPUTS hashtable[] Returns the resolved extension metadata for each dependency of the specified extension. .EXAMPLE PS:> Get-ExtensionDependencies -Extension $extension @{ Name = "some-dependency" Version = "1.0.0" } .NOTES By convention an extension must declare its dependencies in its module manifest under the 'PrivateData' key. The dependencies can be specified in one of two formats: Short-hand syntax: PrivateData = @{ ZeroFailed = @{ ExtensionDependencies = @( 'ExtensionA' ) } } Full syntax: PrivateData = @{ ZeroFailed = @{ ExtensionDependencies = @( @{ Name = 'ExtensionA' Version = '1.0.0' } @{ Name = 'MyExtension' GitRepository = 'https://github.com/myorg/myextension GitRef = 'main' } ) } } Mixed syntax: PrivateData = @{ ZeroFailed = @{ ExtensionDependencies = @( 'ExtensionA' @{ Name = 'ExtensionB' Version = '2.0.0' } ) } } #> [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [hashtable] $Extension ) # Constants $ZF_PRIVATEDATA_KEY_NAME = "ZeroFailed" $ZF_EXTENSION_DEPENDENCIES_KEY_NAME = "ExtensionDependencies" $resolvedDeps = @() $dependenciesConfig = $null # We currently support 2 mechanisms for extensions to declare their dependencies: # 1. via a 'dependencies.psd1' (considered legacy due to a structural bug) # 2. via configuration stored in the module manifest under the 'PrivateData' key # # Option 2 is preferred with Option 1 retained as a fallback for backwards-compatibility. $extensionModuleManifestPath = Join-Path -Path $Extension.Path -ChildPath "$($Extension.Name).psd1" $extensionModuleManifest = Import-PowerShellDataFile -Path $extensionModuleManifestPath if ($extensionModuleManifest.ContainsKey("PrivateData") -and ` $extensionModuleManifest.PrivateData.ContainsKey($ZF_PRIVATEDATA_KEY_NAME) ) { if ($extensionModuleManifest.PrivateData.$ZF_PRIVATEDATA_KEY_NAME.ContainsKey($ZF_EXTENSION_DEPENDENCIES_KEY_NAME)) { Write-Verbose "Reading dependencies from module manifest" $dependenciesConfig = $extensionModuleManifest.PrivateData.$ZF_PRIVATEDATA_KEY_NAME.$ZF_EXTENSION_DEPENDENCIES_KEY_NAME } else { if ($extensionModuleManifest.PrivateData.$ZF_PRIVATEDATA_KEY_NAME.Keys.Count -gt 0) { Write-Warning "Unknown '$ZF_PRIVATEDATA_KEY_NAME' configuration keys were detected in the extension's module manifest: $($extensionModuleManifest.PrivateData.$ZF_PRIVATEDATA_KEY_NAME.Keys)" } } } else { # Fallback to legacy mechanism as backwards-compatibility measure $legacyDepConfigPath = Join-Path -Path $Extension.Path -ChildPath 'dependencies.psd1' if ((Test-Path $legacyDepConfigPath)) { Write-Warning "Reading dependencies from 'dependencies.psd1', which is now deprecated. The extension developer should move them to the module manifest under the 'PrivateData.$ZF_PRIVATEDATA_KEY_NAME.$ZF_EXTENSION_DEPENDENCIES_KEY_NAME' key." # Log a warning if an array syntax has been used (i.e. potentially multiple dependencies have been specified) # Whilst 'Import-PowerShellDataFile' will process such a file without error, it will # only return the first item in the array. if ((Get-Content $legacyDepConfigPath -Raw).TrimStart().StartsWith("@(")) { Write-Warning "Possible multiple dependencies in 'dependencies.psd1'; this is not supported, only the first one will be available. Please migrate to the above method." } $dependenciesConfig = Import-PowerShellDataFile -Path $legacyDepConfigPath if ($dependenciesConfig.Keys.Count -eq 0) { # Ensure an empty .psd1 file is treated as no dependencies $dependenciesConfig = $null } } } # Detect if no dependencies foreach ($dependencyConfig in [array]$dependenciesConfig) { try { # Use existing logic to resolve the supported syntaxes into the canonical form $resolvedDeps += Resolve-ExtensionMetadata -Value $dependencyConfig } catch { throw "Failed to resolve extension metadata for dependency due to invalid configuration: $($_.Exception.Message)`ndependencyConfig: $($dependencyConfig | ConvertTo-Json -Depth 3)" } } Write-Verbose "Resolved Dependencies: $($resolvedDeps | ConvertTo-Json -Depth 3)" return $resolvedDeps } |