Created
July 8, 2025 11:45
-
-
Save mmagician/2fe8f23eb1bfc94638bd612a94b037dd to your computer and use it in GitHub Desktop.
Convert bare PR numbers in CHANGELOG.md to linkable GitHub PR links.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Script to convert bare PR numbers in CHANGELOG.md to linkable GitHub PR links. | |
This script: | |
- Finds all PR numbers in the format (#1234) or (#1234, #5678, #9012) | |
- Converts them to linkable GitHub PR links: [#1234](https://github.com/owner/repo/pull/1234) | |
- Preserves already-linked PR numbers (won't double-link) | |
- Handles multiple PR numbers per line correctly | |
- Supports configurable GitHub repository | |
Usage: | |
python3 add_pr_links.py --repo <repo_url> --file <filename> | |
Repository must be specified as full GitHub URL: | |
- https://github.com/owner/repo | |
Examples: | |
python3 add_pr_links.py --repo https://github.com/0xMiden/miden-base --file CHANGELOG.md | |
""" | |
import argparse | |
import re | |
import sys | |
def normalize_repo_url(repo_input): | |
""" | |
Normalize repository input to a pull request URL. | |
Args: | |
repo_input: Full GitHub URL "https://github.com/owner/repo" | |
Returns: | |
The base URL for pull requests: "https://github.com/owner/repo/pull" | |
""" | |
if not repo_input.startswith("https://github.com/"): | |
raise ValueError(f"Invalid repository format: {repo_input}. Must be full GitHub URL (https://github.com/owner/repo).") | |
base_url = repo_input.rstrip("/") | |
return f"{base_url}/pull" | |
def process_changelog(filename, repo_url): | |
"""Process the changelog file to add PR links.""" | |
# Read the file | |
try: | |
with open(filename, 'r', encoding='utf-8') as file: | |
content = file.read() | |
except FileNotFoundError: | |
print(f"Error: File '{filename}' not found.") | |
return False | |
except Exception as e: | |
print(f"Error reading file: {e}") | |
return False | |
# Pattern to match PR numbers that are NOT already linked | |
# This matches (#1234) or (#1234, #5678, #9012) but NOT ([#1234](link)) | |
pattern = r'(?<!\[)#(\d+)(?!\]\([^)]+\))' | |
def replace_pr_number(match): | |
"""Replace a single PR number with a linked version.""" | |
pr_number = match.group(1) | |
return f'[#{pr_number}]({repo_url}/{pr_number})' | |
# Replace all unlinked PR numbers | |
original_content = content | |
content = re.sub(pattern, replace_pr_number, content) | |
# Count changes made | |
changes_made = len(re.findall(pattern, original_content)) | |
if changes_made == 0: | |
print("No unlinked PR numbers found.") | |
return True | |
# Write the updated content back to file | |
try: | |
with open(filename, 'w', encoding='utf-8') as file: | |
file.write(content) | |
print(f"Successfully updated {changes_made} PR number(s) in {filename}") | |
print(f"Using repository: {repo_url}") | |
return True | |
except Exception as e: | |
print(f"Error writing file: {e}") | |
return False | |
def main(): | |
"""Main function.""" | |
parser = argparse.ArgumentParser( | |
description="Convert bare PR numbers in changelog to linkable GitHub PR links.", | |
formatter_class=argparse.RawDescriptionHelpFormatter, | |
epilog=""" | |
Examples: | |
python3 %(prog)s --repo https://github.com/0xMiden/miden-base --file CHANGELOG.md | |
Repository format: | |
- Full GitHub URL (e.g., "https://github.com/owner/repo") | |
""" | |
) | |
parser.add_argument( | |
'--repo', '-r', | |
required=True, | |
help='GitHub repository URL (https://github.com/owner/repo)' | |
) | |
parser.add_argument( | |
'--file', '-f', | |
required=True, | |
help='Changelog filename' | |
) | |
args = parser.parse_args() | |
try: | |
repo_url = normalize_repo_url(args.repo) | |
except ValueError as e: | |
print(f"Error: {e}") | |
sys.exit(1) | |
success = process_changelog(args.file, repo_url) | |
sys.exit(0 if success else 1) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment