Files
microsoft--powertoys/tools/Test-AutoLabelProduct.ps1
T
wehub-resource-sync 79031da543
Spell checking / Report (Push) (push) Blocked by required conditions
Spell checking / Report (PR) (push) Blocked by required conditions
Spell checking / Check Spelling (push) Has been cancelled
Spell checking / Update PR (push) Has been cancelled
Publish Dev Docs Website / build (push) Failing after 1s
Publish Dev Docs Website / deploy (push) Has been skipped
chore: import upstream snapshot with attribution
2026-07-13 12:16:02 +08:00

194 lines
8.0 KiB
PowerShell

<#
.SYNOPSIS
Test the auto-label-product workflow logic locally against real issues.
.DESCRIPTION
Fetches issues with "Needs-Triage" label but no "Product-*" label from the
PowerToys repo and simulates what the GitHub Action would do (without actually
applying labels). This lets you validate the mapping and AI inference before
merging the workflow.
.PARAMETER Apply
Actually apply the labels via `gh issue edit`. Requires gh auth.
.PARAMETER Limit
Number of issues to process (default: 10).
.EXAMPLE
# Dry run - see what would happen
.\Test-AutoLabelProduct.ps1
.EXAMPLE
# Apply labels to first 5 issues
.\Test-AutoLabelProduct.ps1 -Apply -Limit 5
.NOTES
Prerequisites:
- gh CLI authenticated: `gh auth login`
- PowerShell 7+
#>
param(
[switch]$Apply,
[int]$Limit = 10
)
$ErrorActionPreference = 'Stop'
# ─── Mapping (must match the workflow) ────────────────────────────────────────
$AREA_TO_LABEL = @{
'Advanced Paste' = 'Product-Advanced Paste'
'Always on Top' = 'Product-Always On Top'
'Awake' = 'Product-Awake'
'ColorPicker' = 'Product-Color Picker'
'Command not found' = 'Product-CommandNotFound'
'Command Palette' = 'Product-Command Palette'
'Crop and Lock' = 'Product-CropAndLock'
'Environment Variables' = 'Product-Environment Variables'
'FancyZones' = 'Product-FancyZones'
'FancyZones Editor' = 'Product-FancyZones'
'File Locksmith' = 'Product-File Locksmith'
'File Explorer: Preview Pane' = 'Product-File Explorer'
'File Explorer: Thumbnail preview' = 'Product-File Explorer'
'Hosts File Editor' = 'Product-Hosts File Editor'
'Image Resizer' = 'Product-Image Resizer'
'Keyboard Manager' = 'Product-Keyboard Shortcut Manager'
'Light Switch' = 'Product-LightSwitch'
'Mouse Utilities' = 'Product-Mouse Utilities'
'Mouse Without Borders' = 'Product-Mouse Without Borders'
'New+' = 'Product-New+'
'Peek' = 'Product-Peek'
'Power Display' = 'Product-PowerDisplay'
'PowerRename' = 'Product-PowerRename'
'PowerToys Run' = 'Product-PowerToys Run'
'Quick Accent' = 'Product-Quick Accent'
'Registry Preview' = 'Product-Registry Preview'
'Screen ruler' = 'Product-Screen Ruler'
'Settings' = 'Product-Settings'
'Shortcut Guide' = 'Product-Shortcut Guide'
'TextExtractor' = 'Product-Text Extractor'
'Workspaces' = 'Product-Workspaces'
'ZoomIt' = 'Product-ZoomIt'
'General' = 'Product-General'
'Grab And Move' = 'Product-Grab And Move'
}
# Non-product areas (no label applied, AI fallback triggers)
$NON_PRODUCT_AREAS = @('Installer', 'System tray interaction', 'Welcome / PowerToys Tour window')
# ─── Fetch issues ────────────────────────────────────────────────────────────
Write-Host "`n🔍 Fetching issues with 'Needs-Triage' and no 'Product-*' label (limit: $Limit)..." -ForegroundColor Cyan
# gh search finds issues with Needs-Triage; we filter out those that already have Product- labels
$ghStderrPath = [System.IO.Path]::GetTempFileName()
try {
$issuesJson = gh issue list --repo microsoft/PowerToys --label "Needs-Triage" --limit 100 --json number,title,body,labels --state open 2> $ghStderrPath
$ghExitCode = $LASTEXITCODE
$ghErrorOutput = Get-Content -Path $ghStderrPath -Raw
}
finally {
Remove-Item -Path $ghStderrPath -ErrorAction SilentlyContinue
}
if ($ghExitCode -ne 0) {
Write-Host "❌ Failed to fetch issues. Ensure 'gh auth login' is done." -ForegroundColor Red
if (-not [string]::IsNullOrWhiteSpace($ghErrorOutput)) {
Write-Host $ghErrorOutput
}
exit 1
}
if (-not [string]::IsNullOrWhiteSpace($ghErrorOutput)) {
Write-Host "⚠️ gh emitted stderr output while fetching issues:" -ForegroundColor Yellow
Write-Host $ghErrorOutput
}
$issues = $issuesJson | ConvertFrom-Json
# Filter: only issues WITHOUT any Product-* label
$issues = $issues | Where-Object {
$labels = $_.labels | ForEach-Object { $_.name }
-not ($labels | Where-Object { $_ -like 'Product-*' })
} | Select-Object -First $Limit
Write-Host "📋 Found $($issues.Count) issues to process.`n" -ForegroundColor Green
# ─── Process each issue ──────────────────────────────────────────────────────
$results = @()
foreach ($issue in $issues) {
$body = $issue.body
$title = $issue.title
$number = $issue.number
Write-Host "--- Issue #${number}: ${title} ---" -ForegroundColor Yellow
# Parse "Area(s) with issue?" field
$selectedAreas = @()
if ($body -match '### Area\(s\) with issue\?\s*\r?\n\r?\n([\s\S]*?)(?=\r?\n\r?\n###|\s*$)') {
$areaText = $Matches[1].Trim()
$selectedAreas = $areaText -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ }
}
if ($selectedAreas.Count -eq 0) {
Write-Host " ⚠️ No 'Area(s) with issue?' field found in body." -ForegroundColor DarkYellow
} else {
Write-Host " 📌 Areas selected: $($selectedAreas -join ', ')" -ForegroundColor DarkCyan
}
# Resolve labels
$resolvedLabels = @()
$unmapped = @()
foreach ($area in $selectedAreas) {
if ($AREA_TO_LABEL.ContainsKey($area)) {
$resolvedLabels += $AREA_TO_LABEL[$area]
} elseif ($area -notin $NON_PRODUCT_AREAS) {
$unmapped += $area
}
}
$resolvedLabels = $resolvedLabels | Sort-Object -Unique
if ($unmapped.Count -gt 0) {
Write-Host " ⚠️ Unmapped areas (need mapping update): $($unmapped -join ', ')" -ForegroundColor DarkYellow
}
if ($resolvedLabels.Count -eq 0) {
Write-Host " 🤖 No deterministic match → AI inference would trigger in workflow" -ForegroundColor Magenta
} else {
Write-Host " ✅ Would apply: $($resolvedLabels -join ', ')" -ForegroundColor Green
}
# Apply if requested
if ($Apply -and $resolvedLabels.Count -gt 0) {
foreach ($label in $resolvedLabels) {
Write-Host " 🏷️ Applying label: $label" -ForegroundColor White
gh issue edit $number --repo microsoft/PowerToys --add-label $label 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Host " ❌ Failed to apply '$label' (may not exist in repo)" -ForegroundColor Red
}
}
}
$results += [PSCustomObject]@{
Issue = $number
Title = $title.Substring(0, [Math]::Min(60, $title.Length))
Areas = ($selectedAreas -join ', ')
Labels = ($resolvedLabels -join ', ')
NeedsAI = ($resolvedLabels.Count -eq 0)
}
Write-Host ""
}
# ─── Summary ─────────────────────────────────────────────────────────────────
Write-Host "`n═══ SUMMARY ═══" -ForegroundColor Cyan
$results | Format-Table -AutoSize -Wrap
$aiNeeded = ($results | Where-Object { $_.NeedsAI }).Count
$mapped = ($results | Where-Object { -not $_.NeedsAI }).Count
Write-Host "Deterministic: $mapped | AI fallback needed: $aiNeeded | Total: $($results.Count)" -ForegroundColor Cyan
if (-not $Apply) {
Write-Host "`n💡 This was a DRY RUN. Use -Apply to actually add labels." -ForegroundColor Yellow
}