Public/Update-Toolkit.ps1
|
function Update-Toolkit { <# .SYNOPSIS Self-update the PowerShell Dev Toolkit. .DESCRIPTION For a git-clone install, pulls the latest changes from the toolkit's git remote. For a PowerShell Gallery install, checks the Gallery for a newer version and updates with Update-Module (or Update-PSResource). In both cases the module is re-imported so new commands and aliases take effect immediately. .PARAMETER CheckOnly Only check whether updates are available without applying them. .PARAMETER Force Skip the confirmation prompt and apply updates immediately. .EXAMPLE Update-Toolkit .EXAMPLE Update-Toolkit -CheckOnly .EXAMPLE Update-Toolkit -Force #> [CmdletBinding()] param( [switch]$CheckOnly, [switch]$Force ) $paths = Get-ToolkitPaths if ($paths.InstallType -eq 'Gallery') { Update-ToolkitFromGallery -Paths $paths -CheckOnly:$CheckOnly -Force:$Force } else { Update-ToolkitFromGit -Paths $paths -CheckOnly:$CheckOnly -Force:$Force } } function Update-ToolkitFromGit { <# Git-clone update flow (private). #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] $Paths, [switch]$CheckOnly, [switch]$Force ) $toolkitDir = $Paths.RepoRoot $git = Get-Command git -ErrorAction SilentlyContinue if (-not $git) { Write-Error "Git is not installed or not in PATH." return } Push-Location $toolkitDir try { $currentBranch = git rev-parse --abbrev-ref HEAD 2>$null if (-not $currentBranch) { Write-Error "Failed to determine current git branch." return } Write-Host "Checking for updates..." -ForegroundColor Cyan git fetch origin $currentBranch --quiet 2>$null $localHead = git rev-parse HEAD 2>$null $remoteHead = git rev-parse "origin/$currentBranch" 2>$null if ($localHead -eq $remoteHead) { Write-Host "Already up to date." -ForegroundColor Green $manifest = Import-PowerShellDataFile (Join-Path $Paths.ModuleRoot "PowerShellDevToolkit.psd1") Write-Host " Version: $($manifest.ModuleVersion)" -ForegroundColor Gray Write-Host " Branch: $currentBranch" -ForegroundColor Gray return } $behind = git rev-list --count "HEAD..origin/$currentBranch" 2>$null Write-Host "$behind new commit(s) available on $currentBranch" -ForegroundColor Yellow Write-Host "" $log = git log --oneline "HEAD..origin/$currentBranch" 2>$null if ($log) { Write-Host "Changes:" -ForegroundColor Cyan $log | ForEach-Object { Write-Host " $_" -ForegroundColor Gray } Write-Host "" } $diffStat = git diff --stat "HEAD..origin/$currentBranch" 2>$null if ($diffStat) { Write-Host "Files changed:" -ForegroundColor Cyan $diffStat | ForEach-Object { Write-Host " $_" -ForegroundColor Gray } Write-Host "" } if ($CheckOnly) { return } if (-not $Force) { Write-Host "Apply update? (Y/N): " -NoNewline -ForegroundColor Yellow $response = Read-Host if ($response -ne 'Y' -and $response -ne 'y') { Write-Host "Update cancelled." -ForegroundColor Gray return } } Write-Host "Pulling changes..." -ForegroundColor Cyan $pullOutput = git pull origin $currentBranch 2>&1 $pullExitCode = $LASTEXITCODE if ($pullExitCode -ne 0) { Write-Host "Git pull failed:" -ForegroundColor Red $pullOutput | ForEach-Object { Write-Host " $_" -ForegroundColor Red } Write-Host "" Write-Host "You may need to resolve conflicts manually." -ForegroundColor Yellow return } $pullOutput | ForEach-Object { Write-Host " $_" -ForegroundColor Gray } Write-Host "" Write-Host "Re-importing module..." -ForegroundColor Cyan Import-Module $Paths.ModuleRoot -Force -Global -DisableNameChecking $manifest = Import-PowerShellDataFile (Join-Path $Paths.ModuleRoot "PowerShellDevToolkit.psd1") Write-Host "" Write-Host "Updated to version $($manifest.ModuleVersion)" -ForegroundColor Green Write-Host "All commands and aliases are now current." -ForegroundColor Green Set-ToolkitUpdateTimestamp } finally { Pop-Location } } function Update-ToolkitFromGallery { <# PowerShell Gallery update flow (private). #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] $Paths, [switch]$CheckOnly, [switch]$Force ) Write-Host "Checking the PowerShell Gallery for updates..." -ForegroundColor Cyan $status = Get-ToolkitGalleryStatus -Paths $Paths if (-not $status) { Write-Host "Could not reach the PowerShell Gallery. Check your network connection and try again." -ForegroundColor Red return } if (-not $status.UpdateAvailable) { Write-Host "Already up to date." -ForegroundColor Green Write-Host " Version: $($status.Installed)" -ForegroundColor Gray Write-Host " Source: PowerShell Gallery" -ForegroundColor Gray Set-ToolkitUpdateTimestamp return } Write-Host "Update available: $($status.Installed) -> $($status.Latest)" -ForegroundColor Yellow if ($status.ReleaseNotes) { Write-Host "" Write-Host "Release notes:" -ForegroundColor Cyan ($status.ReleaseNotes -split "`r?`n") | ForEach-Object { Write-Host " $_" -ForegroundColor Gray } } Write-Host "" if ($CheckOnly) { return } if (-not $Force) { Write-Host "Apply update? (Y/N): " -NoNewline -ForegroundColor Yellow $response = Read-Host if ($response -ne 'Y' -and $response -ne 'y') { Write-Host "Update cancelled." -ForegroundColor Gray return } } Write-Host "Updating from the PowerShell Gallery..." -ForegroundColor Cyan try { if (Get-InstalledModule -Name PowerShellDevToolkit -ErrorAction SilentlyContinue) { Update-Module -Name PowerShellDevToolkit -Force -ErrorAction Stop } elseif ((Get-Command Update-PSResource -ErrorAction SilentlyContinue) -and (Get-InstalledPSResource -Name PowerShellDevToolkit -ErrorAction SilentlyContinue)) { Update-PSResource -Name PowerShellDevToolkit -ErrorAction Stop } else { Write-Host "Could not determine how the module was installed." -ForegroundColor Red Write-Host "Update manually with: Update-Module PowerShellDevToolkit (or: Update-PSResource PowerShellDevToolkit)" -ForegroundColor Yellow return } } catch { Write-Host "Update failed: $($_.Exception.Message)" -ForegroundColor Red return } Write-Host "" Write-Host "Re-importing module..." -ForegroundColor Cyan Import-Module PowerShellDevToolkit -Force -Global -DisableNameChecking $newVersion = (Get-Module PowerShellDevToolkit).Version Write-Host "" Write-Host "Updated to version $newVersion" -ForegroundColor Green Write-Host "All commands and aliases are now current." -ForegroundColor Green Set-ToolkitUpdateTimestamp } function Test-ToolkitUpdate { <# .SYNOPSIS Silently check if toolkit updates are available (used on shell startup). .DESCRIPTION Compares the local install against its source (git remote or the PowerShell Gallery). Respects the updateCheckDays setting in config.json so it only hits the network at the configured frequency. Set $env:PSDT_SKIP_UPDATE_CHECK to skip the startup check entirely. #> [CmdletBinding()] param() $paths = Get-ToolkitPaths if ($paths.InstallType -eq 'Git' -and -not (Get-Command git -ErrorAction SilentlyContinue)) { return } $config = Get-ScriptConfig -Quiet $intervalDays = 1 if ($config -and $config.toolkit -and $null -ne $config.toolkit.updateCheckDays) { $intervalDays = [int]$config.toolkit.updateCheckDays } if ($intervalDays -le 0) { return } if (Test-Path $paths.UpdateStampPath) { $lastCheck = (Get-Item $paths.UpdateStampPath).LastWriteTime if (([datetime]::Now - $lastCheck).TotalDays -lt $intervalDays) { return } } if ($paths.InstallType -eq 'Gallery') { $status = Get-ToolkitGalleryStatus -Paths $paths Set-ToolkitUpdateTimestamp if ($status -and $status.UpdateAvailable) { Write-ToolkitUpdateHint "version $($status.Latest) is" } return } Push-Location $paths.RepoRoot try { $branch = git rev-parse --abbrev-ref HEAD 2>$null if (-not $branch) { return } git fetch origin $branch --quiet 2>$null $local = git rev-parse HEAD 2>$null $remote = git rev-parse "origin/$branch" 2>$null Set-ToolkitUpdateTimestamp if ($local -ne $remote) { $behind = git rev-list --count "HEAD..origin/$branch" 2>$null Write-ToolkitUpdateHint "$behind update(s)" } } finally { Pop-Location } } function Write-ToolkitUpdateHint { <# Prints the one-line "update available" hint (private). #> [CmdletBinding()] param([string]$What) Write-Host "" Write-Host "PowerShell Dev Toolkit: $What available. Run " -NoNewline -ForegroundColor Yellow Write-Host "Update-Toolkit" -NoNewline -ForegroundColor Cyan Write-Host " to update." -ForegroundColor Yellow } function Set-ToolkitUpdateTimestamp { <# Touches the .last-update-check stamp file (private). #> [CmdletBinding()] param() $stampFile = (Get-ToolkitPaths).UpdateStampPath $stampDir = Split-Path $stampFile -Parent if (-not (Test-Path $stampDir)) { New-Item -Path $stampDir -ItemType Directory -Force | Out-Null } [IO.File]::WriteAllText($stampFile, (Get-Date -Format 'o')) } |