mirror of
https://github.com/tw93/Mole.git
synced 2026-02-16 17:35:16 +00:00
fix(windows): fix property access errors and scanning performance
- Fix analyze: Add timeout and depth limits to calculateDirSize() to prevent indefinite scanning on large directories like user profile - Fix purge: Add null checks for .Count property access under StrictMode - Fix uninstall: Wrap registry property access in try/catch for items without DisplayName, and add null checks throughout - Fix mole.ps1: Add null check for Arguments.Count - Add try/catch around CursorVisible calls for non-interactive terminals All 73 Pester tests and all Go tests pass.
This commit is contained in:
@@ -124,7 +124,7 @@ function Get-SearchPaths {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Add default paths if no custom paths or custom paths don't exist
|
# Add default paths if no custom paths or custom paths don't exist
|
||||||
if ($paths.Count -eq 0) {
|
if ($null -eq $paths -or @($paths).Count -eq 0) {
|
||||||
foreach ($path in $script:DefaultSearchPaths) {
|
foreach ($path in $script:DefaultSearchPaths) {
|
||||||
if (Test-Path $path) {
|
if (Test-Path $path) {
|
||||||
$paths += $path
|
$paths += $path
|
||||||
@@ -201,7 +201,10 @@ function Find-Projects {
|
|||||||
|
|
||||||
$esc = [char]27
|
$esc = [char]27
|
||||||
$pathCount = 0
|
$pathCount = 0
|
||||||
$totalPaths = $SearchPaths.Count
|
$totalPaths = if ($null -eq $SearchPaths) { 0 } else { @($SearchPaths).Count }
|
||||||
|
if ($totalPaths -eq 0) {
|
||||||
|
return $projects
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($searchPath in $SearchPaths) {
|
foreach ($searchPath in $SearchPaths) {
|
||||||
$pathCount++
|
$pathCount++
|
||||||
@@ -217,16 +220,19 @@ function Find-Projects {
|
|||||||
$projectPath = Split-Path -Parent $item.FullName
|
$projectPath = Split-Path -Parent $item.FullName
|
||||||
|
|
||||||
# Skip if already found or if it's inside node_modules, etc.
|
# Skip if already found or if it's inside node_modules, etc.
|
||||||
if ($projects.Path -contains $projectPath) { continue }
|
$existingPaths = @($projects | ForEach-Object { $_.Path })
|
||||||
|
if ($existingPaths -contains $projectPath) { continue }
|
||||||
if ($projectPath -like "*\node_modules\*") { continue }
|
if ($projectPath -like "*\node_modules\*") { continue }
|
||||||
if ($projectPath -like "*\vendor\*") { continue }
|
if ($projectPath -like "*\vendor\*") { continue }
|
||||||
if ($projectPath -like "*\.git\*") { continue }
|
if ($projectPath -like "*\.git\*") { continue }
|
||||||
|
|
||||||
# Find artifacts in this project
|
# Find artifacts in this project
|
||||||
$artifacts = @(Find-ProjectArtifacts -ProjectPath $projectPath)
|
$artifacts = @(Find-ProjectArtifacts -ProjectPath $projectPath)
|
||||||
|
$artifactCount = if ($null -eq $artifacts) { 0 } else { $artifacts.Count }
|
||||||
|
|
||||||
if ($artifacts.Count -gt 0) {
|
if ($artifactCount -gt 0) {
|
||||||
$totalSize = ($artifacts | Measure-Object -Property SizeKB -Sum).Sum
|
$totalSize = ($artifacts | Measure-Object -Property SizeKB -Sum).Sum
|
||||||
|
if ($null -eq $totalSize) { $totalSize = 0 }
|
||||||
|
|
||||||
$projects += [PSCustomObject]@{
|
$projects += [PSCustomObject]@{
|
||||||
Path = $projectPath
|
Path = $projectPath
|
||||||
@@ -305,7 +311,8 @@ function Show-ProjectSelectionMenu {
|
|||||||
#>
|
#>
|
||||||
param([array]$Projects)
|
param([array]$Projects)
|
||||||
|
|
||||||
if ($Projects.Count -eq 0) {
|
$projectCount = if ($null -eq $Projects) { 0 } else { @($Projects).Count }
|
||||||
|
if ($projectCount -eq 0) {
|
||||||
Write-Warning "No projects with cleanable artifacts found"
|
Write-Warning "No projects with cleanable artifacts found"
|
||||||
return @()
|
return @()
|
||||||
}
|
}
|
||||||
@@ -316,7 +323,7 @@ function Show-ProjectSelectionMenu {
|
|||||||
$pageSize = 12
|
$pageSize = 12
|
||||||
$pageStart = 0
|
$pageStart = 0
|
||||||
|
|
||||||
[Console]::CursorVisible = $false
|
try { [Console]::CursorVisible = $false } catch { }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while ($true) {
|
while ($true) {
|
||||||
@@ -330,7 +337,7 @@ function Show-ProjectSelectionMenu {
|
|||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
# Display projects
|
# Display projects
|
||||||
$pageEnd = [Math]::Min($pageStart + $pageSize, $Projects.Count)
|
$pageEnd = [Math]::Min($pageStart + $pageSize, $projectCount)
|
||||||
|
|
||||||
for ($i = $pageStart; $i -lt $pageEnd; $i++) {
|
for ($i = $pageStart; $i -lt $pageEnd; $i++) {
|
||||||
$project = $Projects[$i]
|
$project = $Projects[$i]
|
||||||
@@ -348,7 +355,7 @@ function Show-ProjectSelectionMenu {
|
|||||||
$name = $name.Substring(0, 27) + "..."
|
$name = $name.Substring(0, 27) + "..."
|
||||||
}
|
}
|
||||||
|
|
||||||
$artifactCount = $project.Artifacts.Count
|
$artifactCount = if ($null -eq $project.Artifacts) { 0 } else { @($project.Artifacts).Count }
|
||||||
|
|
||||||
Write-Host (" {0} {1,-32} {2,10} ({3} items)" -f $checkbox, $name, $project.TotalSizeHuman, $artifactCount) -NoNewline
|
Write-Host (" {0} {1,-32} {2,10} ({3} items)" -f $checkbox, $name, $project.TotalSizeHuman, $artifactCount) -NoNewline
|
||||||
|
|
||||||
@@ -373,9 +380,9 @@ function Show-ProjectSelectionMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Page indicator
|
# Page indicator
|
||||||
$totalPages = [Math]::Ceiling($Projects.Count / $pageSize)
|
$totalPages = [Math]::Ceiling($projectCount / $pageSize)
|
||||||
$currentPage = [Math]::Floor($pageStart / $pageSize) + 1
|
$currentPage = [Math]::Floor($pageStart / $pageSize) + 1
|
||||||
Write-Host "$esc[90mPage $currentPage of $totalPages | Total: $($Projects.Count) projects$esc[0m"
|
Write-Host "$esc[90mPage $currentPage of $totalPages | Total: $projectCount projects$esc[0m"
|
||||||
|
|
||||||
# Handle input
|
# Handle input
|
||||||
$key = [Console]::ReadKey($true)
|
$key = [Console]::ReadKey($true)
|
||||||
@@ -390,7 +397,7 @@ function Show-ProjectSelectionMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
'DownArrow' {
|
'DownArrow' {
|
||||||
if ($currentIndex -lt $Projects.Count - 1) {
|
if ($currentIndex -lt $projectCount - 1) {
|
||||||
$currentIndex++
|
$currentIndex++
|
||||||
if ($currentIndex -ge $pageStart + $pageSize) {
|
if ($currentIndex -ge $pageStart + $pageSize) {
|
||||||
$pageStart += $pageSize
|
$pageStart += $pageSize
|
||||||
@@ -402,7 +409,7 @@ function Show-ProjectSelectionMenu {
|
|||||||
$currentIndex = $pageStart
|
$currentIndex = $pageStart
|
||||||
}
|
}
|
||||||
'PageDown' {
|
'PageDown' {
|
||||||
$pageStart = [Math]::Min($Projects.Count - $pageSize, $pageStart + $pageSize)
|
$pageStart = [Math]::Min($projectCount - $pageSize, $pageStart + $pageSize)
|
||||||
if ($pageStart -lt 0) { $pageStart = 0 }
|
if ($pageStart -lt 0) { $pageStart = 0 }
|
||||||
$currentIndex = $pageStart
|
$currentIndex = $pageStart
|
||||||
}
|
}
|
||||||
@@ -416,11 +423,11 @@ function Show-ProjectSelectionMenu {
|
|||||||
}
|
}
|
||||||
'A' {
|
'A' {
|
||||||
# Select/deselect all
|
# Select/deselect all
|
||||||
if ($selectedIndices.Count -eq $Projects.Count) {
|
if ($selectedIndices.Count -eq $projectCount) {
|
||||||
$selectedIndices.Clear()
|
$selectedIndices.Clear()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for ($i = 0; $i -lt $Projects.Count; $i++) {
|
for ($i = 0; $i -lt $projectCount; $i++) {
|
||||||
$selectedIndices[$i] = $true
|
$selectedIndices[$i] = $true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -440,7 +447,7 @@ function Show-ProjectSelectionMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
[Console]::CursorVisible = $true
|
try { [Console]::CursorVisible = $true } catch { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -543,9 +550,9 @@ function Main {
|
|||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
# Get search paths
|
# Get search paths
|
||||||
$searchPaths = Get-SearchPaths
|
$searchPaths = @(Get-SearchPaths)
|
||||||
|
|
||||||
if ($searchPaths.Count -eq 0) {
|
if ($null -eq $searchPaths -or $searchPaths.Count -eq 0) {
|
||||||
Write-Warning "No valid search paths found"
|
Write-Warning "No valid search paths found"
|
||||||
Write-Host "Run 'mole purge -Paths' to configure search directories"
|
Write-Host "Run 'mole purge -Paths' to configure search directories"
|
||||||
return
|
return
|
||||||
@@ -554,9 +561,9 @@ function Main {
|
|||||||
Write-Info "Searching in $($searchPaths.Count) directories..."
|
Write-Info "Searching in $($searchPaths.Count) directories..."
|
||||||
|
|
||||||
# Find projects
|
# Find projects
|
||||||
$projects = Find-Projects -SearchPaths $searchPaths
|
$projects = @(Find-Projects -SearchPaths $searchPaths)
|
||||||
|
|
||||||
if ($projects.Count -eq 0) {
|
if ($null -eq $projects -or $projects.Count -eq 0) {
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "$esc[32m$($script:Icons.Success)$esc[0m No cleanable artifacts found"
|
Write-Host "$esc[32m$($script:Icons.Success)$esc[0m No cleanable artifacts found"
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
@@ -564,6 +571,7 @@ function Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$totalSize = ($projects | Measure-Object -Property TotalSizeKB -Sum).Sum
|
$totalSize = ($projects | Measure-Object -Property TotalSizeKB -Sum).Sum
|
||||||
|
if ($null -eq $totalSize) { $totalSize = 0 }
|
||||||
$totalSizeHuman = Format-ByteSize -Bytes ($totalSize * 1024)
|
$totalSizeHuman = Format-ByteSize -Bytes ($totalSize * 1024)
|
||||||
|
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
@@ -571,9 +579,9 @@ function Main {
|
|||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
# Project selection
|
# Project selection
|
||||||
$selected = Show-ProjectSelectionMenu -Projects $projects
|
$selected = @(Show-ProjectSelectionMenu -Projects $projects)
|
||||||
|
|
||||||
if ($selected.Count -eq 0) {
|
if ($null -eq $selected -or $selected.Count -eq 0) {
|
||||||
Write-Info "No projects selected"
|
Write-Info "No projects selected"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -582,6 +590,7 @@ function Main {
|
|||||||
Clear-Host
|
Clear-Host
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
$selectedSize = ($selected | Measure-Object -Property TotalSizeKB -Sum).Sum
|
$selectedSize = ($selected | Measure-Object -Property TotalSizeKB -Sum).Sum
|
||||||
|
if ($null -eq $selectedSize) { $selectedSize = 0 }
|
||||||
$selectedSizeHuman = Format-ByteSize -Bytes ($selectedSize * 1024)
|
$selectedSizeHuman = Format-ByteSize -Bytes ($selectedSize * 1024)
|
||||||
|
|
||||||
Write-Host "$esc[33mThe following will be cleaned ($selectedSizeHuman):$esc[0m"
|
Write-Host "$esc[33mThe following will be cleaned ($selectedSizeHuman):$esc[0m"
|
||||||
|
|||||||
@@ -130,44 +130,71 @@ function Get-InstalledApplications {
|
|||||||
$count++
|
$count++
|
||||||
Write-Progress -Activity "Scanning applications" -Status "Registry path $count of $total" -PercentComplete (($count / $total) * 50)
|
Write-Progress -Activity "Scanning applications" -Status "Registry path $count of $total" -PercentComplete (($count / $total) * 50)
|
||||||
|
|
||||||
$items = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue |
|
try {
|
||||||
Where-Object {
|
$regItems = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue
|
||||||
$_.DisplayName -and
|
|
||||||
$_.UninstallString -and
|
|
||||||
-not (Test-ProtectedApp $_.DisplayName)
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($item in $items) {
|
foreach ($item in $regItems) {
|
||||||
# Calculate size
|
# Skip items without required properties
|
||||||
$sizeKB = 0
|
$displayName = $null
|
||||||
if ($item.EstimatedSize) {
|
$uninstallString = $null
|
||||||
$sizeKB = [long]$item.EstimatedSize
|
|
||||||
}
|
|
||||||
elseif ($item.InstallLocation -and (Test-Path $item.InstallLocation)) {
|
|
||||||
$sizeKB = Get-PathSizeKB -Path $item.InstallLocation
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get install date
|
try { $displayName = $item.DisplayName } catch { }
|
||||||
$installDate = $null
|
try { $uninstallString = $item.UninstallString } catch { }
|
||||||
if ($item.InstallDate) {
|
|
||||||
|
if ([string]::IsNullOrWhiteSpace($displayName) -or [string]::IsNullOrWhiteSpace($uninstallString)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-ProtectedApp $displayName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
# Calculate size
|
||||||
|
$sizeKB = 0
|
||||||
try {
|
try {
|
||||||
$installDate = [DateTime]::ParseExact($item.InstallDate, "yyyyMMdd", $null)
|
if ($item.EstimatedSize) {
|
||||||
|
$sizeKB = [long]$item.EstimatedSize
|
||||||
|
}
|
||||||
|
elseif ($item.InstallLocation -and (Test-Path $item.InstallLocation -ErrorAction SilentlyContinue)) {
|
||||||
|
$sizeKB = Get-PathSizeKB -Path $item.InstallLocation
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
|
||||||
|
|
||||||
$apps += [PSCustomObject]@{
|
# Get install date
|
||||||
Name = $item.DisplayName
|
$installDate = $null
|
||||||
Publisher = $item.Publisher
|
try {
|
||||||
Version = $item.DisplayVersion
|
if ($item.InstallDate) {
|
||||||
SizeKB = $sizeKB
|
$installDate = [DateTime]::ParseExact($item.InstallDate, "yyyyMMdd", $null)
|
||||||
SizeHuman = Format-ByteSize -Bytes ($sizeKB * 1024)
|
}
|
||||||
InstallLocation = $item.InstallLocation
|
}
|
||||||
UninstallString = $item.UninstallString
|
catch { }
|
||||||
InstallDate = $installDate
|
|
||||||
Source = "Registry"
|
# Get other properties safely
|
||||||
|
$publisher = $null
|
||||||
|
$version = $null
|
||||||
|
$installLocation = $null
|
||||||
|
|
||||||
|
try { $publisher = $item.Publisher } catch { }
|
||||||
|
try { $version = $item.DisplayVersion } catch { }
|
||||||
|
try { $installLocation = $item.InstallLocation } catch { }
|
||||||
|
|
||||||
|
$apps += [PSCustomObject]@{
|
||||||
|
Name = $displayName
|
||||||
|
Publisher = $publisher
|
||||||
|
Version = $version
|
||||||
|
SizeKB = $sizeKB
|
||||||
|
SizeHuman = Format-ByteSize -Bytes ($sizeKB * 1024)
|
||||||
|
InstallLocation = $installLocation
|
||||||
|
UninstallString = $uninstallString
|
||||||
|
InstallDate = $installDate
|
||||||
|
Source = "Registry"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Debug "Error scanning registry path $path : $_"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# UWP / Store Apps
|
# UWP / Store Apps
|
||||||
@@ -255,8 +282,8 @@ function Show-AppSelectionMenu {
|
|||||||
$searchTerm = ""
|
$searchTerm = ""
|
||||||
$filteredApps = $Apps
|
$filteredApps = $Apps
|
||||||
|
|
||||||
# Hide cursor
|
# Hide cursor (may fail in non-interactive terminals)
|
||||||
[Console]::CursorVisible = $false
|
try { [Console]::CursorVisible = $false } catch { }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while ($true) {
|
while ($true) {
|
||||||
@@ -387,9 +414,9 @@ function Show-AppSelectionMenu {
|
|||||||
# Search mode
|
# Search mode
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "Search: " -NoNewline
|
Write-Host "Search: " -NoNewline
|
||||||
[Console]::CursorVisible = $true
|
try { [Console]::CursorVisible = $true } catch { }
|
||||||
$searchTerm = Read-Host
|
$searchTerm = Read-Host
|
||||||
[Console]::CursorVisible = $false
|
try { [Console]::CursorVisible = $false } catch { }
|
||||||
|
|
||||||
if ($searchTerm) {
|
if ($searchTerm) {
|
||||||
$filteredApps = $Apps | Where-Object { $_.Name -like "*$searchTerm*" }
|
$filteredApps = $Apps | Where-Object { $_.Name -like "*$searchTerm*" }
|
||||||
@@ -412,7 +439,7 @@ function Show-AppSelectionMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
[Console]::CursorVisible = $true
|
try { [Console]::CursorVisible = $true } catch { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -17,6 +18,13 @@ import (
|
|||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Scanning limits to prevent infinite scanning
|
||||||
|
const (
|
||||||
|
dirSizeTimeout = 5 * time.Second // Max time to calculate a single directory size
|
||||||
|
maxFilesPerDir = 50000 // Max files to scan per directory
|
||||||
|
maxScanDepth = 50 // Max recursion depth
|
||||||
|
)
|
||||||
|
|
||||||
// ANSI color codes
|
// ANSI color codes
|
||||||
const (
|
const (
|
||||||
colorReset = "\033[0m"
|
colorReset = "\033[0m"
|
||||||
@@ -555,23 +563,88 @@ func scanDirectory(path string) ([]dirEntry, []fileEntry, int64, error) {
|
|||||||
return dirEntries, largeFiles, totalSize, nil
|
return dirEntries, largeFiles, totalSize, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculateDirSize calculates the size of a directory
|
// calculateDirSize calculates the size of a directory with timeout and limits
|
||||||
func calculateDirSize(path string) int64 {
|
func calculateDirSize(path string) int64 {
|
||||||
var size int64
|
ctx, cancel := context.WithTimeout(context.Background(), dirSizeTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
|
var size int64
|
||||||
if err != nil {
|
var fileCount int64
|
||||||
return nil // Skip errors
|
|
||||||
}
|
// Use a channel to signal completion
|
||||||
if !info.IsDir() {
|
done := make(chan struct{})
|
||||||
size += info.Size()
|
|
||||||
}
|
go func() {
|
||||||
return nil
|
defer close(done)
|
||||||
})
|
walkDirWithLimit(ctx, path, 0, &size, &fileCount)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
// Completed normally
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Timeout - return partial size
|
||||||
|
}
|
||||||
|
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// walkDirWithLimit walks a directory with depth limit and file count limit
|
||||||
|
func walkDirWithLimit(ctx context.Context, path string, depth int, size *int64, fileCount *int64) {
|
||||||
|
// Check context cancellation
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check depth limit
|
||||||
|
if depth > maxScanDepth {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check file count limit
|
||||||
|
if atomic.LoadInt64(fileCount) > maxFilesPerDir {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
// Check cancellation frequently
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check file count limit
|
||||||
|
if atomic.LoadInt64(fileCount) > maxFilesPerDir {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
entryPath := filepath.Join(path, entry.Name())
|
||||||
|
|
||||||
|
if entry.IsDir() {
|
||||||
|
// Skip system/protected directories
|
||||||
|
name := entry.Name()
|
||||||
|
if skipPatterns[name] || strings.HasPrefix(name, ".") && len(name) > 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
walkDirWithLimit(ctx, entryPath, depth+1, size, fileCount)
|
||||||
|
} else {
|
||||||
|
info, err := entry.Info()
|
||||||
|
if err == nil {
|
||||||
|
atomic.AddInt64(size, info.Size())
|
||||||
|
atomic.AddInt64(fileCount, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// formatBytes formats bytes to human readable string
|
// formatBytes formats bytes to human readable string
|
||||||
func formatBytes(bytes int64) string {
|
func formatBytes(bytes int64) string {
|
||||||
const unit = 1024
|
const unit = 1024
|
||||||
|
|||||||
@@ -163,7 +163,8 @@ function Invoke-MoleCommand {
|
|||||||
|
|
||||||
# Execute the command script with arguments using splatting
|
# Execute the command script with arguments using splatting
|
||||||
# This properly handles switch parameters passed as strings
|
# This properly handles switch parameters passed as strings
|
||||||
if ($Arguments -and $Arguments.Count -gt 0) {
|
$argCount = if ($null -eq $Arguments) { 0 } else { @($Arguments).Count }
|
||||||
|
if ($argCount -gt 0) {
|
||||||
# Build a hashtable for splatting
|
# Build a hashtable for splatting
|
||||||
$splatParams = @{}
|
$splatParams = @{}
|
||||||
$positionalArgs = @()
|
$positionalArgs = @()
|
||||||
|
|||||||
Reference in New Issue
Block a user