windowsweep 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI-INTEGRATION-GUIDE.md +16 -12
- package/CHANGELOG.md +82 -0
- package/LICENSE +21 -21
- package/README.md +104 -69
- package/VERSION +1 -1
- package/lib/actions.ps1 +212 -212
- package/lib/config.ps1 +108 -99
- package/lib/constants.ps1 +103 -93
- package/lib/external.ps1 +87 -0
- package/lib/fs.ps1 +189 -180
- package/lib/log.ps1 +140 -140
- package/lib/safety.ps1 +467 -493
- package/lib/scan.ps1 +109 -100
- package/lib/ui.ps1 +275 -275
- package/modules/android_avd.ps1 +36 -36
- package/modules/app_caches.ps1 +58 -58
- package/modules/browsers.ps1 +33 -33
- package/modules/build_tools.ps1 +35 -35
- package/modules/crash_trap.ps1 +89 -89
- package/modules/disk_usage.ps1 +49 -49
- package/modules/docker.ps1 +93 -93
- package/modules/editors.ps1 +150 -150
- package/modules/globals_audit.ps1 +236 -236
- package/modules/health.ps1 +101 -101
- package/modules/installed_programs.ps1 +118 -118
- package/modules/menu.ps1 +63 -63
- package/modules/notify.ps1 +60 -60
- package/modules/orphaned_appdata.ps1 +189 -189
- package/modules/personal.ps1 +126 -126
- package/modules/pkg_managers.ps1 +73 -73
- package/modules/projects.ps1 +160 -160
- package/modules/release_helpers.ps1 +438 -438
- package/modules/reports.ps1 +212 -212
- package/modules/runner.ps1 +246 -239
- package/modules/self_test_contract.ps1 +195 -0
- package/modules/self_test_extra.ps1 +366 -360
- package/modules/startup_audit.ps1 +126 -126
- package/modules/system_admin.ps1 +220 -220
- package/modules/test_runners.ps1 +24 -24
- package/modules/walkthrough.ps1 +80 -79
- package/modules/windows_user.ps1 +118 -118
- package/package.json +3 -3
- package/windowsweep.ps1 +322 -316
package/modules/system_admin.ps1
CHANGED
|
@@ -1,220 +1,220 @@
|
|
|
1
|
-
# system_admin.ps1 - sections 12-16: Windows Update cache, Disk Cleanup engine, DISM, hibernation file, event logs.
|
|
2
|
-
# Every section here needs Administrator rights; the runner skips them (with the --elevate hint) when not elevated.
|
|
3
|
-
|
|
4
|
-
function Get-Targets12 {
|
|
5
|
-
$SR = $Script:P.SR; $PD = $Script:P.PD
|
|
6
|
-
return @(
|
|
7
|
-
(New-Target 12 'Windows Update download cache' "$SR\SoftwareDistribution\Download" -Mode clear -Note 'wuauserv and bits are stopped for the wipe and restarted')
|
|
8
|
-
(New-Target 12 'Delivery Optimization cache' 'Delete-DeliveryOptimizationCache -Force' -Kind cmd)
|
|
9
|
-
(New-Target 12 'Windows temp' "$SR\Temp" -Mode prune -Days $Script:WS.TempDays)
|
|
10
|
-
(New-Target 12 'LocalService temp' "$SR\ServiceProfiles\LocalService\AppData\Local\Temp" -Mode prune -Days $Script:WS.TempDays)
|
|
11
|
-
(New-Target 12 'NetworkService temp' "$SR\ServiceProfiles\NetworkService\AppData\Local\Temp" -Mode prune -Days $Script:WS.TempDays)
|
|
12
|
-
(New-Target 12 'CBS servicing logs' "$SR\Logs\CBS" -Mode prune -Days 30 -Note 'the live CBS.log is open and skipped')
|
|
13
|
-
(New-Target 12 'DISM logs' "$SR\Logs\DISM" -Mode prune -Days 30)
|
|
14
|
-
(New-Target 12 'Windows Update logs' "$SR\Logs\WindowsUpdate" -Mode prune -Days 30)
|
|
15
|
-
(New-Target 12 'Live kernel reports' "$SR\LiveKernelReports" -Mode clear-old -Days 7)
|
|
16
|
-
(New-Target 12 'Windows Error Reporting queue (system)' "$PD\Microsoft\Windows\WER\ReportQueue" -Mode clear)
|
|
17
|
-
(New-Target 12 'Windows Error Reporting archive (system)' "$PD\Microsoft\Windows\WER\ReportArchive" -Mode clear)
|
|
18
|
-
(New-Target 12 'Windows Error Reporting temp (system)' "$PD\Microsoft\Windows\WER\Temp" -Mode clear)
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function Set-ServiceState {
|
|
23
|
-
<# .SYNOPSIS Stop or start a service through the dry-run-aware wrapper. Returns $true when it was running before a stop. #>
|
|
24
|
-
param([string] $Name, [string] $Action)
|
|
25
|
-
$svc = Get-Service -Name $Name -ErrorAction SilentlyContinue
|
|
26
|
-
if (-not $svc) { return $false }
|
|
27
|
-
if ($Action -eq 'stop') {
|
|
28
|
-
if ($svc.Status -ne 'Running') { return $false }
|
|
29
|
-
if ($Script:WS.DryRun) { Write-DryRun "would stop service $Name"; return $true }
|
|
30
|
-
try { Stop-Service -Name $Name -Force -ErrorAction Stop; Write-Note "stopped $Name" } catch { Write-Warn "could not stop $Name : $($_.Exception.Message)" }
|
|
31
|
-
return $true
|
|
32
|
-
}
|
|
33
|
-
if ($Script:WS.DryRun) { Write-DryRun "would start service $Name"; return $true }
|
|
34
|
-
try { Start-Service -Name $Name -ErrorAction Stop; Write-Note "started $Name" } catch { Write-Warn "could not start $Name : $($_.Exception.Message)" }
|
|
35
|
-
return $true
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function Invoke-Section12 {
|
|
39
|
-
$ws = $Script:WS
|
|
40
|
-
Write-SectionIntro @(
|
|
41
|
-
'Windows Update keeps every downloaded package after installing it; Delivery Optimization keeps peer-share',
|
|
42
|
-
'copies; C:\Windows\Temp and the servicing logs grow forever. All of it is rebuilt on demand.'
|
|
43
|
-
)
|
|
44
|
-
if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
|
|
45
|
-
$targets = Get-Targets12
|
|
46
|
-
$null = Show-TargetSizes $targets
|
|
47
|
-
if (-not (Confirm-Section 'Clear Windows Update and system temp caches now?')) { Write-Info 'skipped'; return }
|
|
48
|
-
$wuWasRunning = Set-ServiceState -Name 'wuauserv' -Action 'stop'
|
|
49
|
-
$bitsWasRunning = Set-ServiceState -Name 'bits' -Action 'stop'
|
|
50
|
-
try {
|
|
51
|
-
$null = Invoke-TargetList $targets
|
|
52
|
-
} finally {
|
|
53
|
-
if ($wuWasRunning) { $null = Set-ServiceState -Name 'wuauserv' -Action 'start' }
|
|
54
|
-
if ($bitsWasRunning) { $null = Set-ServiceState -Name 'bits' -Action 'start' }
|
|
55
|
-
}
|
|
56
|
-
if (Get-Command Delete-DeliveryOptimizationCache -ErrorAction SilentlyContinue) {
|
|
57
|
-
if ($ws.DryRun) { Write-DryRun 'would run Delete-DeliveryOptimizationCache -Force' }
|
|
58
|
-
else {
|
|
59
|
-
try { Delete-DeliveryOptimizationCache -Force -ErrorAction Stop; Write-Ok 'Delivery Optimization cache cleared' } catch { Write-Warn "Delivery Optimization: $($_.Exception.Message)" }
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
# ---------------------------------------------------------------------------------------------
|
|
65
|
-
# Section 13 - Disk Cleanup engine (cleanmgr)
|
|
66
|
-
# ---------------------------------------------------------------------------------------------
|
|
67
|
-
|
|
68
|
-
# Handlers this tool enables. Deliberately absent: DownloadsFolder (your files), Recycle Bin (section 11 asks
|
|
69
|
-
# separately), User file versions (File History), Windows ESD installation files (needed for Reset this PC),
|
|
70
|
-
# Language Pack (removes installed languages).
|
|
71
|
-
$Script:WS_CLEANMGR_HANDLERS = @(
|
|
72
|
-
'Active Setup Temp Folders', 'BranchCache', 'Content Indexer Cleaner', 'D3D Shader Cache', 'Delivery Optimization Files',
|
|
73
|
-
'Device Driver Packages', 'Diagnostic Data Viewer database files', 'Downloaded Program Files', 'Internet Cache Files',
|
|
74
|
-
'Offline Pages Files', 'Old ChkDsk Files', 'Previous Installations', 'RetailDemo Offline Content', 'Setup Log Files',
|
|
75
|
-
'System error memory dump files', 'System error minidump files', 'Temporary Files', 'Temporary Setup Files',
|
|
76
|
-
'Temporary Sync Files', 'Thumbnail Cache', 'Update Cleanup', 'Upgrade Discarded Files', 'Windows Defender',
|
|
77
|
-
'Windows Error Reporting Files', 'Windows Upgrade Log Files'
|
|
78
|
-
)
|
|
79
|
-
$Script:WS_CLEANMGR_KEY = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches'
|
|
80
|
-
$Script:WS_CLEANMGR_FLAG = 'StateFlags0077'
|
|
81
|
-
|
|
82
|
-
function Get-Targets13 {
|
|
83
|
-
return @((New-Target 13 'cleanmgr /sagerun:77 with curated handlers' ($Script:WS_CLEANMGR_HANDLERS -join ', ') -Kind cmd -Note 'never Downloads, Recycle Bin, File History, ESD files or language packs'))
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function Invoke-Section13 {
|
|
87
|
-
$ws = $Script:WS
|
|
88
|
-
Write-SectionIntro @(
|
|
89
|
-
"Windows' own Disk Cleanup engine, driven with a curated handler list: temporary files, Update Cleanup,",
|
|
90
|
-
'thumbnail cache, memory dumps, setup and upgrade logs, previous Windows installations. It never touches',
|
|
91
|
-
'your Downloads folder, the Recycle Bin, File History or the files Reset-this-PC needs.'
|
|
92
|
-
)
|
|
93
|
-
if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
|
|
94
|
-
if (-not (Test-CommandPresent 'cleanmgr.exe')) { Write-Warn 'cleanmgr.exe not found on this edition of Windows - skipped'; return }
|
|
95
|
-
$present = @()
|
|
96
|
-
foreach ($h in $Script:WS_CLEANMGR_HANDLERS) { if (Test-Path -LiteralPath (Join-Path $Script:WS_CLEANMGR_KEY $h)) { $present += $h } }
|
|
97
|
-
Write-Info "handlers available on this machine: $($present.Count) of $($Script:WS_CLEANMGR_HANDLERS.Count)"
|
|
98
|
-
foreach ($h in $present) { Write-Note $h }
|
|
99
|
-
if ($ws.DryRun) { Write-DryRun 'would run cleanmgr.exe /sagerun:77 with those handlers (cleanmgr cannot preview sizes)'; return }
|
|
100
|
-
if (-not (Confirm-Section 'Run Disk Cleanup with these handlers now? (a progress window may appear; it can take several minutes)')) { Write-Info 'skipped'; return }
|
|
101
|
-
$before = Get-SystemDriveFree
|
|
102
|
-
try {
|
|
103
|
-
foreach ($h in $present) { Set-ItemProperty -LiteralPath (Join-Path $Script:WS_CLEANMGR_KEY $h) -Name $Script:WS_CLEANMGR_FLAG -Value 2 -Type DWord -ErrorAction Stop }
|
|
104
|
-
$r = Invoke-External -FilePath 'cleanmgr.exe' -ArgumentList @('/sagerun:77') -Destructive -Quiet -Label 'cleanmgr /sagerun:77'
|
|
105
|
-
if ($r.ExitCode -ne 0) { Write-Warn "cleanmgr returned $($r.ExitCode)" }
|
|
106
|
-
} catch {
|
|
107
|
-
Write-Err "could not configure Disk Cleanup: $($_.Exception.Message)"
|
|
108
|
-
} finally {
|
|
109
|
-
foreach ($h in $present) { Remove-ItemProperty -LiteralPath (Join-Path $Script:WS_CLEANMGR_KEY $h) -Name $Script:WS_CLEANMGR_FLAG -ErrorAction SilentlyContinue }
|
|
110
|
-
}
|
|
111
|
-
$gain = [math]::Max([long]0, (Get-SystemDriveFree) - $before)
|
|
112
|
-
Add-Freed $gain
|
|
113
|
-
Write-Ok ("Disk Cleanup finished - system drive gained " + (Format-Bytes $gain))
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
# ---------------------------------------------------------------------------------------------
|
|
117
|
-
# Section 14 - DISM component store
|
|
118
|
-
# ---------------------------------------------------------------------------------------------
|
|
119
|
-
|
|
120
|
-
function Get-Targets14 {
|
|
121
|
-
$note = 'superseded component versions in WinSxS'
|
|
122
|
-
if ($Script:WS.ResetBase) { $note += '; --reset-base also removes the ability to uninstall installed updates' }
|
|
123
|
-
return @((New-Target 14 'Dism /Online /Cleanup-Image /StartComponentCleanup' "$($Script:P.SR)\WinSxS" -Kind cmd -Note $note))
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function Invoke-Section14 {
|
|
127
|
-
$ws = $Script:WS
|
|
128
|
-
Write-SectionIntro @(
|
|
129
|
-
'The component store (WinSxS) keeps superseded versions of Windows components. DISM removes the ones no',
|
|
130
|
-
'installed update needs. It is slow (5-20 minutes) and safe; /ResetBase (opt-in) also drops the ability to',
|
|
131
|
-
'uninstall currently installed updates.'
|
|
132
|
-
)
|
|
133
|
-
if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
|
|
134
|
-
if (-not (Test-CommandPresent 'Dism.exe')) { Write-Warn 'Dism.exe not found - skipped'; return }
|
|
135
|
-
Write-Info 'analyzing the component store (read-only, about a minute)...'
|
|
136
|
-
$a = Invoke-External -FilePath 'Dism.exe' -ArgumentList @('/Online', '/Cleanup-Image', '/AnalyzeComponentStore') -Quiet -Label 'dism analyze'
|
|
137
|
-
foreach ($l in $a.Output) { if ($l -match 'Size|Cleanup|Reclaimable|Date' ) { Write-Note ($l.Trim()) } }
|
|
138
|
-
$dismArgs = @('/Online', '/Cleanup-Image', '/StartComponentCleanup')
|
|
139
|
-
if ($ws.ResetBase) { $dismArgs += '/ResetBase'; Write-Warn '/ResetBase requested: installed updates can no longer be uninstalled afterwards' }
|
|
140
|
-
if (-not (Confirm-Section 'Run the component store cleanup now?')) { Write-Info 'skipped'; return }
|
|
141
|
-
$before = Get-SystemDriveFree
|
|
142
|
-
$r = Invoke-External -FilePath 'Dism.exe' -ArgumentList $dismArgs -Destructive -Quiet -Label 'dism StartComponentCleanup'
|
|
143
|
-
if ($r.Ran) {
|
|
144
|
-
$gain = [math]::Max([long]0, (Get-SystemDriveFree) - $before)
|
|
145
|
-
Add-Freed $gain
|
|
146
|
-
if ($r.ExitCode -eq 0) { Write-Ok ("component store cleanup finished - system drive gained " + (Format-Bytes $gain)) } else { Write-Warn "DISM returned $($r.ExitCode) - see the log" }
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
# ---------------------------------------------------------------------------------------------
|
|
151
|
-
# Section 15 - hibernation file
|
|
152
|
-
# ---------------------------------------------------------------------------------------------
|
|
153
|
-
|
|
154
|
-
function Get-Targets15 {
|
|
155
|
-
return @((New-Target 15 'hiberfil.sys (powercfg /hibernate off | /type reduced)' (Join-Path $Script:P.SD '\hiberfil.sys') -Kind cmd -Note 'off frees the whole file; reduced keeps Fast Startup at roughly
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function Invoke-Section15 {
|
|
159
|
-
$ws = $Script:WS
|
|
160
|
-
$h = Get-HiberfilInfo
|
|
161
|
-
Write-SectionIntro @(
|
|
162
|
-
'hiberfil.sys holds a copy of RAM for Hibernate and Fast Startup, so it is about 40% of your RAM - permanently.',
|
|
163
|
-
' off removes it. Sleep still works; Hibernate, Fast Startup and hibernate-on-critical-battery do not.',
|
|
164
|
-
' reduced keeps Fast Startup only, at a smaller file (Windows decides the size, usually about half).',
|
|
165
|
-
'Running speed is unaffected either way; this is purely disk space.'
|
|
166
|
-
)
|
|
167
|
-
if ($h.Bytes -gt 0) { Write-Kv 'hiberfil.sys now:' ("{0} (hibernation {1})" -f (Format-Bytes $h.Bytes), $h.State) } else { Write-Info 'hiberfil.sys is absent - hibernation is already off'; return }
|
|
168
|
-
if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
|
|
169
|
-
$choice = $ws.Hiberfil
|
|
170
|
-
if (-not $choice) {
|
|
171
|
-
if (-not $ws.Interactive) { Write-Warn 'pass --hiberfil off|reduced|keep to run this section unattended'; return }
|
|
172
|
-
$choice = Read-Choice -Prompt ' [o] turn hibernation off [r] reduced (keep Fast Startup) [k] keep as is > ' -Default 'k'
|
|
173
|
-
switch ($choice) { 'o' { $choice = 'off' } 'r' { $choice = 'reduced' } default { $choice = 'keep' } }
|
|
174
|
-
}
|
|
175
|
-
if ($choice -eq 'keep') { Write-Info 'left unchanged'; return }
|
|
176
|
-
$pcArgs = @('/hibernate', 'off')
|
|
177
|
-
if ($choice -eq 'reduced') { $pcArgs = @('/hibernate', '/type', 'reduced') }
|
|
178
|
-
if ($ws.DryRun) { Write-DryRun ("would run powercfg $($pcArgs -join ' ') (frees up to " + (Format-Bytes $h.Bytes) + ')'); Add-Freed $h.Bytes; return }
|
|
179
|
-
if (-not (Confirm-Ui -Prompt "Run powercfg $($pcArgs -join ' ') now?" -Default 'n')) { Write-Info 'skipped'; return }
|
|
180
|
-
$r = Invoke-External -FilePath 'powercfg.exe' -ArgumentList $pcArgs -Destructive -Quiet -Label "powercfg $($pcArgs -join ' ')"
|
|
181
|
-
Start-Sleep -Seconds 2
|
|
182
|
-
$after = Get-HiberfilInfo
|
|
183
|
-
$gain = [math]::Max([long]0, $h.Bytes - $after.Bytes)
|
|
184
|
-
Add-Freed $gain
|
|
185
|
-
if ($r.ExitCode -eq 0) { Write-Ok ("hibernation set to '$choice' - hiberfil.sys " + (Format-Bytes $h.Bytes) + ' -> ' + (Format-Bytes $after.Bytes) + ' (freed ' + (Format-Bytes $gain) + ')') } else { Write-Warn "powercfg returned $($r.ExitCode)" }
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
# ---------------------------------------------------------------------------------------------
|
|
189
|
-
# Section 16 - event logs
|
|
190
|
-
# ---------------------------------------------------------------------------------------------
|
|
191
|
-
|
|
192
|
-
function Get-Targets16 {
|
|
193
|
-
return @((New-Target 16 'Windows Event Logs (wevtutil cl for every log)' "$($Script:P.SR)\System32\winevt\Logs" -Kind cmd -Note 'PERMANENT - troubleshooting history is lost'))
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function Invoke-Section16 {
|
|
197
|
-
$ws = $Script:WS
|
|
198
|
-
$logDir = Join-Path $Script:P.SR 'System32\winevt\Logs'
|
|
199
|
-
Write-SectionIntro @(
|
|
200
|
-
'Clears every Windows Event Log (Application, System, Security and the hundreds of per-component logs).',
|
|
201
|
-
'This is permanent and removes the history a technician would use to diagnose a problem. Only do this on',
|
|
202
|
-
'a machine that is behaving normally.'
|
|
203
|
-
)
|
|
204
|
-
if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
|
|
205
|
-
$before = Get-DirectoryBytes $logDir
|
|
206
|
-
Write-Kv 'event log store:' (Format-Bytes $before)
|
|
207
|
-
if ($ws.DryRun) { Write-DryRun ("would clear every event log (about " + (Format-Bytes $before) + ')'); Add-Freed $before; return }
|
|
208
|
-
if (-not (Confirm-Ui -Prompt 'Permanently clear ALL Windows Event Logs?' -Default 'n')) { Write-Info 'skipped'; return }
|
|
209
|
-
$list = Invoke-External -FilePath 'wevtutil.exe' -ArgumentList @('el') -Quiet -Label 'wevtutil el'
|
|
210
|
-
$ok = 0; $failed = 0
|
|
211
|
-
foreach ($name in $list.Output) {
|
|
212
|
-
$n = ([string]$name).Trim()
|
|
213
|
-
if (-not $n) { continue }
|
|
214
|
-
$r = Invoke-External -FilePath 'wevtutil.exe' -ArgumentList @('cl', $n) -Destructive -Quiet -Label "wevtutil cl $n"
|
|
215
|
-
if ($r.ExitCode -eq 0) { $ok++ } else { $failed++ }
|
|
216
|
-
}
|
|
217
|
-
$gain = [math]::Max([long]0, $before - (Get-DirectoryBytes $logDir))
|
|
218
|
-
Add-Freed $gain
|
|
219
|
-
Write-Ok ("cleared $ok logs ($failed could not be cleared - analytic/debug channels), freed " + (Format-Bytes $gain))
|
|
220
|
-
}
|
|
1
|
+
# system_admin.ps1 - sections 12-16: Windows Update cache, Disk Cleanup engine, DISM, hibernation file, event logs.
|
|
2
|
+
# Every section here needs Administrator rights; the runner skips them (with the --elevate hint) when not elevated.
|
|
3
|
+
|
|
4
|
+
function Get-Targets12 {
|
|
5
|
+
$SR = $Script:P.SR; $PD = $Script:P.PD
|
|
6
|
+
return @(
|
|
7
|
+
(New-Target 12 'Windows Update download cache' "$SR\SoftwareDistribution\Download" -Mode clear -Note 'wuauserv and bits are stopped for the wipe and restarted')
|
|
8
|
+
(New-Target 12 'Delivery Optimization cache' 'Delete-DeliveryOptimizationCache -Force' -Kind cmd)
|
|
9
|
+
(New-Target 12 'Windows temp' "$SR\Temp" -Mode prune -Days $Script:WS.TempDays)
|
|
10
|
+
(New-Target 12 'LocalService temp' "$SR\ServiceProfiles\LocalService\AppData\Local\Temp" -Mode prune -Days $Script:WS.TempDays)
|
|
11
|
+
(New-Target 12 'NetworkService temp' "$SR\ServiceProfiles\NetworkService\AppData\Local\Temp" -Mode prune -Days $Script:WS.TempDays)
|
|
12
|
+
(New-Target 12 'CBS servicing logs' "$SR\Logs\CBS" -Mode prune -Days 30 -Note 'the live CBS.log is open and skipped')
|
|
13
|
+
(New-Target 12 'DISM logs' "$SR\Logs\DISM" -Mode prune -Days 30)
|
|
14
|
+
(New-Target 12 'Windows Update logs' "$SR\Logs\WindowsUpdate" -Mode prune -Days 30)
|
|
15
|
+
(New-Target 12 'Live kernel reports' "$SR\LiveKernelReports" -Mode clear-old -Days 7)
|
|
16
|
+
(New-Target 12 'Windows Error Reporting queue (system)' "$PD\Microsoft\Windows\WER\ReportQueue" -Mode clear)
|
|
17
|
+
(New-Target 12 'Windows Error Reporting archive (system)' "$PD\Microsoft\Windows\WER\ReportArchive" -Mode clear)
|
|
18
|
+
(New-Target 12 'Windows Error Reporting temp (system)' "$PD\Microsoft\Windows\WER\Temp" -Mode clear)
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function Set-ServiceState {
|
|
23
|
+
<# .SYNOPSIS Stop or start a service through the dry-run-aware wrapper. Returns $true when it was running before a stop. #>
|
|
24
|
+
param([string] $Name, [string] $Action)
|
|
25
|
+
$svc = Get-Service -Name $Name -ErrorAction SilentlyContinue
|
|
26
|
+
if (-not $svc) { return $false }
|
|
27
|
+
if ($Action -eq 'stop') {
|
|
28
|
+
if ($svc.Status -ne 'Running') { return $false }
|
|
29
|
+
if ($Script:WS.DryRun) { Write-DryRun "would stop service $Name"; return $true }
|
|
30
|
+
try { Stop-Service -Name $Name -Force -ErrorAction Stop; Write-Note "stopped $Name" } catch { Write-Warn "could not stop $Name : $($_.Exception.Message)" }
|
|
31
|
+
return $true
|
|
32
|
+
}
|
|
33
|
+
if ($Script:WS.DryRun) { Write-DryRun "would start service $Name"; return $true }
|
|
34
|
+
try { Start-Service -Name $Name -ErrorAction Stop; Write-Note "started $Name" } catch { Write-Warn "could not start $Name : $($_.Exception.Message)" }
|
|
35
|
+
return $true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Invoke-Section12 {
|
|
39
|
+
$ws = $Script:WS
|
|
40
|
+
Write-SectionIntro @(
|
|
41
|
+
'Windows Update keeps every downloaded package after installing it; Delivery Optimization keeps peer-share',
|
|
42
|
+
'copies; C:\Windows\Temp and the servicing logs grow forever. All of it is rebuilt on demand.'
|
|
43
|
+
)
|
|
44
|
+
if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
|
|
45
|
+
$targets = Get-Targets12
|
|
46
|
+
$null = Show-TargetSizes $targets
|
|
47
|
+
if (-not (Confirm-Section 'Clear Windows Update and system temp caches now?')) { Write-Info 'skipped'; return }
|
|
48
|
+
$wuWasRunning = Set-ServiceState -Name 'wuauserv' -Action 'stop'
|
|
49
|
+
$bitsWasRunning = Set-ServiceState -Name 'bits' -Action 'stop'
|
|
50
|
+
try {
|
|
51
|
+
$null = Invoke-TargetList $targets
|
|
52
|
+
} finally {
|
|
53
|
+
if ($wuWasRunning) { $null = Set-ServiceState -Name 'wuauserv' -Action 'start' }
|
|
54
|
+
if ($bitsWasRunning) { $null = Set-ServiceState -Name 'bits' -Action 'start' }
|
|
55
|
+
}
|
|
56
|
+
if (Get-Command Delete-DeliveryOptimizationCache -ErrorAction SilentlyContinue) {
|
|
57
|
+
if ($ws.DryRun) { Write-DryRun 'would run Delete-DeliveryOptimizationCache -Force' }
|
|
58
|
+
else {
|
|
59
|
+
try { Delete-DeliveryOptimizationCache -Force -ErrorAction Stop; Write-Ok 'Delivery Optimization cache cleared' } catch { Write-Warn "Delivery Optimization: $($_.Exception.Message)" }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# ---------------------------------------------------------------------------------------------
|
|
65
|
+
# Section 13 - Disk Cleanup engine (cleanmgr)
|
|
66
|
+
# ---------------------------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
# Handlers this tool enables. Deliberately absent: DownloadsFolder (your files), Recycle Bin (section 11 asks
|
|
69
|
+
# separately), User file versions (File History), Windows ESD installation files (needed for Reset this PC),
|
|
70
|
+
# Language Pack (removes installed languages).
|
|
71
|
+
$Script:WS_CLEANMGR_HANDLERS = @(
|
|
72
|
+
'Active Setup Temp Folders', 'BranchCache', 'Content Indexer Cleaner', 'D3D Shader Cache', 'Delivery Optimization Files',
|
|
73
|
+
'Device Driver Packages', 'Diagnostic Data Viewer database files', 'Downloaded Program Files', 'Internet Cache Files',
|
|
74
|
+
'Offline Pages Files', 'Old ChkDsk Files', 'Previous Installations', 'RetailDemo Offline Content', 'Setup Log Files',
|
|
75
|
+
'System error memory dump files', 'System error minidump files', 'Temporary Files', 'Temporary Setup Files',
|
|
76
|
+
'Temporary Sync Files', 'Thumbnail Cache', 'Update Cleanup', 'Upgrade Discarded Files', 'Windows Defender',
|
|
77
|
+
'Windows Error Reporting Files', 'Windows Upgrade Log Files'
|
|
78
|
+
)
|
|
79
|
+
$Script:WS_CLEANMGR_KEY = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches'
|
|
80
|
+
$Script:WS_CLEANMGR_FLAG = 'StateFlags0077'
|
|
81
|
+
|
|
82
|
+
function Get-Targets13 {
|
|
83
|
+
return @((New-Target 13 'cleanmgr /sagerun:77 with curated handlers' ($Script:WS_CLEANMGR_HANDLERS -join ', ') -Kind cmd -Note 'never Downloads, Recycle Bin, File History, ESD files or language packs'))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function Invoke-Section13 {
|
|
87
|
+
$ws = $Script:WS
|
|
88
|
+
Write-SectionIntro @(
|
|
89
|
+
"Windows' own Disk Cleanup engine, driven with a curated handler list: temporary files, Update Cleanup,",
|
|
90
|
+
'thumbnail cache, memory dumps, setup and upgrade logs, previous Windows installations. It never touches',
|
|
91
|
+
'your Downloads folder, the Recycle Bin, File History or the files Reset-this-PC needs.'
|
|
92
|
+
)
|
|
93
|
+
if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
|
|
94
|
+
if (-not (Test-CommandPresent 'cleanmgr.exe')) { Write-Warn 'cleanmgr.exe not found on this edition of Windows - skipped'; return }
|
|
95
|
+
$present = @()
|
|
96
|
+
foreach ($h in $Script:WS_CLEANMGR_HANDLERS) { if (Test-Path -LiteralPath (Join-Path $Script:WS_CLEANMGR_KEY $h)) { $present += $h } }
|
|
97
|
+
Write-Info "handlers available on this machine: $($present.Count) of $($Script:WS_CLEANMGR_HANDLERS.Count)"
|
|
98
|
+
foreach ($h in $present) { Write-Note $h }
|
|
99
|
+
if ($ws.DryRun) { Write-DryRun 'would run cleanmgr.exe /sagerun:77 with those handlers (cleanmgr cannot preview sizes)'; return }
|
|
100
|
+
if (-not (Confirm-Section 'Run Disk Cleanup with these handlers now? (a progress window may appear; it can take several minutes)')) { Write-Info 'skipped'; return }
|
|
101
|
+
$before = Get-SystemDriveFree
|
|
102
|
+
try {
|
|
103
|
+
foreach ($h in $present) { Set-ItemProperty -LiteralPath (Join-Path $Script:WS_CLEANMGR_KEY $h) -Name $Script:WS_CLEANMGR_FLAG -Value 2 -Type DWord -ErrorAction Stop }
|
|
104
|
+
$r = Invoke-External -FilePath 'cleanmgr.exe' -ArgumentList @('/sagerun:77') -Destructive -Quiet -Label 'cleanmgr /sagerun:77'
|
|
105
|
+
if ($r.ExitCode -ne 0) { Write-Warn "cleanmgr returned $($r.ExitCode)" }
|
|
106
|
+
} catch {
|
|
107
|
+
Write-Err "could not configure Disk Cleanup: $($_.Exception.Message)"
|
|
108
|
+
} finally {
|
|
109
|
+
foreach ($h in $present) { Remove-ItemProperty -LiteralPath (Join-Path $Script:WS_CLEANMGR_KEY $h) -Name $Script:WS_CLEANMGR_FLAG -ErrorAction SilentlyContinue }
|
|
110
|
+
}
|
|
111
|
+
$gain = [math]::Max([long]0, (Get-SystemDriveFree) - $before)
|
|
112
|
+
Add-Freed $gain
|
|
113
|
+
Write-Ok ("Disk Cleanup finished - system drive gained " + (Format-Bytes $gain))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# ---------------------------------------------------------------------------------------------
|
|
117
|
+
# Section 14 - DISM component store
|
|
118
|
+
# ---------------------------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
function Get-Targets14 {
|
|
121
|
+
$note = 'superseded component versions in WinSxS'
|
|
122
|
+
if ($Script:WS.ResetBase) { $note += '; --reset-base also removes the ability to uninstall installed updates' }
|
|
123
|
+
return @((New-Target 14 'Dism /Online /Cleanup-Image /StartComponentCleanup' "$($Script:P.SR)\WinSxS" -Kind cmd -Note $note))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function Invoke-Section14 {
|
|
127
|
+
$ws = $Script:WS
|
|
128
|
+
Write-SectionIntro @(
|
|
129
|
+
'The component store (WinSxS) keeps superseded versions of Windows components. DISM removes the ones no',
|
|
130
|
+
'installed update needs. It is slow (5-20 minutes) and safe; /ResetBase (opt-in) also drops the ability to',
|
|
131
|
+
'uninstall currently installed updates.'
|
|
132
|
+
)
|
|
133
|
+
if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
|
|
134
|
+
if (-not (Test-CommandPresent 'Dism.exe')) { Write-Warn 'Dism.exe not found - skipped'; return }
|
|
135
|
+
Write-Info 'analyzing the component store (read-only, about a minute)...'
|
|
136
|
+
$a = Invoke-External -FilePath 'Dism.exe' -ArgumentList @('/Online', '/Cleanup-Image', '/AnalyzeComponentStore') -Quiet -Label 'dism analyze'
|
|
137
|
+
foreach ($l in $a.Output) { if ($l -match 'Size|Cleanup|Reclaimable|Date' ) { Write-Note ($l.Trim()) } }
|
|
138
|
+
$dismArgs = @('/Online', '/Cleanup-Image', '/StartComponentCleanup')
|
|
139
|
+
if ($ws.ResetBase) { $dismArgs += '/ResetBase'; Write-Warn '/ResetBase requested: installed updates can no longer be uninstalled afterwards' }
|
|
140
|
+
if (-not (Confirm-Section 'Run the component store cleanup now?')) { Write-Info 'skipped'; return }
|
|
141
|
+
$before = Get-SystemDriveFree
|
|
142
|
+
$r = Invoke-External -FilePath 'Dism.exe' -ArgumentList $dismArgs -Destructive -Quiet -Label 'dism StartComponentCleanup'
|
|
143
|
+
if ($r.Ran) {
|
|
144
|
+
$gain = [math]::Max([long]0, (Get-SystemDriveFree) - $before)
|
|
145
|
+
Add-Freed $gain
|
|
146
|
+
if ($r.ExitCode -eq 0) { Write-Ok ("component store cleanup finished - system drive gained " + (Format-Bytes $gain)) } else { Write-Warn "DISM returned $($r.ExitCode) - see the log" }
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# ---------------------------------------------------------------------------------------------
|
|
151
|
+
# Section 15 - hibernation file
|
|
152
|
+
# ---------------------------------------------------------------------------------------------
|
|
153
|
+
|
|
154
|
+
function Get-Targets15 {
|
|
155
|
+
return @((New-Target 15 'hiberfil.sys (powercfg /hibernate off | /type reduced)' (Join-Path $Script:P.SD '\hiberfil.sys') -Kind cmd -Note 'off frees the whole file, about 40% of RAM; reduced keeps Fast Startup at roughly half that'))
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function Invoke-Section15 {
|
|
159
|
+
$ws = $Script:WS
|
|
160
|
+
$h = Get-HiberfilInfo
|
|
161
|
+
Write-SectionIntro @(
|
|
162
|
+
'hiberfil.sys holds a copy of RAM for Hibernate and Fast Startup, so it is about 40% of your RAM - permanently.',
|
|
163
|
+
' off removes it. Sleep still works; Hibernate, Fast Startup and hibernate-on-critical-battery do not.',
|
|
164
|
+
' reduced keeps Fast Startup only, at a smaller file (Windows decides the size, usually about half).',
|
|
165
|
+
'Running speed is unaffected either way; this is purely disk space.'
|
|
166
|
+
)
|
|
167
|
+
if ($h.Bytes -gt 0) { Write-Kv 'hiberfil.sys now:' ("{0} (hibernation {1})" -f (Format-Bytes $h.Bytes), $h.State) } else { Write-Info 'hiberfil.sys is absent - hibernation is already off'; return }
|
|
168
|
+
if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
|
|
169
|
+
$choice = $ws.Hiberfil
|
|
170
|
+
if (-not $choice) {
|
|
171
|
+
if (-not $ws.Interactive) { Write-Warn 'pass --hiberfil off|reduced|keep to run this section unattended'; return }
|
|
172
|
+
$choice = Read-Choice -Prompt ' [o] turn hibernation off [r] reduced (keep Fast Startup) [k] keep as is > ' -Default 'k'
|
|
173
|
+
switch ($choice) { 'o' { $choice = 'off' } 'r' { $choice = 'reduced' } default { $choice = 'keep' } }
|
|
174
|
+
}
|
|
175
|
+
if ($choice -eq 'keep') { Write-Info 'left unchanged'; return }
|
|
176
|
+
$pcArgs = @('/hibernate', 'off')
|
|
177
|
+
if ($choice -eq 'reduced') { $pcArgs = @('/hibernate', '/type', 'reduced') }
|
|
178
|
+
if ($ws.DryRun) { Write-DryRun ("would run powercfg $($pcArgs -join ' ') (frees up to " + (Format-Bytes $h.Bytes) + ')'); Add-Freed $h.Bytes; return }
|
|
179
|
+
if (-not (Confirm-Ui -Prompt "Run powercfg $($pcArgs -join ' ') now?" -Default 'n')) { Write-Info 'skipped'; return }
|
|
180
|
+
$r = Invoke-External -FilePath 'powercfg.exe' -ArgumentList $pcArgs -Destructive -Quiet -Label "powercfg $($pcArgs -join ' ')"
|
|
181
|
+
Start-Sleep -Seconds 2
|
|
182
|
+
$after = Get-HiberfilInfo
|
|
183
|
+
$gain = [math]::Max([long]0, $h.Bytes - $after.Bytes)
|
|
184
|
+
Add-Freed $gain
|
|
185
|
+
if ($r.ExitCode -eq 0) { Write-Ok ("hibernation set to '$choice' - hiberfil.sys " + (Format-Bytes $h.Bytes) + ' -> ' + (Format-Bytes $after.Bytes) + ' (freed ' + (Format-Bytes $gain) + ')') } else { Write-Warn "powercfg returned $($r.ExitCode)" }
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# ---------------------------------------------------------------------------------------------
|
|
189
|
+
# Section 16 - event logs
|
|
190
|
+
# ---------------------------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
function Get-Targets16 {
|
|
193
|
+
return @((New-Target 16 'Windows Event Logs (wevtutil cl for every log)' "$($Script:P.SR)\System32\winevt\Logs" -Kind cmd -Note 'PERMANENT - troubleshooting history is lost'))
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function Invoke-Section16 {
|
|
197
|
+
$ws = $Script:WS
|
|
198
|
+
$logDir = Join-Path $Script:P.SR 'System32\winevt\Logs'
|
|
199
|
+
Write-SectionIntro @(
|
|
200
|
+
'Clears every Windows Event Log (Application, System, Security and the hundreds of per-component logs).',
|
|
201
|
+
'This is permanent and removes the history a technician would use to diagnose a problem. Only do this on',
|
|
202
|
+
'a machine that is behaving normally.'
|
|
203
|
+
)
|
|
204
|
+
if (-not $ws.IsAdmin) { Write-Warn 'needs Administrator - skipped'; return }
|
|
205
|
+
$before = Get-DirectoryBytes $logDir
|
|
206
|
+
Write-Kv 'event log store:' (Format-Bytes $before)
|
|
207
|
+
if ($ws.DryRun) { Write-DryRun ("would clear every event log (about " + (Format-Bytes $before) + ')'); Add-Freed $before; return }
|
|
208
|
+
if (-not (Confirm-Ui -Prompt 'Permanently clear ALL Windows Event Logs?' -Default 'n')) { Write-Info 'skipped'; return }
|
|
209
|
+
$list = Invoke-External -FilePath 'wevtutil.exe' -ArgumentList @('el') -Quiet -Label 'wevtutil el'
|
|
210
|
+
$ok = 0; $failed = 0
|
|
211
|
+
foreach ($name in $list.Output) {
|
|
212
|
+
$n = ([string]$name).Trim()
|
|
213
|
+
if (-not $n) { continue }
|
|
214
|
+
$r = Invoke-External -FilePath 'wevtutil.exe' -ArgumentList @('cl', $n) -Destructive -Quiet -Label "wevtutil cl $n"
|
|
215
|
+
if ($r.ExitCode -eq 0) { $ok++ } else { $failed++ }
|
|
216
|
+
}
|
|
217
|
+
$gain = [math]::Max([long]0, $before - (Get-DirectoryBytes $logDir))
|
|
218
|
+
Add-Freed $gain
|
|
219
|
+
Write-Ok ("cleared $ok logs ($failed could not be cleared - analytic/debug channels), freed " + (Format-Bytes $gain))
|
|
220
|
+
}
|
package/modules/test_runners.ps1
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
# test_runners.ps1 - section 3: downloaded test browsers (Cypress, Playwright, Puppeteer). Keep the newest of each.
|
|
2
|
-
|
|
3
|
-
$Script:WS_GROUP_BY_FAMILY = { param($name) ($name -replace '[-_]?v?\d[\d.]*$', '') }
|
|
4
|
-
|
|
5
|
-
function Get-Targets03 {
|
|
6
|
-
$P = $Script:P
|
|
7
|
-
return @(
|
|
8
|
-
(New-Target 3 'Cypress binary versions' "$($P.L)\Cypress\Cache" -Mode units -Dev $true -KeepNewest $true -Note 'one folder per Cypress version; newest kept')
|
|
9
|
-
(New-Target 3 'Playwright browsers' "$($P.L)\ms-playwright" -Mode units -Dev $true -KeepNewest $true -GroupBy $Script:WS_GROUP_BY_FAMILY -ExcludeNames @('.links', 'daemon') -Note 'newest build of each browser kept')
|
|
10
|
-
(New-Target 3 'Playwright-Go browsers' "$($P.L)\ms-playwright-go" -Mode units -Dev $true -KeepNewest $true -GroupBy $Script:WS_GROUP_BY_FAMILY)
|
|
11
|
-
(New-Target 3 'Puppeteer browsers' "$($P.U)\.cache\puppeteer\*" -Kind glob -Mode units -Dev $true -KeepNewest $true -Note 'per browser family: newest build kept')
|
|
12
|
-
)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function Invoke-Section03 {
|
|
16
|
-
Write-SectionIntro @(
|
|
17
|
-
'Test runners download whole browsers per version. The newest build of each browser is always kept; older',
|
|
18
|
-
'builds go once idle for the window. A missing build is re-downloaded by the next install or "playwright install".'
|
|
19
|
-
) -Dev $true
|
|
20
|
-
$targets = Get-Targets03
|
|
21
|
-
$null = Show-TargetSizes $targets
|
|
22
|
-
if (-not (Confirm-Section 'Prune superseded test-runner browsers now?')) { Write-Info 'skipped'; return }
|
|
23
|
-
$null = Invoke-TargetList $targets
|
|
24
|
-
}
|
|
1
|
+
# test_runners.ps1 - section 3: downloaded test browsers (Cypress, Playwright, Puppeteer). Keep the newest of each.
|
|
2
|
+
|
|
3
|
+
$Script:WS_GROUP_BY_FAMILY = { param($name) ($name -replace '[-_]?v?\d[\d.]*$', '') }
|
|
4
|
+
|
|
5
|
+
function Get-Targets03 {
|
|
6
|
+
$P = $Script:P
|
|
7
|
+
return @(
|
|
8
|
+
(New-Target 3 'Cypress binary versions' "$($P.L)\Cypress\Cache" -Mode units -Dev $true -KeepNewest $true -Note 'one folder per Cypress version; newest kept')
|
|
9
|
+
(New-Target 3 'Playwright browsers' "$($P.L)\ms-playwright" -Mode units -Dev $true -KeepNewest $true -GroupBy $Script:WS_GROUP_BY_FAMILY -ExcludeNames @('.links', 'daemon') -Note 'newest build of each browser kept')
|
|
10
|
+
(New-Target 3 'Playwright-Go browsers' "$($P.L)\ms-playwright-go" -Mode units -Dev $true -KeepNewest $true -GroupBy $Script:WS_GROUP_BY_FAMILY)
|
|
11
|
+
(New-Target 3 'Puppeteer browsers' "$($P.U)\.cache\puppeteer\*" -Kind glob -Mode units -Dev $true -KeepNewest $true -Note 'per browser family: newest build kept')
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Invoke-Section03 {
|
|
16
|
+
Write-SectionIntro @(
|
|
17
|
+
'Test runners download whole browsers per version. The newest build of each browser is always kept; older',
|
|
18
|
+
'builds go once idle for the window. A missing build is re-downloaded by the next install or "playwright install".'
|
|
19
|
+
) -Dev $true
|
|
20
|
+
$targets = Get-Targets03
|
|
21
|
+
$null = Show-TargetSizes $targets
|
|
22
|
+
if (-not (Confirm-Section 'Prune superseded test-runner browsers now?')) { Write-Info 'skipped'; return }
|
|
23
|
+
$null = Invoke-TargetList $targets
|
|
24
|
+
}
|