1+ #!/usr/bin/env python3
2+ """
3+ Script to automatically update the changelog with PR information.
4+ """
5+
6+ import argparse
7+ import re
8+ import sys
9+ from pathlib import Path
10+
11+
12+ def find_unreleased_section (lines ):
13+ """Find the line number where UNRELEASED section starts and ends."""
14+ unreleased_start = None
15+ content_start = None
16+
17+ for i , line in enumerate (lines ):
18+ if line .strip () == "## UNRELEASED" :
19+ unreleased_start = i
20+ continue
21+
22+ if unreleased_start is not None and content_start is None :
23+ # Skip empty lines after ## UNRELEASED
24+ if line .strip () == "" :
25+ continue
26+ else :
27+ content_start = i
28+ break
29+
30+ return unreleased_start , content_start
31+
32+
33+ def update_changelog (changelog_path , pr_number , pr_title , pr_url ):
34+ """Update the changelog with the new PR entry."""
35+
36+ # Read the current changelog
37+ with open (changelog_path , 'r' , encoding = 'utf-8' ) as f :
38+ lines = f .readlines ()
39+
40+ # Find the UNRELEASED section
41+ unreleased_start , content_start = find_unreleased_section (lines )
42+
43+ if unreleased_start is None :
44+ print ("ERROR: Could not find '## UNRELEASED' section in changelog" )
45+ return False
46+
47+ if content_start is None :
48+ print ("ERROR: Could not find content start after UNRELEASED section" )
49+ return False
50+
51+ # Create the new entry
52+ new_entry = f"- { pr_title } [#{ pr_number } ]({ pr_url } )\n "
53+
54+ # Check if this PR entry already exists to avoid duplicates
55+ for line in lines [content_start :]:
56+ if f"[#{ pr_number } ]" in line :
57+ print (f"Changelog entry for PR #{ pr_number } already exists" )
58+ return False
59+ # Stop checking when we reach the next section
60+ if line .startswith ("## " ) and not line .strip () == "## UNRELEASED" :
61+ break
62+
63+ # Insert the new entry at the beginning of the unreleased content
64+ lines .insert (content_start , new_entry )
65+
66+ # Write the updated changelog
67+ with open (changelog_path , 'w' , encoding = 'utf-8' ) as f :
68+ f .writelines (lines )
69+
70+ print (f"Added changelog entry for PR #{ pr_number } : { pr_title } " )
71+ return True
72+
73+
74+ def main ():
75+ parser = argparse .ArgumentParser (description = 'Update changelog with PR information' )
76+ parser .add_argument ('--pr-number' , required = True , help = 'Pull request number' )
77+ parser .add_argument ('--pr-title' , required = True , help = 'Pull request title' )
78+ parser .add_argument ('--pr-url' , required = True , help = 'Pull request URL' )
79+ parser .add_argument ('--changelog-path' , default = 'docs/changelog.md' , help = 'Path to changelog file' )
80+
81+ args = parser .parse_args ()
82+
83+ changelog_path = Path (args .changelog_path )
84+
85+ if not changelog_path .exists ():
86+ print (f"ERROR: Changelog file not found: { changelog_path } " )
87+ sys .exit (1 )
88+
89+ success = update_changelog (changelog_path , args .pr_number , args .pr_title , args .pr_url )
90+
91+ if not success :
92+ sys .exit (1 )
93+
94+
95+ if __name__ == '__main__' :
96+ main ()
0 commit comments