Skip to content

Instantly share code, notes, and snippets.

@guibranco
Created July 1, 2025 15:21
Show Gist options
  • Select an option

  • Save guibranco/6471f6ce03cf9ba5f76c37c35d185bf8 to your computer and use it in GitHub Desktop.

Select an option

Save guibranco/6471f6ce03cf9ba5f76c37c35d185bf8 to your computer and use it in GitHub Desktop.
PowerShell cleanup script to remove temporary/packages/modules files from a developer machine
param (
[switch]$DryRun,
[switch]$Verbose
)
# πŸ•“ Track start time
$StartTime = Get-Date
$LogEntries = @()
# πŸ›‘οΈ Admin check
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(`
[Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Warning "This script requires administrator privileges. Please run it as an administrator."
exit 1
}
# πŸ“„ Log file paths
$PlainLog = "cleanup_log.txt"
$HtmlLog = "cleanup_log.html"
function Log {
param (
[string]$Message,
[string]$Type = "INFO" # INFO | WARN | ERROR | ACTION
)
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$entry = [PSCustomObject]@{
Time = $Timestamp
Type = $Type
Message = $Message
}
$LogEntries += $entry
if ($Verbose) {
$prefix = if ($Type -eq "ERROR") { "[X]" } elseif ($Type -eq "WARN") { "[!]" } elseif ($Type -eq "ACTION") { "[>]" } else { "[Β·]" }
Write-Host "$prefix $Timestamp - $Message"
}
"$Timestamp [$Type] $Message" | Out-File -Append -FilePath $PlainLog
}
# πŸ“‚ Relative folder names to delete recursively
$DirList = @(
"bin", "obj", "node_modules", "target\debug", "target\tmp",
".angular", ".terraform",
"visuals\traces", "visuals\videos", "visuals\screenshots", "visuals\information"
)
# πŸ“ Full path folders to delete directly
$AdditionalDirList = @(
$env:TEMP,
"C:\Windows\Temp",
"$env:USERPROFILE\AppData\Local\Temp",
"C:\ProgramData\Microsoft\Windows\WER\ReportQueue",
"C:\Windows\SoftwareDistribution\Download"
)
$VsCodeDirs = @(
"$env:APPDATA\Code\Cache",
"$env:APPDATA\Code\Cookies",
"$env:APPDATA\Code\LocalStorage",
"$env:APPDATA\Code\User\workspaceStorage"
)
$CacheDirs = @(
"$env:APPDATA\Local\pip\cache",
"$env:GOPATH\pkg\mod",
"$env:USERPROFILE\.cargo",
"$env:APPDATA\npm-cache",
"$env:USERPROFILE\AppData\Local\Microsoft\VisualStudio"
)
function Remove-Directory {
param (
[string]$Path,
[string]$Reason = "Deleting directory",
[switch]$DryRun
)
if (Test-Path $Path) {
if ($DryRun) {
Log "${Reason}: ${Path} - WOULD DELETE (dry run)" "ACTION"
} else {
try {
Remove-Item -Path $Path -Recurse -Force -ErrorAction Stop
Log "${Reason}: ${Path} - SUCCESS" "ACTION"
} catch {
Log "${Reason}: ${Path} - ERROR: $_" "ERROR"
}
}
} else {
Log "${Reason}: ${Path} - DOES NOT EXIST" "WARN"
}
}
Log "Cleanup started. DryRun=${DryRun}, Verbose=${Verbose}" "INFO"
# πŸ” Recursively find and remove relative folders
foreach ($dir in $DirList) {
Log "Searching for '${dir}' folders recursively..." "INFO"
Get-ChildItem -Directory -Recurse -Filter $dir -ErrorAction SilentlyContinue | ForEach-Object {
Remove-Directory -Path $_.FullName -Reason "Recursive match" -DryRun:$DryRun
}
}
# ❌ Remove full path folders
foreach ($path in $AdditionalDirList + $VsCodeDirs + $CacheDirs) {
Remove-Directory -Path $path -Reason "Fixed path" -DryRun:$DryRun
}
# 🧼 NuGet cache
Log "Clearing NuGet cache..." "INFO"
if ($DryRun) {
Log "NuGet cache: WOULD RUN 'nuget locals all -clear'" "ACTION"
} else {
try {
nuget locals all -clear | Out-Null
Log "NuGet cache cleared." "ACTION"
} catch {
Log "NuGet cache clear failed: $_" "ERROR"
}
}
# 🐳 Docker cache
Log "Cleaning Docker cache..." "INFO"
if ($DryRun) {
Log "Docker: WOULD RUN 'docker system prune -af'" "ACTION"
} else {
try {
docker system prune -af | Out-Null
Log "Docker cache cleaned." "ACTION"
} catch {
Log "Docker prune failed: $_" "ERROR"
}
}
# πŸ“¦ npm cache
Log "Cleaning npm cache..." "INFO"
if ($DryRun) {
Log "npm: WOULD RUN 'npm cache clean --force'" "ACTION"
} else {
try {
npm cache clean --force | Out-Null
Log "npm cache cleaned." "ACTION"
} catch {
Log "npm cache clean failed: $_" "ERROR"
}
}
# βœ… Execution time summary
$EndTime = Get-Date
$Duration = $EndTime - $StartTime
Log "Cleanup completed in $(${Duration}.TotalSeconds) seconds." "INFO"
# πŸ–₯️ HTML log generation
$htmlContent = @"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cleanup Report</title>
<style>
body { font-family: Consolas, monospace; padding: 20px; background: #f9f9f9; }
table { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 12px; border: 1px solid #ccc; }
th { background-color: #f0f0f0; }
.INFO { color: #333; }
.WARN { color: orange; }
.ERROR { color: red; font-weight: bold; }
.ACTION { color: #2a7ae2; }
</style>
</head>
<body>
<h2>🧹 Cleanup Report</h2>
<p><strong>Start Time:</strong> ${StartTime}<br>
<strong>End Time:</strong> ${EndTime}<br>
<strong>Duration:</strong> $([math]::Round(${Duration}.TotalSeconds, 2)) seconds<br>
<strong>Dry Run:</strong> ${DryRun}<br>
<strong>Verbose:</strong> ${Verbose}</p>
<table>
<tr><th>Time</th><th>Type</th><th>Message</th></tr>
"@
foreach ($entry in $LogEntries) {
$htmlContent += "<tr><td>$($entry.Time)</td><td class='$($entry.Type)'>$($entry.Type)</td><td>$($entry.Message)</td></tr>`n"
}
$htmlContent += @"
</table>
</body>
</html>
"@
$htmlContent | Out-File -Encoding UTF8 -FilePath $HtmlLog
Log "HTML report written to ${HtmlLog}" "INFO"
@guibranco
Copy link
Author

Command Description
.\cleanup.ps1 Run silent cleanup
.\cleanup.ps1 -Verbose Cleanup with console output
.\cleanup.ps1 -DryRun -Verbose Simulate cleanup (no deletions)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment