Support/Package/Execution/Eigenverft.Manifested.Sandbox.Package.PathRegistration.ps1
|
<#
Eigenverft.Manifested.Sandbox.Package.PathRegistration #> function Add-PackagePathCleanupDirectory { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [AllowEmptyCollection()] [System.Collections.Generic.List[string]]$CleanupDirectories, [AllowNull()] [string]$DirectoryPath ) $normalizedDirectoryPath = Get-NormalizedPathEntry -PathEntry $DirectoryPath if ([string]::IsNullOrWhiteSpace($normalizedDirectoryPath)) { return } if ($normalizedDirectoryPath -notin @($CleanupDirectories.ToArray())) { $CleanupDirectories.Add($normalizedDirectoryPath) | Out-Null } } function Get-PackagePathRegistrationSourceValues { [CmdletBinding()] param( [AllowNull()] [psobject]$PackageResult, [AllowNull()] [psobject]$Source ) $values = New-Object System.Collections.Generic.List[string] $sourceKind = if ($Source -and $Source.PSObject.Properties['kind']) { [string]$Source.kind } else { $null } if ([string]::Equals($sourceKind, 'shim', [System.StringComparison]::OrdinalIgnoreCase)) { if (-not $Source.PSObject.Properties['use'] -or -not [string]::Equals([string]$Source.use, 'presenceDiscovery.commands', [System.StringComparison]::Ordinal)) { throw "Package pathRegistration source kind 'shim' requires source.use = 'presenceDiscovery.commands'." } foreach ($command in @(Get-PackagePresenceDiscoveryEntryPoints -Definition $PackageResult.PackageConfig.Definition -ToolKind 'commands' -ExposedOnly)) { $commandName = ([string]$command.name).Trim() if (-not [string]::IsNullOrWhiteSpace($commandName) -and $commandName -notin @($values.ToArray())) { $values.Add($commandName) | Out-Null } } return @($values.ToArray()) } if ($Source -and $Source.PSObject.Properties['value'] -and -not [string]::IsNullOrWhiteSpace([string]$Source.value)) { $values.Add([string]$Source.value) | Out-Null } if ($Source -and $Source.PSObject.Properties['values'] -and $null -ne $Source.values) { foreach ($value in @($Source.values)) { $textValue = ([string]$value).Trim() if ([string]::IsNullOrWhiteSpace($textValue)) { continue } if ($textValue -notin @($values.ToArray())) { $values.Add($textValue) | Out-Null } } } return @($values.ToArray()) } function Get-PackagePathRegistrationSourcePath { <# .SYNOPSIS Resolves the raw source path for PATH registration. .DESCRIPTION Interprets the configured pathRegistration source and returns the concrete file or directory path for the requested install directory. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$PackageResult, [string]$InstallDirectoryOverride ) $install = Get-PackageAssignedOperation -Release $PackageResult.Package if (-not $install -or -not $install.PSObject.Properties['pathRegistration'] -or $null -eq $install.pathRegistration) { return $null } $pathRegistration = $install.pathRegistration if (-not $pathRegistration.PSObject.Properties['source'] -or $null -eq $pathRegistration.source) { return $null } $source = $pathRegistration.source $sourceKind = if ($source.PSObject.Properties['kind']) { [string]$source.kind } else { $null } $sourceValues = @(Get-PackagePathRegistrationSourceValues -PackageResult $PackageResult -Source $source) $sourceValue = if ($sourceValues.Count -gt 0) { [string]$sourceValues[0] } else { $null } if ([string]::IsNullOrWhiteSpace($sourceKind) -or $sourceValues.Count -eq 0) { throw "Package pathRegistration requires source.kind and a resolvable source value when pathRegistration.mode is not 'none'." } $baseInstallDirectory = if (-not [string]::IsNullOrWhiteSpace($InstallDirectoryOverride)) { $InstallDirectoryOverride } else { $PackageResult.InstallDirectory } switch -Exact ($sourceKind) { 'commandEntryPoint' { if ($sourceValues.Count -gt 1) { throw "Package pathRegistration source kind 'commandEntryPoint' supports only one source value." } $sourcePath = Resolve-PackagePresenceToolPath -Definition $PackageResult.PackageConfig.Definition -ToolKind 'commands' -Name $sourceValue -InstallDirectory $baseInstallDirectory if (-not [string]::IsNullOrWhiteSpace($sourcePath)) { return $sourcePath } throw "Package pathRegistration source commandEntryPoint '$sourceValue' was not found in presenceDiscovery.commands." } 'appEntryPoint' { if ($sourceValues.Count -gt 1) { throw "Package pathRegistration source kind 'appEntryPoint' supports only one source value." } $sourcePath = Resolve-PackagePresenceToolPath -Definition $PackageResult.PackageConfig.Definition -ToolKind 'apps' -Name $sourceValue -InstallDirectory $baseInstallDirectory if (-not [string]::IsNullOrWhiteSpace($sourcePath)) { return $sourcePath } throw "Package pathRegistration source appEntryPoint '$sourceValue' was not found in presenceDiscovery.apps." } 'installRelativeDirectory' { if ($sourceValues.Count -gt 1) { throw "Package pathRegistration source kind 'installRelativeDirectory' supports only one source value." } return (Join-Path $baseInstallDirectory (($sourceValue) -replace '/', '\')) } 'shim' { if (-not [string]::IsNullOrWhiteSpace($InstallDirectoryOverride)) { return (Get-PackageCommandShimPath -PackageResult $PackageResult -CommandName $sourceValues[0]) } $shimResults = @( foreach ($commandName in $sourceValues) { New-PackageCommandShim -PackageResult $PackageResult -CommandName $commandName } ) return $shimResults[0].ShimPath } default { throw "Unsupported Package pathRegistration source kind '$sourceKind'." } } } function Test-PackageShouldApplyPathRegistration { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$PackageResult ) return ([string]$PackageResult.InstallOrigin) -in @('PackageInstalled', 'PackageReused') } function Get-PackagePathRegistrationCleanupDirectories { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$PackageResult ) $currentInstallDirectory = Get-NormalizedPathEntry -PathEntry ([string]$PackageResult.InstallDirectory) $cleanupDirectories = New-Object System.Collections.Generic.List[string] $candidateInstallDirectories = New-Object System.Collections.Generic.List[string] $assignedForPath = Get-PackageAssignedOperation -Release $PackageResult.Package if (-not $assignedForPath -or -not $assignedForPath.PSObject.Properties['pathRegistration'] -or $null -eq $assignedForPath.pathRegistration) { return @() } $currentSource = $assignedForPath.pathRegistration.source $currentSourceKind = if ($currentSource.PSObject.Properties['kind']) { [string]$assignedForPath.pathRegistration.source.kind } else { $null } $currentSourceValues = @(Get-PackagePathRegistrationSourceValues -PackageResult $PackageResult -Source $currentSource) if ($PackageResult.ExistingPackage -and [string]::Equals([string]$PackageResult.ExistingPackage.Classification, 'PackageTarget', [System.StringComparison]::OrdinalIgnoreCase) -and -not [string]::IsNullOrWhiteSpace([string]$PackageResult.ExistingPackage.InstallDirectory)) { $candidateInstallDirectories.Add([string]$PackageResult.ExistingPackage.InstallDirectory) | Out-Null } if ($PackageResult.Ownership -and $PackageResult.Ownership.OwnershipRecord -and $PackageResult.Ownership.OwnershipRecord.PSObject.Properties['installDirectory'] -and -not [string]::IsNullOrWhiteSpace([string]$PackageResult.Ownership.OwnershipRecord.installDirectory)) { $candidateInstallDirectories.Add([string]$PackageResult.Ownership.OwnershipRecord.installDirectory) | Out-Null } foreach ($candidateInstallDirectory in @($candidateInstallDirectories.ToArray())) { $normalizedCandidateInstallDirectory = Get-NormalizedPathEntry -PathEntry $candidateInstallDirectory if ([string]::IsNullOrWhiteSpace($normalizedCandidateInstallDirectory) -or [string]::Equals($normalizedCandidateInstallDirectory, $currentInstallDirectory, [System.StringComparison]::OrdinalIgnoreCase)) { continue } if ([string]::Equals($currentSourceKind, 'shim', [System.StringComparison]::OrdinalIgnoreCase)) { continue } $candidateSourcePath = Get-PackagePathRegistrationSourcePath -PackageResult $PackageResult -InstallDirectoryOverride $candidateInstallDirectory $candidateSourceKind = $currentSourceKind $candidateRegisteredDirectory = Resolve-PathRegistrationDirectory -SourcePath $candidateSourcePath -SourceKind $candidateSourceKind Add-PackagePathCleanupDirectory -CleanupDirectories $cleanupDirectories -DirectoryPath $candidateRegisteredDirectory } if ([string]::Equals($currentSourceKind, 'shim', [System.StringComparison]::OrdinalIgnoreCase) -and $currentSourceValues.Count -gt 0) { $directCleanupInstallDirectories = New-Object System.Collections.Generic.List[string] $directCandidateInstallDirectories = @($candidateInstallDirectories.ToArray()) + @([string]$PackageResult.InstallDirectory) foreach ($candidateInstallDirectory in $directCandidateInstallDirectories) { if ([string]::IsNullOrWhiteSpace($candidateInstallDirectory)) { continue } $normalizedCandidateInstallDirectory = Get-NormalizedPathEntry -PathEntry $candidateInstallDirectory if ([string]::IsNullOrWhiteSpace($normalizedCandidateInstallDirectory) -or $normalizedCandidateInstallDirectory -in @($directCleanupInstallDirectories.ToArray())) { continue } $directCleanupInstallDirectories.Add($normalizedCandidateInstallDirectory) | Out-Null } foreach ($candidateInstallDirectory in @($directCleanupInstallDirectories.ToArray())) { foreach ($currentSourceValue in $currentSourceValues) { $directCommandPath = Resolve-PackagePresenceToolPath -Definition $PackageResult.PackageConfig.Definition -ToolKind 'commands' -Name $currentSourceValue -InstallDirectory $candidateInstallDirectory if ([string]::IsNullOrWhiteSpace($directCommandPath)) { continue } $directCommandDirectory = Resolve-PathRegistrationDirectory -SourcePath $directCommandPath -SourceKind 'commandEntryPoint' Add-PackagePathCleanupDirectory -CleanupDirectories $cleanupDirectories -DirectoryPath $directCommandDirectory } } } return @($cleanupDirectories.ToArray()) } function Register-PackagePath { <# .SYNOPSIS Applies Package PATH registration for a validated install. .DESCRIPTION Updates process and persisted PATH scopes according to assigned.pathRegistration. User mode updates Process and User PATH. Machine mode updates Process and Machine PATH. None skips registration. Package only writes PATH entries for Package-owned outcomes and only cleans stale Package-owned paths for the same install slot. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$PackageResult ) $install = Get-PackageAssignedOperation -Release $PackageResult.Package $pathRegistration = if ($install -and $install.PSObject.Properties['pathRegistration']) { $install.pathRegistration } else { $null } $mode = if ($pathRegistration -and $pathRegistration.PSObject.Properties['mode'] -and -not [string]::IsNullOrWhiteSpace([string]$pathRegistration.mode)) { ([string]$pathRegistration.mode).ToLowerInvariant() } else { 'none' } $source = if ($pathRegistration -and $pathRegistration.PSObject.Properties['source']) { $pathRegistration.source } else { $null } $sourceKind = if ($source -and $source.PSObject.Properties['kind']) { [string]$source.kind } else { $null } $sourceValues = @(Get-PackagePathRegistrationSourceValues -PackageResult $PackageResult -Source $source) $sourceValue = if ($sourceValues.Count -gt 0) { $sourceValues -join ',' } else { $null } if ($mode -eq 'none') { $pathRegistrationResult = [pscustomobject]@{ Status = 'Skipped' Mode = 'none' SourceKind = $sourceKind SourceValue = $sourceValue SourceValues = @($sourceValues) SourcePath = $null RegisteredPath = $null CleanupDirectories = @() CleanedTargets = @() UpdatedTargets = @() } if ($PackageResult.PSObject.Properties['PathRegistration']) { $PackageResult.PathRegistration = $pathRegistrationResult } else { $PackageResult | Add-Member -MemberType NoteProperty -Name PathRegistration -Value $pathRegistrationResult } Write-PackageExecutionMessage -Message '[STATE] PATH registration skipped because mode is none.' return $PackageResult } if ($mode -notin @('user', 'machine')) { throw "Unsupported Package pathRegistration.mode '$mode'." } if (-not (Test-PackageShouldApplyPathRegistration -PackageResult $PackageResult)) { $pathRegistrationResult = [pscustomobject]@{ Status = 'SkippedNotPackageOwned' Mode = $mode SourceKind = $sourceKind SourceValue = $sourceValue SourceValues = @($sourceValues) SourcePath = $null RegisteredPath = $null CleanupDirectories = @() CleanedTargets = @() UpdatedTargets = @() } if ($PackageResult.PSObject.Properties['PathRegistration']) { $PackageResult.PathRegistration = $pathRegistrationResult } else { $PackageResult | Add-Member -MemberType NoteProperty -Name PathRegistration -Value $pathRegistrationResult } Write-PackageExecutionMessage -Message ("[STATE] PATH registration skipped because installOrigin '{0}' is not Package-owned." -f [string]$PackageResult.InstallOrigin) return $PackageResult } $sourcePath = Get-PackagePathRegistrationSourcePath -PackageResult $PackageResult if ([string]::IsNullOrWhiteSpace($sourcePath) -or -not (Test-Path -LiteralPath $sourcePath)) { throw "Package PATH registration source path '$sourcePath' was not found." } $registeredPath = Resolve-PathRegistrationDirectory -SourcePath $sourcePath -SourceKind $sourceKind $cleanupDirectories = @(Get-PackagePathRegistrationCleanupDirectories -PackageResult $PackageResult) $registrationResult = Register-PathEnvironment -Mode $mode -RegisteredPath $registeredPath -CleanupDirectories $cleanupDirectories $pathRegistrationResult = [pscustomobject]@{ Status = $registrationResult.Status Mode = $registrationResult.Mode SourceKind = $sourceKind SourceValue = $sourceValue SourceValues = @($sourceValues) SourcePath = $sourcePath RegisteredPath = $registrationResult.RegisteredPath CleanupDirectories = @($registrationResult.CleanupDirectories) CleanedTargets = @($registrationResult.CleanedTargets) UpdatedTargets = @($registrationResult.UpdatedTargets) } if ($PackageResult.PSObject.Properties['PathRegistration']) { $PackageResult.PathRegistration = $pathRegistrationResult } else { $PackageResult | Add-Member -MemberType NoteProperty -Name PathRegistration -Value $pathRegistrationResult } Write-PackageExecutionMessage -Message ("[STATE] PATH registration resolved source kind='{0}' value='{1}' to '{2}'." -f $sourceKind, $sourceValue, $registeredPath) if ($cleanupDirectories.Count -gt 0) { Write-PackageExecutionMessage -Message ("[STATE] PATH cleanup directories for this Package install slot: {0}" -f ($cleanupDirectories -join ', ')) } Write-PackageExecutionMessage -Message ("[ACTION] PATH registration status='{0}' mode='{1}' updatedTargets='{2}' cleanedTargets='{3}'." -f $PackageResult.PathRegistration.Status, $mode, $(if ($registrationResult.UpdatedTargets.Count -gt 0) { @($registrationResult.UpdatedTargets) -join ',' } else { '<none>' }), $(if ($registrationResult.CleanedTargets.Count -gt 0) { @($registrationResult.CleanedTargets) -join ',' } else { '<none>' })) return $PackageResult } function Unregister-PackagePathForRemoval { <# .SYNOPSIS Removes Package PATH entries recorded during assignment, without re-adding a primary path. .DESCRIPTION Uses the persisted pathRegistration snapshot from inventory (attached to PackageResult.PathRegistration) to remove registeredPath and cleanup directories from PATH for the same mode as registration. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [psobject]$PackageResult ) $pathRegistration = if ($PackageResult.PSObject.Properties['PathRegistration'] -and $null -ne $PackageResult.PathRegistration) { $PackageResult.PathRegistration } else { $null } if (-not $pathRegistration -or -not $pathRegistration.PSObject.Properties['mode']) { Write-PackageExecutionMessage -Message '[STATE] PATH unregistration skipped because no pathRegistration was resolved.' return $PackageResult } $mode = ([string]$pathRegistration.mode).ToLowerInvariant() if ($mode -eq 'none' -or [string]::Equals([string]$pathRegistration.Status, 'Skipped', [System.StringComparison]::OrdinalIgnoreCase) -or [string]::Equals([string]$pathRegistration.Status, 'SkippedNotPackageOwned', [System.StringComparison]::OrdinalIgnoreCase)) { Write-PackageExecutionMessage -Message '[STATE] PATH unregistration skipped because registration was not active.' return $PackageResult } if ($mode -notin @('user', 'machine')) { throw "Unsupported Package pathRegistration.mode '$mode' for removal." } $registeredPath = if ($pathRegistration.PSObject.Properties['registeredPath'] -and -not [string]::IsNullOrWhiteSpace([string]$pathRegistration.registeredPath)) { [System.IO.Path]::GetFullPath([string]$pathRegistration.registeredPath) } else { $null } $cleanupDirectories = New-Object System.Collections.Generic.List[string] if ($registeredPath) { $cleanupDirectories.Add($registeredPath) | Out-Null } $cleanupFromRecord = if ($pathRegistration.PSObject.Properties['CleanupDirectories'] -and $null -ne $pathRegistration.CleanupDirectories) { @($pathRegistration.CleanupDirectories) } else { @() } foreach ($dir in @($cleanupFromRecord)) { if (-not [string]::IsNullOrWhiteSpace([string]$dir)) { $normalized = Get-NormalizedPathEntry -PathEntry ([string]$dir) if ($normalized -and $normalized -notin @($cleanupDirectories.ToArray())) { $cleanupDirectories.Add($normalized) | Out-Null } } } if ($PackageResult.InstallDirectory -and (Test-Path -LiteralPath $PackageResult.InstallDirectory -PathType Container)) { foreach ($extra in @(Get-PackagePathRegistrationCleanupDirectories -PackageResult $PackageResult)) { $normalized = Get-NormalizedPathEntry -PathEntry $extra if ($normalized -and $normalized -notin @($cleanupDirectories.ToArray())) { $cleanupDirectories.Add($normalized) | Out-Null } } } if ($cleanupDirectories.Count -eq 0) { Write-PackageExecutionMessage -Message '[STATE] PATH unregistration skipped because no directories were recorded for cleanup.' return $PackageResult } $unregisterResult = Remove-PathEnvironmentDirectories -Mode $mode -DirectoryPaths @($cleanupDirectories.ToArray()) Write-PackageExecutionMessage -Message ("[ACTION] PATH unregistration status='{0}' mode='{1}' updatedTargets='{2}'." -f $unregisterResult.Status, $mode, $(if ($unregisterResult.UpdatedTargets.Count -gt 0) { @($unregisterResult.UpdatedTargets) -join ',' } else { '<none>' })) return $PackageResult } |