Support/ExecutionCore/Eigenverft.Manifested.Sandbox.ExecutionCore.Archive.ps1
|
<#
Eigenverft.Manifested.Sandbox.ExecutionCore.Archive All archive helpers for ExecutionCore live in this one file. - Expand-ArchiveToDirectory — what Package and callers use: path checks, destination creation, .nupkg→.zip alias, then extract. - Expand-ZipArchiveFileToDirectory — internal seam: .zip file + existing directory → bytes on disk. Replace its body (Expand-Archive today) with PS 5.1–friendly .NET ZipFile / long-path logic when needed; keep Expand-ArchiveToDirectory unchanged. - New-TemporaryStageDirectory, Get-ExpandedArchiveRoot, Expand-ArchiveToStage — staging and layout heuristics for install flows. Rationale: a separate ArchiveExpand.ps1 was only a thin wrapper (~60 lines, one public function). That split cost more mental overhead than it saved; extract backend stays a clearly marked function here until a real alternative implementation warrants a file of its own. #> function Expand-ZipArchiveFileToDirectory { <# .SYNOPSIS Extracts a .zip file into an existing destination directory. .DESCRIPTION Not the main entry point — use Expand-ArchiveToDirectory from outside this module. Callers pass a resolved .zip path (.nupkg must already be aliased to .zip by Expand-ArchiveToDirectory). This function is the single place to swap Expand-Archive for a .NET-based extractor. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$ZipArchivePath, [Parameter(Mandatory = $true)] [string]$DestinationDirectory, [switch]$Overwrite ) if ([string]::IsNullOrWhiteSpace($ZipArchivePath)) { throw 'A zip archive path is required.' } if ([string]::IsNullOrWhiteSpace($DestinationDirectory)) { throw 'A destination directory is required.' } $resolvedZip = [System.IO.Path]::GetFullPath($ZipArchivePath) $resolvedDest = [System.IO.Path]::GetFullPath($DestinationDirectory) if (-not (Test-Path -LiteralPath $resolvedZip -PathType Leaf)) { throw "Zip archive '$resolvedZip' was not found." } $ext = [System.IO.Path]::GetExtension($resolvedZip) if (-not [string]::Equals($ext, '.zip', [System.StringComparison]::OrdinalIgnoreCase)) { throw "Expand-ZipArchiveFileToDirectory expects a .zip file; got extension '$ext'." } if (-not (Test-Path -LiteralPath $resolvedDest -PathType Container)) { throw "Destination directory '$resolvedDest' must exist before extraction." } if ($Overwrite) { $null = Expand-Archive -LiteralPath $resolvedZip -DestinationPath $resolvedDest -Force } else { $null = Expand-Archive -LiteralPath $resolvedZip -DestinationPath $resolvedDest } return $resolvedDest } function Expand-ArchiveToDirectory { <# .SYNOPSIS Expands an archive into a destination directory. .DESCRIPTION Ensures the destination directory exists, normalizes .nupkg to a temporary .zip when needed, then calls Expand-ZipArchiveFileToDirectory for the actual extract. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$ArchivePath, [Parameter(Mandatory = $true)] [string]$DestinationDirectory, [switch]$Overwrite ) if ([string]::IsNullOrWhiteSpace($ArchivePath)) { throw 'An archive path is required.' } if ([string]::IsNullOrWhiteSpace($DestinationDirectory)) { throw 'A destination directory is required.' } $resolvedArchivePath = [System.IO.Path]::GetFullPath($ArchivePath) if (-not (Test-Path -LiteralPath $resolvedArchivePath -PathType Leaf)) { throw "Archive '$resolvedArchivePath' was not found." } $resolvedDestinationDirectory = [System.IO.Path]::GetFullPath($DestinationDirectory) $null = New-Item -ItemType Directory -Path $resolvedDestinationDirectory -Force $archivePathForExpansion = $resolvedArchivePath $archiveAliasPath = $null if ([string]::Equals([System.IO.Path]::GetExtension($resolvedArchivePath), '.nupkg', [System.StringComparison]::OrdinalIgnoreCase)) { $archiveAliasPath = Join-Path $resolvedDestinationDirectory ('{0}.zip' -f [System.IO.Path]::GetFileNameWithoutExtension($resolvedArchivePath)) Copy-Item -LiteralPath $resolvedArchivePath -Destination $archiveAliasPath -Force $archivePathForExpansion = $archiveAliasPath } try { $null = Expand-ZipArchiveFileToDirectory -ZipArchivePath $archivePathForExpansion -DestinationDirectory $resolvedDestinationDirectory -Overwrite:$Overwrite } finally { if ($archiveAliasPath -and (Test-Path -LiteralPath $archiveAliasPath)) { Remove-Item -LiteralPath $archiveAliasPath -Force -ErrorAction SilentlyContinue } } return $resolvedDestinationDirectory } function New-TemporaryStageDirectory { <# .SYNOPSIS Creates a short temporary stage directory for execution-time work. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Prefix ) if ([string]::IsNullOrWhiteSpace($Prefix)) { throw 'A stage prefix is required.' } $stageRoot = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), 'evf', 'stg', $Prefix)) $null = New-Item -ItemType Directory -Path $stageRoot -Force do { $stagePath = Join-Path $stageRoot ([Guid]::NewGuid().ToString('N').Substring(0, 12)) } while (Test-Path -LiteralPath $stagePath) $null = New-Item -ItemType Directory -Path $stagePath -Force return ([System.IO.Path]::GetFullPath($stagePath)) } function Get-ExpandedArchiveRoot { <# .SYNOPSIS Resolves the effective expanded root inside a stage directory. .DESCRIPTION If the archive expands under one top-level directory and no files are written directly to the stage root, the child directory is returned. Otherwise the stage root itself is returned. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$StagePath ) if ([string]::IsNullOrWhiteSpace($StagePath)) { throw 'A stage path is required.' } $resolvedStagePath = [System.IO.Path]::GetFullPath($StagePath) if (-not (Test-Path -LiteralPath $resolvedStagePath -PathType Container)) { throw "Stage path '$resolvedStagePath' was not found." } $directories = @(Get-ChildItem -LiteralPath $resolvedStagePath -Directory -Force -ErrorAction SilentlyContinue) $files = @(Get-ChildItem -LiteralPath $resolvedStagePath -File -Force -ErrorAction SilentlyContinue) if ($directories.Count -eq 1 -and $files.Count -eq 0) { return $directories[0].FullName } return $resolvedStagePath } function Expand-ArchiveToStage { <# .SYNOPSIS Expands an archive into a new temporary stage directory. .DESCRIPTION Creates a temporary stage directory, expands the archive into it, and returns both the stage path and the effective expanded root. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$ArchivePath, [Parameter(Mandatory = $true)] [string]$Prefix ) $stagePath = New-TemporaryStageDirectory -Prefix $Prefix Expand-ArchiveToDirectory -ArchivePath $ArchivePath -DestinationDirectory $stagePath -Overwrite | Out-Null return [pscustomobject]@{ StagePath = $stagePath ExpandedRoot = (Get-ExpandedArchiveRoot -StagePath $stagePath) } } |