Private/Layout/Test-AvmModuleLayout.ps1
|
function Test-AvmModuleLayout { <# .SYNOPSIS Verify the on-disk layout, file casing and manifest shape of an Avm.Authoring module folder. .DESCRIPTION Implements the structural checks defined in the implementation spec section 12 (layout enforcement). Used by the InvokeBuild 'layout' task and available to any tooling that needs the same guarantees: - The folder name uses the exact expected casing. - The manifest (.psd1) and module (.psm1) file names exist with exact casing on case-sensitive file systems. - The manifest Name field matches the manifest file basename exactly, including casing. - The manifest declares a PowerShellVersion >= the minimum the spec requires. Throws on the first failure so the caller (build, CI gate, smoke test) sees one canonical error. Returns the validated module manifest object on success. .PARAMETER ModuleRoot Absolute path to the module folder (the directory that contains the .psd1). .PARAMETER ExpectedFolderName Expected leaf folder name with exact casing. Defaults to 'Avm.Authoring'. .PARAMETER MinimumPowerShellVersion Minimum value accepted for the manifest's PowerShellVersion field. Defaults to 7.4 (spec section 2). .OUTPUTS Microsoft.PowerShell.Commands.PSModuleInfo from Test-ModuleManifest. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSModuleInfo])] param( [Parameter(Mandatory)] [string] $ModuleRoot, [string] $ExpectedFolderName = 'Avm.Authoring', [version] $MinimumPowerShellVersion = [version]'7.4' ) Set-StrictMode -Version 3.0 $ErrorActionPreference = 'Stop' if (-not (Test-Path -LiteralPath $ModuleRoot -PathType Container)) { throw [AvmConfigurationException]::new("Module root does not exist: $ModuleRoot") } $manifestPath = Join-Path $ModuleRoot "$ExpectedFolderName.psd1" if (-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) { throw [AvmConfigurationException]::new("Manifest not found: $manifestPath") } $manifest = Test-ModuleManifest -Path $manifestPath # Folder casing. $folder = Split-Path -Leaf $ModuleRoot if ($folder -cne $ExpectedFolderName) { throw [AvmConfigurationException]::new( "Module folder casing is '$folder'; expected '$ExpectedFolderName'.") } # File casing for the manifest and the root .psm1. foreach ($expected in @("$ExpectedFolderName.psd1", "$ExpectedFolderName.psm1")) { $found = Get-ChildItem -Path $ModuleRoot -File | Where-Object { $_.Name -ceq $expected } if (-not $found) { throw [AvmConfigurationException]::new( "Expected file '$expected' not found with exact casing in $ModuleRoot.") } } # Manifest Name vs file basename casing. $expectedName = [System.IO.Path]::GetFileNameWithoutExtension($manifestPath) if ($manifest.Name -cne $expectedName) { throw [AvmConfigurationException]::new( "Manifest Name '$($manifest.Name)' does not match file basename '$expectedName' (case-sensitive).") } # PowerShellVersion must be >= the spec floor. if ($manifest.PowerShellVersion -lt $MinimumPowerShellVersion) { throw [AvmConfigurationException]::new( "Manifest PowerShellVersion is '$($manifest.PowerShellVersion)'; must be >= $MinimumPowerShellVersion.") } return $manifest } |