Skip to content

Commit 3a04d66

Browse files
committed
Add version management utility and version info module
Introduces src/libcrypto/_version.py as the single source of version metadata for the LibCrypto package. Adds version_manager.py, a script to show, set, bump, and check version numbers programmatically across the project.
1 parent a6f9ebc commit 3a04d66

File tree

2 files changed

+233
-0
lines changed

2 files changed

+233
-0
lines changed

src/libcrypto/_version.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""
2+
Version information for LibCrypto package.
3+
This is the single source of truth for version numbers.
4+
5+
Requires Python 3.8+
6+
"""
7+
8+
__author__ = "Mmdrza"
9+
__author_email__ = "[email protected]"
10+
__description__ = (
11+
"A professional library For Cryptography and Cryptocurrencies in Python."
12+
)
13+
__url__ = "https://github.com/Pymmdrza/libcrypto"
14+
15+
# Version components for programmatic access
16+
VERSION_MAJOR = 1
17+
VERSION_MINOR = 0
18+
VERSION_PATCH = 3
19+
VERSION_SUFFIX = "" # e.g., "a1", "b1", "rc1", "" for stable
20+
21+
# Build the version string
22+
if VERSION_SUFFIX:
23+
__version__ = f"{VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_PATCH}{VERSION_SUFFIX}"
24+
else:
25+
__version__ = f"{VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_PATCH}"
26+
27+
# Additional metadata
28+
__license__ = "MIT"
29+
__copyright__ = "2025 Mmdrza"

version_manager.py

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Version management utility for LibFake package.
4+
Use this script to update version numbers across the entire project.
5+
"""
6+
7+
import os
8+
import re
9+
import argparse
10+
import sys
11+
from pathlib import Path
12+
13+
14+
def get_current_version():
15+
"""Get the current version from _version.py"""
16+
version_file = Path(__file__).parent / "src" / "libfake" / "_version.py"
17+
18+
if not version_file.exists():
19+
raise FileNotFoundError(f"Version file not found: {version_file}")
20+
21+
with open(version_file, "r", encoding="utf-8") as f:
22+
content = f.read()
23+
24+
# Extract version components
25+
major_match = re.search(r"VERSION_MAJOR\s*=\s*(\d+)", content)
26+
minor_match = re.search(r"VERSION_MINOR\s*=\s*(\d+)", content)
27+
patch_match = re.search(r"VERSION_PATCH\s*=\s*(\d+)", content)
28+
suffix_match = re.search(r'VERSION_SUFFIX\s*=\s*["\']([^"\']*)["\']', content)
29+
30+
if not all([major_match, minor_match, patch_match, suffix_match]):
31+
raise ValueError("Could not parse version components from _version.py")
32+
33+
major = int(major_match.group(1))
34+
minor = int(minor_match.group(1))
35+
patch = int(patch_match.group(1))
36+
suffix = suffix_match.group(1)
37+
38+
return major, minor, patch, suffix
39+
40+
41+
def update_version(major, minor, patch, suffix=""):
42+
"""Update the version in _version.py"""
43+
version_file = Path(__file__).parent / "src" / "libcrypto" / "_version.py"
44+
45+
with open(version_file, "r", encoding="utf-8") as f:
46+
content = f.read()
47+
48+
# Update version components
49+
content = re.sub(r"VERSION_MAJOR\s*=\s*\d+", f"VERSION_MAJOR = {major}", content)
50+
content = re.sub(r"VERSION_MINOR\s*=\s*\d+", f"VERSION_MINOR = {minor}", content)
51+
content = re.sub(r"VERSION_PATCH\s*=\s*\d+", f"VERSION_PATCH = {patch}", content)
52+
content = re.sub(
53+
r'VERSION_SUFFIX\s*=\s*["\'][^"\']*["\']',
54+
f'VERSION_SUFFIX = "{suffix}"',
55+
content,
56+
)
57+
58+
with open(version_file, "w", encoding="utf-8") as f:
59+
f.write(content)
60+
61+
# Calculate version string
62+
if suffix:
63+
version_str = f"{major}.{minor}.{patch}{suffix}"
64+
else:
65+
version_str = f"{major}.{minor}.{patch}"
66+
67+
return version_str
68+
69+
70+
def validate_version_consistency():
71+
"""Check if all files use the same version"""
72+
from src.libfake._version import __version__
73+
74+
issues = []
75+
76+
# Check if import works
77+
try:
78+
from src.libfake import __version__ as init_version
79+
80+
if __version__ != init_version:
81+
issues.append(
82+
f"__init__.py version mismatch: {init_version} != {__version__}"
83+
)
84+
except ImportError as e:
85+
issues.append(f"Could not import version from __init__.py: {e}")
86+
87+
return issues
88+
89+
90+
def bump_version(component, suffix=""):
91+
"""Bump version component (major, minor, patch)"""
92+
major, minor, patch, current_suffix = get_current_version()
93+
94+
if component == "major":
95+
major += 1
96+
minor = 0
97+
patch = 0
98+
elif component == "minor":
99+
minor += 1
100+
patch = 0
101+
elif component == "patch":
102+
patch += 1
103+
else:
104+
raise ValueError(f"Invalid component: {component}")
105+
106+
return update_version(major, minor, patch, suffix)
107+
108+
109+
def main():
110+
"""Main function for version management."""
111+
parser = argparse.ArgumentParser(
112+
description="LibFake Version Management Tool",
113+
formatter_class=argparse.RawDescriptionHelpFormatter,
114+
epilog="""
115+
Examples:
116+
python version_manager.py --show Show current version
117+
python version_manager.py --set 1.2.3 Set specific version
118+
python version_manager.py --bump patch Bump patch version
119+
python version_manager.py --bump minor Bump minor version
120+
python version_manager.py --bump major Bump major version
121+
python version_manager.py --set 2.0.0 --suffix rc1 Set version with suffix
122+
python version_manager.py --check Check version consistency
123+
""",
124+
)
125+
126+
group = parser.add_mutually_exclusive_group(required=True)
127+
group.add_argument("--show", "-s", action="store_true", help="Show current version")
128+
group.add_argument("--set", type=str, help="Set specific version (e.g., 1.2.3)")
129+
group.add_argument(
130+
"--bump",
131+
"-b",
132+
choices=["major", "minor", "patch"],
133+
help="Bump version component",
134+
)
135+
group.add_argument(
136+
"--check",
137+
"-c",
138+
action="store_true",
139+
help="Check version consistency across files",
140+
)
141+
142+
parser.add_argument(
143+
"--suffix", type=str, default="", help="Version suffix (e.g., a1, b1, rc1)"
144+
)
145+
146+
args = parser.parse_args()
147+
148+
try:
149+
if args.show:
150+
major, minor, patch, suffix = get_current_version()
151+
if suffix:
152+
version_str = f"{major}.{minor}.{patch}{suffix}"
153+
else:
154+
version_str = f"{major}.{minor}.{patch}"
155+
156+
print(f"Current version: {version_str}")
157+
print(
158+
f"Components: major={major}, minor={minor}, patch={patch}, suffix='{suffix}'"
159+
)
160+
161+
elif args.set:
162+
# Parse version string
163+
version_parts = args.set.split(".")
164+
if len(version_parts) != 3:
165+
raise ValueError(
166+
"Version must be in format major.minor.patch (e.g., 1.2.3)"
167+
)
168+
169+
try:
170+
major = int(version_parts[0])
171+
minor = int(version_parts[1])
172+
patch = int(version_parts[2])
173+
except ValueError:
174+
raise ValueError("Version components must be integers")
175+
176+
new_version = update_version(major, minor, patch, args.suffix)
177+
print(f"Version updated to: {new_version}")
178+
179+
elif args.bump:
180+
new_version = bump_version(args.bump, args.suffix)
181+
print(f"Version bumped to: {new_version}")
182+
183+
elif args.check:
184+
issues = validate_version_consistency()
185+
if issues:
186+
print("Version consistency issues found:")
187+
for issue in issues:
188+
print(f" ❌ {issue}")
189+
sys.exit(1)
190+
else:
191+
major, minor, patch, suffix = get_current_version()
192+
if suffix:
193+
version_str = f"{major}.{minor}.{patch}{suffix}"
194+
else:
195+
version_str = f"{major}.{minor}.{patch}"
196+
print(f"✅ All versions are consistent: {version_str}")
197+
198+
except Exception as e:
199+
print(f"Error: {e}", file=sys.stderr)
200+
sys.exit(1)
201+
202+
203+
if __name__ == "__main__":
204+
main()

0 commit comments

Comments
 (0)