Install.ps1
|
# ColorScripts-Enhanced Installation Script [CmdletBinding(SupportsShouldProcess = $true)] param( [switch]$AllUsers, [switch]$AddToProfile, [switch]$SkipStartupScript, [switch]$BuildCache ) function Get-ModuleInstallRoot { [CmdletBinding()] param( [switch]$AllUsersScope ) $separator = [System.IO.Path]::PathSeparator $paths = $env:PSModulePath -split [System.Text.RegularExpressions.Regex]::Escape($separator) | Where-Object { $_ } if ($AllUsersScope) { if ($PROFILE.AllUsersAllHosts) { return Split-Path -Path $PROFILE.AllUsersAllHosts -Parent } return $paths | Where-Object { $_ -notlike "*$HOME*" } | Select-Object -First 1 } if ($PROFILE.CurrentUserAllHosts) { return Split-Path -Path $PROFILE.CurrentUserAllHosts -Parent } $userHome = [Environment]::GetFolderPath('UserProfile') if (-not $userHome) { $userHome = $HOME } return $paths | Where-Object { $userHome -and $_ -like "$userHome*" } | Select-Object -First 1 } function Assert-ColorScriptsInstallPrivilege { param( [switch]$AllUsers ) if (-not $AllUsers -or $PSVersionTable.PSEdition -ne 'Desktop' -or $WhatIfPreference) { return } $principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { throw 'AllUsers installation requires Administrator privileges.' } } function Write-ColorScriptsInstallHeader { Write-Host '' Write-Host '========================================================' -ForegroundColor Cyan Write-Host ' ColorScripts-Enhanced Module Installation' -ForegroundColor Cyan Write-Host '========================================================' -ForegroundColor Cyan } function New-ColorScriptsInstallRoot { [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory)] [string]$Path, [Parameter(Mandatory)] [System.Management.Automation.PSCmdlet]$Cmdlet ) if ((Test-Path -LiteralPath $Path) -or -not $Cmdlet.ShouldProcess($Path, 'Create module directory')) { return } New-Item -Path $Path -ItemType Directory -Force | Out-Null Write-Host '[OK] Created module directory' -ForegroundColor Green } function Remove-ColorScriptsInstallDirectoryWithRetry { param( [Parameter(Mandatory)] [string]$Path ) $attempt = 0 $maxAttempts = 5 while ($attempt -lt $maxAttempts) { $attempt++ try { Remove-Item -LiteralPath $Path -Recurse -Force -ErrorAction Stop return $true } catch [System.IO.IOException], [System.UnauthorizedAccessException] { Start-Sleep -Milliseconds (80 * $attempt) } } return $false } function Move-ColorScriptsInstallDirectoryAside { param( [Parameter(Mandatory)] [string]$Path ) try { $backupPath = "{0}.old.{1}" -f $Path, ([guid]::NewGuid().ToString('N')) Move-Item -LiteralPath $Path -Destination $backupPath -Force -ErrorAction Stop } catch { Write-Warning ("Failed to remove existing module '{0}': {1}" -f $Path, $_.Exception.Message) return $false } try { Remove-Item -LiteralPath $backupPath -Recurse -Force -ErrorAction Stop } catch { Write-Warning ("Moved existing module to '{0}' but could not delete it: {1}" -f $backupPath, $_.Exception.Message) } return $true } function Remove-ExistingColorScriptsInstall { [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory)] [string]$Path, [Parameter(Mandatory)] [System.Management.Automation.PSCmdlet]$Cmdlet ) if (-not (Test-Path -LiteralPath $Path) -or -not $Cmdlet.ShouldProcess($Path, 'Remove existing module')) { return } $removed = Remove-ColorScriptsInstallDirectoryWithRetry -Path $Path if (-not $removed) { $removed = Move-ColorScriptsInstallDirectoryAside -Path $Path } if ($removed) { Write-Host '[OK] Removed existing module' -ForegroundColor Yellow } } function Copy-AndImportColorScriptsInstall { [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory)] [string]$SourcePath, [Parameter(Mandatory)] [string]$DestinationPath, [Parameter(Mandatory)] [System.Management.Automation.PSCmdlet]$Cmdlet ) if ($Cmdlet.ShouldProcess($DestinationPath, 'Copy module files')) { Copy-Item -Path $SourcePath -Destination $DestinationPath -Recurse -Force Write-Host '[OK] Module files copied' -ForegroundColor Green } if ($Cmdlet.ShouldProcess($DestinationPath, 'Import module')) { Import-Module (Join-Path -Path $DestinationPath -ChildPath 'ColorScripts-Enhanced.psd1') -Force Write-Host '[OK] Module imported successfully' -ForegroundColor Green } } function Add-ColorScriptsInstallProfile { [CmdletBinding(SupportsShouldProcess = $true)] param( [switch]$SkipStartupScript, [Parameter(Mandatory)] [System.Management.Automation.PSCmdlet]$Cmdlet ) $profileArguments = @{ Scope = 'CurrentUserAllHosts' } if ($SkipStartupScript) { $profileArguments.SkipStartupScript = $true } if (-not $Cmdlet.ShouldProcess($PROFILE.CurrentUserAllHosts, 'Update profile')) { return $null } $profileResult = Add-ColorScriptProfile @profileArguments if ($profileResult.Changed) { Write-Host "[OK] Profile updated: $($profileResult.Path)" -ForegroundColor Green } else { Write-Host 'Profile already configured. Use -Force with Add-ColorScriptProfile to overwrite.' -ForegroundColor Yellow } return $profileResult } function New-ColorScriptsInstallCache { [CmdletBinding(SupportsShouldProcess = $true)] [OutputType([object[]])] param( [Parameter(Mandatory)] [System.Management.Automation.PSCmdlet]$Cmdlet ) if (-not $Cmdlet.ShouldProcess('ColorScripts-Enhanced cache', 'Build caches for eligible scripts')) { return @() } Write-Host '' Write-Host 'Building cache for eligible colorscripts...' -ForegroundColor Cyan $cacheResults = @(New-ColorScriptCache -ErrorAction Stop) $successCount = @($cacheResults | Where-Object { $_.Status -in @('Updated', 'SkippedUpToDate') }).Count $failureCount = @($cacheResults | Where-Object { $_.Status -eq 'Failed' }).Count Write-Host " Updated: $successCount" -ForegroundColor Green if ($failureCount) { Write-Host " Failed: $failureCount" -ForegroundColor Red } return $cacheResults } function Write-ColorScriptsInstallSummary { param( [Parameter(Mandatory)] [string]$SourcePath, [Parameter(Mandatory)] [string]$DestinationPath, [switch]$AllUsers, [switch]$BuildCache ) $scope = if ($AllUsers) { 'AllUsers' } else { 'CurrentUser' } Write-Host '' Write-Host "[INFO] Source: $SourcePath" -ForegroundColor Yellow Write-Host "[INFO] Destination: $DestinationPath" -ForegroundColor Yellow Write-Host "[INFO] Scope: $scope" -ForegroundColor Yellow Write-Host '' Write-Host 'ColorScripts-Enhanced installed successfully!' -ForegroundColor Green Write-Host 'Quick start:' -ForegroundColor Yellow Write-Host ' Show-ColorScript # Display a random colorscript' Write-Host ' scs mandelbrot-zoom # Display a specific colorscript' Write-Host ' Get-ColorScriptList # List all scripts' Write-Host ' New-ColorScriptCache # Pre-build policy-selected caches' if (-not $BuildCache) { Write-Host "Tip: Run 'New-ColorScriptCache' to prime policy-selected computational renderers." -ForegroundColor Cyan } } function Invoke-ColorScriptsEnhancedInstall { [CmdletBinding(SupportsShouldProcess = $true)] param( [switch]$AllUsers, [switch]$AddToProfile, [switch]$SkipStartupScript, [switch]$BuildCache, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$SourcePath ) $moduleRoot = Get-ModuleInstallRoot -AllUsersScope:$AllUsers if (-not $moduleRoot) { throw 'Unable to determine module installation path.' } Assert-ColorScriptsInstallPrivilege -AllUsers:$AllUsers $destinationPath = Join-Path -Path $moduleRoot -ChildPath 'ColorScripts-Enhanced' Write-ColorScriptsInstallHeader $originalErrorActionPreference = $ErrorActionPreference $ErrorActionPreference = 'Stop' try { New-ColorScriptsInstallRoot -Path $moduleRoot -Cmdlet $PSCmdlet Remove-ExistingColorScriptsInstall -Path $destinationPath -Cmdlet $PSCmdlet Copy-AndImportColorScriptsInstall -SourcePath $SourcePath -DestinationPath $destinationPath -Cmdlet $PSCmdlet $profileResult = if ($AddToProfile) { Add-ColorScriptsInstallProfile -SkipStartupScript:$SkipStartupScript -Cmdlet $PSCmdlet } else { $null } $cacheResults = if ($BuildCache) { @(New-ColorScriptsInstallCache -Cmdlet $PSCmdlet) } else { @() } Write-ColorScriptsInstallSummary -SourcePath $SourcePath -DestinationPath $destinationPath -AllUsers:$AllUsers -BuildCache:$BuildCache return [pscustomobject]@{ SourcePath = $SourcePath DestinationPath = $destinationPath Scope = if ($AllUsers) { 'AllUsers' } else { 'CurrentUser' } ProfileResult = $profileResult CacheResults = $cacheResults } } finally { $ErrorActionPreference = $originalErrorActionPreference } } # If the script is dot-sourced (e.g., in tests), do not execute the install workflow. # Note: In some hosts (including Pester execution contexts) $MyInvocation.InvocationName may not # reliably report '.' for dot-sourced scripts, so we use multiple signals. $isDotSourced = $false try { if ($MyInvocation.InvocationName -eq '.') { $isDotSourced = $true } elseif ($MyInvocation.Line -and $MyInvocation.Line.TrimStart().StartsWith('.')) { $isDotSourced = $true } else { try { $frame = Get-PSCallStack | Select-Object -First 1 if ($frame -and $frame.Command -and $frame.Command.TrimStart().StartsWith('.')) { $isDotSourced = $true } } catch { Write-Verbose ("Dot-source detection via call stack failed: {0}" -f $_.Exception.Message) } } } catch { $isDotSourced = $false } if (-not $isDotSourced) { Invoke-ColorScriptsEnhancedInstall -SourcePath $PSScriptRoot @PSBoundParameters } |