Skip to content

feat: Add update deadline prompt system#1132

Open
rtylerdavis wants to merge 2 commits intoRomanitho:developfrom
rtylerdavis:feature/update-deadline-prompt
Open

feat: Add update deadline prompt system#1132
rtylerdavis wants to merge 2 commits intoRomanitho:developfrom
rtylerdavis:feature/update-deadline-prompt

Conversation

@rtylerdavis
Copy link

Summary

Implements the update deadline prompt system proposed in #1130. When WAU_UpdateDeadlineDays is 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.

Update Prompt Dialog

Features

  • Per-app deadline tracking (clock starts on first detection, never resets on version bumps)
  • WPF dialog via ServiceUI.exe with per-app selective update checkboxes
  • Final-day enforcement: apps due today are auto-checked, locked, highlighted in red -- Remind button hidden
  • Color-coded urgency (red for today/overdue, amber for 1-3 days, white for 4+)
  • Configurable reminder interval (WAU_ReminderIntervalDays)
  • Custom company branding (WAU_CompanyName)
  • User-context app support (respects WAU_UserContext)
  • Whitelist/blacklist respected in deadline tracking
  • Self-healing: stale entries purged automatically, failed updates preserved for retry
  • X button blocked, auto-dismiss timer

Configuration

Setting Default Description
WAU_UpdateDeadlineDays 0 (disabled) Days from first detection to forced update
WAU_ReminderIntervalDays 2 Days between reminder prompts
WAU_CompanyName (empty) Company name in prompt header

Dedicated WAU-Notifier.admx/.adml included for Intune/GPO deployment. Uses a separate ADMX namespace (WAUNotifier.Policies) to coexist with the existing WAU.admx. The three settings are also added to the upstream WAU.admx/.adml for single-file deployments.

Files Changed

Modified:

  • Winget-Upgrade.ps1 -- deadline mode if/else branch around main update loop
  • config/WAU-MSI_Actions.ps1 -- registers/unregisters UpdatePrompt and UpdateNow tasks

New:

  • WAU-UpdatePrompt.ps1 -- WPF dialog, user interaction, partial update logic
  • WAU-UpdateNow.ps1 -- processes updates from pending-updates.json
  • functions/Get-UpdateDeadlines.ps1 -- reads/purges deadline registry entries
  • functions/Set-UpdateDeadline.ps1 -- creates/updates deadline entries
  • functions/Start-UpdatePromptTask.ps1 -- writes JSON, fires prompt task
  • WAU-Notifier.admx / WAU-Notifier.adml -- ADMX policy definitions

Testing

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

@github-actions github-actions bot added the invalid-branch Invalid branch label Mar 11, 2026
@rtylerdavis rtylerdavis changed the base branch from main to develop March 11, 2026 05:20
@github-actions github-actions bot removed the invalid-branch Invalid branch label Mar 11, 2026
@Romanitho
Copy link
Owner

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.
As mentioned, I’m also starting a new repo for the C# version. This is a new challenge for me, and I’m planning to rewrite the code to keep WAU’s core functionality while improving the overall logic.
Since the project began, many things have evolved and features were added over time; I think the move to C# is the perfect opportunity for a full refactor. I specifically want to use the Winget COM API to make the agent simpler and more robust. This deadline prompt system would, of course, become a central feature of the new WAU agent.

@Romanitho
Copy link
Owner

To maintain brand consistency and avoid any user confusion, would it be possible to use the native WAU icon instead?

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.ps1 with a deadline-mode branch, including user-context app handling and scheduled-task triggering.
  • Adds/extends ADMX/ADML policy definitions for WAU_UpdateDeadlineDays, WAU_ReminderIntervalDays, and WAU_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

Comment on lines +509 to 559
$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"
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@rtylerdavis
Copy link
Author

To maintain brand consistency and avoid any user confusion, would it be possible to use the native WAU icon instead?

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)
@rtylerdavis rtylerdavis force-pushed the feature/update-deadline-prompt branch from 36b2433 to 763bb8b Compare March 26, 2026 16:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants