tools/Install-GitEasy.ps1
|
<# .SYNOPSIS Deploy the GitEasy module from its dev folder to a system module location. .DESCRIPTION Install-GitEasy.ps1 copies the runtime essentials of GitEasy (manifest, root module, Public\, Private\, README, LICENSE, CHANGELOG) from the dev folder to a system module path so that any PowerShell session on the machine can do `Import-Module GitEasy` without specifying a path. By default, the script installs to BOTH: - C:\Program Files\WindowsPowerShell\Modules\GitEasy (Windows PowerShell 5.1) - C:\Program Files\PowerShell\Modules\GitEasy (PowerShell 7+) Writing to Program Files requires administrative privileges. If the script is run from a non-elevated session, it tells you so and stops without modifying anything. A backup of any existing install is taken first (renamed to `GitEasy.bak.<timestamp>`), so a rollback is one rename away. The dev folder contents that are NOT copied to the install location: - Tests\, tools\, .github\ - Update-GitEasy*Wiki.ps1 (development tools) - Examples\, docs\ - Any \*.bak files .PARAMETER Source The dev folder to install from. Defaults to C:\Sysadmin\Scripts\GitEasy. .PARAMETER Target One or more target install paths. Defaults to both Windows PowerShell 5.1 and PowerShell 7+ AllUsers locations. .PARAMETER SkipPesterCheck Skip the pre-install Pester run. By default, the script refuses to install if any Pester test fails. .EXAMPLE .\tools\Install-GitEasy.ps1 .EXAMPLE .\tools\Install-GitEasy.ps1 -Target 'C:\Program Files\WindowsPowerShell\Modules\GitEasy' .NOTES Run from an elevated PowerShell session (Run as Administrator). Writing to Program Files without elevation fails with a permission error. #> [CmdletBinding()] param( [string]$Source = 'C:\Sysadmin\Scripts\GitEasy', [string[]]$Target = @( 'C:\Program Files\WindowsPowerShell\Modules\GitEasy', 'C:\Program Files\PowerShell\Modules\GitEasy' ), [switch]$SkipPesterCheck ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' Write-Host '' Write-Host 'STATE CHECK: Install GitEasy from dev folder' -ForegroundColor Cyan if (-not (Test-Path -LiteralPath $Source -PathType Container)) { throw "Source folder does not exist: $Source" } $manifestPath = Join-Path $Source 'GitEasy.psd1' if (-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) { throw "Source folder is not a GitEasy module (no GitEasy.psd1 found): $Source" } $manifest = Import-PowerShellDataFile -LiteralPath $manifestPath $moduleVersion = $manifest.ModuleVersion Write-Host "Source: $Source" Write-Host "Module version: $moduleVersion" Write-Host "Targets:" foreach ($t in $Target) { Write-Host " $t" } Write-Host '' # Pester gate (pinned to Pester 3 - tests are written in Pester 3 syntax) if (-not $SkipPesterCheck) { Write-Host 'Running Pester suite before install...' -ForegroundColor Cyan $pester3 = Get-Module -ListAvailable Pester | Where-Object { $_.Version.Major -lt 4 } | Sort-Object Version -Descending | Select-Object -First 1 if (-not $pester3) { throw 'Pester 3 is not installed. Install it with: Install-Module Pester -RequiredVersion 3.4.0 -SkipPublisherCheck -Scope AllUsers -Force' } Remove-Module Pester -Force -ErrorAction SilentlyContinue Import-Module $pester3.Path -Force $pesterResult = Invoke-Pester -Script (Join-Path $Source 'Tests') -PassThru -Quiet if ($pesterResult.FailedCount -gt 0) { $pesterResult.TestResult | Where-Object { -not $_.Passed } | ForEach-Object { Write-Host " FAIL: $($_.Name)" -ForegroundColor Red } throw "Pester reported $($pesterResult.FailedCount) failure(s). Refusing to install. Use -SkipPesterCheck to override." } Write-Host " $($pesterResult.PassedCount)/$($pesterResult.TotalCount) tests passed." -ForegroundColor Green Write-Host '' } # Permission check on each target foreach ($t in $Target) { $parent = Split-Path -Path $t -Parent if (-not (Test-Path -LiteralPath $parent -PathType Container)) { Write-Host "Skipping target (parent folder does not exist): $t" -ForegroundColor Yellow continue } $probeFile = Join-Path $parent ('.giteasy-install-probe-' + [guid]::NewGuid().ToString('N')) try { [System.IO.File]::WriteAllText($probeFile, 'probe', [System.Text.UTF8Encoding]::new($false)) Remove-Item -LiteralPath $probeFile -Force -ErrorAction SilentlyContinue } catch { throw "No write permission on $parent. Run this script from an elevated PowerShell (Run as Administrator) to install to Program Files." } } # Build the runtime manifest of files to copy $includePaths = @( 'GitEasy.psd1', 'GitEasy.psm1', 'README.md', 'LICENSE', 'CHANGELOG.md' ) $includeFolders = @( 'Public', 'Private', 'Format' ) $installedTargets = @() foreach ($t in $Target) { $parent = Split-Path -Path $t -Parent if (-not (Test-Path -LiteralPath $parent -PathType Container)) { continue } Write-Host "Installing to: $t" -ForegroundColor Cyan if (Test-Path -LiteralPath $t -PathType Container) { $stamp = (Get-Date).ToString('yyyyMMddHHmmss') $backupPath = "$t.bak.$stamp" Rename-Item -LiteralPath $t -NewName (Split-Path -Path $backupPath -Leaf) -Force Write-Host " Backed up existing install: $backupPath" } New-Item -Path $t -ItemType Directory -Force | Out-Null foreach ($f in $includePaths) { $src = Join-Path $Source $f if (Test-Path -LiteralPath $src -PathType Leaf) { Copy-Item -LiteralPath $src -Destination (Join-Path $t $f) -Force } } foreach ($folder in $includeFolders) { $src = Join-Path $Source $folder if (Test-Path -LiteralPath $src -PathType Container) { $dest = Join-Path $t $folder Copy-Item -LiteralPath $src -Destination $dest -Recurse -Force } } Write-Host " Copied module runtime to $t" $installedTargets += $t } if ($installedTargets.Count -eq 0) { Write-Warning 'No targets were installed (no parent folders existed).' return } # Verify by importing the module from a fresh PowerShell child Write-Host '' Write-Host 'Verifying install...' -ForegroundColor Cyan $verifyOutput = & powershell.exe -NoProfile -Command "Import-Module GitEasy -Force; (Get-Module GitEasy | Select-Object -First 1).Version.ToString()" 2>&1 $verifyExit = $LASTEXITCODE if ($verifyExit -ne 0 -or [string]::IsNullOrWhiteSpace($verifyOutput)) { Write-Warning "Could not verify install via Import-Module. Output: $verifyOutput" } else { $reportedVersion = ($verifyOutput | Select-Object -Last 1).ToString().Trim() if ($reportedVersion -eq $moduleVersion) { Write-Host " Verified: GitEasy $reportedVersion is importable without a path." -ForegroundColor Green } else { Write-Warning "Verified import returned version $reportedVersion (expected $moduleVersion)." } } Write-Host '' Write-Host "Install complete. GitEasy $moduleVersion is now available system-wide." -ForegroundColor Green Write-Host 'Any PowerShell session can now run: Import-Module GitEasy' -ForegroundColor Green |