core/public/Write-JaxUpdateNotice.ps1
|
function Write-JaxUpdateNotice { <# .SYNOPSIS Print a short "a newer Jax is available" hint, at most once per shell session. .DESCRIPTION Reads only the locally cached PowerShell Gallery result, so it never waits on the network. When that cache is stale it starts a detached background refresh and prints nothing on this run; the next run uses the fresh result. Every failure mode - no cache, no network, an unreadable state file, a read-only HOME - is silent. The hint is skipped when output is redirected (scripts, pipelines, and agent sessions), when CI is set, and when JAX_NO_UPDATE_NOTICE is set. .EXAMPLE Write-JaxUpdateNotice -RuntimeRoot $PSScriptRoot #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $RuntimeRoot, [string] $CachePath = (Join-Path $HOME '.jax/update-check.json'), [string] $StatePath = (Join-Path $HOME '.jax/update-notice.json'), [TimeSpan] $CacheTtl = [TimeSpan]::FromHours(24), [switch] $Force ) try { if (-not $Force) { $optOut = [string]$env:JAX_NO_UPDATE_NOTICE if (-not [string]::IsNullOrWhiteSpace($optOut) -and $optOut -notin @('0', 'false', 'no')) { return } if (-not [string]::IsNullOrWhiteSpace([string]$env:CI)) { return } if ([Console]::IsOutputRedirected) { return } } $versionPath = Join-Path ([IO.Path]::GetFullPath($RuntimeRoot)) 'VERSION' if (-not (Test-Path -LiteralPath $versionPath -PathType Leaf)) { return } $currentVersion = $null if (-not [version]::TryParse((Get-Content -LiteralPath $versionPath -Raw).Trim(), [ref] $currentVersion)) { return } $cacheFile = [IO.Path]::GetFullPath($CachePath) $stateFile = [IO.Path]::GetFullPath($StatePath) $cache = Read-JaxJsonFile -Path $cacheFile $checkedAt = ConvertTo-JaxUtcDate -Value (Get-JaxJsonProperty -Object $cache -Name 'CheckedAtUtc') if ($null -eq $checkedAt -or ([DateTime]::UtcNow - $checkedAt) -ge $CacheTtl) { Start-JaxUpdateCheck -RuntimeRoot $RuntimeRoot -CachePath $cacheFile -StatePath $stateFile return } if ([string](Get-JaxJsonProperty -Object $cache -Name 'Status') -ne 'Available') { return } $latestVersion = $null $latestText = [string](Get-JaxJsonProperty -Object $cache -Name 'LatestVersion') if (-not [version]::TryParse($latestText, [ref] $latestVersion)) { return } if ($latestVersion -le $currentVersion) { return } $sessionKey = Get-JaxShellSessionKey $state = Read-JaxJsonFile -Path $stateFile foreach ($entry in @(Get-JaxJsonProperty -Object $state -Name 'NotifiedSessions')) { if ($null -eq $entry) { continue } if ([string](Get-JaxJsonProperty -Object $entry -Name 'Session') -ne $sessionKey) { continue } if ([string](Get-JaxJsonProperty -Object $entry -Name 'Version') -eq $latestVersion.ToString()) { return } } Save-JaxUpdateNoticeState -Path $stateFile -State $state ` -NotifiedSession $sessionKey -NotifiedVersion $latestVersion.ToString() # The update command depends on how this copy was installed. Update-PSResource # only knows about copies PSResourceGet installed; aimed at ~/.jax/module it # either errors or updates a copy the `jx` shim never loads. Guarded, because # the notice is the point and the tailored command is the garnish - a failed # probe must not swallow "a new version exists". Gallery copies are not # enumerated here: PSModulePath is far too slow to walk on every run. $install = try { Get-JaxInstallInfo -RuntimeRoot $RuntimeRoot } catch { $null } Write-Host '' Write-Host (" Jax {0} is available (you have {1})." -f $latestVersion, $currentVersion) -ForegroundColor Cyan if ($install -and $install.Flavour -eq 'Repo') { Write-Host (" You are running from a checkout at {0}" -f $install.RuntimeRoot) -ForegroundColor DarkGray Write-Host ' Update: git pull, then ./Install-Jax.ps1' -ForegroundColor DarkGray } else { Write-Host ' Update: jax update' -ForegroundColor DarkGray } } catch { # An update hint must never slow down, interrupt, or fail a Jax run. } } |