feat: Add update deadline prompt system#1132
feat: Add update deadline prompt system#1132rtylerdavis wants to merge 2 commits intoRomanitho:developfrom
Conversation
|
Nice, massive piece of work! I'll need some time to review and approve the code. It won't be part of the next release, but I'll target the one after that. |
|
To maintain brand consistency and avoid any user confusion, would it be possible to use the native WAU icon instead? |
There was a problem hiding this comment.
Pull request overview
Adds an “update deadline” mode to Winget-AutoUpdate (WAU) that tracks per-app deadlines and, when configured, prompts logged-in users via a WPF dialog (ServiceUI) to run updates now or snooze, instead of always updating silently.
Changes:
- Introduces deadline tracking (HKLM registry) plus JSON payload handoff to a new WPF prompt and an on-demand “UpdateNow” worker task.
- Extends
Winget-Upgrade.ps1with a deadline-mode branch, including user-context app handling and scheduled-task triggering. - Adds/extends ADMX/ADML policy definitions for
WAU_UpdateDeadlineDays,WAU_ReminderIntervalDays, andWAU_CompanyName, plus installer task registration.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| Sources/Winget-AutoUpdate/functions/Start-UpdatePromptTask.ps1 | Writes pending-updates.json and triggers the UpdatePrompt scheduled task |
| Sources/Winget-AutoUpdate/functions/Set-UpdateDeadline.ps1 | Creates/updates per-app deadline registry entries |
| Sources/Winget-AutoUpdate/functions/Get-UpdateDeadlines.ps1 | Reads/purges deadline registry entries |
| Sources/Winget-AutoUpdate/config/WAU-MSI_Actions.ps1 | Registers UpdatePrompt/UpdateNow scheduled tasks and uninstalls them |
| Sources/Winget-AutoUpdate/Winget-Upgrade.ps1 | Adds deadline-mode control flow, user-context handoff files, and prompting logic |
| Sources/Winget-AutoUpdate/WAU-UpdatePrompt.ps1 | Implements the WPF dialog and user actions (update/snooze/auto-dismiss) |
| Sources/Winget-AutoUpdate/WAU-UpdateNow.ps1 | Processes selected updates and cleans up deadline entries |
| Sources/Policies/ADMX/en-US/WAU.adml | Adds strings/presentations for the new deadline policies |
| Sources/Policies/ADMX/en-US/WAU-Notifier.adml | New dedicated ADML resource file for notifier/deadline policies |
| Sources/Policies/ADMX/WAU.admx | Adds the new deadline/reminder/company policies to the primary WAU ADMX |
| Sources/Policies/ADMX/WAU-Notifier.admx | New dedicated ADMX namespace for notifier/deadline policies |
| .github/workflows/GA_Close-Inactive-Issues.yml | Expands GITHUB_TOKEN permissions for the stale-issues workflow |
| $today = (Get-Date).Date | ||
| $overdueEntries = @($deadlines | Where-Object { $_.Deadline.Date -lt $today }) | ||
| $pendingEntries = @($deadlines | Where-Object { $_.Deadline.Date -ge $today }) | ||
|
|
||
| # User-scoped overdue apps cannot be force-updated by SYSTEM. | ||
| # Move them to pending so they appear in the prompt instead. | ||
| $overdueUserIds = @($overdueEntries | ForEach-Object { | ||
| $appId = $_.AppId | ||
| $app = $deadlineApps | Where-Object { $_.Id -eq $appId } | Select-Object -First 1 | ||
| if ($app -and $app.Scope -eq 'user') { $appId } | ||
| }) | ||
| if ($overdueUserIds.Count -gt 0) { | ||
| $pendingEntries = @($pendingEntries) + @($overdueEntries | Where-Object { $_.AppId -in $overdueUserIds }) | ||
| $overdueEntries = @($overdueEntries | Where-Object { $_.AppId -notin $overdueUserIds }) | ||
| Write-ToLog "$($overdueUserIds.Count) overdue user-scoped apps moved to prompt" "DarkYellow" | ||
| } | ||
|
|
||
| Write-ToLog "Deadline summary: $($overdueEntries.Count) overdue (machine), $($pendingEntries.Count) pending" | ||
|
|
||
| # Step 3: Forced background update for overdue apps -- no dialog shown. | ||
| if ($overdueEntries.Count -gt 0) { | ||
| Write-ToLog "Processing $($overdueEntries.Count) overdue apps" "DarkYellow" | ||
| foreach ($entry in $overdueEntries) { | ||
| $app = $deadlineApps | Where-Object { $_.Id -eq $entry.AppId } | Select-Object -First 1 | ||
| if ($app -and $app.Version -ne "Unknown") { | ||
| Write-ToLog "Forced update (deadline reached): $($app.Name)" | ||
| Update-App $app | ||
| if (Confirm-Installation $app.Id $app.AvailableVersion) { | ||
| $DeadlineAppRegPath = "HKLM:\SOFTWARE\Romanitho\Winget-AutoUpdate\UpdateDeadlines\$($app.Id)" | ||
| if (Test-Path $DeadlineAppRegPath) { | ||
| Remove-Item -Path $DeadlineAppRegPath -Recurse -Force -ErrorAction SilentlyContinue | ||
| Write-ToLog "Deadline entry purged (update confirmed): $($app.Id)" | ||
| } | ||
| } | ||
| else { | ||
| Write-ToLog "$($app.Name) : forced update could not be confirmed -- deadline entry preserved" "Yellow" | ||
| } | ||
| } | ||
| elseif ($app -and $app.Version -eq "Unknown") { | ||
| Write-ToLog "$($app.Name) : Skipped forced update because current version is 'Unknown'" "Gray" | ||
| } | ||
| } | ||
| #if app with wildcard is in "excluded list", skip it | ||
| elseif ($toSkip | Where-Object { $app.Id -like $_ }) { | ||
| Write-ToLog "$($app.Name) : Skipped upgrade because it is *wildcard* in the excluded app list" "Gray" | ||
| } | ||
|
|
||
| # Step 4: Show deadline prompt for pending apps if a user is logged in and | ||
| # the snooze window has expired. | ||
| if ($pendingEntries.Count -gt 0) { | ||
| $explorerprocesses = @(Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE Name='explorer.exe'" -ErrorAction SilentlyContinue) | ||
| if ($explorerprocesses.Count -eq 0) { | ||
| Write-ToLog "No user logged on -- skipping update prompt for $($pendingEntries.Count) pending apps" "Gray" | ||
| } |
There was a problem hiding this comment.
Deadline handling treats apps due today as “pending” and only shows the prompt. If no user is logged on (no explorer.exe), the prompt is skipped and those apps won’t be updated until they become overdue tomorrow, which effectively extends the deadline. Consider treating Deadline.Date -le $today as enforceable when no interactive user is present (or forcing updates when the deadline is reached and prompting isn’t possible).
There was a problem hiding this comment.
This is intentional. Apps due "today" are kept in the pending/prompt path because the deadline prompt is the user's last chance to see what's about to update before it happens silently. If no user is logged in, the app becomes overdue on the next cycle (at most ~24h depending on the WAU schedule) and gets force-updated then. Changing this to force-update "today" apps when no session is available would mean users who log in later that day never see the prompt, worse UX for the common case to save at most one cycle in an uncommon edge case.
of course, I just threw that in as a demo to make sure it worked, at first I was just using the info.png from your source, but I'm open whatever icon you'd like! |
Adds configurable update deadline enforcement to WAU. When WAU_UpdateDeadlineDays > 0, instead of silently installing updates, WAU tracks deadlines per-app and presents a WPF dialog to the user with options to update now, select specific apps, or snooze. New files: - WAU-UpdatePrompt.ps1: WPF dialog via ServiceUI.exe - WAU-UpdateNow.ps1: processes user-initiated updates - functions/Get-UpdateDeadlines.ps1: reads/purges deadline registry - functions/Set-UpdateDeadline.ps1: creates/updates deadline entries - functions/Start-UpdatePromptTask.ps1: writes JSON, fires prompt task - WAU-Notifier.admx/adml: ADMX policy definitions for Intune/GPO Modified files: - Winget-Upgrade.ps1: deadline mode branch around main update loop - WAU-MSI_Actions.ps1: registers UpdatePrompt and UpdateNow tasks Features: - Per-app deadline clock (never resets on version bumps) - Per-app selective update via checkboxes - Final-day enforcement: auto-checked, locked, red highlight, no snooze - Color-coded urgency (red/amber/white) - User-context app support - Whitelist/blacklist respected - Custom company branding (WAU_CompanyName) - Self-healing (stale entries purged automatically) - X button blocked, auto-dismiss timer Resolves Romanitho#1121. See Romanitho#1130 for full proposal.
- Use DateTime.ParseExact with InvariantCulture for yyyy-MM-dd dates instead of culture-dependent DateTime.Parse (Get-UpdateDeadlines, WAU-UpdatePrompt) - Use SID S-1-5-11 instead of localized 'Authenticated Users' string for ACL rule creation/comparison (Winget-Upgrade ProgramData ACL) - Validate ReminderIntervalDays with TryParse and enforce minimum of 1 (WAU-UpdatePrompt) - Rewrite pending-updates.json from in-memory data on full update path to guard against race with main SYSTEM cycle (WAU-UpdatePrompt) - Wrap Export-Csv for user-context-outdated.csv in try/catch so a transient IO failure doesn't abort the user-context run (Winget-Upgrade) - Guard Start-ScheduledTask with try/catch in Start-UpdatePromptTask - Fix misleading comment in Set-UpdateDeadline (said "newer version" but logic checks for any change)
36b2433 to
763bb8b
Compare
Summary
Implements the update deadline prompt system proposed in #1130. When
WAU_UpdateDeadlineDaysis configured, WAU presents a WPF dialog to logged-in users listing pending updates with per-app deadlines instead of silently installing. Fully backward-compatible -- zero effect when not configured.Features
WAU_ReminderIntervalDays)WAU_CompanyName)WAU_UserContext)Configuration
WAU_UpdateDeadlineDays0(disabled)WAU_ReminderIntervalDays2WAU_CompanyNameDedicated
WAU-Notifier.admx/.admlincluded for Intune/GPO deployment. Uses a separate ADMX namespace (WAUNotifier.Policies) to coexist with the existingWAU.admx. The three settings are also added to the upstreamWAU.admx/.admlfor single-file deployments.Files Changed
Modified:
Winget-Upgrade.ps1-- deadline mode if/else branch around main update loopconfig/WAU-MSI_Actions.ps1-- registers/unregisters UpdatePrompt and UpdateNow tasksNew:
WAU-UpdatePrompt.ps1-- WPF dialog, user interaction, partial update logicWAU-UpdateNow.ps1-- processes updates from pending-updates.jsonfunctions/Get-UpdateDeadlines.ps1-- reads/purges deadline registry entriesfunctions/Set-UpdateDeadline.ps1-- creates/updates deadline entriesfunctions/Start-UpdatePromptTask.ps1-- writes JSON, fires prompt taskWAU-Notifier.admx/WAU-Notifier.adml-- ADMX policy definitionsTesting
Tested on x64 and ARM64 (Snapdragon Surface Laptop) via both manual install and Intune deployment. Full test plan covering install/uninstall lifecycle, deadline creation, prompt interaction, overdue enforcement, user-context apps, whitelist/blacklist, ADMX policy delivery, and edge cases.
Related: #1121