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/lib/log.ps1
CHANGED
|
@@ -1,140 +1,140 @@
|
|
|
1
|
-
# log.ps1 - session log, freed-bytes tally, drive snapshots and the JSON session report.
|
|
2
|
-
|
|
3
|
-
function Initialize-Log {
|
|
4
|
-
<# .SYNOPSIS Create the data directories and open this run's log with a credit header. #>
|
|
5
|
-
$ws = $Script:WS
|
|
6
|
-
foreach ($d in @($ws.LogsDir, $ws.ReportsDir)) {
|
|
7
|
-
if (-not (Test-Path -LiteralPath $d)) { New-Item -ItemType Directory -Force -Path $d | Out-Null }
|
|
8
|
-
}
|
|
9
|
-
$ws.LogFile = Join-Path $ws.LogsDir ("$Script:WS_NAME-" + $ws.Stamp + '.log')
|
|
10
|
-
$elev = 'no'
|
|
11
|
-
if ($ws.IsAdmin) { $elev = 'yes' }
|
|
12
|
-
$dry = 'no'
|
|
13
|
-
if ($ws.DryRun) { $dry = 'yes' }
|
|
14
|
-
$header = @(
|
|
15
|
-
'# ============================================================'
|
|
16
|
-
"# $Script:WS_NAME v$(Get-ToolVersion) - session log"
|
|
17
|
-
"# Author: $Script:WS_AUTHOR <$Script:WS_EMAIL>"
|
|
18
|
-
"# Web: $Script:WS_WEB"
|
|
19
|
-
"# License: $Script:WS_LICENSE"
|
|
20
|
-
"# Started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss zzz')"
|
|
21
|
-
"# Mode: $($ws.Mode)"
|
|
22
|
-
"# Host: $env:COMPUTERNAME"
|
|
23
|
-
"# User: $env:USERNAME"
|
|
24
|
-
"# Elevated: $elev"
|
|
25
|
-
"# Dry-run: $dry"
|
|
26
|
-
"# Launcher: $($ws.Launcher)"
|
|
27
|
-
'# ============================================================'
|
|
28
|
-
)
|
|
29
|
-
try { [IO.File]::WriteAllLines($ws.LogFile, $header) } catch { $ws.LogFile = $null }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function Write-LogLine {
|
|
33
|
-
<# .SYNOPSIS Append one timestamped line to the run log; never throws. #>
|
|
34
|
-
param([string] $Text)
|
|
35
|
-
$f = $Script:WS.LogFile
|
|
36
|
-
if (-not $f) { return }
|
|
37
|
-
try { [IO.File]::AppendAllText($f, ("[{0}] {1}`r`n" -f (Get-Date -Format 'HH:mm:ss'), $Text)) } catch { $null = $_ }
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function Add-Freed {
|
|
41
|
-
<# .SYNOPSIS Tally bytes freed (or, in dry-run, bytes that would be freed). #>
|
|
42
|
-
param([long] $Bytes)
|
|
43
|
-
if ($Bytes -le 0) { return }
|
|
44
|
-
if ($Script:WS.DryRun) { $Script:WS.TotalEstimated += $Bytes } else { $Script:WS.TotalFreed += $Bytes }
|
|
45
|
-
$Script:WS.SectionFreed += $Bytes
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function Get-DriveSnapshot {
|
|
49
|
-
<# .SYNOPSIS Size/free bytes of every ready fixed drive. #>
|
|
50
|
-
$rows = @()
|
|
51
|
-
foreach ($d in [IO.DriveInfo]::GetDrives()) {
|
|
52
|
-
try {
|
|
53
|
-
if (-not $d.IsReady) { continue }
|
|
54
|
-
if ($d.DriveType -ne [IO.DriveType]::Fixed) { continue }
|
|
55
|
-
$rows += [ordered]@{ drive = $d.Name.TrimEnd('\'); size_bytes = [long]$d.TotalSize; free_bytes = [long]$d.AvailableFreeSpace }
|
|
56
|
-
} catch { $null = $_ }
|
|
57
|
-
}
|
|
58
|
-
return $rows
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function Get-SystemDriveFree {
|
|
62
|
-
<# .SYNOPSIS Free bytes on the system drive (used for before/after deltas). #>
|
|
63
|
-
try { return [long](New-Object IO.DriveInfo($env:SystemDrive)).AvailableFreeSpace } catch { return [long]0 }
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function Show-DriveTable {
|
|
67
|
-
<# .SYNOPSIS Print size / used / free / percent for every fixed drive. #>
|
|
68
|
-
Write-UiLine (" {0,-6} {1,10} {2,10} {3,10} {4,6}" -f 'DRIVE', 'SIZE', 'USED', 'FREE', 'USE%') 'White'
|
|
69
|
-
foreach ($d in (Get-DriveSnapshot)) {
|
|
70
|
-
$used = $d.size_bytes - $d.free_bytes
|
|
71
|
-
$pct = 0
|
|
72
|
-
if ($d.size_bytes -gt 0) { $pct = [math]::Round(100.0 * $used / $d.size_bytes) }
|
|
73
|
-
$color = 'Gray'
|
|
74
|
-
if ($pct -ge 90) { $color = 'Red' } elseif ($pct -ge 80) { $color = 'Yellow' }
|
|
75
|
-
Write-UiLine (" {0,-6} {1,10} {2,10} {3,10} {4,5}%" -f $d.drive, (Format-Bytes $d.size_bytes), (Format-Bytes $used), (Format-Bytes $d.free_bytes), $pct) $color
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function Initialize-Report {
|
|
80
|
-
$ws = $Script:WS
|
|
81
|
-
$ws.Report = @{
|
|
82
|
-
startedAt = (Get-Date).ToString('yyyy-MM-ddTHH:mm:sszzz')
|
|
83
|
-
startedTicks = (Get-Date).Ticks
|
|
84
|
-
drivesBefore = @(Get-DriveSnapshot)
|
|
85
|
-
steps = New-Object System.Collections.ArrayList
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function Add-ReportStep {
|
|
90
|
-
<# .SYNOPSIS Record a section outcome for the JSON report. Status: ran | skipped | refused | failed | dry-run #>
|
|
91
|
-
param([int] $Section, [string] $Title, [string] $Status, [long] $Freed = 0, [string] $Note = '')
|
|
92
|
-
$ws = $Script:WS
|
|
93
|
-
if (-not $ws.Report) { return }
|
|
94
|
-
$null = $ws.Report.steps.Add([ordered]@{ n = ($ws.Report.steps.Count + 1); section = $Section; title = $Title; status = $Status; freed_bytes = $Freed; note = $Note })
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function Save-Report {
|
|
98
|
-
<# .SYNOPSIS Write the canonical JSON report (schema 1). Returns the path or $null. #>
|
|
99
|
-
$ws = $Script:WS
|
|
100
|
-
if ($ws.NoReport -or -not $ws.Report) { return $null }
|
|
101
|
-
$ran = 0; $skipped = 0
|
|
102
|
-
foreach ($s in $ws.Report.steps) { if ($s.status -eq 'ran' -or $s.status -eq 'dry-run') { $ran++ } else { $skipped++ } }
|
|
103
|
-
$dev = $null
|
|
104
|
-
if ($null -ne $ws.Developer) { $dev = [bool]$ws.Developer }
|
|
105
|
-
$doc = [ordered]@{
|
|
106
|
-
schema_version = 1
|
|
107
|
-
credits = [ordered]@{
|
|
108
|
-
tool = $Script:WS_NAME; tool_version = (Get-ToolVersion); tool_homepage = $Script:WS_REPO; tool_license = $Script:WS_LICENSE
|
|
109
|
-
author = [ordered]@{ name = $Script:WS_AUTHOR; email = $Script:WS_EMAIL; website = $Script:WS_WEB; linkedin = $Script:WS_LINKEDIN }
|
|
110
|
-
}
|
|
111
|
-
meta = [ordered]@{
|
|
112
|
-
started_at = $ws.Report.startedAt
|
|
113
|
-
finished_at = (Get-Date).ToString('yyyy-MM-ddTHH:mm:sszzz')
|
|
114
|
-
duration_seconds = [int][math]::Round(((Get-Date).Ticks - $ws.Report.startedTicks) / 10000000)
|
|
115
|
-
host = $env:COMPUTERNAME; user = $env:USERNAME
|
|
116
|
-
os = $ws.OsCaption; powershell = $PSVersionTable.PSVersion.ToString()
|
|
117
|
-
mode = $ws.Mode; dry_run = [bool]$ws.DryRun; elevated = [bool]$ws.IsAdmin; developer_mode = $dev
|
|
118
|
-
idle_days = $ws.Days; temp_days = $ws.TempDays
|
|
119
|
-
log_file = $ws.LogFile; launcher = $ws.Launcher; via_npx = [bool]$ws.Npx
|
|
120
|
-
}
|
|
121
|
-
disk = [ordered]@{ before = @($ws.Report.drivesBefore); after = @(Get-DriveSnapshot) }
|
|
122
|
-
steps = @($ws.Report.steps)
|
|
123
|
-
totals = [ordered]@{
|
|
124
|
-
total_reclaimed_bytes = [long]$ws.TotalFreed
|
|
125
|
-
total_reclaimed_human = (Format-Bytes $ws.TotalFreed)
|
|
126
|
-
total_estimated_bytes = [long]$ws.TotalEstimated
|
|
127
|
-
total_estimated_human = (Format-Bytes $ws.TotalEstimated)
|
|
128
|
-
steps_run = $ran; steps_skipped = $skipped
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
$path = Join-Path $ws.ReportsDir ('report-' + $ws.Stamp + '.json')
|
|
132
|
-
try {
|
|
133
|
-
[IO.File]::WriteAllText($path, ($doc | ConvertTo-Json -Depth 8), (New-Object System.Text.UTF8Encoding($false)))
|
|
134
|
-
$ws.ReportFile = $path
|
|
135
|
-
return $path
|
|
136
|
-
} catch {
|
|
137
|
-
Write-Warn "could not write report: $($_.Exception.Message)"
|
|
138
|
-
return $null
|
|
139
|
-
}
|
|
140
|
-
}
|
|
1
|
+
# log.ps1 - session log, freed-bytes tally, drive snapshots and the JSON session report.
|
|
2
|
+
|
|
3
|
+
function Initialize-Log {
|
|
4
|
+
<# .SYNOPSIS Create the data directories and open this run's log with a credit header. #>
|
|
5
|
+
$ws = $Script:WS
|
|
6
|
+
foreach ($d in @($ws.LogsDir, $ws.ReportsDir)) {
|
|
7
|
+
if (-not (Test-Path -LiteralPath $d)) { New-Item -ItemType Directory -Force -Path $d | Out-Null }
|
|
8
|
+
}
|
|
9
|
+
$ws.LogFile = Join-Path $ws.LogsDir ("$Script:WS_NAME-" + $ws.Stamp + '.log')
|
|
10
|
+
$elev = 'no'
|
|
11
|
+
if ($ws.IsAdmin) { $elev = 'yes' }
|
|
12
|
+
$dry = 'no'
|
|
13
|
+
if ($ws.DryRun) { $dry = 'yes' }
|
|
14
|
+
$header = @(
|
|
15
|
+
'# ============================================================'
|
|
16
|
+
"# $Script:WS_NAME v$(Get-ToolVersion) - session log"
|
|
17
|
+
"# Author: $Script:WS_AUTHOR <$Script:WS_EMAIL>"
|
|
18
|
+
"# Web: $Script:WS_WEB"
|
|
19
|
+
"# License: $Script:WS_LICENSE"
|
|
20
|
+
"# Started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss zzz')"
|
|
21
|
+
"# Mode: $($ws.Mode)"
|
|
22
|
+
"# Host: $env:COMPUTERNAME"
|
|
23
|
+
"# User: $env:USERNAME"
|
|
24
|
+
"# Elevated: $elev"
|
|
25
|
+
"# Dry-run: $dry"
|
|
26
|
+
"# Launcher: $($ws.Launcher)"
|
|
27
|
+
'# ============================================================'
|
|
28
|
+
)
|
|
29
|
+
try { [IO.File]::WriteAllLines($ws.LogFile, $header) } catch { $ws.LogFile = $null }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function Write-LogLine {
|
|
33
|
+
<# .SYNOPSIS Append one timestamped line to the run log; never throws. #>
|
|
34
|
+
param([string] $Text)
|
|
35
|
+
$f = $Script:WS.LogFile
|
|
36
|
+
if (-not $f) { return }
|
|
37
|
+
try { [IO.File]::AppendAllText($f, ("[{0}] {1}`r`n" -f (Get-Date -Format 'HH:mm:ss'), $Text)) } catch { $null = $_ }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function Add-Freed {
|
|
41
|
+
<# .SYNOPSIS Tally bytes freed (or, in dry-run, bytes that would be freed). #>
|
|
42
|
+
param([long] $Bytes)
|
|
43
|
+
if ($Bytes -le 0) { return }
|
|
44
|
+
if ($Script:WS.DryRun) { $Script:WS.TotalEstimated += $Bytes } else { $Script:WS.TotalFreed += $Bytes }
|
|
45
|
+
$Script:WS.SectionFreed += $Bytes
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function Get-DriveSnapshot {
|
|
49
|
+
<# .SYNOPSIS Size/free bytes of every ready fixed drive. #>
|
|
50
|
+
$rows = @()
|
|
51
|
+
foreach ($d in [IO.DriveInfo]::GetDrives()) {
|
|
52
|
+
try {
|
|
53
|
+
if (-not $d.IsReady) { continue }
|
|
54
|
+
if ($d.DriveType -ne [IO.DriveType]::Fixed) { continue }
|
|
55
|
+
$rows += [ordered]@{ drive = $d.Name.TrimEnd('\'); size_bytes = [long]$d.TotalSize; free_bytes = [long]$d.AvailableFreeSpace }
|
|
56
|
+
} catch { $null = $_ }
|
|
57
|
+
}
|
|
58
|
+
return $rows
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function Get-SystemDriveFree {
|
|
62
|
+
<# .SYNOPSIS Free bytes on the system drive (used for before/after deltas). #>
|
|
63
|
+
try { return [long](New-Object IO.DriveInfo($env:SystemDrive)).AvailableFreeSpace } catch { return [long]0 }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function Show-DriveTable {
|
|
67
|
+
<# .SYNOPSIS Print size / used / free / percent for every fixed drive. #>
|
|
68
|
+
Write-UiLine (" {0,-6} {1,10} {2,10} {3,10} {4,6}" -f 'DRIVE', 'SIZE', 'USED', 'FREE', 'USE%') 'White'
|
|
69
|
+
foreach ($d in (Get-DriveSnapshot)) {
|
|
70
|
+
$used = $d.size_bytes - $d.free_bytes
|
|
71
|
+
$pct = 0
|
|
72
|
+
if ($d.size_bytes -gt 0) { $pct = [math]::Round(100.0 * $used / $d.size_bytes) }
|
|
73
|
+
$color = 'Gray'
|
|
74
|
+
if ($pct -ge 90) { $color = 'Red' } elseif ($pct -ge 80) { $color = 'Yellow' }
|
|
75
|
+
Write-UiLine (" {0,-6} {1,10} {2,10} {3,10} {4,5}%" -f $d.drive, (Format-Bytes $d.size_bytes), (Format-Bytes $used), (Format-Bytes $d.free_bytes), $pct) $color
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function Initialize-Report {
|
|
80
|
+
$ws = $Script:WS
|
|
81
|
+
$ws.Report = @{
|
|
82
|
+
startedAt = (Get-Date).ToString('yyyy-MM-ddTHH:mm:sszzz')
|
|
83
|
+
startedTicks = (Get-Date).Ticks
|
|
84
|
+
drivesBefore = @(Get-DriveSnapshot)
|
|
85
|
+
steps = New-Object System.Collections.ArrayList
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function Add-ReportStep {
|
|
90
|
+
<# .SYNOPSIS Record a section outcome for the JSON report. Status: ran | skipped | refused | failed | dry-run #>
|
|
91
|
+
param([int] $Section, [string] $Title, [string] $Status, [long] $Freed = 0, [string] $Note = '')
|
|
92
|
+
$ws = $Script:WS
|
|
93
|
+
if (-not $ws.Report) { return }
|
|
94
|
+
$null = $ws.Report.steps.Add([ordered]@{ n = ($ws.Report.steps.Count + 1); section = $Section; title = $Title; status = $Status; freed_bytes = $Freed; note = $Note })
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function Save-Report {
|
|
98
|
+
<# .SYNOPSIS Write the canonical JSON report (schema 1). Returns the path or $null. #>
|
|
99
|
+
$ws = $Script:WS
|
|
100
|
+
if ($ws.NoReport -or -not $ws.Report) { return $null }
|
|
101
|
+
$ran = 0; $skipped = 0
|
|
102
|
+
foreach ($s in $ws.Report.steps) { if ($s.status -eq 'ran' -or $s.status -eq 'dry-run') { $ran++ } else { $skipped++ } }
|
|
103
|
+
$dev = $null
|
|
104
|
+
if ($null -ne $ws.Developer) { $dev = [bool]$ws.Developer }
|
|
105
|
+
$doc = [ordered]@{
|
|
106
|
+
schema_version = 1
|
|
107
|
+
credits = [ordered]@{
|
|
108
|
+
tool = $Script:WS_NAME; tool_version = (Get-ToolVersion); tool_homepage = $Script:WS_REPO; tool_license = $Script:WS_LICENSE
|
|
109
|
+
author = [ordered]@{ name = $Script:WS_AUTHOR; email = $Script:WS_EMAIL; website = $Script:WS_WEB; linkedin = $Script:WS_LINKEDIN }
|
|
110
|
+
}
|
|
111
|
+
meta = [ordered]@{
|
|
112
|
+
started_at = $ws.Report.startedAt
|
|
113
|
+
finished_at = (Get-Date).ToString('yyyy-MM-ddTHH:mm:sszzz')
|
|
114
|
+
duration_seconds = [int][math]::Round(((Get-Date).Ticks - $ws.Report.startedTicks) / 10000000)
|
|
115
|
+
host = $env:COMPUTERNAME; user = $env:USERNAME
|
|
116
|
+
os = $ws.OsCaption; powershell = $PSVersionTable.PSVersion.ToString()
|
|
117
|
+
mode = $ws.Mode; dry_run = [bool]$ws.DryRun; elevated = [bool]$ws.IsAdmin; developer_mode = $dev
|
|
118
|
+
idle_days = $ws.Days; temp_days = $ws.TempDays
|
|
119
|
+
log_file = $ws.LogFile; launcher = $ws.Launcher; via_npx = [bool]$ws.Npx
|
|
120
|
+
}
|
|
121
|
+
disk = [ordered]@{ before = @($ws.Report.drivesBefore); after = @(Get-DriveSnapshot) }
|
|
122
|
+
steps = @($ws.Report.steps)
|
|
123
|
+
totals = [ordered]@{
|
|
124
|
+
total_reclaimed_bytes = [long]$ws.TotalFreed
|
|
125
|
+
total_reclaimed_human = (Format-Bytes $ws.TotalFreed)
|
|
126
|
+
total_estimated_bytes = [long]$ws.TotalEstimated
|
|
127
|
+
total_estimated_human = (Format-Bytes $ws.TotalEstimated)
|
|
128
|
+
steps_run = $ran; steps_skipped = $skipped
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
$path = Join-Path $ws.ReportsDir ('report-' + $ws.Stamp + '.json')
|
|
132
|
+
try {
|
|
133
|
+
[IO.File]::WriteAllText($path, ($doc | ConvertTo-Json -Depth 8), (New-Object System.Text.UTF8Encoding($false)))
|
|
134
|
+
$ws.ReportFile = $path
|
|
135
|
+
return $path
|
|
136
|
+
} catch {
|
|
137
|
+
Write-Warn "could not write report: $($_.Exception.Message)"
|
|
138
|
+
return $null
|
|
139
|
+
}
|
|
140
|
+
}
|