Private/ConvertTo-HDTOperatingSystemCatalog.ps1
|
function ConvertTo-HDTOperatingSystemCatalog { <# .SYNOPSIS Projects a validated os.yaml document into the catalog object the engine works with. .DESCRIPTION One projection, used by both Import-HDTOperatingSystem and Get-HDTOperatingSystem, so what the importer returns and what the reader returns are the same shape by construction rather than by two authors agreeing. IT RESOLVES ImagePath, AND THAT PATH IS THE SEAM 04-02 MARKED AND 05-02 CLOSED. Content access is abstracted behind a provider, and -Content is where one arrives: with -Content ImagePath is what the provider answered for the path relative to the provider's own root; without -Content a relative sourcePath is resolved against the operating system folder, exactly as before. The second form is not a leftover: Import-HDTOperatingSystem runs on an administrator's workstation with no provider in sight, and every test written before 05-02 still exercises it. NO STEP LOGIC MOVED WHEN THE PROVIDER ARRIVED. Invoke-HDTApplyImageStep passes $Context.Service.Content when the run was started with one and is otherwise unchanged, which tests/unit/Invoke-HDTApplyImageStep.Tests.ps1 asserts by running the same step through the Local and the Smb providers and comparing the ordered list of every service call: the arguments differ, the operations do not. A ROOTED sourcePath IS KEPT AS IT IS, provider or no provider. Media too large to bring into the share is registered where it stands, and a catalog that silently re-rooted it would send the apply step to a path with nothing in it. .PARAMETER Document The validated document, as ConvertFrom-HDTYaml returns it or as Import-HDTOperatingSystem built it. .PARAMETER OsFolder The operating system folder, built with Get-HDTWorkspacePath. .PARAMETER CatalogPath The os.yaml the document came from or is destined for. .PARAMETER Content An IContentProvider, or nothing. When supplied, a relative sourcePath is resolved through it - made relative to the provider's own root first, so 'sources\install.wim' under OperatingSystems\Win11-LTSC-2024 is asked for as 'OperatingSystems\Win11-LTSC-2024\sources\install.wim'. .OUTPUTS System.Management.Automation.PSCustomObject .EXAMPLE ConvertTo-HDTOperatingSystemCatalog -Document $document -OsFolder $folder -CatalogPath $path #> [CmdletBinding()] [OutputType([pscustomobject])] param( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNull()] [System.Collections.IDictionary] $Document, [Parameter(Mandatory = $true, Position = 1)] [ValidateNotNullOrEmpty()] [string] $OsFolder, [Parameter(Mandatory = $true, Position = 2)] [ValidateNotNullOrEmpty()] [string] $CatalogPath, [Parameter()] [AllowNull()] [object] $Content = $null ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $read = { param($Key) if ($Document.Contains($Key)) { return $Document[$Key] } return $null } $sourcePath = [string] (& $read 'sourcePath') # [IO.Path]::Combine, not Join-Path, for the reason Get-HDTWorkspacePath # gives: building a path must not require the drive to be mounted. $imagePath = $sourcePath if (-not [System.IO.Path]::IsPathRooted($sourcePath)) { if ($null -eq $Content) { $imagePath = [System.IO.Path]::Combine($OsFolder, $sourcePath.TrimStart('\', '/')) } else { # The provider answers for paths relative to ITS root, so the # operating system folder is made relative to that root first. A # folder that is not under the provider's root stays absolute and # the provider returns it unchanged, which is the same answer # DESIGN 9.3's registered-where-it-stands case gets. $folder = $OsFolder $root = [string] $Content.Root if ((-not [string]::IsNullOrEmpty($root)) -and $folder.StartsWith($root, [System.StringComparison]::OrdinalIgnoreCase)) { $folder = $folder.Substring($root.Length).TrimStart('\', '/') } $imagePath = [string] $Content.ResolveContent( [System.IO.Path]::Combine($folder, $sourcePath.TrimStart('\', '/'))) } } $image = New-Object -TypeName System.Collections.ArrayList foreach ($current in @($Document['images'])) { $entry = { param($Key) if ($current.Contains($Key)) { return $current[$Key] } return $null } [void] $image.Add([pscustomobject] @{ Index = [int] (& $entry 'index') Name = [string] (& $entry 'name') Description = [string] (& $entry 'description') Edition = [string] (& $entry 'edition') SizeBytes = [long] $(if ($null -eq (& $entry 'sizeBytes')) { 0 } else { (& $entry 'sizeBytes') }) Version = [string] (& $entry 'version') }) } $defaultIndex = 0 if ($null -ne (& $read 'defaultIndex')) { $defaultIndex = [int] (& $read 'defaultIndex') } return [pscustomobject] @{ SchemaVersion = [int] (& $read 'schemaVersion') Id = [string] (& $read 'id') Name = [string] (& $read 'name') Description = [string] (& $read 'description') Type = [string] (& $read 'type') Architecture = [string] (& $read 'architecture') SourcePath = $sourcePath ImportedUtc = [string] (& $read 'importedUtc') DefaultIndex = $defaultIndex Path = $CatalogPath OsFolder = $OsFolder ImagePath = $imagePath Images = [object[]] @($image) } } |