mcpbeat

Generate Release Notes

azure/generate-release-notes

> Auto-generate release notes from a list of PRs for Azure Functions Python Worker components (worker, runtime v1, or runtime v2). Analyzes PR file changes to determine component relevance, groups by category/prefix, and outputs organized release notes. Use when the user asks to "generate release notes", "create release notes from PRs", "format these PRs for release", or provides a list of PRs to organize.

9k tokens
context cost
the whole folder, loaded on every use
3
files
instructions only
0
copies elsewhere
how many repositories repackaged it
358
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/Azure/azure-functions-python-worker --skill generate-release-notes

What comes with it

11 168 bytes besides the instruction
QUICKSTART.md
category-reference.md

The instruction itself

19 sections, as written by the author

Generate Release Notes

Auto-generate organized release notes from a list of PRs for Azure Functions Python Worker components.

Workflow

1. Parse Input

Accept:

  • List of PRs: Each PR should include:
  • PR title (with prefix like fix:, feat:, build:, etc.)
  • PR link (full GitHub URL or #number)
  • PR author (GitHub username)
  • Component: One of:
  • worker - Python Worker component
  • runtime v1 - Runtime V1 component
  • runtime v2 - Runtime V2 component

Example input format:

## What's Changed
* fix: fix protobuf import for V2 by @hallvictoria in https://github.com/Azure/azure-functions-python-worker/pull/1736
* feat: allow event loop to be uvloop by @EvanR-Dev in https://github.com/Azure/azure-functions-python-worker/pull/1697
...

2. Extract PR Information

For each PR in the list, extract:

  • PR number (from URL or #number)
  • PR title
  • PR author
  • PR link

Store this information for later use.

3. Analyze PR File Changes

For each PR, determine which files were changed:

Using GitHub CLI:

gh pr view <number> --json files | ConvertFrom-Json

This returns the list of changed files in the PR.

4. Determine Component Relevance

For each PR, analyze the changed files and classify based on paths:

| Path Pattern | Component | Rule |

|-------------|-----------|------|

| runtimes/v1/** | runtime v1 | Any file inside runtimes/v1/ directory |

| runtimes/v2/** | runtime v2 | Any file inside runtimes/v2/ directory |

| workers/** | worker | Any file inside workers/ directory |

| Other paths | all | Changes outside these directories affect all components |

Component matching logic:

  • If only runtimes/v1/ files changed → runtime v1 only
  • If only runtimes/v2/ files changed → runtime v2 only
  • If only workers/ files changed → worker only
  • If files from multiple directories changed → all relevant components
  • If files outside these three directories changed → all components (worker, runtime v1, runtime v2)

Worker-specific exclusions:

Some PRs should ONLY appear in worker releases, even if they change root-level files:

  • Azure Functions SDK version updates - PRs that update the azure-functions package version in workers/pyproject.toml
  • Example title patterns: "Update Python SDK Version to X.Y.Z"
  • File changed: workers/pyproject.toml (dependency update)
  • Rule: Include ONLY in worker releases
  • Worker version updates - PRs that update the worker version in workers/*/version.py
  • Example title patterns: "Update version to X.Y.Z"
  • Files changed: workers/azure_functions_worker/version.py or similar
  • Rule: Include ONLY in worker releases

Detection logic for worker-specific PRs:

If PR title matches "Update Python SDK Version" OR "Update version to":
  Check if files changed include:
    - workers/pyproject.toml (for SDK updates)
    - workers/*/version.py (for worker version updates)
  If yes:
    - Include in worker release ONLY
    - Exclude from runtime v1 and runtime v2 releases

Filter PRs based on the user-specified component:

  • Keep PRs that affect the specified component
  • Exclude PRs that don't touch any files in that component's scope
  • For runtime v1/v2 releases: Also exclude worker-specific version update PRs

5. Extract PR Category from Title

Parse the PR title prefix to determine the category:

| Prefix | Category | Description |

|--------|----------|-------------|

| feat: | Features | New features or capabilities |

| fix: | Bug Fixes | Bug fixes and corrections |

| build: | Build & Dependencies | Version updates, dependency changes, build configuration |

| refactor: | Refactoring | Code restructuring without behavior changes |

| test: | Tests | Test additions or modifications |

| chore: | Chores | Maintenance tasks, cleanup |

| docs: | Documentation | Documentation updates |

| perf: | Performance | Performance improvements |

| ci: | CI/CD | Continuous integration/deployment changes |

| style: | Style | Code style, formatting |

| revert: | Reverts | Reverting previous changes |

If no recognized prefix is found, use the first word or classify as "Other".

6. Extract Runtime and SDK Versions (Worker Releases Only)

For worker component releases only, read the current versions from workers/pyproject.toml:

Extract the versions of these packages:

  • azure-functions-runtime
  • azure-functions-runtime-v1
  • azure-functions (with Python version conditions)

Important: The azure-functions SDK has different versions for different Python version ranges. Extract all variants.

Using PowerShell:

$pyprojectContent = Get-Content workers/pyproject.toml -Raw

# Extract runtime versions (single version each)
$runtimeMatch = [regex]::Match($pyprojectContent, 'azure-functions-runtime==([^;"\s]+)')
$runtimeV1Match = [regex]::Match($pyprojectContent, 'azure-functions-runtime-v1==([^;"\s]+)')

# Extract all azure-functions SDK versions with their Python version conditions
$sdkMatches = [regex]::Matches($pyprojectContent, '"azure-functions==([^;"]+);\s*([^"]+)"')
$sdkVersions = @()
foreach ($match in $sdkMatches) {
    $version = $match.Groups[1].Value
    $condition = $match.Groups[2].Value
    $sdkVersions += @{Version=$version; Condition=$condition}
}

Using Python:

import re
with open('workers/pyproject.toml', 'r') as f:
    content = f.read()
    
    # Extract runtime versions
    runtime = re.search(r'azure-functions-runtime==([^;"\s]+)', content)
    runtime_v1 = re.search(r'azure-functions-runtime-v1==([^;"\s]+)', content)
    
    # Extract all azure-functions versions with Python conditions
    sdk_pattern = r'"azure-functions==([^;"]+);\s*([^"]+)"'
    sdk_matches = re.findall(sdk_pattern, content)
    # sdk_matches = [(version, condition), ...]
    # Example: [('1.24.0', "python_version < '3.10'"), ('1.25.0b4', "python_version >= '3.10' and python_version < '3.13'")]

Format for output:

  • Runtime versions: single line each
  • SDK versions: one line per Python version range with the condition in a human-readable format

Store these versions for inclusion in the output.

Note: Skip this step for runtime v1 and runtime v2 releases.

7. Classify PRs by Python Version (Worker Releases Only)

For worker component releases only, classify each PR by which Python versions it affects:

Python version classification rules:

  • Special case - Worker version updates:
  • PRs with title "Update version to 4.X.X" (worker component version)
  • These go in a separate "Worker Version" section
  • Example: "Update version to 4.43.0"
  • Check if PR touches worker code:
  • If PR does NOT change any files under workers/ directory → General/Build section
  • These are repo-wide changes that don't affect worker code directly
  • Examples: root-level config changes, CI/CD changes, documentation
  • Check file paths for worker code changes:
  • Python <= 3.12 only: PR changes ONLY files under workers/azure_functions_worker/
  • Python 3.13+ only: PR changes ONLY files under workers/proxy_worker/
  • Both: PR changes files in both directories, OR changes files under workers/ that affect both (e.g., workers/pyproject.toml, workers/README.md)
  • Special handling for SDK version updates:
  • PRs with title "Update Python SDK Version to X.Y.Z" need additional classification
  • Read workers/pyproject.toml to determine which Python version range uses this version
  • Example: If updating to 2.0.0 and pyproject shows azure-functions==2.0.0; python_version >= '3.13', then this PR is Python 3.13+ only
  • Example: If updating to 1.25.0b4 and pyproject shows azure-functions==1.25.0b4; python_version >= '3.10' and python_version < '3.13', then this PR is Python <= 3.12 only

Classification logic:

for pr in filtered_prs:
    files = get_pr_files(pr.number)
    
    # Check if it's a worker version update
    if re.match(r'[Uu]pdate\s+version\s+to\s+4\.\d+\.\d+', pr.title):
        pr.section = 'worker_version'
        continue
    
    # Check if PR touches workers/ directory at all
    touches_workers = any(f.startswith('workers/') for f in files)
    
    if not touches_workers:
        # General/build changes that don't touch worker code
        pr.section = 'general'
        continue
    
    # Check if it's an SDK version update
    if 'Update Python SDK Version' in pr.title:
        version = extract_version_from_title(pr.title)
        pyproject = read_pyproject()
        python_range = find_python_range_for_sdk_version(pyproject, version)
        
        if '3.13' in python_range and '3.10' not in python_range:
            pr.python_versions = ['3.13+']
        elif '3.10' in python_range or '3.12' in python_range:
            pr.python_versions = ['<=3.12']
        else:
            pr.python_versions = ['<=3.12', '3.13+']
    else:
        # Regular file-based classification for worker code
        affects_legacy = any(f.startswith('workers/azure_functions_worker/') for f in files)
        affects_proxy = any(f.startswith('workers/proxy_worker/') for f in files)
        affects_workers_general = any(
            f.startswith('workers/') and 
            not f.startswith('workers/azure_functions_worker/') and 
            not f.startswith('workers/proxy_worker/') 
            for f in files
        )
        
        if affects_legacy and not affects_proxy and not affects_workers_general:
            pr.python_versions = ['<=3.12']
        elif affects_proxy and not affects_legacy and not affects_workers_general:
            pr.python_versions = ['3.13+']
        else:
            # Both directories or workers/ general files
            pr.python_versions = ['<=3.12', '3.13+']

Notes:

  • PRs affecting both worker directories appear in both Python version sections
  • PRs affecting workers/ general files (e.g., workers/pyproject.toml) appear in both Python sections
  • PRs NOT touching workers/ at all go in the "General" section
  • Worker version update PRs go in the "Worker Version" section
  • SDK version updates are classified based on the Python version range in pyproject.toml
  • This classification is ONLY for worker releases, not runtime releases

8. Group PRs by Category

Organize filtered PRs into categories based on their prefix.

Recommended category order for output:

  • Features
  • Bug Fixes
  • Build & Dependencies
  • Refactoring
  • Tests
  • Chores
  • Other categories (alphabetically)

9. Deduplicate Version Update PRs

For Build & Dependencies category only, deduplicate PRs that update the same package multiple times:

Package patterns to check:

  • Update Python SDK Version to X.Y.Z → package: azure-functions
  • Update Python Runtime Version to X.Y.Z → package: azure-functions-runtime
  • Update version to X.Y.Z (runtime v1) → package: azure-functions-runtime-v1
  • Update azurefunctions-extensions-<name> version to X.Y.Z → package: azurefunctions-extensions-<name>
  • Update <package-name> version to X.Y.Z → package: <package-name>

Deduplication logic:

  • Parse each PR title in the Build & Dependencies category
  • Extract the package name using these patterns:
  • Title contains "Python SDK Version" → azure-functions
  • Title contains "Python Runtime Version" and not "v1" → azure-functions-runtime
  • Title contains "Python Runtime Version" and "v1" → azure-functions-runtime-v1
  • Title matches "Update azurefunctions-extensions-<name> version" → extract azurefunctions-extensions-<name>
  • Title matches "Update <package> version" → extract package name
  • Group PRs by package name
  • For each package with multiple updates, keep only the PR with the highest PR number (most recent)
  • Discard older version updates for the same package

Example:

Input PRs (Build & Dependencies):
- Update Python SDK Version to 1.24.0b4 (#1755)
- Update Python SDK Version to 1.25.0b2 (#1798)
- Update Python SDK Version to 1.25.0b3 (#1822)
- Update azurefunctions-extensions-blob version to 1.1.1 (#1762)
- Update azurefunctions-extensions-blob version to 1.1.2 (#1821)

After deduplication:
- Update Python SDK Version to 1.25.0b3 (#1822) ✓ (highest PR for azure-functions)
- Update azurefunctions-extensions-blob version to 1.1.2 (#1821) ✓ (highest PR for extensions-blob)

Removed:
- #1755, #1798 (older azure-functions updates)
- #1762 (older extensions-blob update)

Implementation approach:

# Pseudocode
package_updates = {}
for pr in build_dependency_prs:
    package_name = extract_package_name(pr.title)
    if package_name:
        if package_name not in package_updates or pr.number > package_updates[package_name].number:
            package_updates[package_name] = pr

# Keep only the deduplicated PRs
deduplicated_prs = list(package_updates.values())

Note: Only apply deduplication to PRs that match version update patterns. Keep other build/dependency PRs as-is.

10. Format Output

Generate clean, human-readable release notes in Markdown format.

For worker releases:

# Release Notes

## General

### Features
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

### Bug Fixes
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

### Build & Dependencies
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

[... other categories ...]

## Worker Version
* <title without prefix> ([#<number>](<link>)) - @<author>

## Python <= 3.12

### Features
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

### Bug Fixes
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

### Build & Dependencies
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

### Refactoring
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

### Tests
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

### Chores
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

## Python 3.13+

### Features
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

### Bug Fixes
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

### Build & Dependencies
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

### Refactoring
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

### Tests
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

### Chores
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

## Runtime and SDK Versions
azure-functions-runtime==<version>
azure-functions-runtime-v1==<version>
azure-functions==<version> (Python < 3.10)
azure-functions==<version> (Python 3.10-3.12)
azure-functions==<version> (Python 3.13+)

For runtime v1/v2 releases:

# Release Notes

## Features
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

## Bug Fixes
* <title without prefix> ([#<number>](<link>)) - @<author>
* ...

[... other categories ...]

Formatting rules:

  • Worker releases only:
  • Section order:
  • General: PRs not touching workers/ directory (optional - only if PRs exist)
  • Worker Version: Worker version update PRs (e.g., "Update version to 4.43.0")
  • Python <= 3.12: PRs affecting azure_functions_worker/
  • Python 3.13+: PRs affecting proxy_worker/
  • Runtime and SDK Versions: At the END
  • Within each section (except Worker Version and Runtime sections), organize by category (Features, Bug Fixes, etc.)
  • PRs affecting both Python worker directories appear in both Python sections
  • Only include sections and categories that have PRs
  • Remove the prefix from the title (e.g., fix: )
  • Capitalize the first letter of the title
  • Include PR link in standard format #number
  • Include author attribution with @username
  • Sort PRs within each category by PR number (ascending)

11. Save to Temporary File

Save the formatted release notes to a temporary markdown file for easy copying:

File location:

  • Windows: $env:TEMP\release-notes-<component>.md
  • Linux/Mac: /tmp/release-notes-<component>.md

Example:

  • Worker release: release-notes-worker.md
  • Runtime v1 release: release-notes-runtime-v1.md
  • Runtime v2 release: release-notes-runtime-v2.md

Using PowerShell:

$component = "worker"  # or "runtime-v1" or "runtime-v2"
$outputPath = Join-Path $env:TEMP "release-notes-$component.md"
$releaseNotes | Out-File -FilePath $outputPath -Encoding utf8
Write-Host "Release notes saved to: $outputPath"

Using Python:

import tempfile
import os

component = "worker"  # or "runtime-v1" or "runtime-v2"
temp_dir = tempfile.gettempdir()
output_path = os.path.join(temp_dir, f"release-notes-{component}.md")

with open(output_path, 'w', encoding='utf-8') as f:
    f.write(release_notes)
    
print(f"Release notes saved to: {output_path}")

After saving:

  • Display the file path to the user
  • Optionally open the file in the default editor or VS Code
  • User can easily copy the contents from the file

12. Handle Edge Cases

PR without recognizable prefix:

  • Place in "Other" category
  • Keep original title

PR affects multiple components:

  • Include in all relevant component release notes
  • Note in output if the same PR appears in multiple components

Large PR lists (>30 PRs):

  • Process in batches to avoid rate limits
  • Show progress updates to user

Authentication errors:

  • Check gh auth status
  • Prompt user to authenticate if needed

PR not found:

  • Note the PR number that couldn't be fetched
  • Continue with remaining PRs
  • Report missing PRs at the end

Example Workflow

Example 1: Worker Release

User provides:

Component: worker

PRs:
* fix: fix protobuf import by @user1 in https://github.com/org/repo/pull/100
* feat: add new feature for proxy worker by @user2 in https://github.com/org/repo/pull/101
* fix: legacy worker bug fix by @user3 in https://github.com/org/repo/pull/105
* build: update version to 4.40.0 by @user4 in https://github.com/org/repo/pull/102
* build: update Python SDK Version to 1.24.0b4 by @user5 in https://github.com/org/repo/pull/103
* build: update Python SDK Version to 1.25.0b2 by @user6 in https://github.com/org/repo/pull/150
* build: update Python SDK Version to 1.25.0b3 by @user7 in https://github.com/org/repo/pull/160
* build: update pyproject for azure-functions 2.x structure by @user8 in https://github.com/org/repo/pull/200

Agent:

  • Extracts PR numbers: 100, 101, 102, 103, 105, 150, 160, 200
  • Fetches file changes for each PR
  • PR #100: Changed setup.cfg (root level, not in workers/) → General section
  • PR #101: Changed workers/proxy_worker/handler.py → Python 3.13+ only
  • PR #102: Changed workers/azure_functions_worker/version.py + title matches \"Update version to 4.X.X\" → Worker Version section
  • PR #103, #150, #160: Changed workers/pyproject.toml (SDK updates) → Check version in pyproject
  • PR #105: Changed workers/azure_functions_worker/dispatcher.py → Python <= 3.12 only
  • PR #200: Changed .github/workflows/ci.yml (not in workers/) → General section
  • Classifies PRs by section:\n - PR #100: Root-level file, not in workers/ → General section\n - PR #101: Only proxy_worker/ → Python 3.13+ only\n - PR #102: Title \"Update version to 4.40.0\" → Worker Version section\n - PR #103, #150, #160: SDK updates - check pyproject.toml:\n - If version 1.25.0b3 is for Python 3.10-3.12 → Python <= 3.12 only\n - PR #105: Only azure_functions_worker/ → Python <= 3.12 only\n - PR #200: CI file, not in workers/ → General section\n10. Deduplicates version updates:\n - Package azure-functions: PRs #103, #150, #160\n - Keeps only #160 (highest PR number)\n - Removes #103 and #150\n11. Reads workers/pyproject.toml to extract versions:\n - azure-functions-runtime==1.1.0\n - azure-functions-runtime-v1==1.1.0\n - azure-functions==1.24.0 (Python < 3.10)\n - azure-functions==1.25.0b4 (Python 3.10-3.12)\n - azure-functions==2.0.0 (Python 3.13+)\n12. Groups by section and category, then outputs:\n\nmarkdown\n# Release Notes\n\n## General\n\n### Bug Fixes\n* Fix protobuf import (#100) - @user1\n\n### Build & Dependencies\n* Update pyproject for azure-functions 2.x structure (#200) - @user8\n\n## Worker Version\n* Update version to 4.40.0 (#102) - @user4\n\n## Python <= 3.12\n\n### Bug Fixes\n* Legacy worker bug fix (#105) - @user3\n\n### Build & Dependencies\n* Update Python SDK Version to 1.25.0b3 (#160) - @user7\n\n## Python 3.13+\n\n### Features\n* Add new feature for proxy worker (#101) - @user2\n\n## Runtime and SDK Versions\nazure-functions-runtime==1.1.0\nazure-functions-runtime-v1==1.1.0\nazure-functions==1.24.0 (Python < 3.10)\nazure-functions==1.25.0b4 (Python 3.10-3.12)\nazure-functions==2.0.0 (Python 3.13+)\n\n\nNote: \n- PR #100 and #200 in General section (don't touch workers/ directory)\n- PR #102 in Worker Version section (matches \"Update version to 4.X.X\")\n- PR #105 only in Python <= 3.12 (only affects azure_functions_worker/)\n- PR #101 only in Python 3.13+ (only affects proxy_worker/)\n- PR #160 only in Python <= 3.12 (SDK version 1.25.0b3 is for Python 3.10-3.12)\n- PRs #103 and #150 were excluded due to deduplication\n- Runtime and SDK Versions section is at the END

Example 2: Runtime V2 Release with Deduplication

User provides:

Component: runtime v2

PRs:
* fix: fix protobuf import by @user1 in https://github.com/org/repo/pull/100
* feat: add new feature by @user2 in https://github.com/org/repo/pull/101
* build: update version to 1.0.0b1 by @user3 in https://github.com/org/repo/pull/110
* build: update version to 1.0.0b2 by @user4 in https://github.com/org/repo/pull/120
* build: update version to 1.1.0b1 by @user5 in https://github.com/org/repo/pull/130
* build: update azurefunctions-extensions-blob version to 1.1.1 by @user6 in https://github.com/org/repo/pull/140
* build: update azurefunctions-extensions-blob version to 1.1.2 by @user7 in https://github.com/org/repo/pull/145
* build: update Python SDK Version to 1.24.0 by @user8 in https://github.com/org/repo/pull/150

Agent:

  • Extracts PR numbers: 100, 101, 110, 120, 130, 140, 145, 150
  • Fetches file changes for each PR
  • PR #100: Changed setup.cfg (root level) → all components ✓
  • PR #101: Changed runtimes/v2/handler.py → runtime v2 ✓
  • PR #110, #120, #130: Changed runtimes/v2/version.py → runtime v2 ✓
  • PR #140, #145: Changed runtimes/v2/pyproject.toml (extensions-blob) → runtime v2 ✓
  • PR #150: Changed workers/pyproject.toml (azure-functions) → worker-specific only ✗ (exclude)
  • Deduplicates version updates:
  • Package azure-functions-runtime-v2: PRs #110, #120, #130 → Keeps only #130
  • Package azurefunctions-extensions-blob: PRs #140, #145 → Keeps only #145
  • Groups by category and outputs:
# Release Notes

## Features
* Add new feature ([#101](link)) - @user2

## Bug Fixes
* Fix protobuf import ([#100](link)) - @user1

## Build & Dependencies
* Update version to 1.1.0b1 ([#130](link)) - @user5
* Update azurefunctions-extensions-blob version to 1.1.2 ([#145](link)) - @user7

Notes:

  • PRs #110 and #120 were excluded (older azure-functions-runtime-v2 updates)
  • PR #140 was excluded (older azurefunctions-extensions-blob update)
  • PR #150 was excluded (worker-specific azure-functions SDK update)

Tips for Best Results

  • Ensure PRs follow conventional commit format with prefixes
  • Have GitHub CLI installed and authenticated (gh auth login)
  • Provide clear component name (exact match: worker, runtime v1, or runtime v2)
  • For PRs from different repos, include full URLs
  • If the repo is not the current workspace, the agent will need to clone or access it
  • The release notes will be saved to a temporary file for easy copying: $env:TEMP\release-notes-<component>.md (Windows) or /tmp/release-notes-<component>.md (Linux/Mac)

Notes

  • This skill is specific to the Azure Functions Python Worker repository structure
  • Assumes repository access permissions for fetching PR data
  • Can be adapted for other repositories by modifying the component path patterns in Step 4

How to use it

Copy the folder

Take azure/generate-release-notes from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.