mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-19 09:40:21 +01:00
Compare commits
3 Commits
async-cpp-
...
dev/vanzue
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
380669dd81 | ||
|
|
d24569d49c | ||
|
|
94cbea91a0 |
1
.github/actions/spell-check/excludes.txt
vendored
1
.github/actions/spell-check/excludes.txt
vendored
@@ -93,6 +93,7 @@
|
|||||||
\.xz$
|
\.xz$
|
||||||
\.zip$
|
\.zip$
|
||||||
^\.github/actions/spell-check/
|
^\.github/actions/spell-check/
|
||||||
|
^\.github/skills/
|
||||||
^\.github/workflows/spelling\d*\.yml$
|
^\.github/workflows/spelling\d*\.yml$
|
||||||
^\.gitmodules$
|
^\.gitmodules$
|
||||||
^\Q.pipelines/ESRPSigning_core.json\E$
|
^\Q.pipelines/ESRPSigning_core.json\E$
|
||||||
|
|||||||
152
.github/skills/changelog-generator/SKILL.md
vendored
Normal file
152
.github/skills/changelog-generator/SKILL.md
vendored
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
---
|
||||||
|
name: changelog-generator
|
||||||
|
description: Automatically creates user-facing changelogs from git commits by analyzing commit history, categorizing changes, and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Changelog Generator
|
||||||
|
|
||||||
|
This skill transforms technical git commits into polished, user-friendly changelogs that your customers and users will actually understand and appreciate.
|
||||||
|
|
||||||
|
## When to Use This Skill
|
||||||
|
|
||||||
|
- Preparing release notes for a new version
|
||||||
|
- Generating changelog between two tags/commits
|
||||||
|
- Creating draft release notes from recent commits
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```
|
||||||
|
Create a changelog from commits since v0.96.1
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
Create release notes for version 0.97.0 starting from tag v0.96.1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||||
|
│ 1. Fetch │───▶│ 2. Filter │───▶│ 3. Categorize│───▶│ 4. Generate │
|
||||||
|
│ Commits │ │ & Dedupe │ │ by Module │ │ Descriptions │
|
||||||
|
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
|
||||||
|
│ │ │ │
|
||||||
|
▼ ▼ ▼ ▼
|
||||||
|
📄 github-api 📄 commit- 📄 module- 📄 user-facing-
|
||||||
|
reference.md filtering.md mapping.md description.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sub-Skills Reference
|
||||||
|
|
||||||
|
This skill is composed of the following sub-skills.
|
||||||
|
|
||||||
|
**⚠️ IMPORTANT: Do NOT read all sub-skills at once!**
|
||||||
|
- Only read a sub-skill when you reach that step in the workflow
|
||||||
|
- This saves context window and improves accuracy
|
||||||
|
- Use `read_file` to load a sub-skill only when needed
|
||||||
|
|
||||||
|
| Step | Sub-Skill | When to Read |
|
||||||
|
|------|-----------|--------------|
|
||||||
|
| Fetch data | [github-api-reference.md](sub-skills/github-api-reference.md) | When fetching commits/PRs |
|
||||||
|
| Filter commits | [commit-filtering.md](sub-skills/commit-filtering.md) | When checking if commit should be skipped |
|
||||||
|
| Categorize | [module-mapping.md](sub-skills/module-mapping.md) | When determining which module a PR belongs to |
|
||||||
|
| Generate text | [user-facing-description.md](sub-skills/user-facing-description.md) | When writing the changelog entry text |
|
||||||
|
| Attribution | [contributor-attribution.md](sub-skills/contributor-attribution.md) | When checking if author needs thanks |
|
||||||
|
| Large releases | [progress-tracking.md](sub-skills/progress-tracking.md) | Only if processing 50+ commits |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step-by-Step Summary
|
||||||
|
|
||||||
|
### Step 1: Get Commit Range
|
||||||
|
```powershell
|
||||||
|
# Count commits between tags
|
||||||
|
gh api repos/microsoft/PowerToys/compare/v0.96.0...v0.96.1 --jq '.commits | length'
|
||||||
|
```
|
||||||
|
👉 Details: [github-api-reference.md](sub-skills/github-api-reference.md)
|
||||||
|
|
||||||
|
### Step 2: Filter Commits
|
||||||
|
- Skip commits already in start tag
|
||||||
|
- Skip cherry-picks and backports
|
||||||
|
- Deduplicate by PR number
|
||||||
|
|
||||||
|
👉 Details: [commit-filtering.md](sub-skills/commit-filtering.md)
|
||||||
|
|
||||||
|
### Step 3: For Each PR, Generate Entry
|
||||||
|
1. Get PR title, body, files, labels
|
||||||
|
2. Determine module from file paths or labels
|
||||||
|
3. Check if user-facing (skip internal changes)
|
||||||
|
4. Transform to user-friendly description
|
||||||
|
5. Add contributor attribution if needed
|
||||||
|
|
||||||
|
👉 Details: [user-facing-description.md](sub-skills/user-facing-description.md), [module-mapping.md](sub-skills/module-mapping.md), [contributor-attribution.md](sub-skills/contributor-attribution.md)
|
||||||
|
|
||||||
|
### Step 4: Checkpoint (if 50+ commits)
|
||||||
|
- Save progress after every 15-20 commits
|
||||||
|
- Track processed PRs for deduplication
|
||||||
|
- Enable resume from interruption
|
||||||
|
|
||||||
|
👉 Details: [progress-tracking.md](sub-skills/progress-tracking.md)
|
||||||
|
|
||||||
|
### Step 5: Format Output
|
||||||
|
```markdown
|
||||||
|
## ✨ What's new
|
||||||
|
**Version X.XX (Month Year)**
|
||||||
|
|
||||||
|
**✨ Highlights**
|
||||||
|
- [Most impactful change 1]
|
||||||
|
- [Most impactful change 2]
|
||||||
|
|
||||||
|
### [Module Name - Alphabetical]
|
||||||
|
- [Description]. Thanks [@contributor](https://github.com/contributor)!
|
||||||
|
|
||||||
|
### Development
|
||||||
|
- [Internal changes]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## ✨ What's new
|
||||||
|
**Version 0.96.1 (December 2025)**
|
||||||
|
|
||||||
|
**✨ Highlights**
|
||||||
|
- Advanced Paste now supports multiple AI providers.
|
||||||
|
- PowerRename can extract photo metadata for renaming.
|
||||||
|
|
||||||
|
### Advanced Paste
|
||||||
|
- Added support for Azure OpenAI, Google Gemini, Mistral, and more.
|
||||||
|
|
||||||
|
### Awake
|
||||||
|
- The countdown timer now stays accurate over long periods. Thanks [@daverayment](https://github.com/daverayment)!
|
||||||
|
|
||||||
|
### Development
|
||||||
|
- Resolved build warnings in Command Palette projects.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
1. **Propose highlights** after all entries are generated
|
||||||
|
2. **Check PR body** when title is unclear
|
||||||
|
3. **Thank external contributors** - see [contributor-attribution.md](sub-skills/contributor-attribution.md)
|
||||||
|
4. **Use progress tracking** for large releases - see [progress-tracking.md](sub-skills/progress-tracking.md)
|
||||||
|
5. **Save output** to `release-change-note-draft.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Problem | Solution |
|
||||||
|
|---------|----------|
|
||||||
|
| Lost progress mid-generation | Read `release-notes-progress.md` to resume |
|
||||||
|
| Duplicate entries | Check processed PR list, dedupe by PR number |
|
||||||
|
| Commit already released | Use `git merge-base --is-ancestor` to verify |
|
||||||
|
| API rate limited | Check `gh api rate_limit`, wait or use token |
|
||||||
|
|
||||||
|
👉 See sub-skills for detailed troubleshooting.
|
||||||
107
.github/skills/changelog-generator/sub-skills/commit-filtering.md
vendored
Normal file
107
.github/skills/changelog-generator/sub-skills/commit-filtering.md
vendored
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# Commit Filtering Rules
|
||||||
|
|
||||||
|
This sub-skill defines rules for filtering commits to include in the changelog.
|
||||||
|
|
||||||
|
## Understanding the Branch Model
|
||||||
|
|
||||||
|
PowerToys uses a release branch model where fixes are cherry-picked from main:
|
||||||
|
|
||||||
|
```
|
||||||
|
main: A---B---C---D---E---F---G---H (HEAD)
|
||||||
|
\
|
||||||
|
release: X---Y---Z (v0.96.1 tag)
|
||||||
|
↑
|
||||||
|
(X, Y are cherry-picks of C, E from main)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key insight:** When comparing `v0.96.1...main`:
|
||||||
|
- The release tag (v0.96.1) is on a **different branch** than main
|
||||||
|
- GitHub compare finds the merge-base and returns commits on main after that point
|
||||||
|
- Commits C and E appear in the results even though they were cherry-picked to release as X and Y
|
||||||
|
- **The SHAs are different**, so SHA-based filtering won't work!
|
||||||
|
|
||||||
|
## ⚠️ CRITICAL: Filter by PR Number, Not SHA
|
||||||
|
|
||||||
|
Since cherry-picks have different SHAs, you **MUST** check by PR number:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Extract PR number from commit message and check if it exists in the release tag
|
||||||
|
$prNumber = "43785"
|
||||||
|
$startTag = "v0.96.1"
|
||||||
|
|
||||||
|
# Search the release branch for this PR number in commit messages
|
||||||
|
$cherryPicked = git log $startTag --oneline --grep="#$prNumber"
|
||||||
|
if ($cherryPicked) {
|
||||||
|
Write-Host "SKIP: PR #$prNumber was cherry-picked to $startTag"
|
||||||
|
} else {
|
||||||
|
Write-Host "INCLUDE: PR #$prNumber is new since $startTag"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete Filtering Workflow
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$startTag = "v0.96.1" # Release tag (on release branch)
|
||||||
|
$endRef = "main" # Target (main branch)
|
||||||
|
|
||||||
|
# Step 1: Get all commits from main since the merge-base with release tag
|
||||||
|
$commits = gh api "repos/microsoft/PowerToys/compare/$startTag...$endRef" `
|
||||||
|
--jq '.commits[] | {sha: .sha, message: .commit.message}' | ConvertFrom-Json
|
||||||
|
|
||||||
|
# Step 2: Build list of PR numbers already in the release tag
|
||||||
|
$releasePRs = git log $startTag --oneline | Select-String -Pattern '#(\d+)' -AllMatches |
|
||||||
|
ForEach-Object { $_.Matches.Groups[1].Value } | Sort-Object -Unique
|
||||||
|
|
||||||
|
Write-Host "PRs already in $startTag : $($releasePRs.Count)"
|
||||||
|
|
||||||
|
# Step 3: Filter commits - skip if PR was cherry-picked to release
|
||||||
|
$newCommits = @()
|
||||||
|
foreach ($commit in $commits) {
|
||||||
|
if ($commit.message -match '#(\d+)') {
|
||||||
|
$prNumber = $matches[1]
|
||||||
|
if ($releasePRs -contains $prNumber) {
|
||||||
|
Write-Host "SKIP: PR #$prNumber already in $startTag (cherry-picked)"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$newCommits += $commit
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "New commits to process: $($newCommits.Count)"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Why SHA-Based Methods Don't Work Here
|
||||||
|
|
||||||
|
| Method | Works for same branch? | Works for cross-branch (cherry-picks)? |
|
||||||
|
|--------|------------------------|----------------------------------------|
|
||||||
|
| `git merge-base --is-ancestor` | ✅ Yes | ❌ No - different SHAs |
|
||||||
|
| `git tag --contains` | ✅ Yes | ❌ No - tag is on different branch |
|
||||||
|
| GitHub Compare API | ✅ Yes | ❌ No - returns commits by SHA |
|
||||||
|
| **PR number matching** | ✅ Yes | ✅ **Yes** |
|
||||||
|
|
||||||
|
## Skip Rules Summary
|
||||||
|
|
||||||
|
| Priority | Condition | Action |
|
||||||
|
|----------|-----------|--------|
|
||||||
|
| 1 | PR number found in `git log $startTag --grep="#$prNumber"` | **SKIP** - cherry-picked |
|
||||||
|
| 2 | Same PR number already processed in this run | **SKIP** - duplicate |
|
||||||
|
| 3 | Bot author (dependabot, etc.) | **SKIP** - unless user-visible |
|
||||||
|
| 4 | Internal-only change (CI, tests, refactor) | Move to **Development** section |
|
||||||
|
|
||||||
|
## User-Facing vs Non-User-Facing
|
||||||
|
|
||||||
|
**Include in changelog:**
|
||||||
|
- New features and capabilities
|
||||||
|
- Bug fixes that affect users
|
||||||
|
- UI/UX improvements
|
||||||
|
- Performance improvements users would notice
|
||||||
|
- Breaking changes or behavior modifications
|
||||||
|
- Security fixes
|
||||||
|
|
||||||
|
**Exclude from changelog (put in Development section):**
|
||||||
|
- Internal refactoring
|
||||||
|
- CI/CD changes
|
||||||
|
- Code style fixes
|
||||||
|
- Test additions/modifications
|
||||||
|
- Documentation-only changes
|
||||||
|
- Dependency updates (unless user-visible impact)
|
||||||
110
.github/skills/changelog-generator/sub-skills/contributor-attribution.md
vendored
Normal file
110
.github/skills/changelog-generator/sub-skills/contributor-attribution.md
vendored
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# Contributor Attribution Rules
|
||||||
|
|
||||||
|
This sub-skill defines when and how to credit contributors in changelog entries.
|
||||||
|
|
||||||
|
## Attribution Rules
|
||||||
|
|
||||||
|
- Check [COMMUNITY.md](../../../COMMUNITY.md) for the "PowerToys core team" section
|
||||||
|
- If author is **NOT** in core team → Add `Thanks [@username](https://github.com/username)!`
|
||||||
|
- If author **IS** in core team → No attribution needed
|
||||||
|
- Microsoft employees working on PowerToys as their job → No attribution needed
|
||||||
|
|
||||||
|
## Core Team Members
|
||||||
|
|
||||||
|
Current core team members (from COMMUNITY.md) - do NOT thank these:
|
||||||
|
|
||||||
|
```
|
||||||
|
@craigloewen-msft, @niels9001, @dhowett, @yeelam-gordon, @jamrobot,
|
||||||
|
@lei9444, @shuaiyuanxx, @moooyo, @haoliuu, @chenmy77, @chemwolf6922,
|
||||||
|
@yaqingmi, @zhaoqpcn, @urnotdfs, @zhaopy536, @wang563681252, @vanzue,
|
||||||
|
@zadjii-msft, @khmyznikov, @chatasweetie, @MichaelJolley, @Jaylyn-Barbee,
|
||||||
|
@zateutsch, @crutkas
|
||||||
|
```
|
||||||
|
|
||||||
|
## Check Author Script
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$coreTeam = @(
|
||||||
|
'craigloewen-msft', 'niels9001', 'dhowett', 'yeelam-gordon', 'jamrobot',
|
||||||
|
'lei9444', 'shuaiyuanxx', 'moooyo', 'haoliuu', 'chenmy77', 'chemwolf6922',
|
||||||
|
'yaqingmi', 'zhaoqpcn', 'urnotdfs', 'zhaopy536', 'wang563681252', 'vanzue',
|
||||||
|
'zadjii-msft', 'khmyznikov', 'chatasweetie', 'MichaelJolley', 'Jaylyn-Barbee',
|
||||||
|
'zateutsch', 'crutkas'
|
||||||
|
)
|
||||||
|
|
||||||
|
function Get-Attribution {
|
||||||
|
param([string]$author)
|
||||||
|
|
||||||
|
if ($coreTeam -contains $author) {
|
||||||
|
return $null # No attribution needed
|
||||||
|
}
|
||||||
|
return "Thanks [@$author](https://github.com/$author)!"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
$author = gh pr view 12345 --repo microsoft/PowerToys --json author --jq '.author.login'
|
||||||
|
$attribution = Get-Attribution $author
|
||||||
|
if ($attribution) {
|
||||||
|
Write-Host "Add: $attribution"
|
||||||
|
} else {
|
||||||
|
Write-Host "No attribution needed (core team member)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Attribution Format
|
||||||
|
|
||||||
|
**With attribution:**
|
||||||
|
```markdown
|
||||||
|
- The Awake countdown timer now stays accurate over long periods. Thanks [@daverayment](https://github.com/daverayment)!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Without attribution (core team):**
|
||||||
|
```markdown
|
||||||
|
- Added new feature to Command Palette for opening settings.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Special Cases
|
||||||
|
|
||||||
|
1. **Co-authored commits**: Credit the primary author (first in list)
|
||||||
|
2. **Bot accounts** (dependabot, etc.): No attribution
|
||||||
|
3. **Former core team members**: Check if they were core team at time of PR
|
||||||
|
4. **Multiple PRs by same external contributor**: Thank them on each entry
|
||||||
|
|
||||||
|
## High-Impact Community Members
|
||||||
|
|
||||||
|
These contributors have made significant ongoing contributions and are recognized in COMMUNITY.md.
|
||||||
|
**ALWAYS thank these contributors** - they are NOT core team and deserve recognition:
|
||||||
|
|
||||||
|
```
|
||||||
|
@davidegiacometti, @htcfreek, @daverayment, @jiripolasek
|
||||||
|
```
|
||||||
|
|
||||||
|
Check COMMUNITY.md for the full up-to-date list under "High impact community members" section.
|
||||||
|
|
||||||
|
## Updated Check Author Script
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$coreTeam = @(
|
||||||
|
'craigloewen-msft', 'niels9001', 'dhowett', 'yeelam-gordon', 'jamrobot',
|
||||||
|
'lei9444', 'shuaiyuanxx', 'moooyo', 'haoliuu', 'chenmy77', 'chemwolf6922',
|
||||||
|
'yaqingmi', 'zhaoqpcn', 'urnotdfs', 'zhaopy536', 'wang563681252', 'vanzue',
|
||||||
|
'zadjii-msft', 'khmyznikov', 'chatasweetie', 'MichaelJolley', 'Jaylyn-Barbee',
|
||||||
|
'zateutsch', 'crutkas'
|
||||||
|
)
|
||||||
|
|
||||||
|
# High-impact community members - ALWAYS thank these!
|
||||||
|
$highImpactCommunity = @(
|
||||||
|
'davidegiacometti', 'htcfreek', 'daverayment', 'jiripolasek'
|
||||||
|
)
|
||||||
|
|
||||||
|
function Get-Attribution {
|
||||||
|
param([string]$author)
|
||||||
|
|
||||||
|
# Core team and bots don't need thanks
|
||||||
|
if ($coreTeam -contains $author -or $author -match '\[bot\]$') {
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
# Everyone else (including high-impact community) gets thanked
|
||||||
|
return "Thanks [@$author](https://github.com/$author)!"
|
||||||
|
}
|
||||||
|
```
|
||||||
101
.github/skills/changelog-generator/sub-skills/github-api-reference.md
vendored
Normal file
101
.github/skills/changelog-generator/sub-skills/github-api-reference.md
vendored
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# GitHub API Reference for Changelog Generation
|
||||||
|
|
||||||
|
This sub-skill provides GitHub API commands and scripts for fetching commit and PR data.
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Ensure gh CLI is authenticated
|
||||||
|
gh auth status
|
||||||
|
|
||||||
|
# Or use personal access token
|
||||||
|
$headers = @{ Authorization = "token $env:GITHUB_TOKEN" }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Useful API Endpoints
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Compare two refs (tags, branches, commits)
|
||||||
|
gh api repos/microsoft/PowerToys/compare/v0.96.0...v0.96.1
|
||||||
|
|
||||||
|
# List commits with pagination
|
||||||
|
gh api "repos/microsoft/PowerToys/commits?per_page=100&page=1"
|
||||||
|
|
||||||
|
# Get PR associated with a commit
|
||||||
|
gh api repos/microsoft/PowerToys/commits/{sha}/pulls
|
||||||
|
|
||||||
|
# Get PR details
|
||||||
|
gh api repos/microsoft/PowerToys/pulls/{number}
|
||||||
|
|
||||||
|
# Get files changed in a PR
|
||||||
|
gh api repos/microsoft/PowerToys/pulls/{number}/files
|
||||||
|
|
||||||
|
# Search PRs merged in date range
|
||||||
|
gh api "search/issues?q=repo:microsoft/PowerToys+is:pr+is:merged+merged:2025-01-01..2025-01-31"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fetch Commits in Range
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$owner = "microsoft"
|
||||||
|
$repo = "PowerToys"
|
||||||
|
$startTag = "v0.96.0"
|
||||||
|
$endTag = "v0.96.1"
|
||||||
|
|
||||||
|
# Get all commits between two tags
|
||||||
|
gh api repos/$owner/$repo/compare/$startTag...$endTag --jq '.commits[] | {sha: .sha, message: .commit.message, author: .author.login, date: .commit.author.date}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Get PR Details for a Commit
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Get PR associated with a commit SHA
|
||||||
|
gh api "repos/$owner/$repo/commits/{sha}/pulls" --jq '.[0] | {number: .number, title: .title, body: .body, user: .user.login, labels: [.labels[].name]}'
|
||||||
|
|
||||||
|
# Get full PR details
|
||||||
|
gh pr view 1234 --repo microsoft/PowerToys --json title,body,author,files,labels,mergedAt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Batch Processing Script
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$startTag = "v0.96.0"
|
||||||
|
$endTag = "v0.96.1"
|
||||||
|
|
||||||
|
$commits = gh api repos/microsoft/PowerToys/compare/$startTag...$endTag --jq '.commits[].sha' | ForEach-Object {
|
||||||
|
$sha = $_
|
||||||
|
$prInfo = gh api "repos/microsoft/PowerToys/commits/$sha/pulls" 2>$null | ConvertFrom-Json
|
||||||
|
if ($prInfo) {
|
||||||
|
[PSCustomObject]@{
|
||||||
|
SHA = $sha
|
||||||
|
PRNumber = $prInfo[0].number
|
||||||
|
Title = $prInfo[0].title
|
||||||
|
Author = $prInfo[0].user.login
|
||||||
|
Labels = $prInfo[0].labels.name -join ", "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$commits | Format-Table
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rate Limiting
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Check remaining rate limit
|
||||||
|
gh api rate_limit --jq '.rate'
|
||||||
|
# Authenticated: 5000 requests/hour
|
||||||
|
# Unauthenticated: 60 requests/hour
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pagination for Large Results
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# GitHub API returns max 100 items per page
|
||||||
|
$page = 1
|
||||||
|
$allCommits = @()
|
||||||
|
do {
|
||||||
|
$commits = gh api "repos/$owner/$repo/commits?per_page=100&page=$page" | ConvertFrom-Json
|
||||||
|
$allCommits += $commits
|
||||||
|
$page++
|
||||||
|
} while ($commits.Count -eq 100)
|
||||||
|
```
|
||||||
137
.github/skills/changelog-generator/sub-skills/module-mapping.md
vendored
Normal file
137
.github/skills/changelog-generator/sub-skills/module-mapping.md
vendored
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
# PowerToys Module Path Mapping
|
||||||
|
|
||||||
|
This sub-skill maps file paths to PowerToys module names for categorization.
|
||||||
|
|
||||||
|
## Module Path Mapping Table
|
||||||
|
|
||||||
|
| Module | Path Pattern |
|
||||||
|
|--------|--------------|
|
||||||
|
| Advanced Paste | `src/modules/AdvancedPaste/**` |
|
||||||
|
| Always On Top | `src/modules/alwaysontop/**` |
|
||||||
|
| Awake | `src/modules/Awake/**` |
|
||||||
|
| Color Picker | `src/modules/colorPicker/**` |
|
||||||
|
| Command Palette | `src/modules/cmdpal/**` |
|
||||||
|
| Crop And Lock | `src/modules/CropAndLock/**` |
|
||||||
|
| Environment Variables | `src/modules/EnvironmentVariables/**` |
|
||||||
|
| FancyZones | `src/modules/fancyzones/**` |
|
||||||
|
| File Explorer Add-ons | `src/modules/previewpane/**`, `src/modules/FileExplorerPreview/**` |
|
||||||
|
| File Locksmith | `src/modules/FileLocksmith/**` |
|
||||||
|
| Find My Mouse | `src/modules/MouseUtils/FindMyMouse/**` |
|
||||||
|
| Hosts File Editor | `src/modules/Hosts/**` |
|
||||||
|
| Image Resizer | `src/modules/imageresizer/**` |
|
||||||
|
| Keyboard Manager | `src/modules/keyboardmanager/**` |
|
||||||
|
| Light Switch | `src/modules/LightSwitch/**` |
|
||||||
|
| Mouse Highlighter | `src/modules/MouseUtils/MouseHighlighter/**` |
|
||||||
|
| Mouse Jump | `src/modules/MouseUtils/MouseJump/**` |
|
||||||
|
| Mouse Pointer Crosshairs | `src/modules/MouseUtils/MousePointerCrosshairs/**` |
|
||||||
|
| Mouse Without Borders | `src/modules/MouseWithoutBorders/**` |
|
||||||
|
| New+ | `src/modules/NewPlus/**` |
|
||||||
|
| Paste As Plain Text | `src/modules/PastePlain/**` |
|
||||||
|
| Peek | `src/modules/Peek/**` |
|
||||||
|
| PowerRename | `src/modules/powerrename/**` |
|
||||||
|
| PowerToys Run | `src/modules/launcher/**` |
|
||||||
|
| Quick Accent | `src/modules/QuickAccent/**` |
|
||||||
|
| Registry Preview | `src/modules/RegistryPreview/**` |
|
||||||
|
| Screen Ruler | `src/modules/MeasureTool/**` |
|
||||||
|
| Shortcut Guide | `src/modules/ShortcutGuide/**` |
|
||||||
|
| Text Extractor | `src/modules/TextExtractor/**` |
|
||||||
|
| Video Conference Mute | `src/modules/videoconference/**` |
|
||||||
|
| Workspaces | `src/modules/Workspaces/**` |
|
||||||
|
| ZoomIt | `src/modules/ZoomIt/**` |
|
||||||
|
| Settings | `src/settings-ui/**` |
|
||||||
|
| Runner | `src/runner/**` |
|
||||||
|
| Installer | `installer/**` |
|
||||||
|
| General / Infrastructure | `src/common/**`, `.github/**`, `tools/**` |
|
||||||
|
|
||||||
|
## Categorization by PR Labels
|
||||||
|
|
||||||
|
Common PowerToys PR labels for modules:
|
||||||
|
- `Product-FancyZones`
|
||||||
|
- `Product-PowerToys Run`
|
||||||
|
- `Product-Awake`
|
||||||
|
- `Product-ColorPicker`
|
||||||
|
- `Product-Keyboard Manager`
|
||||||
|
- etc.
|
||||||
|
|
||||||
|
## Auto-Categorization Script
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
function Get-ModuleFromPath {
|
||||||
|
param([string]$filePath)
|
||||||
|
|
||||||
|
$moduleMap = @{
|
||||||
|
'src/modules/AdvancedPaste/' = 'Advanced Paste'
|
||||||
|
'src/modules/alwaysontop/' = 'Always On Top'
|
||||||
|
'src/modules/Awake/' = 'Awake'
|
||||||
|
'src/modules/colorPicker/' = 'Color Picker'
|
||||||
|
'src/modules/cmdpal/' = 'Command Palette'
|
||||||
|
'src/modules/CropAndLock/' = 'Crop And Lock'
|
||||||
|
'src/modules/EnvironmentVariables/' = 'Environment Variables'
|
||||||
|
'src/modules/fancyzones/' = 'FancyZones'
|
||||||
|
'src/modules/previewpane/' = 'File Explorer Add-ons'
|
||||||
|
'src/modules/FileExplorerPreview/' = 'File Explorer Add-ons'
|
||||||
|
'src/modules/FileLocksmith/' = 'File Locksmith'
|
||||||
|
'src/modules/MouseUtils/FindMyMouse/' = 'Find My Mouse'
|
||||||
|
'src/modules/Hosts/' = 'Hosts File Editor'
|
||||||
|
'src/modules/imageresizer/' = 'Image Resizer'
|
||||||
|
'src/modules/keyboardmanager/' = 'Keyboard Manager'
|
||||||
|
'src/modules/LightSwitch/' = 'Light Switch'
|
||||||
|
'src/modules/MouseUtils/MouseHighlighter/' = 'Mouse Highlighter'
|
||||||
|
'src/modules/MouseUtils/MouseJump/' = 'Mouse Jump'
|
||||||
|
'src/modules/MouseUtils/MousePointerCrosshairs/' = 'Mouse Pointer Crosshairs'
|
||||||
|
'src/modules/MouseWithoutBorders/' = 'Mouse Without Borders'
|
||||||
|
'src/modules/NewPlus/' = 'New+'
|
||||||
|
'src/modules/PastePlain/' = 'Paste As Plain Text'
|
||||||
|
'src/modules/Peek/' = 'Peek'
|
||||||
|
'src/modules/powerrename/' = 'PowerRename'
|
||||||
|
'src/modules/launcher/' = 'PowerToys Run'
|
||||||
|
'src/modules/QuickAccent/' = 'Quick Accent'
|
||||||
|
'src/modules/RegistryPreview/' = 'Registry Preview'
|
||||||
|
'src/modules/MeasureTool/' = 'Screen Ruler'
|
||||||
|
'src/modules/ShortcutGuide/' = 'Shortcut Guide'
|
||||||
|
'src/modules/TextExtractor/' = 'Text Extractor'
|
||||||
|
'src/modules/videoconference/' = 'Video Conference Mute'
|
||||||
|
'src/modules/Workspaces/' = 'Workspaces'
|
||||||
|
'src/modules/ZoomIt/' = 'ZoomIt'
|
||||||
|
'src/settings-ui/' = 'Settings'
|
||||||
|
'src/runner/' = 'Runner'
|
||||||
|
'installer/' = 'Installer'
|
||||||
|
'src/common/' = 'General'
|
||||||
|
'.github/' = 'Development'
|
||||||
|
'tools/' = 'Development'
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($pattern in $moduleMap.Keys) {
|
||||||
|
if ($filePath -like "*$pattern*") {
|
||||||
|
return $moduleMap[$pattern]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'General'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: categorize a PR by its changed files
|
||||||
|
$files = gh pr view 12345 --repo microsoft/PowerToys --json files --jq '.files[].path'
|
||||||
|
$modules = $files | ForEach-Object { Get-ModuleFromPath $_ } | Sort-Object -Unique
|
||||||
|
Write-Host "PR affects modules: $($modules -join ', ')"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Organization
|
||||||
|
|
||||||
|
Modules should be listed in **alphabetical order** in the changelog:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Advanced Paste
|
||||||
|
- ...
|
||||||
|
|
||||||
|
### Awake
|
||||||
|
- ...
|
||||||
|
|
||||||
|
### Command Palette
|
||||||
|
- ...
|
||||||
|
|
||||||
|
### General
|
||||||
|
- ...
|
||||||
|
|
||||||
|
### Development
|
||||||
|
- ...
|
||||||
|
```
|
||||||
121
.github/skills/changelog-generator/sub-skills/progress-tracking.md
vendored
Normal file
121
.github/skills/changelog-generator/sub-skills/progress-tracking.md
vendored
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# Progress Tracking for Large Changelogs
|
||||||
|
|
||||||
|
This sub-skill provides the checkpoint mechanism for processing many commits without losing progress.
|
||||||
|
|
||||||
|
## When to Use Progress Tracking
|
||||||
|
|
||||||
|
- Processing 50+ commits
|
||||||
|
- Long-running changelog generation
|
||||||
|
- Risk of context overflow in AI conversations
|
||||||
|
|
||||||
|
## Checkpoint Mechanism
|
||||||
|
|
||||||
|
1. **Before starting**: Create a progress tracking file `release-notes-progress.md`
|
||||||
|
2. **After each batch**: Append processed results to `release-change-note-draft.md`
|
||||||
|
3. **Track position**: Record the last processed commit SHA in `release-notes-progress.md`
|
||||||
|
|
||||||
|
## Progress File Template
|
||||||
|
|
||||||
|
Create `release-notes-progress.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Release Notes Generation Progress
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
- Start Tag: v0.96.0
|
||||||
|
- End Tag: v0.96.1
|
||||||
|
- Total Commits: 127
|
||||||
|
- Batch Size: 20
|
||||||
|
|
||||||
|
## Progress Tracker
|
||||||
|
| Batch | Status | Last SHA | PRs Processed |
|
||||||
|
|-------|--------|----------|---------------|
|
||||||
|
| 1 (1-20) | ✅ Done | abc1234 | #1001, #1002, #1003... |
|
||||||
|
| 2 (21-40) | ✅ Done | def5678 | #1004, #1005... |
|
||||||
|
| 3 (41-60) | 🔄 In Progress | ghi9012 | #1006... |
|
||||||
|
| 4 (61-80) | ⏳ Pending | - | - |
|
||||||
|
| 5 (81-100) | ⏳ Pending | - | - |
|
||||||
|
| 6 (101-120) | ⏳ Pending | - | - |
|
||||||
|
| 7 (121-127) | ⏳ Pending | - | - |
|
||||||
|
|
||||||
|
## Processed PRs (deduplication list)
|
||||||
|
#1001, #1002, #1003, #1004, #1005, #1006
|
||||||
|
|
||||||
|
## Last Checkpoint
|
||||||
|
- Timestamp: 2025-01-07 10:30:00
|
||||||
|
- Last processed commit: ghi9012
|
||||||
|
- Next commit to process: jkl3456
|
||||||
|
```
|
||||||
|
|
||||||
|
## Batch Processing Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 1. Get total commit count and create progress file │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 2. Filter: Skip commits already in start tag │
|
||||||
|
│ - Check if commit is ancestor of start tag │
|
||||||
|
│ - Skip cherry-picks or backports already released │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 3. Process batch of 15-20 commits │
|
||||||
|
│ - Fetch commit details │
|
||||||
|
│ - Get associated PRs │
|
||||||
|
│ - Generate changelog entries │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 4. CHECKPOINT: Save progress │
|
||||||
|
│ - Append entries to release-change-note-draft.md │
|
||||||
|
│ - Update release-notes-progress.md with last SHA │
|
||||||
|
│ - Record processed PR numbers │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 5. Check: More commits remaining? │
|
||||||
|
│ YES → Go to step 3 with next batch │
|
||||||
|
│ NO → Go to step 6 │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 6. Final merge and formatting │
|
||||||
|
│ - Combine all batches │
|
||||||
|
│ - Deduplicate by PR number │
|
||||||
|
│ - Sort by module alphabetically │
|
||||||
|
│ - Add highlights section │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resuming from Checkpoint
|
||||||
|
|
||||||
|
If interrupted, read `release-notes-progress.md` to find:
|
||||||
|
1. Which batch was last completed
|
||||||
|
2. The SHA of the last processed commit
|
||||||
|
3. Which PRs have already been processed (for deduplication)
|
||||||
|
|
||||||
|
Then continue from the next unprocessed commit:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Find where you left off
|
||||||
|
$lastSha = "abc1234" # from progress file
|
||||||
|
$remainingShas = gh api repos/microsoft/PowerToys/compare/$lastSha...main --jq '.commits[].sha'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Batch Size Recommendations
|
||||||
|
|
||||||
|
| Total Commits | Recommended Batch Size |
|
||||||
|
|---------------|------------------------|
|
||||||
|
| < 30 | Process all at once |
|
||||||
|
| 30-100 | 15-20 per batch |
|
||||||
|
| 100-300 | 20 per batch |
|
||||||
|
| 300+ | 25 per batch + parallel processing |
|
||||||
|
|
||||||
|
## Deduplication
|
||||||
|
|
||||||
|
Track processed PR numbers to avoid duplicates:
|
||||||
|
- Same PR can appear multiple times (multiple commits)
|
||||||
|
- Cherry-picks may reference same PR
|
||||||
|
- Always check `Processed PRs` list before generating entry
|
||||||
119
.github/skills/changelog-generator/sub-skills/user-facing-description.md
vendored
Normal file
119
.github/skills/changelog-generator/sub-skills/user-facing-description.md
vendored
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# Generating User-Facing Descriptions
|
||||||
|
|
||||||
|
This sub-skill explains how to transform technical PR/commit info into user-friendly changelog entries.
|
||||||
|
|
||||||
|
## Information Sources (Priority Order)
|
||||||
|
|
||||||
|
1. **PR Title** - Often the best summary, already user-facing
|
||||||
|
2. **PR Description/Body** - Look for "What does this PR do?" or summary sections
|
||||||
|
3. **Commit Message** - First line is usually descriptive
|
||||||
|
4. **Changed Files** - Infer the impact from what was modified
|
||||||
|
5. **PR Labels** - Indicate type (bug, feature, enhancement)
|
||||||
|
|
||||||
|
## Data Collection Command
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Get all relevant info for a PR
|
||||||
|
$prNumber = 12345
|
||||||
|
$prData = gh pr view $prNumber --repo microsoft/PowerToys --json title,body,author,files,labels,mergedAt
|
||||||
|
|
||||||
|
# Parse the data
|
||||||
|
$pr = $prData | ConvertFrom-Json
|
||||||
|
Write-Host "Title: $($pr.title)"
|
||||||
|
Write-Host "Author: $($pr.author.login)"
|
||||||
|
Write-Host "Labels: $($pr.labels.name -join ', ')"
|
||||||
|
Write-Host "Files changed: $($pr.files.Count)"
|
||||||
|
Write-Host "Body preview: $($pr.body.Substring(0, [Math]::Min(500, $pr.body.Length)))"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Transformation Rules
|
||||||
|
|
||||||
|
| Source Info | Transformation | Example Output |
|
||||||
|
|-------------|----------------|----------------|
|
||||||
|
| PR Title: "Fix null reference in FancyZones editor" | Describe the fix from user perspective | "Fixed a crash that could occur when editing zone layouts." |
|
||||||
|
| PR Title: "Add support for XYZ format" | State the new capability | "Added support for XYZ format in File Explorer preview." |
|
||||||
|
| PR Title: "[FancyZones] Refactor grid logic" | Check if user-visible; if not → Development section | (Development) "Refactored FancyZones grid logic for improved maintainability." |
|
||||||
|
| PR with label "bug" | Frame as a fix | "Fixed an issue where..." |
|
||||||
|
| PR with label "enhancement" | Frame as improvement | "Improved..." or "Enhanced..." |
|
||||||
|
|
||||||
|
## Description Generation Process
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────────────────────────────┐
|
||||||
|
│ INPUT: PR #12345 │
|
||||||
|
│ Title: "Fix Awake timer drift after system sleep" │
|
||||||
|
│ Body: "The timer was resetting incorrectly when the system │
|
||||||
|
│ resumed from sleep, causing the countdown to be wrong." │
|
||||||
|
│ Author: @daverayment │
|
||||||
|
│ Labels: [bug, Product-Awake] │
|
||||||
|
└────────────────────────────────────────────────────────────────┘
|
||||||
|
▼
|
||||||
|
┌────────────────────────────────────────────────────────────────┐
|
||||||
|
│ ANALYSIS: │
|
||||||
|
│ 1. Is it user-facing? YES (timer behavior affects users) │
|
||||||
|
│ 2. Category: Bug fix │
|
||||||
|
│ 3. Module: Awake (from label + file paths) │
|
||||||
|
│ 4. Author in core team? NO → needs attribution │
|
||||||
|
└────────────────────────────────────────────────────────────────┘
|
||||||
|
▼
|
||||||
|
┌────────────────────────────────────────────────────────────────┐
|
||||||
|
│ OUTPUT: │
|
||||||
|
│ "The Awake countdown timer now stays accurate over long │
|
||||||
|
│ periods. Thanks [@daverayment](https://github.com/daverayment)!" │
|
||||||
|
└────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Writing Style Guidelines
|
||||||
|
|
||||||
|
**DO:**
|
||||||
|
- Start with what changed or what users can now do
|
||||||
|
- Use active voice: "Added...", "Fixed...", "Improved..."
|
||||||
|
- Be specific about the benefit to users
|
||||||
|
- Keep it concise (1-2 sentences max)
|
||||||
|
- Include link to documentation if it's a new feature
|
||||||
|
|
||||||
|
**DON'T:**
|
||||||
|
- Use technical jargon users won't understand
|
||||||
|
- Reference internal code names, file names, or class names
|
||||||
|
- Say "Fixed bug" without explaining what was wrong
|
||||||
|
- Include PR numbers in the description (they're tracked separately)
|
||||||
|
|
||||||
|
## Example Transformations
|
||||||
|
|
||||||
|
| Technical PR Title | User-Facing Description |
|
||||||
|
|--------------------|------------------------|
|
||||||
|
| "Fix NRE in FZEditor when zones is null" | "Fixed a crash that could occur when opening the FancyZones editor with no saved layouts." |
|
||||||
|
| "Add ExifTool integration for PowerRename" | "PowerRename can now extract and use photo metadata (EXIF, XMP) in renaming patterns like `%Camera`, `%Lens`, and `%ExposureTime`." |
|
||||||
|
| "Perf: Reduce memory allocation in PT Run" | "Improved PowerToys Run startup performance and reduced memory usage." |
|
||||||
|
| "Update Newtonsoft.Json to 13.0.3" | (Development) "Updated Newtonsoft.Json dependency." OR skip if no user impact |
|
||||||
|
| "Add unit tests for color picker" | SKIP - not user-facing |
|
||||||
|
| "Fix typo in settings UI" | "Fixed a typo in the Settings interface." (only if visible to users) |
|
||||||
|
|
||||||
|
## Context-Aware Description
|
||||||
|
|
||||||
|
Sometimes you need to read the PR body or changed files to understand the impact:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# If PR title is unclear, check the body for context
|
||||||
|
$prBody = gh pr view 12345 --repo microsoft/PowerToys --json body --jq '.body'
|
||||||
|
|
||||||
|
# Look for common patterns in PR descriptions:
|
||||||
|
# - "## Summary" or "## Description"
|
||||||
|
# - "This PR fixes/adds/improves..."
|
||||||
|
# - "Before/After" comparisons
|
||||||
|
# - Screenshots (indicate UI changes)
|
||||||
|
|
||||||
|
# Check what files changed to understand scope
|
||||||
|
$files = gh pr view 12345 --repo microsoft/PowerToys --json files --jq '.files[].path'
|
||||||
|
# If mostly .xaml files → UI change
|
||||||
|
# If mostly .cs/.cpp in one module → module-specific change
|
||||||
|
# If in src/common/ → potentially affects multiple modules
|
||||||
|
```
|
||||||
|
|
||||||
|
## Handling Ambiguous PRs
|
||||||
|
|
||||||
|
If a PR's impact is unclear:
|
||||||
|
1. **Check the linked issue** - often has user-reported symptoms
|
||||||
|
2. **Look at file changes** - understand what was modified
|
||||||
|
3. **Check PR comments** - may have discussion about user impact
|
||||||
|
4. **When in doubt** - put in Development section or ask for clarification
|
||||||
Reference in New Issue
Block a user