mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
Add correct header ps1 comment
This commit is contained in:
@@ -1,23 +1,59 @@
|
||||
param(
|
||||
[string]$BaseCsv = ".\sorted_prs_93_round1.csv",
|
||||
[string]$AllCsv = ".\sorted_prs.csv",
|
||||
[string]$OutCsv = ".\sorted_prs_93_incremental.csv",
|
||||
[string]$Key = "Number"
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Produce an incremental PR CSV containing rows present in a newer full export but absent from a baseline export.
|
||||
|
||||
.DESCRIPTION
|
||||
Compares two previously generated sorted PR CSV files (same schema). Any row whose key column value
|
||||
(defaults to 'Number') does not exist in the baseline file is emitted to a new incremental CSV, preserving
|
||||
the original column order. If no new rows are found, an empty CSV (with headers when determinable) is written.
|
||||
|
||||
.PARAMETER BaseCsv
|
||||
Path to the baseline (earlier) PR CSV.
|
||||
|
||||
.PARAMETER AllCsv
|
||||
Path to the newer full PR CSV containing superset (or equal set) of rows.
|
||||
|
||||
.PARAMETER OutCsv
|
||||
Path to write the incremental CSV containing only new rows.
|
||||
|
||||
.PARAMETER Key
|
||||
Column name used as unique identifier (defaults to 'Number'). Must exist in both CSVs.
|
||||
|
||||
.EXAMPLE
|
||||
pwsh ./diff_prs.ps1 -BaseCsv sorted_prs_prev.csv -AllCsv sorted_prs.csv -OutCsv sorted_prs_incremental.csv
|
||||
|
||||
.NOTES
|
||||
Requires: PowerShell 7+, both CSVs with identical column schemas.
|
||||
Exit code 0 on success (even if zero incremental rows). Throws on missing files.
|
||||
#>
|
||||
|
||||
[CmdletBinding()] param(
|
||||
[Parameter(Mandatory=$false)][string]$BaseCsv = "./sorted_prs_93_round1.csv",
|
||||
[Parameter(Mandatory=$false)][string]$AllCsv = "./sorted_prs.csv",
|
||||
[Parameter(Mandatory=$false)][string]$OutCsv = "./sorted_prs_93_incremental.csv",
|
||||
[Parameter(Mandatory=$false)][string]$Key = "Number"
|
||||
)
|
||||
|
||||
# Fail on errors
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if (-not (Test-Path -LiteralPath $BaseCsv)) {
|
||||
throw "Base CSV not found: $BaseCsv"
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $AllCsv)) {
|
||||
throw "All CSV not found: $AllCsv"
|
||||
}
|
||||
function Write-Info($m) { Write-Host "[info] $m" -ForegroundColor Cyan }
|
||||
function Write-Warn($m) { Write-Host "[warn] $m" -ForegroundColor Yellow }
|
||||
|
||||
if (-not (Test-Path -LiteralPath $BaseCsv)) { throw "Base CSV not found: $BaseCsv" }
|
||||
if (-not (Test-Path -LiteralPath $AllCsv)) { throw "All CSV not found: $AllCsv" }
|
||||
|
||||
# Load CSVs
|
||||
$baseRows = Import-Csv -LiteralPath $BaseCsv
|
||||
$allRows = Import-Csv -LiteralPath $AllCsv
|
||||
|
||||
if (-not $baseRows) { Write-Warn "Base CSV has no rows." }
|
||||
if (-not $allRows) { Write-Warn "All CSV has no rows." }
|
||||
|
||||
# Validate key presence
|
||||
if ($baseRows -and -not ($baseRows[0].PSObject.Properties.Name -contains $Key)) { throw "Key column '$Key' not found in base CSV." }
|
||||
if ($allRows -and -not ($allRows[0].PSObject.Properties.Name -contains $Key)) { throw "Key column '$Key' not found in all CSV." }
|
||||
|
||||
# Build a set of existing keys from base
|
||||
$set = New-Object 'System.Collections.Generic.HashSet[string]'
|
||||
foreach ($row in $baseRows) {
|
||||
@@ -25,7 +61,7 @@ foreach ($row in $baseRows) {
|
||||
if ($null -ne $val) { [void]$set.Add($val) }
|
||||
}
|
||||
|
||||
# Filter rows in AllCsv whose key is not in base
|
||||
# Filter rows in AllCsv whose key is not in base (these are the new / incremental rows)
|
||||
$incremental = @()
|
||||
foreach ($row in $allRows) {
|
||||
$val = [string]($row.$Key)
|
||||
@@ -38,23 +74,27 @@ if ($allRows.Count -gt 0) {
|
||||
$columns = $allRows[0].PSObject.Properties.Name
|
||||
}
|
||||
|
||||
if ($incremental.Count -gt 0) {
|
||||
if ($columns.Count -gt 0) {
|
||||
$incremental | Select-Object -Property $columns | Export-Csv -LiteralPath $OutCsv -NoTypeInformation -Encoding UTF8
|
||||
try {
|
||||
if ($incremental.Count -gt 0) {
|
||||
if ($columns.Count -gt 0) {
|
||||
$incremental | Select-Object -Property $columns | Export-Csv -LiteralPath $OutCsv -NoTypeInformation -Encoding UTF8
|
||||
} else {
|
||||
$incremental | Export-Csv -LiteralPath $OutCsv -NoTypeInformation -Encoding UTF8
|
||||
}
|
||||
} else {
|
||||
$incremental | Export-Csv -LiteralPath $OutCsv -NoTypeInformation -Encoding UTF8
|
||||
}
|
||||
} else {
|
||||
# Write an empty CSV with headers if we know them
|
||||
if ($columns.Count -gt 0) {
|
||||
# Create one empty object with the same properties to export just headers
|
||||
$obj = [PSCustomObject]@{}
|
||||
foreach ($c in $columns) { $obj | Add-Member -NotePropertyName $c -NotePropertyValue $null }
|
||||
$obj | Select-Object -Property $columns | Export-Csv -LiteralPath $OutCsv -NoTypeInformation -Encoding UTF8
|
||||
} else {
|
||||
'' | Out-File -LiteralPath $OutCsv -Encoding UTF8
|
||||
# Write an empty CSV with headers if we know them (facilitates downstream tooling expecting header row)
|
||||
if ($columns.Count -gt 0) {
|
||||
$obj = [PSCustomObject]@{}
|
||||
foreach ($c in $columns) { $obj | Add-Member -NotePropertyName $c -NotePropertyValue $null }
|
||||
$obj | Select-Object -Property $columns | Export-Csv -LiteralPath $OutCsv -NoTypeInformation -Encoding UTF8
|
||||
} else {
|
||||
'' | Out-File -LiteralPath $OutCsv -Encoding UTF8
|
||||
}
|
||||
}
|
||||
Write-Info ("Incremental rows: {0}" -f $incremental.Count)
|
||||
Write-Info ("Output: {0}" -f (Resolve-Path -LiteralPath $OutCsv))
|
||||
}
|
||||
catch {
|
||||
Write-Host "[error] Failed writing output CSV: $_" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ("Incremental rows: {0}" -f $incremental.Count)
|
||||
Write-Host ("Output: {0}" -f (Resolve-Path -LiteralPath $OutCsv))
|
||||
|
||||
@@ -1,23 +1,73 @@
|
||||
# === CONFIGURATION ===
|
||||
$repo = "microsoft/Powertoys" # e.g., "microsoft/winget-cli"
|
||||
$milestone = "PowerToys 0.94" # Milestone title
|
||||
$outputJson = "milestone_prs.json"
|
||||
$outputCsv = "sorted_prs.csv"
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Export merged pull requests for a milestone into JSON and CSV (sorted) with optional Copilot review summarization.
|
||||
|
||||
.DESCRIPTION
|
||||
Uses the GitHub CLI (gh) to list merged PRs for the specified milestone, captures basic metadata,
|
||||
attempts to obtain a Copilot review summary (choosing the longest Copilot-authored review body),
|
||||
filters labels to a predefined allow-list, and outputs:
|
||||
* Raw JSON list (for traceability)
|
||||
* Sorted CSV (first label alphabetical) used by downstream grouping scripts.
|
||||
|
||||
.PARAMETER Repo
|
||||
GitHub repository in the form 'owner/name'. Default: 'microsoft/PowerToys'.
|
||||
|
||||
.PARAMETER Milestone
|
||||
Exact milestone title (as it appears on GitHub), e.g. 'PowerToys 0.95'.
|
||||
|
||||
.PARAMETER OutputJson
|
||||
Path for raw JSON output. Default: 'milestone_prs.json'.
|
||||
|
||||
.PARAMETER OutputCsv
|
||||
Path for sorted CSV output. Default: 'sorted_prs.csv'.
|
||||
|
||||
.EXAMPLE
|
||||
pwsh ./dump-prs-information.ps1 -Milestone 'PowerToys 0.95'
|
||||
|
||||
.EXAMPLE
|
||||
pwsh ./dump-prs-information.ps1 -Repo microsoft/PowerToys -Milestone 'PowerToys 0.95' -OutputCsv m1.csv
|
||||
|
||||
.NOTES
|
||||
Requires: gh CLI authenticated with repo read access.
|
||||
This script intentionally does NOT use Set-StrictMode (per current repository guidance for release tooling).
|
||||
#>
|
||||
[CmdletBinding()] param(
|
||||
[Parameter(Mandatory=$false)][string]$Repo = 'microsoft/PowerToys',
|
||||
[Parameter(Mandatory=$true)][string]$Milestone,
|
||||
[Parameter(Mandatory=$false)][string]$OutputJson = 'milestone_prs.json',
|
||||
[Parameter(Mandatory=$false)][string]$OutputCsv = 'sorted_prs.csv'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Write-Info($m){ Write-Host "[info] $m" -ForegroundColor Cyan }
|
||||
function Write-Warn($m){ Write-Host "[warn] $m" -ForegroundColor Yellow }
|
||||
function Write-Err($m){ Write-Host "[error] $m" -ForegroundColor Red }
|
||||
|
||||
if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { Write-Err "GitHub CLI 'gh' not found in PATH."; exit 1 }
|
||||
|
||||
Write-Info "Fetching merged PRs for milestone '$Milestone' from $Repo ..."
|
||||
$searchQuery = "milestone:`"$Milestone`""
|
||||
$ghCommand = "gh pr list --repo $Repo --state merged --search '$searchQuery' --json number,title,labels,author,url,body --limit 200"
|
||||
try {
|
||||
Invoke-Expression $ghCommand | Out-File -Encoding UTF8 -FilePath $OutputJson
|
||||
}
|
||||
catch {
|
||||
Write-Err "Failed querying PRs: $_"; exit 1
|
||||
}
|
||||
|
||||
# === STEP 1: Query PRs from GitHub ===
|
||||
Write-Host "Fetching PRs for milestone '$milestone'..."
|
||||
$searchQuery = "milestone:`"$($milestone)`""
|
||||
$ghCommand = "gh pr list --repo $repo --state merged --search '$searchQuery' --json number,title,labels,author,url,body --limit 200"
|
||||
Invoke-Expression "$ghCommand" | Out-File -Encoding UTF8 -FilePath $outputJson
|
||||
if (-not (Test-Path -LiteralPath $OutputJson)) { Write-Err "JSON output not created: $OutputJson"; exit 1 }
|
||||
|
||||
# === STEP 2: Parse and Sort ===
|
||||
$prs = Get-Content $outputJson | ConvertFrom-Json
|
||||
Write-Info "Parsing JSON ..."
|
||||
$prs = Get-Content $OutputJson | ConvertFrom-Json
|
||||
if (-not $prs) { Write-Warn "No PRs returned for milestone '$Milestone'"; exit 0 }
|
||||
$sorted = $prs | Sort-Object { $_.labels[0]?.name }
|
||||
|
||||
Write-Host "Fetching Copilot reviews for each PR..."
|
||||
Write-Info "Fetching Copilot reviews for each PR (longest Copilot-authored body)."
|
||||
$csvData = $sorted | ForEach-Object {
|
||||
$prNumber = $_.number
|
||||
Write-Host "Processing PR #$prNumber..."
|
||||
Write-Info "Processing PR #$prNumber ..."
|
||||
|
||||
# Get Copilot review for this PR
|
||||
$copilotOverview = ""
|
||||
@@ -37,13 +87,13 @@ $csvData = $sorted | ForEach-Object {
|
||||
if ($copilotReviews -and $copilotReviews.Count -gt 0) {
|
||||
$longest = $copilotReviews | Sort-Object { $_.body.Length } -Descending | Select-Object -First 1
|
||||
$copilotOverview = $longest.body.Replace("`r", "").Replace("`n", " ") -replace '\s+', ' '
|
||||
Write-Host " Selected Copilot review (author=$($longest.author.login) length=$($longest.body.Length))"
|
||||
Write-Info " Copilot review selected (author=$($longest.author.login) length=$($longest.body.Length))"
|
||||
} else {
|
||||
Write-Host " No Copilot reviews found for PR #$prNumber"
|
||||
Write-Warn " No Copilot reviews found for PR #$prNumber"
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Host " Warning: Could not fetch reviews for PR #$prNumber"
|
||||
Write-Warn " Could not fetch reviews for PR #$prNumber"
|
||||
}
|
||||
|
||||
# Filter labels to only include specific patterns
|
||||
@@ -68,6 +118,6 @@ $csvData = $sorted | ForEach-Object {
|
||||
}
|
||||
|
||||
# === STEP 3: Output CSV ===
|
||||
Write-Host "Saving to $outputCsv..."
|
||||
$csvData | Export-Csv $outputCsv -NoTypeInformation
|
||||
Write-Host "✅ Done. Open '$outputCsv' to group PRs and send them back."
|
||||
Write-Info "Saving CSV to $OutputCsv ..."
|
||||
$csvData | Export-Csv $OutputCsv -NoTypeInformation -Encoding UTF8
|
||||
Write-Info "Done. Rows: $($csvData.Count). CSV: $(Resolve-Path -LiteralPath $OutputCsv)"
|
||||
@@ -1,3 +1,36 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Export merged PR metadata between two commits (exclusive start, inclusive end) to JSON and CSV.
|
||||
|
||||
.DESCRIPTION
|
||||
Identifies merge/squash commits reachable from EndCommit but not StartCommit, extracts PR numbers,
|
||||
queries GitHub for metadata plus (optionally) Copilot review/comment summaries, filters labels, then
|
||||
emits a JSON artifact and a sorted CSV (first label alphabetical) analogous to dump-prs-information.ps1.
|
||||
|
||||
.PARAMETER StartCommit
|
||||
Exclusive starting commit (SHA, tag, or ref). Commits AFTER this one are considered.
|
||||
|
||||
.PARAMETER EndCommit
|
||||
Inclusive ending commit (SHA, tag, or ref). Default: HEAD.
|
||||
|
||||
.PARAMETER Repo
|
||||
GitHub repository (owner/name). Default: microsoft/PowerToys.
|
||||
|
||||
.PARAMETER OutputCsv
|
||||
Destination CSV path. Default: sorted_prs.csv.
|
||||
|
||||
.PARAMETER OutputJson
|
||||
Destination JSON path containing raw PR objects. Default: milestone_prs.json.
|
||||
|
||||
.EXAMPLE
|
||||
pwsh ./dump-prs-since-commit.ps1 -StartCommit 0123abcd
|
||||
|
||||
.EXAMPLE
|
||||
pwsh ./dump-prs-since-commit.ps1 -StartCommit 0123abcd -EndCommit 89ef7654 -OutputCsv delta.csv
|
||||
|
||||
.NOTES
|
||||
Requires: git, gh (authenticated). No Set-StrictMode to keep parity with existing release scripts.
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$StartCommit, # exclusive start (commits AFTER this one)
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
<#
|
||||
Groups PRs from sorted_prs.csv by Labels and emits per-label CSV files.
|
||||
Each output CSV keeps the original columns and the same PR order as in the input.
|
||||
.SYNOPSIS
|
||||
Group PR rows by their Labels column and emit per-label CSV files.
|
||||
|
||||
.DESCRIPTION
|
||||
Reads a milestone PR CSV (usually produced by dump-prs-information / dump-prs-since-commit scripts),
|
||||
splits rows by label list, normalizes/sorts individual labels, and writes one CSV per unique label combination.
|
||||
Each output preserves the original row ordering within that subset and column order from the source.
|
||||
|
||||
.PARAMETER CsvPath
|
||||
Input CSV containing PR rows with a 'Labels' column (comma-separated list).
|
||||
|
||||
.PARAMETER OutDir
|
||||
Output directory to place grouped CSVs (created if missing). Default: 'grouped_csv'.
|
||||
|
||||
.NOTES
|
||||
Label combinations are joined using ' | ' when multiple labels present. Filenames are sanitized (invalid characters,
|
||||
whitespace collapsed) and truncated to <= 120 characters.
|
||||
#>
|
||||
param(
|
||||
[string]$CsvPath = "sorted_prs.csv",
|
||||
@@ -18,9 +33,13 @@ Write-Info "Reading CSV: $CsvPath"
|
||||
$rows = Import-Csv -LiteralPath $CsvPath
|
||||
Write-Info ("Loaded {0} rows" -f $rows.Count)
|
||||
|
||||
function Sanitize-FileName([string]$name) {
|
||||
if ([string]::IsNullOrWhiteSpace($name)) { return 'Unnamed' }
|
||||
$s = $name -replace '[<>:"/\\|?*]', '-' # invalid path chars
|
||||
function ConvertTo-SafeFileName {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$Name
|
||||
)
|
||||
if ([string]::IsNullOrWhiteSpace($Name)) { return 'Unnamed' }
|
||||
$s = $Name -replace '[<>:"/\\|?*]', '-' # invalid path chars
|
||||
$s = $s -replace '\s+', '-' # spaces to dashes
|
||||
$s = $s -replace '-{2,}', '-' # collapse dashes
|
||||
$s = $s.Trim('-')
|
||||
@@ -29,7 +48,7 @@ function Sanitize-FileName([string]$name) {
|
||||
return $s
|
||||
}
|
||||
|
||||
# Group rows by label combination; preserve CSV order inside each group
|
||||
# Build groups keyed by normalized, sorted label combinations. Preserve original CSV row order.
|
||||
$groups = @{}
|
||||
foreach ($row in $rows) {
|
||||
$labelsRaw = $row.Labels
|
||||
@@ -55,7 +74,7 @@ Write-Info ("Generating {0} grouped CSV file(s) into: {1}" -f $groups.Count, $Ou
|
||||
|
||||
foreach ($key in $groups.Keys) {
|
||||
$labelParts = if ($key -eq 'Unlabeled') { @('Unlabeled') } else { $key -split '\s\|\s' }
|
||||
$safeName = ($labelParts | ForEach-Object { Sanitize-FileName $_ }) -join '-'
|
||||
$safeName = ($labelParts | ForEach-Object { ConvertTo-SafeFileName -Name $_ }) -join '-'
|
||||
$filePath = Join-Path $OutDir ("$safeName.csv")
|
||||
|
||||
# Keep same columns and order
|
||||
|
||||
Reference in New Issue
Block a user