mirror of
https://github.com/Raphire/Win11Debloat.git
synced 2026-08-22 07:36:25 +00:00
feat: add timeout for WinGet uninstall to prevent hanging (#731)
This commit is contained in:
@@ -96,24 +96,42 @@ function Remove-SelectedApps {
|
|||||||
Uninstalls an app via WinGet and/or schedules its removal.
|
Uninstalls an app via WinGet and/or schedules its removal.
|
||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
Runs winget uninstall for a single app. If the User or Sysprep
|
Runs winget uninstall for a single app, with a bounded execution time.
|
||||||
parameter was passed, also schedules removal for future logins.
|
If the User or Sysprep parameter was passed, also schedules removal for
|
||||||
|
future logins.
|
||||||
|
|
||||||
.PARAMETER app
|
.PARAMETER app
|
||||||
The WinGet package ID to uninstall (e.g. 'Microsoft.BingNews').
|
The WinGet package ID to uninstall (e.g. 'Microsoft.BingNews').
|
||||||
|
|
||||||
|
.PARAMETER TimeoutSeconds
|
||||||
|
Maximum time to allow the foreground WinGet uninstall to run. Defaults
|
||||||
|
to 120 seconds.
|
||||||
#>
|
#>
|
||||||
function Remove-WinGetApp {
|
function Remove-WinGetApp {
|
||||||
param([string]$app)
|
param(
|
||||||
|
[string]$app,
|
||||||
|
[int]$TimeoutSeconds = 120
|
||||||
|
)
|
||||||
|
|
||||||
if (-not $script:WingetInstalled) {
|
if (-not $script:WingetInstalled) {
|
||||||
Write-Host "ERROR: WinGet is either not installed or is outdated, $app could not be removed" -ForegroundColor Red
|
Write-Host "ERROR: WinGet is either not installed or is outdated, $app could not be removed" -ForegroundColor Red
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoke-NonBlocking -ScriptBlock {
|
try {
|
||||||
param($appId)
|
Invoke-NonBlocking -ScriptBlock {
|
||||||
winget uninstall --accept-source-agreements --disable-interactivity --id $appId
|
param($appId)
|
||||||
} -ArgumentList $app
|
winget uninstall --accept-source-agreements --disable-interactivity --id $appId
|
||||||
|
} -ArgumentList $app -TimeoutSeconds $TimeoutSeconds
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
if ($_.Exception.Message -like 'Operation timed out after *') {
|
||||||
|
Write-Host "WinGet uninstall for $app did not complete within $TimeoutSeconds seconds: $_" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Host "WinGet uninstall for $app failed: $_" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($script:Params.ContainsKey("User")) {
|
if ($script:Params.ContainsKey("User")) {
|
||||||
Write-Host "Adding scheduled task to uninstall $app for user $(Get-UserName)..."
|
Write-Host "Adding scheduled task to uninstall $app for user $(Get-UserName)..."
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ BeforeAll {
|
|||||||
function Get-TargetUserForAppRemoval { 'AllUsers' }
|
function Get-TargetUserForAppRemoval { 'AllUsers' }
|
||||||
function Get-WingetInstalledApps { param($TimeOut, [switch]$NonBlocking) @() }
|
function Get-WingetInstalledApps { param($TimeOut, [switch]$NonBlocking) @() }
|
||||||
function Test-AppInWingetList { param($appId, $InstalledList) $false }
|
function Test-AppInWingetList { param($appId, $InstalledList) $false }
|
||||||
function Invoke-NonBlocking { param($ScriptBlock, $ArgumentList) }
|
function Invoke-NonBlocking { param($ScriptBlock, $ArgumentList, $TimeoutSeconds) }
|
||||||
function Get-UserName { 'Alice' }
|
function Get-UserName { 'Alice' }
|
||||||
function Invoke-ForceRemoveEdge {}
|
function Invoke-ForceRemoveEdge {}
|
||||||
function Show-MessageBox { 'No' }
|
function Show-MessageBox { 'No' }
|
||||||
@@ -109,6 +109,31 @@ Describe 'Remove-WinGetApp' {
|
|||||||
Should -Invoke Invoke-NonBlocking -Times 1 -Exactly -ParameterFilter { $ArgumentList -eq 'One.App' }
|
Should -Invoke Invoke-NonBlocking -Times 1 -Exactly -ParameterFilter { $ArgumentList -eq 'One.App' }
|
||||||
Should -Invoke Set-RunOnceWingetTask -Times $Scheduled -Exactly -ParameterFilter { $appId -eq 'One.App' }
|
Should -Invoke Set-RunOnceWingetTask -Times $Scheduled -Exactly -ParameterFilter { $appId -eq 'One.App' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
It 'limits a foreground winget uninstall to two minutes' {
|
||||||
|
Remove-WinGetApp -app 'One.App'
|
||||||
|
Should -Invoke Invoke-NonBlocking -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$ArgumentList -eq 'One.App' -and $TimeoutSeconds -eq 120
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'passes a specified foreground winget uninstall timeout' {
|
||||||
|
Remove-WinGetApp -app 'One.App' -TimeoutSeconds 30
|
||||||
|
Should -Invoke Invoke-NonBlocking -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$ArgumentList -eq 'One.App' -and $TimeoutSeconds -eq 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'reports a timed-out winget uninstall and continues' {
|
||||||
|
$script:Params = @{ User = 'Alice' }
|
||||||
|
Mock Invoke-NonBlocking { throw 'Operation timed out after 120 seconds' }
|
||||||
|
|
||||||
|
{ Remove-WinGetApp -app 'One.App' } | Should -Not -Throw
|
||||||
|
Should -Invoke Set-RunOnceWingetTask -Times 1 -Exactly
|
||||||
|
Should -Invoke Write-Host -Times 1 -Exactly -ParameterFilter {
|
||||||
|
$Object -like '*did not complete within 120 seconds*' -and $ForegroundColor -eq 'Red'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Describe 'Remove-AppxApp' {
|
Describe 'Remove-AppxApp' {
|
||||||
|
|||||||
Reference in New Issue
Block a user