Public/Remove-StalePackageVersion.ps1

function Remove-StalePackageVersion {
    <#
    .SYNOPSIS
        Removes stale NuGet package version directories from a repository folder.
 
    .DESCRIPTION
        Recursively searches RootPath for directories whose names match a
        version pattern (semantic-version style by default) and whose last write
        time is older than the DaysOld threshold, then deletes them.
        Deleted directories are emitted to the pipeline. Supports -WhatIf and -Confirm.
 
    .PARAMETER RootPath
        The root directory to search for stale package version folders.
 
    .PARAMETER OlderThanDays
        The age threshold in days (0-365). Directories last modified more than
        this many days ago are removed. Defaults to 7.
 
    .PARAMETER VersionPattern
        A regular expression matched against each directory name to identify
        package version folders. Defaults to a semantic-version style pattern
        (e.g. 1.2.3, 0.0.1234, 2.0.0.5, 1.2.3-beta).
 
    .EXAMPLE
        Remove-StalePackageVersion -RootPath C:\packages
 
        Removes version folders under C:\packages that are older than 7 days.
 
    .EXAMPLE
        Remove-StalePackageVersion -RootPath C:\packages -OlderThanDays 30 -WhatIf
 
        Previews which version folders older than 30 days would be removed.
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType([System.IO.DirectoryInfo])]
    param(
        [Parameter(Mandatory = $true)]
        [string]$RootPath,

        [ValidateRange(0, 365)]
        [Alias('DaysOld')]
        [int]$OlderThanDays = 7,

        [string]$VersionPattern = '^\d+\.\d+\.\d+(\.\d+)?(-[0-9A-Za-z.-]+)?$'
    )

    if (-not (Test-Path -LiteralPath $RootPath -PathType Container)) {
        throw "Root path does not exist or is not a directory: $RootPath"
    }

    $cutoffDate = (Get-Date).AddDays(-$OlderThanDays)
    $versionRegex = [regex]::new($VersionPattern)

    $staleDirectories = Get-ChildItem -LiteralPath $RootPath -Directory -Recurse -Force |
        Where-Object {
            $versionRegex.IsMatch($_.Name) -and $_.LastWriteTime -lt $cutoffDate
        }

    if (-not $staleDirectories) {
        Write-Verbose "No stale package version directories found under: $RootPath"
        return
    }

    $failures = [System.Collections.Generic.List[string]]::new()

    foreach ($directory in $staleDirectories) {
        if (-not $PSCmdlet.ShouldProcess($directory.FullName, 'Remove directory')) {
            continue
        }
        try {
            Remove-Item -LiteralPath $directory.FullName -Recurse -Force -ErrorAction Stop
            Write-Verbose "Deleted: $($directory.FullName)"
            $directory
        }
        catch {
            $failures.Add("$($directory.FullName): $($_.Exception.Message)")
        }
    }

    if ($failures.Count -gt 0) {
        $noun = if ($failures.Count -eq 1) { 'directory' } else { 'directories' }
        throw "Failed to delete $($failures.Count) package version $noun:`n$($failures -join "`n")"
    }
}