Skip to content

Commit 8eedd1a

Browse files
bivlkedclaude
andcommitted
fix: PS 7.4+ compatibility, improved statistics accuracy (v2.12)
Fixed: - Removed deprecated -UseBasicParsing from Invoke-WebRequest (PS 7.4+) - DISM ReportOnly now shows /ResetBase flag with warning - AppUpdatesCount only incremented on successful winget upgrade Added: - ConvertFrom-HumanReadableSize helper function - Get-RecycleBinSize function for accurate size tracking - FreedByCategory statistics for Docker, WSL, Recycle Bin, npm cache Improved: - ReportOnly mode shows actual Recycle Bin size Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ca94b46 commit 8eedd1a

5 files changed

Lines changed: 175 additions & 23 deletions

File tree

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
---
99

10+
## [2.12] - 2026-01-17
11+
12+
### Fixed
13+
- **PowerShell 7.4+ compatibility**: Removed deprecated `-UseBasicParsing` parameter from `Invoke-WebRequest`
14+
- This parameter was removed in PS 7.4 and caused errors during PSGallery connectivity check
15+
- Now works correctly on all PowerShell 7.x versions
16+
17+
- **DISM ReportOnly accuracy**: ReportOnly mode now correctly shows `/ResetBase` flag
18+
- Added warning that `/ResetBase` removes ability to uninstall updates
19+
- Previously the preview message didn't include `/ResetBase` which was misleading
20+
21+
- **AppUpdatesCount accuracy**: Fixed inflated statistics when winget fails
22+
- `AppUpdatesCount` is now only incremented when `winget upgrade` succeeds (exit code 0)
23+
- Previously showed available updates count even when installation failed
24+
25+
### Added
26+
- **Improved space freed statistics**:
27+
- Docker cleanup: Now parses `docker system prune` output and adds reclaimed space to statistics
28+
- WSL compaction: Now tracks freed space by category ("WSL")
29+
- Recycle Bin: Now measures size before cleanup and shows in both ReportOnly preview and results
30+
- npm cache: Now tracks size freed via `npm cache clean --force`
31+
32+
- **New helper functions**:
33+
- `ConvertFrom-HumanReadableSize`: Converts strings like "2.5 GB" to bytes (inverse of `Format-FileSize`)
34+
- `Get-RecycleBinSize`: Measures total size of Recycle Bin items via Shell.Application COM
35+
36+
### Improved
37+
- **ReportOnly mode**: Recycle Bin now shows actual size instead of generic "Would clean: Recycle Bin"
38+
39+
---
40+
1041
## [2.11] - 2026-01-17
1142

1243
### Fixed

CLAUDE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# WinClean - Инструкции для Claude
22

33
> Последнее обновление: 2026-01-17
4-
> Текущая версия скрипта: 2.11
4+
> Текущая версия скрипта: 2.12
55
66
---
77

@@ -11,7 +11,7 @@
1111

1212
| Параметр | Значение |
1313
|----------|----------|
14-
| **Версия** | 2.11 |
14+
| **Версия** | 2.12 |
1515
| **Язык** | PowerShell 7.1+ |
1616
| **Платформа** | Windows 11 (23H2/24H2/25H2) |
1717
| **Лицензия** | MIT |
@@ -22,7 +22,7 @@
2222
### Статус публикации
2323

2424
- **GitHub**: ✅ Опубликован, активно развивается
25-
- **PSGallery**: ✅ Опубликован (v2.11, 17.01.2026)
25+
- **PSGallery**: ✅ Опубликован (v2.12, 17.01.2026)
2626
- **CI/CD**: ✅ GitHub Actions с PSScriptAnalyzer
2727

2828
---

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
### Ultimate Windows 11 Maintenance Script
88

9-
[![Version](https://img.shields.io/badge/version-2.11-blue.svg)](https://github.com/bivlked/WinClean/releases)
9+
[![Version](https://img.shields.io/badge/version-2.12-blue.svg)](https://github.com/bivlked/WinClean/releases)
1010
[![PSGallery](https://img.shields.io/powershellgallery/v/WinClean?label=PSGallery&logo=powershell&logoColor=white)](https://www.powershellgallery.com/packages/WinClean)
1111
[![CI](https://github.com/bivlked/WinClean/actions/workflows/ci.yml/badge.svg)](https://github.com/bivlked/WinClean/actions/workflows/ci.yml)
1212
[![PowerShell 7.1+](https://img.shields.io/badge/PowerShell-7.1%2B-5391FE?logo=powershell&logoColor=white)](https://github.com/PowerShell/PowerShell)
@@ -319,7 +319,7 @@ C:\Users\YourName\
319319

320320
```
321321
┌────────────────────────────────────────────────────────────────┐
322-
│ WinClean v2.11
322+
│ WinClean v2.12
323323
├────────────────────────────────────────────────────────────────┤
324324
│ PREPARATION │
325325
│ ├─ ✓ Check Administrator Rights │

README_RU.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
### Комплексный скрипт обслуживания Windows 11
88

9-
[![Версия](https://img.shields.io/badge/версия-2.11-blue.svg)](https://github.com/bivlked/WinClean/releases)
9+
[![Версия](https://img.shields.io/badge/версия-2.12-blue.svg)](https://github.com/bivlked/WinClean/releases)
1010
[![PSGallery](https://img.shields.io/powershellgallery/v/WinClean?label=PSGallery&logo=powershell&logoColor=white)](https://www.powershellgallery.com/packages/WinClean)
1111
[![CI](https://github.com/bivlked/WinClean/actions/workflows/ci.yml/badge.svg)](https://github.com/bivlked/WinClean/actions/workflows/ci.yml)
1212
[![PowerShell 7.1+](https://img.shields.io/badge/PowerShell-7.1%2B-5391FE?logo=powershell&logoColor=white)](https://github.com/PowerShell/PowerShell)
@@ -319,7 +319,7 @@ C:\Users\ВашеИмя\
319319

320320
```
321321
┌────────────────────────────────────────────────────────────────┐
322-
│ WinClean v2.11
322+
│ WinClean v2.12
323323
├────────────────────────────────────────────────────────────────┤
324324
│ ПОДГОТОВКА │
325325
│ ├─ ✓ Проверка прав администратора │

WinClean.ps1

Lines changed: 137 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<#PSScriptInfo
2-
.VERSION 2.11
2+
.VERSION 2.12
33
.GUID 8f7c3b2a-1d4e-5f6a-9b8c-0d1e2f3a4b5c
44
.AUTHOR bivlked
55
.COMPANYNAME
@@ -12,6 +12,7 @@
1212
.REQUIREDSCRIPTS
1313
.EXTERNALSCRIPTDEPENDENCIES
1414
.RELEASENOTES
15+
v2.12: PS 7.4+ compatibility, improved statistics (Docker/WSL/RecycleBin), ReportOnly accuracy
1516
v2.11: Added timeouts for winget/DISM operations, fixed version display, improved reliability
1617
v2.10: Added auto-update check at startup (checks PSGallery for new version)
1718
v2.9: Fixed PSWindowsUpdate installation hanging (TLS 1.2, timeouts)
@@ -21,7 +22,7 @@
2122

2223
<#
2324
.SYNOPSIS
24-
WinClean - Ultimate Windows 11 Maintenance Script v2.11
25+
WinClean - Ultimate Windows 11 Maintenance Script v2.12
2526
.DESCRIPTION
2627
Комплексный скрипт для обновления и очистки Windows 11:
2728
- Обновление Windows (включая драйверы)
@@ -35,8 +36,13 @@
3536
- Подробный цветной вывод + лог-файл
3637
.NOTES
3738
Author: biv
38-
Version: 2.11
39+
Version: 2.12
3940
Requires: PowerShell 7.1+, Windows 11, Administrator rights
41+
Changes in 2.12:
42+
- Fixed PS 7.4+ compatibility (removed deprecated -UseBasicParsing)
43+
- Fixed DISM ReportOnly to show /ResetBase and warning
44+
- Fixed AppUpdatesCount to only count successful updates
45+
- Added statistics for Docker, WSL, Recycle Bin, npm cache
4046
Changes in 2.11:
4147
- Fixed version display bugs (banner and log showed v2.9 instead of current version)
4248
- Added timeouts for winget/DISM operations to prevent script hangs
@@ -210,7 +216,7 @@ if (-not $LogPath) {
210216
}
211217

212218
# Script version (single source of truth for version checking)
213-
$script:Version = "2.11"
219+
$script:Version = "2.12"
214220

215221
# Protected paths that should never be deleted
216222
$script:ProtectedPaths = @(
@@ -426,8 +432,9 @@ function Test-PSGalleryConnection {
426432
#>
427433
try {
428434
# Check PSGallery API endpoint (faster than main page)
435+
# Note: -UseBasicParsing removed - it was deprecated in PS 6.0 and removed in PS 7.4+
429436
$response = Invoke-WebRequest -Uri "https://www.powershellgallery.com/api/v2" `
430-
-TimeoutSec 10 -UseBasicParsing -ErrorAction Stop
437+
-TimeoutSec 10 -ErrorAction Stop
431438
return $response.StatusCode -eq 200
432439
} catch {
433440
return $false
@@ -765,6 +772,38 @@ function Format-FileSize {
765772
return "$Bytes B"
766773
}
767774

775+
function ConvertFrom-HumanReadableSize {
776+
<#
777+
.SYNOPSIS
778+
Converts human-readable size string to bytes (inverse of Format-FileSize)
779+
.EXAMPLE
780+
ConvertFrom-HumanReadableSize "2.5 GB" # Returns 2684354560
781+
ConvertFrom-HumanReadableSize "512MB" # Returns 536870912
782+
#>
783+
param([string]$SizeString)
784+
785+
if (-not $SizeString) { return 0 }
786+
787+
# Handle formats: "2.5 GB", "2.5GB", "512 MB", "100.5MB"
788+
if ($SizeString -match '^([\d.,]+)\s*([KMGT]?B)$') {
789+
$value = [double]($Matches[1] -replace ',', '.')
790+
$unit = $Matches[2].ToUpper()
791+
792+
$multiplier = switch ($unit) {
793+
'B' { 1 }
794+
'KB' { 1KB }
795+
'MB' { 1MB }
796+
'GB' { 1GB }
797+
'TB' { 1TB }
798+
default { 1 }
799+
}
800+
801+
return [long]($value * $multiplier)
802+
}
803+
804+
return 0
805+
}
806+
768807
function Test-PathProtected {
769808
<#
770809
.SYNOPSIS
@@ -1301,11 +1340,11 @@ function Update-Applications {
13011340
return
13021341
}
13031342

1304-
$script:Stats.AppUpdatesCount = $updateCount
1305-
13061343
if ($upgradeProcess.ExitCode -eq 0) {
1344+
$script:Stats.AppUpdatesCount = $updateCount
13071345
Write-Log "Application updates completed successfully" -Level SUCCESS
13081346
} else {
1347+
# Don't count as successful updates if winget returned an error
13091348
Write-Log "Application updates completed with code: $($upgradeProcess.ExitCode)" -Level WARNING
13101349
$script:Stats.WarningsCount++
13111350
}
@@ -1496,22 +1535,68 @@ function Clear-WindowsUpdateCache {
14961535
}
14971536
}
14981537

1538+
function Get-RecycleBinSize {
1539+
<#
1540+
.SYNOPSIS
1541+
Gets the total size of items in the Recycle Bin
1542+
#>
1543+
$totalSize = [long]0
1544+
try {
1545+
$shell = New-Object -ComObject Shell.Application
1546+
$recycleBin = $shell.Namespace(0xA)
1547+
foreach ($item in $recycleBin.Items()) {
1548+
try {
1549+
# Try ExtendedProperty first (more reliable)
1550+
$itemSize = $item.ExtendedProperty("System.Size")
1551+
if ($itemSize) {
1552+
$totalSize += [long]$itemSize
1553+
}
1554+
} catch {
1555+
# Ignore errors for individual items
1556+
}
1557+
}
1558+
} catch {
1559+
# Return 0 if we can't access recycle bin
1560+
}
1561+
return $totalSize
1562+
}
1563+
14991564
function Clear-WinCleanRecycleBin {
15001565
<#
15011566
.SYNOPSIS
1502-
Empties the Recycle Bin
1567+
Empties the Recycle Bin with size tracking
15031568
#>
15041569
Write-Log "Recycle Bin" -Level SECTION
15051570

1571+
# Measure size before cleanup
1572+
$sizeBefore = Get-RecycleBinSize
1573+
15061574
if ($ReportOnly) {
1507-
Write-Log "Would clean: Recycle Bin" -Level DETAIL
1575+
if ($sizeBefore -gt 0) {
1576+
Write-Log "Would clean: Recycle Bin - $(Format-FileSize $sizeBefore)" -Level DETAIL
1577+
} else {
1578+
Write-Log "Recycle Bin is empty" -Level DETAIL
1579+
}
1580+
return
1581+
}
1582+
1583+
if ($sizeBefore -eq 0) {
1584+
Write-Log "Recycle Bin is already empty" -Level INFO
15081585
return
15091586
}
15101587

15111588
try {
15121589
# Use full cmdlet path to explicitly call the built-in cmdlet
15131590
Microsoft.PowerShell.Management\Clear-RecycleBin -Force -ErrorAction Stop
1514-
Write-Log "Recycle Bin emptied" -Level SUCCESS
1591+
1592+
# Update statistics
1593+
$script:Stats.TotalFreedBytes += $sizeBefore
1594+
if (-not $script:Stats.FreedByCategory.ContainsKey("Recycle Bin")) {
1595+
$script:Stats.FreedByCategory["Recycle Bin"] = 0
1596+
}
1597+
$script:Stats.FreedByCategory["Recycle Bin"] += $sizeBefore
1598+
1599+
Write-Log "Recycle Bin emptied - $(Format-FileSize $sizeBefore)" -Level SUCCESS
15151600
} catch {
15161601
# Fallback to COM method
15171602
try {
@@ -1524,7 +1609,14 @@ function Clear-WinCleanRecycleBin {
15241609
Remove-Item -LiteralPath $_.Path -Recurse -Force -ErrorAction SilentlyContinue
15251610
}
15261611

1527-
Write-Log "Recycle Bin emptied ($count items)" -Level SUCCESS
1612+
# Update statistics even for fallback method
1613+
$script:Stats.TotalFreedBytes += $sizeBefore
1614+
if (-not $script:Stats.FreedByCategory.ContainsKey("Recycle Bin")) {
1615+
$script:Stats.FreedByCategory["Recycle Bin"] = 0
1616+
}
1617+
$script:Stats.FreedByCategory["Recycle Bin"] += $sizeBefore
1618+
1619+
Write-Log "Recycle Bin emptied ($count items) - $(Format-FileSize $sizeBefore)" -Level SUCCESS
15281620
} catch {
15291621
Write-Log "Could not empty Recycle Bin: $_" -Level WARNING
15301622
$script:Stats.WarningsCount++
@@ -1948,8 +2040,21 @@ function Clear-DeveloperCaches {
19482040
$npm = Get-Command npm -ErrorAction SilentlyContinue
19492041
if ($npm) {
19502042
try {
2043+
$sizeBefore = Get-FolderSize $npmCache
19512044
& npm cache clean --force 2>&1 | Out-Null
1952-
Write-Log "npm cache cleaned (via npm)" -Level SUCCESS
2045+
$sizeAfter = Get-FolderSize $npmCache
2046+
$freed = $sizeBefore - $sizeAfter
2047+
2048+
if ($freed -gt 0) {
2049+
$script:Stats.TotalFreedBytes += $freed
2050+
if (-not $script:Stats.FreedByCategory.ContainsKey("Developer")) {
2051+
$script:Stats.FreedByCategory["Developer"] = 0
2052+
}
2053+
$script:Stats.FreedByCategory["Developer"] += $freed
2054+
Write-Log "npm cache cleaned - $(Format-FileSize $freed)" -Level SUCCESS
2055+
} else {
2056+
Write-Log "npm cache cleaned (via npm)" -Level SUCCESS
2057+
}
19532058
} catch {
19542059
Remove-FolderContent -Path $npmCache -Category "Developer" -Description "npm cache"
19552060
}
@@ -2076,9 +2181,20 @@ function Clear-DockerWSL {
20762181
Write-Log "Running docker system prune..." -Level INFO
20772182
$result = docker system prune -f 2>&1
20782183

2079-
# Parse reclaimed space
2080-
if ($result -match "reclaimed\s+([\d.]+\s*[KMGT]?B)") {
2081-
Write-Log "Docker cleanup: $($Matches[1]) reclaimed" -Level SUCCESS
2184+
# Parse reclaimed space and add to statistics
2185+
if ($result -match "reclaimed\s+([\d.,]+\s*[KMGT]?B)") {
2186+
$reclaimedStr = $Matches[1]
2187+
$reclaimedBytes = ConvertFrom-HumanReadableSize $reclaimedStr
2188+
2189+
Write-Log "Docker cleanup: $reclaimedStr reclaimed" -Level SUCCESS
2190+
2191+
if ($reclaimedBytes -gt 0) {
2192+
$script:Stats.TotalFreedBytes += $reclaimedBytes
2193+
if (-not $script:Stats.FreedByCategory.ContainsKey("Docker")) {
2194+
$script:Stats.FreedByCategory["Docker"] = 0
2195+
}
2196+
$script:Stats.FreedByCategory["Docker"] += $reclaimedBytes
2197+
}
20822198
} else {
20832199
Write-Log "Docker cleanup completed" -Level SUCCESS
20842200
}
@@ -2151,6 +2267,10 @@ exit
21512267
if ($saved -gt 0) {
21522268
Write-Log "Compacted $($vhdxFile.Name): $(Format-FileSize $saved) saved" -Level SUCCESS
21532269
$script:Stats.TotalFreedBytes += $saved
2270+
if (-not $script:Stats.FreedByCategory.ContainsKey("WSL")) {
2271+
$script:Stats.FreedByCategory["WSL"] = 0
2272+
}
2273+
$script:Stats.FreedByCategory["WSL"] += $saved
21542274
} else {
21552275
Write-Log "Compacted $($vhdxFile.Name): no space saved" -Level INFO
21562276
}
@@ -2277,7 +2397,8 @@ function Invoke-DISMCleanup {
22772397
Write-Log "Windows Component Cleanup (DISM)" -Level SECTION
22782398

22792399
if ($ReportOnly) {
2280-
Write-Log "Would run: DISM /Online /Cleanup-Image /StartComponentCleanup" -Level DETAIL
2400+
Write-Log "Would run: DISM /Online /Cleanup-Image /StartComponentCleanup /ResetBase" -Level DETAIL
2401+
Write-Log "Note: /ResetBase removes ability to uninstall updates" -Level WARNING
22812402
return
22822403
}
22832404

0 commit comments

Comments
 (0)