Public/Start-WingetUpdateCheck.ps1
|
function Start-WingetUpdateCheck { <# .SYNOPSIS Checks for winget updates in the background. .DESCRIPTION This command is typically triggered by your PowerShell profile if you've enabled update notifications via Enable-WingetUpdateNotifications. It prints the last known update count straight away, then refreshes the list in a background job and pops a Windows notification if any updates are found. Results are cached so Get-WingetUpdates can show them instantly. It respects the interval set in the configuration. .PARAMETER Force Ignore the configured check interval and check immediately. #> [CmdletBinding()] param( [switch]$Force ) $configDir = Get-WingetBatchConfigDir $config = Get-WingetBatchConfigData if (-not $config['UpdateNotificationsEnabled']) { return } $cacheFile = Join-Path $configDir "update_cache.json" # Show what we already know without waiting for a new check if (Test-Path $cacheFile) { try { $cache = Get-Content $cacheFile -Raw | ConvertFrom-Json $count = if ($null -ne $cache.UpdateCount) { [int]$cache.UpdateCount } else { @($cache.Updates).Count } if ($count -gt 0) { Write-Host "$count winget package update(s) available - run " -ForegroundColor Yellow -NoNewline Write-Host "Get-WingetUpdates" -ForegroundColor White } } catch { } } if (-not $Force) { if ($config['LastCheck']) { try { $lastCheckTime = [datetime]$config['LastCheck'] $interval = [double]$config['CheckInterval'] if ($interval -gt 0 -and (Get-Date) -lt $lastCheckTime.AddHours($interval)) { return } if ($interval -le 0 -and -not $config['CheckOnStartup']) { return } } catch { # Invalid date format, just proceed } } elseif (-not $config['CheckOnStartup']) { # First run with startup checks disabled: start the interval clock now $config['LastCheck'] = (Get-Date).ToString("o") Save-WingetBatchConfigData -Config $config return } } # Update LastCheck immediately to prevent multiple rapid checks from multiple terminal sessions $config['LastCheck'] = (Get-Date).ToString("o") Save-WingetBatchConfigData -Config $config $jobScript = { param($CacheFile) try { Import-Module Microsoft.WinGet.Client -ErrorAction Stop $updates = @(Microsoft.WinGet.Client\Get-WinGetPackage -ErrorAction Stop | Where-Object { $_.IsUpdateAvailable } | ForEach-Object { [PSCustomObject]@{ Id = $_.Id Name = $_.Name InstalledVersion = $_.InstalledVersion AvailableVersion = @($_.AvailableVersions)[0] Source = $_.Source } }) # Same format Get-WingetUpdates reads, so it can skip its own scan @{ LastChecked = (Get-Date).ToString('o') UpdateCount = $updates.Count Updates = $updates } | ConvertTo-Json -Depth 5 | Set-Content -Path $CacheFile -Encoding UTF8 if ($updates.Count -gt 0) { $names = @($updates | Select-Object -First 3 | ForEach-Object { if ($_.Name) { $_.Name } else { $_.Id } }) $pkgText = $names -join ", " if ($updates.Count -gt 3) { $pkgText += " and $($updates.Count - 3) more" } Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing $balloon = New-Object System.Windows.Forms.NotifyIcon $balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon((Get-Process -Id $PID).Path) $balloon.BalloonTipIcon = "Info" $balloon.BalloonTipTitle = "Winget Updates Available ($($updates.Count))" $balloon.BalloonTipText = "Updates available for: $pkgText.`nRun 'Get-WingetUpdates' to install." $balloon.Visible = $true $balloon.ShowBalloonTip(10000) Start-Sleep -Seconds 10 $balloon.Dispose() } } catch { # Ignore background job errors so it fails silently in profile } } # Start the check in a background job so it doesn't block the user's terminal startup Start-Job -ScriptBlock $jobScript -ArgumentList (Join-Path $configDir "update_cache.json") -Name "WingetUpdateCheck" | Out-Null } |