1
1
"""
2
2
Changelog Modifier and Version Bumper for Cargo.toml and Changelog.md
3
3
4
- This script automates the process of version bumping and changelog updates for Rust projects managed with Cargo.
5
- It reads the version from the cargo.toml file, increments it based on the specified component (major, minor, or patch),
4
+ This script automates the process of version bumping and changelog updates for Rust projects managed with Cargo.
5
+ It reads the version from the cargo.toml file, increments it based on the specified component (major, minor, or patch),
6
6
and updates both the cargo.toml and changelog.md files accordingly.
7
7
8
8
It can also add PR entries to the UNRELEASED section of the changelog.
9
9
10
10
Usage:
11
11
Version bumping:
12
12
$ python modify_changelog.py bump_version [major|minor|patch]
13
-
13
+
14
14
Adding PR entry:
15
15
$ python modify_changelog.py update_changelog <number> <title>
16
16
20
20
major - Increments the major component of the version, sets minor and patch to 0
21
21
minor - Increments the minor component of the version, sets patch to 0
22
22
patch - Increments the patch component of the version
23
-
23
+
24
24
update_changelog - Add a PR entry to the UNRELEASED section
25
25
number - PR number
26
26
title - PR title
@@ -59,20 +59,19 @@ def find_unreleased_section(lines):
59
59
"""Find the line number where UNRELEASED section starts and ends."""
60
60
unreleased_start = None
61
61
content_start = None
62
-
62
+
63
63
for i , line in enumerate (lines ):
64
64
if line .strip () == "## UNRELEASED" :
65
65
unreleased_start = i
66
66
continue
67
-
67
+
68
68
if unreleased_start is not None and content_start is None :
69
69
# Skip empty lines after ## UNRELEASED
70
70
if line .strip () == "" :
71
71
continue
72
- else :
73
- content_start = i
74
- break
75
-
72
+ content_start = i
73
+ break
74
+
76
75
return unreleased_start , content_start
77
76
78
77
@@ -87,35 +86,34 @@ def update_changelog_version(file_path: Path, new_version: str) -> None:
87
86
88
87
def update_changelog_pr (file_path : Path , pr_number : str , pr_title : str , pr_url : str ) -> bool :
89
88
"""Update the changelog with the new PR entry. If entry exists, update it; otherwise add new entry."""
90
-
91
89
# Read the current changelog
92
- with open (file_path , 'r' , encoding = ' utf-8' ) as f :
90
+ with open (file_path , encoding = " utf-8" ) as f :
93
91
lines = f .readlines ()
94
-
92
+
95
93
# Find the UNRELEASED section
96
94
unreleased_start , content_start = find_unreleased_section (lines )
97
-
95
+
98
96
if unreleased_start is None :
99
97
print ("ERROR: Could not find '## UNRELEASED' section in changelog" )
100
98
return False
101
-
99
+
102
100
if content_start is None :
103
101
print ("ERROR: Could not find content start after UNRELEASED section" )
104
102
return False
105
-
103
+
106
104
# Create the new entry
107
105
new_entry = f"- { pr_title } [#{ pr_number } ]({ pr_url } )\n "
108
-
106
+
109
107
# Check if this PR entry already exists and update it if so
110
108
existing_entry_index = None
111
109
for i , line in enumerate (lines [content_start :], start = content_start ):
112
110
if f"[#{ pr_number } ]" in line :
113
111
existing_entry_index = i
114
112
break
115
113
# Stop checking when we reach the next section
116
- if line .startswith ("## " ) and not line .strip () = = "## UNRELEASED" :
114
+ if line .startswith ("## " ) and line .strip () ! = "## UNRELEASED" :
117
115
break
118
-
116
+
119
117
if existing_entry_index is not None :
120
118
# Update existing entry
121
119
lines [existing_entry_index ] = new_entry
@@ -124,11 +122,11 @@ def update_changelog_pr(file_path: Path, pr_number: str, pr_title: str, pr_url:
124
122
# Insert the new entry at the beginning of the unreleased content
125
123
lines .insert (content_start , new_entry )
126
124
print (f"Added changelog entry for PR #{ pr_number } : { pr_title } " )
127
-
125
+
128
126
# Write the updated changelog
129
- with open (file_path , 'w' , encoding = ' utf-8' ) as f :
127
+ with open (file_path , "w" , encoding = " utf-8" ) as f :
130
128
f .writelines (lines )
131
-
129
+
132
130
return True
133
131
134
132
@@ -141,7 +139,7 @@ def handle_bump_version(args):
141
139
if not cargo_path .exists ():
142
140
print ("ERROR: Cargo.toml not found." )
143
141
sys .exit (1 )
144
-
142
+
145
143
if not changelog_path .exists ():
146
144
print ("ERROR: Changelog file not found." )
147
145
sys .exit (1 )
@@ -164,49 +162,47 @@ def handle_update_changelog(args):
164
162
"""Handle update changelog subcommand."""
165
163
pr_number = args .number
166
164
pr_title = args .title
167
-
165
+
168
166
# Construct PR URL from repository info and PR number
169
167
# Default to the egglog-python repository
170
168
pr_url = f"https://github.com/egraphs-good/egglog-python/pull/{ pr_number } "
171
-
172
- changelog_path = Path (getattr (args , ' changelog_path' , ' docs/changelog.md' ))
173
-
169
+
170
+ changelog_path = Path (getattr (args , " changelog_path" , " docs/changelog.md" ))
171
+
174
172
if not changelog_path .exists ():
175
173
print (f"ERROR: Changelog file not found: { changelog_path } " )
176
174
sys .exit (1 )
177
-
175
+
178
176
success = update_changelog_pr (changelog_path , pr_number , pr_title , pr_url )
179
177
if not success :
180
178
sys .exit (1 )
181
179
182
180
183
181
def main ():
184
- parser = argparse .ArgumentParser (description = ' Changelog modifier and version bumper' )
185
- subparsers = parser .add_subparsers (dest = ' command' , help = ' Available commands' )
186
-
182
+ parser = argparse .ArgumentParser (description = " Changelog modifier and version bumper" )
183
+ subparsers = parser .add_subparsers (dest = " command" , help = " Available commands" )
184
+
187
185
# Bump version subcommand
188
- bump_parser = subparsers .add_parser ('bump_version' , help = 'Bump version and update changelog' )
189
- bump_parser .add_argument ('bump_type' , choices = ['major' , 'minor' , 'patch' ],
190
- help = 'Type of version bump' )
191
-
186
+ bump_parser = subparsers .add_parser ("bump_version" , help = "Bump version and update changelog" )
187
+ bump_parser .add_argument ("bump_type" , choices = ["major" , "minor" , "patch" ], help = "Type of version bump" )
188
+
192
189
# Update changelog subcommand
193
- changelog_parser = subparsers .add_parser ('update_changelog' , help = 'Add PR entry to changelog' )
194
- changelog_parser .add_argument ('number' , help = 'Pull request number' )
195
- changelog_parser .add_argument ('title' , help = 'Pull request title' )
196
- changelog_parser .add_argument ('--changelog-path' , default = 'docs/changelog.md' ,
197
- help = 'Path to changelog file' )
198
-
190
+ changelog_parser = subparsers .add_parser ("update_changelog" , help = "Add PR entry to changelog" )
191
+ changelog_parser .add_argument ("number" , help = "Pull request number" )
192
+ changelog_parser .add_argument ("title" , help = "Pull request title" )
193
+ changelog_parser .add_argument ("--changelog-path" , default = "docs/changelog.md" , help = "Path to changelog file" )
194
+
199
195
args = parser .parse_args ()
200
-
196
+
201
197
if not args .command :
202
198
parser .print_help ()
203
199
sys .exit (1 )
204
-
205
- if args .command == ' bump_version' :
200
+
201
+ if args .command == " bump_version" :
206
202
handle_bump_version (args )
207
- elif args .command == ' update_changelog' :
203
+ elif args .command == " update_changelog" :
208
204
handle_update_changelog (args )
209
205
210
206
211
207
if __name__ == "__main__" :
212
- main ()
208
+ main ()
0 commit comments