Skip to content

Commit d9bbdf8

Browse files
committed
Added improved release script
Signed-off-by: Nick Brook <[email protected]>
1 parent 879b2b2 commit d9bbdf8

2 files changed

Lines changed: 205 additions & 2 deletions

File tree

.pre-commit-config.yaml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@ repos:
1818
rev: v1.15.0
1919
hooks:
2020
- id: mypy
21-
exclude: ^(examples)
21+
exclude: ^(examples|scripts)
2222
args: [
2323
--config-file, pyproject.toml,
24-
2524
]
2625
additional_dependencies: [
2726
bleak==0.22.3,
2827
rich==13.9.4,
2928
packaging,
3029
prompt_toolkit>=3.0.0,
3130
]
31+
32+
- repo: https://github.com/PyCQA/bandit
33+
rev: '1.8.3'
34+
hooks:
35+
- id: bandit
36+
args: [ -c, pyproject.toml, "-r", "."]

scripts/release.py

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
#!/usr/bin/env python3
2+
"""Script to bump the version of the package.
3+
4+
Usage: python scripts/bump_version.py [major|minor|patch]
5+
"""
6+
7+
import argparse
8+
import re
9+
import subprocess
10+
import sys
11+
from datetime import datetime
12+
from pathlib import Path
13+
14+
import tomli
15+
import tomli_w
16+
17+
18+
def update_pyproject_toml(new_version):
19+
"""Update the version in pyproject.toml."""
20+
pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
21+
content = tomli.loads(pyproject_path.read_text())
22+
23+
# Update version in project section
24+
if "project" in content:
25+
content["project"]["version"] = new_version
26+
else:
27+
print("Could not find [project] section in pyproject.toml")
28+
sys.exit(1)
29+
30+
pyproject_path.write_text(tomli_w.dumps(content))
31+
print(f"Updated version in pyproject.toml to {new_version}")
32+
33+
34+
def update_init_py(new_version):
35+
"""Update the version in __init__.py."""
36+
init_py_path = Path(__file__).parent.parent / "test-a-ble" / "__init__.py"
37+
content = init_py_path.read_text()
38+
39+
# Replace the version
40+
content = re.sub(
41+
r'__version__ = "[0-9]+\.[0-9]+\.[0-9]+"',
42+
f'__version__ = "{new_version}"',
43+
content,
44+
)
45+
46+
init_py_path.write_text(content)
47+
print(f"Updated version in __init__.py to {new_version}")
48+
49+
50+
def update_docs_conf_py(new_version):
51+
"""Update the version in docs/source/conf.py."""
52+
conf_py_path = Path(__file__).parent.parent / "docs" / "source" / "conf.py"
53+
content = conf_py_path.read_text()
54+
55+
# Replace the version
56+
content = re.sub(r"release = '[0-9]+\.[0-9]+\.[0-9]+'", f"release = '{new_version}'", content)
57+
58+
conf_py_path.write_text(content)
59+
print(f"Updated version in docs/source/conf.py to {new_version}")
60+
61+
62+
def update_changelog(new_version):
63+
"""Update the changelog with a new version section."""
64+
changelog_path = Path(__file__).parent.parent / "CHANGELOG.md"
65+
content = changelog_path.read_text()
66+
67+
# Check if the new version already exists in the changelog
68+
if f"## [{new_version}]" in content:
69+
print(f"Version {new_version} already exists in CHANGELOG.md")
70+
return
71+
72+
# Get the current date
73+
today = datetime.now().strftime("%Y-%m-%d")
74+
75+
# Create a new version section
76+
new_section = f"""## [{new_version}] - {today}
77+
78+
### Added
79+
-
80+
81+
### Changed
82+
-
83+
84+
### Fixed
85+
-
86+
87+
"""
88+
89+
# Insert the new section after the header
90+
content = re.sub(
91+
r"(## \[)",
92+
f"{new_section}\n\n\1",
93+
content,
94+
flags=re.DOTALL,
95+
)
96+
97+
changelog_path.write_text(content)
98+
print(f"Updated CHANGELOG.md with new version {new_version}")
99+
100+
101+
def get_current_version():
102+
"""Get the current version from pyproject.toml."""
103+
pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
104+
try:
105+
content = tomli.loads(pyproject_path.read_text())
106+
if "project" in content and "version" in content["project"]:
107+
return content["project"]["version"]
108+
except Exception as e:
109+
print(f"Error reading pyproject.toml: {e}")
110+
sys.exit(1)
111+
112+
print("Could not find version in pyproject.toml")
113+
sys.exit(1)
114+
115+
116+
def bump_version(current_version, part):
117+
"""Bump the version according to the specified part."""
118+
major, minor, patch = map(int, current_version.split("."))
119+
120+
if part == "major":
121+
major += 1
122+
minor = 0
123+
patch = 0
124+
elif part == "minor":
125+
minor += 1
126+
patch = 0
127+
elif part == "patch":
128+
patch += 1
129+
else:
130+
print(f"Invalid part: {part}")
131+
sys.exit(1)
132+
133+
return f"{major}.{minor}.{patch}"
134+
135+
136+
def run_git_command(cmd, check=True):
137+
"""Run a git command safely."""
138+
git_exe = "git" # Could be made configurable if needed
139+
try:
140+
return subprocess.run([git_exe, *cmd], check=check, shell=False, text=True, capture_output=True) # noqa: S603 # nosec: B603
141+
except subprocess.CalledProcessError as e:
142+
print(f"Git command failed: {e}")
143+
sys.exit(1)
144+
145+
146+
def main():
147+
"""Execute the main function."""
148+
parser = argparse.ArgumentParser(description="Bump the version of the package.")
149+
parser.add_argument(
150+
"part",
151+
nargs="?",
152+
choices=["major", "minor", "patch"],
153+
help="The part of the version to bump (optional)",
154+
)
155+
args = parser.parse_args()
156+
157+
current_version = get_current_version()
158+
if args.part:
159+
new_version = bump_version(current_version, args.part)
160+
print(f"Bumping version from {current_version} to {new_version}")
161+
162+
update_pyproject_toml(new_version)
163+
update_init_py(new_version)
164+
update_docs_conf_py(new_version)
165+
update_changelog(new_version)
166+
167+
print(f"Version bumped to {new_version}")
168+
print("Don't forget to commit the changes and create a new tag:")
169+
print(f"git commit -am 'Bump version to {new_version}'")
170+
else:
171+
new_version = current_version
172+
print("Commands to run to create a new tag:")
173+
174+
print(f"git tag -a v{new_version} -m 'Version {new_version}'")
175+
print("git push && git push --tags")
176+
print("\nDo you want to run the git commands now? (y/n)")
177+
response = input().strip().lower()
178+
179+
if response in {"y", "yes"}:
180+
print("Running git commands...")
181+
if args.part:
182+
run_git_command(["commit", "-am", f"Bump version to {new_version}"])
183+
run_git_command(["tag", "-a", f"v{new_version}", "-m", f"Version {new_version}"])
184+
185+
print("Do you want to push the changes? (y/n)")
186+
push_response = input().strip().lower()
187+
if push_response in {"y", "yes"}:
188+
run_git_command(["push"])
189+
run_git_command(["push", "--tags"])
190+
print("Changes pushed successfully.")
191+
else:
192+
print("Changes committed and tagged locally. Run 'git push && git push --tags' when ready.")
193+
else:
194+
print("Commands not executed. Run them manually when ready.")
195+
196+
197+
if __name__ == "__main__":
198+
main()

0 commit comments

Comments
 (0)