<#
.SYNOPSIS
Deletes GitHub Actions artifacts using the gh CLI on Windows.
.DESCRIPTION
Requires gh CLI installed and authenticated (gh auth login).
Install gh: winget install --id GitHub.cli
.PARAMETER Repo
Repository in OWNER/REPO format.
.PARAMETER OlderThanDays
Only delete artifacts older than N days. 0 (default) deletes all.
.PARAMETER DryRun
Preview without deleting.
.EXAMPLE
.\Delete-Artifacts.ps1 -Repo "myorg/myrepo" -DryRun
.\Delete-Artifacts.ps1 -Repo "myorg/myrepo"
.\Delete-Artifacts.ps1 -Repo "myorg/myrepo" -OlderThanDays 7
#>
param(
[Parameter(Mandatory = $true)]
[string]$Repo,
[int]$OlderThanDays = 0,
[switch]$DryRun
)
# Verify gh is installed and authenticated
if (-not (Get-Command gh -ErrorAction SilentlyContinue)) {
Write-Error "gh CLI not found. Install with: winget install --id GitHub.cli"
exit 1
}
$authStatus = gh auth status 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Error "gh CLI not authenticated. Run: gh auth login"
exit 1
}
Write-Host "Fetching artifacts from $Repo..." -ForegroundColor Cyan
# Compute cutoff date if filtering by age
$cutoff = if ($OlderThanDays -gt 0) {
(Get-Date).ToUniversalTime().AddDays(-$OlderThanDays)
} else { $null }
if ($cutoff) {
Write-Host "Will only delete artifacts created before: $($cutoff.ToString('o'))" -ForegroundColor Cyan
}
# Fetch all artifacts (gh handles pagination with --paginate)
try {
$json = gh api --paginate "repos/$Repo/actions/artifacts" --jq '.artifacts[]' 2>$null
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to fetch artifacts from $Repo. Check repo name and permissions."
exit 1
}
$artifacts = $json | ConvertFrom-Json
}
catch {
Write-Error "Failed to parse artifacts: $_"
exit 1
}
if (-not $artifacts -or $artifacts.Count -eq 0) {
Write-Host "No artifacts found." -ForegroundColor Yellow
exit 0
}
Write-Host "Found $($artifacts.Count) artifact(s)." -ForegroundColor Cyan
Write-Host ""
$deleted = 0
$skipped = 0
$failed = 0
foreach ($artifact in $artifacts) {
$createdAt = [datetime]$artifact.created_at
$sizeMB = [math]::Round($artifact.size_in_bytes / 1MB, 2)
$label = "[$($artifact.id)] $($artifact.name) ($sizeMB MB, $($createdAt.ToString('yyyy-MM-dd')))"
if ($cutoff -and $createdAt -gt $cutoff) {
Write-Host " SKIP $label" -ForegroundColor DarkGray
$skipped++
continue
}
if ($DryRun) {
Write-Host " WOULD DELETE $label" -ForegroundColor Yellow
$deleted++
continue
}
gh api -X DELETE "repos/$Repo/actions/artifacts/$($artifact.id)" --silent 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Host " DELETED $label" -ForegroundColor Green
$deleted++
}
else {
Write-Host " FAILED $label" -ForegroundColor Red
$failed++
}
}
Write-Host ""
Write-Host "Summary:" -ForegroundColor Cyan
Write-Host " Deleted/Would delete: $deleted"
Write-Host " Skipped : $skipped"
Write-Host " Failed : $failed"