Skip to content

Commit 8ba2c11

Browse files
authored
chore(releasing): improve release/create_release_pull_request.py script (#1551)
* chore(releasing): improve release/create_release_pull_request.py script * overwrite version with tomlkit * print tweaks
1 parent d6cae02 commit 8ba2c11

File tree

2 files changed

+127
-35
lines changed

2 files changed

+127
-35
lines changed

release/create_release_pull_request.py

Lines changed: 126 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
import argparse
33
import os
44
import subprocess
5+
import sys
56
from inspect import getsourcefile
67
from os.path import abspath
78

89
import semver
10+
import tomlkit
911

1012
from utils.validate_version import assert_version_is_not_published
1113

@@ -15,38 +17,109 @@
1517
SCRIPTS_DIR = os.path.join(REPO_ROOT_DIR, "scripts")
1618
SCRIPT_FILENAME = os.path.basename(getsourcefile(lambda: 0))
1719

18-
def overwrite_version(version):
20+
def get_current_version():
21+
"""Read the current version from Cargo.toml using tomlkit"""
1922
toml_path = os.path.join(REPO_ROOT_DIR, "Cargo.toml")
20-
with open(toml_path, "r") as file:
21-
lines = file.readlines()
22-
23-
# This will preserve line order.
24-
current_version = None
25-
for i, line in enumerate(lines):
26-
if line.startswith("version ="):
27-
current_version = line.split("=")[1].strip().strip('"')
28-
if current_version == version:
29-
print(f"Already at version {version}.")
30-
exit(1)
31-
lines[i] = f"version = \"{version}\"\n"
32-
break
33-
34-
if current_version is None:
35-
print("The `version` field is not present in Cargo.toml.")
36-
exit(1)
23+
24+
try:
25+
with open(toml_path, "r") as file:
26+
doc = tomlkit.parse(file.read())
27+
28+
if "package" in doc and "version" in doc["package"]:
29+
return doc["package"]["version"]
30+
else:
31+
print("Error: The `version` field is not present in Cargo.toml [package] section.")
32+
sys.exit(1)
33+
except FileNotFoundError:
34+
print(f"Error: Cargo.toml not found at {toml_path}")
35+
sys.exit(1)
36+
except Exception as e:
37+
print(f"Error: Failed to parse Cargo.toml: {e}")
38+
sys.exit(1)
39+
40+
def overwrite_version(version, dry_run=False):
41+
"""
42+
Update version in Cargo.toml using tomlkit.
43+
Preserves formatting, comments, and structure.
44+
"""
45+
toml_path = os.path.join(REPO_ROOT_DIR, "Cargo.toml")
46+
47+
try:
48+
with open(toml_path, "r") as file:
49+
content = file.read()
50+
doc = tomlkit.parse(content)
51+
except FileNotFoundError:
52+
print(f"Error: Cargo.toml not found at {toml_path}")
53+
sys.exit(1)
54+
except Exception as e:
55+
print(f"Error: Failed to parse Cargo.toml: {e}")
56+
sys.exit(1)
57+
58+
# Check if [package] section exists
59+
if "package" not in doc:
60+
print("Error: [package] section not found in Cargo.toml")
61+
sys.exit(1)
62+
63+
# Check if version field exists
64+
if "version" not in doc["package"]:
65+
print("Error: version field not found in [package] section")
66+
sys.exit(1)
67+
68+
current_version = doc["package"]["version"]
69+
70+
if current_version == version:
71+
print(f"Already at version {version}.")
72+
sys.exit(1)
3773

3874
commit_message = f"chore(deps): change version from {current_version} with {version}"
39-
print(commit_message)
75+
print(f"Updating version in Cargo.toml: {current_version} -> {version}")
4076

77+
if dry_run:
78+
print("Dry-run mode: Skipping version file write and commit.")
79+
return
80+
81+
# Update version in the document
82+
doc["package"]["version"] = version
83+
84+
# Write back to file (preserves formatting and comments)
4185
with open(toml_path, "w") as file:
42-
file.writelines(lines)
86+
file.write(tomlkit.dumps(doc))
4387

4488
# Update VRL version in Cargo.lock
4589
subprocess.run(["cargo", "update", "-p", "vrl"], check=True, cwd=REPO_ROOT_DIR)
4690

4791
subprocess.run(["git", "commit", "-a", "-m", commit_message], check=True, cwd=REPO_ROOT_DIR)
4892

4993

94+
def resolve_version(version_arg):
95+
"""
96+
Resolve version argument to actual version string.
97+
Supports:
98+
- Exact version (e.g., "1.2.3")
99+
- "major" - bump major version
100+
- "minor" - bump minor version
101+
- "patch" - bump patch version
102+
"""
103+
bump_types = ["major", "minor", "patch"]
104+
105+
if version_arg.lower() in bump_types:
106+
current_version_str = get_current_version()
107+
current_version = semver.VersionInfo.parse(current_version_str)
108+
109+
if version_arg.lower() == "major":
110+
new_version = current_version.bump_major()
111+
elif version_arg.lower() == "minor":
112+
new_version = current_version.bump_minor()
113+
elif version_arg.lower() == "patch":
114+
new_version = current_version.bump_patch()
115+
116+
new_version_str = str(new_version)
117+
print(f"Bumping {version_arg} version: {current_version_str} -> {new_version_str}")
118+
return new_version_str
119+
else:
120+
# Assume it's an exact version
121+
return version_arg
122+
50123
def validate_version(version):
51124
try:
52125
semver.VersionInfo.parse(version)
@@ -56,24 +129,33 @@ def validate_version(version):
56129

57130
assert_version_is_not_published(version)
58131

59-
def generate_changelog():
132+
def generate_changelog(dry_run=False):
60133
print("Generating changelog...")
134+
if dry_run:
135+
print("Dry-run mode: Skipping changelog generation and commit.")
136+
return
61137
subprocess.run(["./generate_release_changelog.sh", "--no-prompt"], check=True, cwd=SCRIPTS_DIR)
62138
subprocess.run(["git", "commit", "-a", "-m", "chore(releasing): generate changelog"],
63139
check=True,
64140
cwd=REPO_ROOT_DIR)
65141

66142
def create_branch(branch_name, dry_run=False):
67143
print(f"Creating branch: {branch_name}")
144+
if dry_run:
145+
print("Dry-run mode: Skipping branch creation.")
146+
return
68147
subprocess.run(["git", "checkout", "-b", branch_name], check=True, cwd=REPO_ROOT_DIR)
69-
if not dry_run:
70-
subprocess.run(["git", "push", "-u", "origin", branch_name],
71-
check=True,
72-
cwd=REPO_ROOT_DIR)
148+
subprocess.run(["git", "push", "-u", "origin", branch_name],
149+
check=True,
150+
cwd=REPO_ROOT_DIR)
73151

74-
def create_pull_request(branch_name, new_version, dry_run=False):
152+
def create_pull_request(branch_name, new_version, issue_link=None, dry_run=False):
75153
title = f"chore(releasing): Prepare {new_version} release"
76154
body = f"Generated with {SCRIPT_FILENAME}"
155+
156+
if issue_link:
157+
body += f"\n\nRelated issue: {issue_link}"
158+
77159
print(f"Creating pull request with title: {title}")
78160
if dry_run:
79161
print("Dry-run mode: Skipping PR creation.")
@@ -87,24 +169,33 @@ def create_pull_request(branch_name, new_version, dry_run=False):
87169

88170
def main():
89171
parser = argparse.ArgumentParser(description="Prepare a new release")
90-
parser.add_argument("version", help="The new version to release")
172+
parser.add_argument("version", help="The new version to release (e.g., '1.2.3', 'major', 'minor', or 'patch')")
173+
parser.add_argument("--issue", "-i", dest="issue_link",
174+
help="GitHub issue link to include in the PR body (e.g., 'https://github.com/owner/repo/issues/123')")
91175
parser.add_argument("--dry-run", action="store_true",
92-
help="Run the script without making remote changes")
176+
help="Run the script without making any changes (read-only)")
93177
args = parser.parse_args()
94178

95-
new_version = args.version
179+
# Resolve version (could be exact version or bump type)
180+
new_version = resolve_version(args.version)
96181
dry_run = args.dry_run
182+
issue_link = args.issue_link
183+
184+
if not dry_run:
185+
validate_version(new_version)
97186

98-
validate_version(new_version)
99187
branch_name = f"prepare-{new_version}-release"
100188
create_branch(branch_name, dry_run)
101-
overwrite_version(new_version)
102-
generate_changelog()
103-
subprocess.run(["git", "push"], check=True, cwd=REPO_ROOT_DIR)
104-
create_pull_request(branch_name, new_version, dry_run)
189+
overwrite_version(new_version, dry_run)
190+
generate_changelog(dry_run)
191+
192+
if not dry_run:
193+
subprocess.run(["git", "push"], check=True, cwd=REPO_ROOT_DIR)
194+
195+
create_pull_request(branch_name, new_version, issue_link, dry_run)
105196

106197
if dry_run:
107-
print("Dry-run completed. No actual remote changes were made.")
198+
print("\nDry-run completed. No changes were made.")
108199

109200
if __name__ == "__main__":
110201
main()

release/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
requests
22
semver
3+
tomlkit

0 commit comments

Comments
 (0)