Commands/Depot/Eigenverft.Manifested.Package.Cmd.PackageDepot.ps1
|
<#
Public depot-management command surface. #> function Get-PackageDepot { [CmdletBinding()] param( [string]$DepotId ) $depots = @(Get-PackageDepotSummaries) if ([string]::IsNullOrWhiteSpace($DepotId)) { return $depots } $match = @($depots | Where-Object { [string]::Equals([string]$_.DepotId, $DepotId, [System.StringComparison]::OrdinalIgnoreCase) }) if ($match.Count -eq 0) { throw "Package depot '$DepotId' was not found in '$((Get-PackageDepotInventoryPath))'." } return $match } function Invoke-PackageDepotMaterialize { <# .SYNOPSIS Materializes every already trusted package available for the current platform. .DESCRIPTION Discovers package definitions whose effective catalog status is signedTrusted, deduplicates their publisher/definition identities, and materializes each package plus its dependencies into configured depots. The command never prompts for new catalog trust, imports signing keys, accepts unsigned definitions, or removes files. Sync-PackageDepot is a deprecated alias for this command. .PARAMETER AllTrusted Confirms that the complete already-trusted current-platform catalog is the materialize scope. .PARAMETER ExcludeDefinitionId Omits matching definition ids from this run. .PARAMETER FailFast Stops after the first package that does not materialize successfully. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] [Alias('Sync-PackageDepot')] param( [Parameter(Mandatory = $true)] [switch]$AllTrusted, [AllowNull()] [string]$PublisherId = $null, [AllowNull()] [string[]]$Tag = @(), [AllowNull()] [string[]]$ExcludeDefinitionId = @(), [switch]$FailFast ) if ([string]::Equals($MyInvocation.InvocationName, 'Sync-PackageDepot', [System.StringComparison]::OrdinalIgnoreCase)) { Write-Warning "Sync-PackageDepot is deprecated. Use Invoke-PackageDepotMaterialize instead." } if (-not $AllTrusted.IsPresent) { throw 'Invoke-PackageDepotMaterialize currently requires -AllTrusted.' } $searchParameters = @{ CurrentPlatformOnly = $true IncludeIneligible = $true } if (-not [string]::IsNullOrWhiteSpace($PublisherId)) { $searchParameters.PublisherId = $PublisherId } $normalizedTags = @($Tag | ForEach-Object { ([string]$_).Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) if ($normalizedTags.Count -gt 0) { $searchParameters.Tag = $normalizedTags } $excluded = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase) foreach ($definitionId in @($ExcludeDefinitionId)) { $normalizedDefinitionId = ([string]$definitionId).Trim() if (-not [string]::IsNullOrWhiteSpace($normalizedDefinitionId)) { $excluded.Add($normalizedDefinitionId) | Out-Null } } $trustedCandidates = @( Search-Package @searchParameters | Where-Object { [string]::Equals([string]$_.CatalogTrustStatus, 'signedTrusted', [System.StringComparison]::OrdinalIgnoreCase) -and -not $excluded.Contains([string]$_.DefinitionId) } ) $selected = @( $trustedCandidates | Sort-Object -Property PublisherId, DefinitionId, EndpointSearchOrder, @{ Expression = 'DefinitionRevision'; Descending = $true } | Group-Object -Property { Get-PackageDependencyReferenceKey -PublisherId ([string]$_.PublisherId) -DefinitionId ([string]$_.DefinitionId) } | ForEach-Object { $_.Group | Select-Object -First 1 } | Sort-Object -Property PublisherId, DefinitionId ) if ($selected.Count -eq 0) { Write-Verbose 'No already signed-and-trusted package definitions are available for the requested current-platform scope.' return } $target = '{0} already trusted current-platform package definition(s)' -f $selected.Count if (-not $PSCmdlet.ShouldProcess($target, 'Materialize complete artifact sets into configured package depots')) { foreach ($package in $selected) { $assignmentPlan = New-PackageAssignmentPlanCore -PublisherId ([string]$package.PublisherId) -DefinitionId ([string]$package.DefinitionId) -Purpose Inspection -MaterializeOnly -RequireAlreadyTrusted [pscustomobject]@{ PSTypeName = 'Eigenverft.Manifested.Package.DepotSyncResult' PublisherId = [string]$package.PublisherId DefinitionId = [string]$package.DefinitionId Version = [string]$package.Version Status = if ($assignmentPlan.Accepted) { 'Planned' } else { 'Blocked' } AssignmentPlan = $assignmentPlan BlockerSummary = @($assignmentPlan.Blockers) PackageResult = @() ErrorMessage = $null } } return } foreach ($package in $selected) { $packageResults = @() $errorMessage = $null try { $packageResults = @(Invoke-Package -PublisherId ([string]$package.PublisherId) -DefinitionId ([string]$package.DefinitionId) -MaterializeOnly -RequireAlreadyTrusted) } catch { $errorMessage = $_.Exception.Message } $failedResults = @($packageResults | Where-Object { -not ([string]$_.Status -in @('Ready', 'Materialized')) }) $status = if (-not [string]::IsNullOrWhiteSpace($errorMessage) -or $failedResults.Count -gt 0 -or $packageResults.Count -eq 0) { 'Failed' } else { 'Materialized' } $syncResult = [pscustomobject]@{ PSTypeName = 'Eigenverft.Manifested.Package.DepotSyncResult' PublisherId = [string]$package.PublisherId DefinitionId = [string]$package.DefinitionId Version = [string]$package.Version Status = $status AssignmentPlan = $null BlockerSummary = @() PackageResult = @($packageResults) ErrorMessage = $errorMessage } $syncResult if ($FailFast.IsPresent -and [string]::Equals($status, 'Failed', [System.StringComparison]::OrdinalIgnoreCase)) { break } } } function Add-PackageDepot { [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$DepotId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$BasePath, [int]$SearchOrder = -1, [string]$After, [string[]]$SiteCode = @(), [switch]$Disabled, [switch]$Writable, [switch]$MirrorTarget, [switch]$EnsureExists ) Assert-PackageDepotId -DepotId $DepotId $documentInfo = Get-PackageDepotInventoryEditInfo if (Get-PackageDepotSourceProperty -Document $documentInfo.Document -DepotId $DepotId) { throw "Package depot '$DepotId' already exists in '$($documentInfo.Path)'. Use Set-PackageDepot to modify it." } $resolvedSearchOrder = if ($SearchOrder -ge 0) { $SearchOrder } elseif (-not [string]::IsNullOrWhiteSpace($After)) { Get-PackageDepotSearchOrderAfter -Document $documentInfo.Document -AfterDepotId $After } else { Get-PackageNextDepotSearchOrder -Document $documentInfo.Document } $source = New-PackageFilesystemDepotSource -BasePath $BasePath -SearchOrder $resolvedSearchOrder -Enabled (-not $Disabled.IsPresent) -SiteCodes $SiteCode -Writable $Writable.IsPresent -MirrorTarget $MirrorTarget.IsPresent -EnsureExists $EnsureExists.IsPresent if ($PSCmdlet.ShouldProcess($documentInfo.Path, "Add package depot '$DepotId'")) { $documentInfo.Document.acquisitionEnvironment.environmentSources | Add-Member -MemberType NoteProperty -Name $DepotId -Value $source -Force Save-PackageDepotInventoryDocument -DocumentInfo $documentInfo } $after = Get-PackageDepot -DepotId $DepotId $notes = New-Object System.Collections.Generic.List[string] if ($Disabled.IsPresent) { $notes.Add("Depot '$DepotId' was added disabled; package acquisition will not use it until enabled.") | Out-Null } if (-not $Writable.IsPresent) { $notes.Add("Depot '$DepotId' is read-only from Package perspective by default; downloads will not be mirrored there.") | Out-Null } return New-PackageDepotCommandResult -Action 'Add' -DepotId $DepotId -InventoryPath $documentInfo.Path -Before $null -After $after -Status 'Added' -Notes @($notes.ToArray()) } function Add-TeamPackageDepot { [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$BasePath, [ValidateNotNullOrEmpty()] [string]$DepotId = 'teamPackageDepot', [int]$SearchOrder = -1, [string]$After, [string[]]$SiteCode = @(), [switch]$Disabled ) $parameters = @{ DepotId = $DepotId BasePath = $BasePath SiteCode = $SiteCode Writable = $true MirrorTarget = $true EnsureExists = $true } if ($SearchOrder -ge 0) { $parameters.SearchOrder = $SearchOrder } elseif (-not [string]::IsNullOrWhiteSpace($After)) { $parameters.After = $After } else { $parameters.SearchOrder = 150 } if ($Disabled.IsPresent) { $parameters.Disabled = $true } if ($PSBoundParameters.ContainsKey('WhatIf')) { $parameters.WhatIf = [bool]$PSBoundParameters.WhatIf } if ($PSBoundParameters.ContainsKey('Confirm')) { $parameters.Confirm = [bool]$PSBoundParameters.Confirm } return Add-PackageDepot @parameters } function Set-PackageDepot { [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$DepotId, [string]$BasePath, [Nullable[int]]$SearchOrder, [string[]]$SiteCode, [switch]$ClearSiteCode, [switch]$Enable, [switch]$Disable, [switch]$Readable, [switch]$NotReadable, [switch]$Writable, [switch]$ReadOnly, [switch]$MirrorTarget, [switch]$NoMirrorTarget, [switch]$EnsureExists, [switch]$NoEnsureExists ) Assert-PackageDepotId -DepotId $DepotId if ($Enable.IsPresent -and $Disable.IsPresent) { throw 'Use either -Enable or -Disable, not both.' } if ($Readable.IsPresent -and $NotReadable.IsPresent) { throw 'Use either -Readable or -NotReadable, not both.' } if ($Writable.IsPresent -and $ReadOnly.IsPresent) { throw 'Use either -Writable or -ReadOnly, not both.' } if ($MirrorTarget.IsPresent -and $NoMirrorTarget.IsPresent) { throw 'Use either -MirrorTarget or -NoMirrorTarget, not both.' } if ($EnsureExists.IsPresent -and $NoEnsureExists.IsPresent) { throw 'Use either -EnsureExists or -NoEnsureExists, not both.' } if ($ClearSiteCode.IsPresent -and $PSBoundParameters.ContainsKey('SiteCode')) { throw 'Use either -SiteCode or -ClearSiteCode, not both.' } $before = Get-PackageDepot -DepotId $DepotId $documentInfo = Get-PackageDepotInventoryEditInfo $sourceProperty = Get-PackageDepotSourceProperty -Document $documentInfo.Document -DepotId $DepotId if (-not $sourceProperty) { throw "Package depot '$DepotId' was not found in '$($documentInfo.Path)'. Use Add-PackageDepot to create it." } $source = $sourceProperty.Value if (-not [string]::Equals([string]$source.kind, 'filesystem', [System.StringComparison]::OrdinalIgnoreCase)) { throw "Package depot '$DepotId' is kind '$($source.kind)'. Only filesystem depots can be modified by Set-PackageDepot." } if ($PSBoundParameters.ContainsKey('BasePath')) { $source | Add-Member -MemberType NoteProperty -Name 'basePath' -Value $BasePath -Force } if ($PSBoundParameters.ContainsKey('SearchOrder')) { $source | Add-Member -MemberType NoteProperty -Name 'searchOrder' -Value ([int]$SearchOrder.Value) -Force } if ($Enable.IsPresent) { $source | Add-Member -MemberType NoteProperty -Name 'enabled' -Value $true -Force } if ($Disable.IsPresent) { $source | Add-Member -MemberType NoteProperty -Name 'enabled' -Value $false -Force } if ($Readable.IsPresent) { $source | Add-Member -MemberType NoteProperty -Name 'readable' -Value $true -Force } if ($NotReadable.IsPresent) { $source | Add-Member -MemberType NoteProperty -Name 'readable' -Value $false -Force } if ($Writable.IsPresent) { $source | Add-Member -MemberType NoteProperty -Name 'writable' -Value $true -Force } if ($ReadOnly.IsPresent) { $source | Add-Member -MemberType NoteProperty -Name 'writable' -Value $false -Force $source | Add-Member -MemberType NoteProperty -Name 'mirrorTarget' -Value $false -Force $source | Add-Member -MemberType NoteProperty -Name 'ensureExists' -Value $false -Force } if ($MirrorTarget.IsPresent) { $source | Add-Member -MemberType NoteProperty -Name 'mirrorTarget' -Value $true -Force } if ($NoMirrorTarget.IsPresent) { $source | Add-Member -MemberType NoteProperty -Name 'mirrorTarget' -Value $false -Force } if ($EnsureExists.IsPresent) { $source | Add-Member -MemberType NoteProperty -Name 'ensureExists' -Value $true -Force } if ($NoEnsureExists.IsPresent) { $source | Add-Member -MemberType NoteProperty -Name 'ensureExists' -Value $false -Force } if ($PSBoundParameters.ContainsKey('SiteCode')) { $source | Add-Member -MemberType NoteProperty -Name 'siteCodes' -Value @($SiteCode | ForEach-Object { ([string]$_).Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) -Force } if ($ClearSiteCode.IsPresent -and $source.PSObject.Properties['siteCodes']) { $source.PSObject.Properties.Remove('siteCodes') } if ([bool]$source.mirrorTarget -and -not [bool]$source.writable) { throw "Package depot '$DepotId' cannot use MirrorTarget=true while Writable=false. Add -Writable or remove -MirrorTarget." } if ([bool]$source.ensureExists -and -not [bool]$source.writable) { throw "Package depot '$DepotId' cannot use EnsureExists=true while Writable=false. Add -Writable or remove -EnsureExists." } if ($PSCmdlet.ShouldProcess($documentInfo.Path, "Set package depot '$DepotId'")) { Save-PackageDepotInventoryDocument -DocumentInfo $documentInfo } $after = Get-PackageDepot -DepotId $DepotId $notes = New-Object System.Collections.Generic.List[string] if (-not [bool]$after.Enabled) { $notes.Add("Depot '$DepotId' was updated but remains disabled; package acquisition will not use it until enabled.") | Out-Null } elseif (-not [bool]$after.Effective) { $notes.Add("Depot '$DepotId' is enabled but is not currently effective. Check site codes or config validation notes.") | Out-Null } if ($PSBoundParameters.ContainsKey('BasePath')) { $notes.Add("Depot '$DepotId' base path is now '$($after.BasePath)' and resolves to '$($after.ResolvedBasePath)'.") | Out-Null } return New-PackageDepotCommandResult -Action 'Set' -DepotId $DepotId -InventoryPath $documentInfo.Path -Before $before -After $after -Status 'Updated' -Notes @($notes.ToArray()) } function Remove-PackageDepot { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$DepotId, [switch]$Force ) Assert-PackageDepotId -DepotId $DepotId if ([string]::Equals($DepotId, 'defaultPackageDepot', [System.StringComparison]::OrdinalIgnoreCase) -and -not $Force.IsPresent) { throw "Removing 'defaultPackageDepot' can leave Package without a local durable depot. Re-run with -Force if this is intentional." } $before = Get-PackageDepot -DepotId $DepotId $documentInfo = Get-PackageDepotInventoryEditInfo $sourceProperty = Get-PackageDepotSourceProperty -Document $documentInfo.Document -DepotId $DepotId if (-not $sourceProperty) { throw "Package depot '$DepotId' was not found in '$($documentInfo.Path)'." } if ($PSCmdlet.ShouldProcess($documentInfo.Path, "Remove package depot '$DepotId'")) { $documentInfo.Document.acquisitionEnvironment.environmentSources.PSObject.Properties.Remove($DepotId) Save-PackageDepotInventoryDocument -DocumentInfo $documentInfo } return New-PackageDepotCommandResult -Action 'Remove' -DepotId $DepotId -InventoryPath $documentInfo.Path -Before $before -After $null -Status 'Removed' -Notes @( "Depot '$DepotId' was removed from configuration only. Existing depot files were not deleted." ) } |