306 lines
9.1 KiB
Python
Executable File
306 lines
9.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Script to update the version in pyproject.toml and Makefile from the .env file.
|
|
|
|
This script reads the VERSION variable from .env and updates the version
|
|
field in pyproject.toml and Makefile to keep them synchronized.
|
|
"""
|
|
|
|
import argparse
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def read_version_from_env(env_path: Path) -> str | None:
|
|
"""
|
|
Read the VERSION variable from the .env file.
|
|
|
|
Args:
|
|
env_path: Path to the .env file
|
|
|
|
Returns:
|
|
The version string or None if not found
|
|
"""
|
|
try:
|
|
with open(env_path, encoding="utf-8") as f:
|
|
content = f.read()
|
|
|
|
# Look for VERSION="x.y.z" pattern
|
|
match = re.search(r'VERSION\s*=\s*["\']([^"\']+)["\']', content)
|
|
if match:
|
|
return match.group(1)
|
|
else:
|
|
print("ERROR: VERSION not found in .env file")
|
|
return None
|
|
|
|
except FileNotFoundError:
|
|
print(f"ERROR: .env file not found at {env_path}")
|
|
return None
|
|
except Exception as e:
|
|
print(f"ERROR: Failed to read .env file: {e}")
|
|
return None
|
|
|
|
|
|
def update_pyproject_version(pyproject_path: Path, new_version: str) -> bool:
|
|
"""
|
|
Update the version in pyproject.toml.
|
|
|
|
Args:
|
|
pyproject_path: Path to the pyproject.toml file
|
|
new_version: The new version string
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
try:
|
|
with open(pyproject_path, encoding="utf-8") as f:
|
|
content = f.read()
|
|
|
|
# Split content into lines for more precise matching
|
|
lines = content.split("\n")
|
|
in_project_section = False
|
|
version_line_index = None
|
|
current_version = None
|
|
|
|
# Find the version line specifically in the [project] section
|
|
for i, line in enumerate(lines):
|
|
line_stripped = line.strip()
|
|
|
|
# Check if we're entering the [project] section
|
|
if line_stripped == "[project]":
|
|
in_project_section = True
|
|
continue
|
|
|
|
# Check if we're leaving the [project] section (entering a new section)
|
|
if (
|
|
in_project_section
|
|
and line_stripped.startswith("[")
|
|
and line_stripped != "[project]"
|
|
):
|
|
in_project_section = False
|
|
continue
|
|
|
|
# Look for version = "x.y.z" only within [project] section
|
|
if in_project_section and line_stripped.startswith("version"):
|
|
version_pattern = r'^version\s*=\s*["\']([^"\']+)["\']'
|
|
version_match = re.match(version_pattern, line_stripped)
|
|
if version_match:
|
|
current_version = version_match.group(1)
|
|
version_line_index = i
|
|
break
|
|
|
|
if current_version is None or version_line_index is None:
|
|
print(
|
|
"ERROR: version field not found in [project] section of pyproject.toml"
|
|
)
|
|
return False
|
|
|
|
if current_version == new_version:
|
|
print(f"pyproject.toml version is already up to date: {current_version}")
|
|
return True
|
|
|
|
# Replace only the specific version line in the [project] section
|
|
old_line = lines[version_line_index]
|
|
new_line = re.sub(
|
|
r'^(\s*version\s*=\s*["\'])([^"\']+)(["\'])(.*)$',
|
|
f"\\g<1>{new_version}\\g<3>\\g<4>",
|
|
old_line,
|
|
)
|
|
lines[version_line_index] = new_line
|
|
|
|
# Reconstruct the content
|
|
new_content = "\n".join(lines)
|
|
|
|
# Write back to file
|
|
with open(pyproject_path, "w", encoding="utf-8") as f:
|
|
f.write(new_content)
|
|
|
|
print(f"Updated pyproject.toml version from {current_version} to {new_version}")
|
|
return True
|
|
|
|
except FileNotFoundError:
|
|
print(f"ERROR: pyproject.toml file not found at {pyproject_path}")
|
|
return False
|
|
except Exception as e:
|
|
print(f"ERROR: Failed to update pyproject.toml: {e}")
|
|
return False
|
|
|
|
|
|
def update_makefile_version(makefile_path: Path, new_version: str) -> bool:
|
|
"""
|
|
Update the version in Makefile.
|
|
|
|
Args:
|
|
makefile_path: Path to the Makefile
|
|
new_version: The new version string
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
try:
|
|
with open(makefile_path, encoding="utf-8") as f:
|
|
content = f.read()
|
|
|
|
# Split content into lines for processing
|
|
lines = content.split("\n")
|
|
version_line_index = None
|
|
current_version = None
|
|
|
|
# Find the VERSION= line
|
|
for i, line in enumerate(lines):
|
|
# Look for VERSION=x.y.z pattern (at start of line or after whitespace)
|
|
version_pattern = r"^(\s*)VERSION\s*=\s*(.+)$"
|
|
version_match = re.match(version_pattern, line)
|
|
if version_match:
|
|
current_version = version_match.group(2).strip()
|
|
version_line_index = i
|
|
break
|
|
|
|
if current_version is None or version_line_index is None:
|
|
print("ERROR: VERSION variable not found in Makefile")
|
|
return False
|
|
|
|
if current_version == new_version:
|
|
print(f"Makefile version is already up to date: {current_version}")
|
|
return True
|
|
|
|
# Replace the VERSION line
|
|
old_line = lines[version_line_index]
|
|
new_line = re.sub(
|
|
r"^(\s*VERSION\s*=\s*)(.+)$",
|
|
f"\\g<1>{new_version}",
|
|
old_line,
|
|
)
|
|
lines[version_line_index] = new_line
|
|
|
|
# Reconstruct the content
|
|
new_content = "\n".join(lines)
|
|
|
|
# Write back to file
|
|
with open(makefile_path, "w", encoding="utf-8") as f:
|
|
f.write(new_content)
|
|
|
|
print(f"Updated Makefile version from {current_version} to {new_version}")
|
|
return True
|
|
|
|
except FileNotFoundError:
|
|
print(f"ERROR: Makefile not found at {makefile_path}")
|
|
return False
|
|
except Exception as e:
|
|
print(f"ERROR: Failed to update Makefile: {e}")
|
|
return False
|
|
|
|
|
|
def update_uv_lock(project_root: Path) -> bool:
|
|
"""
|
|
Update uv.lock file to reflect changes in pyproject.toml.
|
|
|
|
Args:
|
|
project_root: Path to the project root directory
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
try:
|
|
print("Updating uv.lock file...")
|
|
|
|
# Run uv lock to update the lock file
|
|
result = subprocess.run(
|
|
["uv", "lock"],
|
|
cwd=project_root,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=60, # 60 second timeout
|
|
)
|
|
|
|
if result.returncode == 0:
|
|
print("Successfully updated uv.lock")
|
|
return True
|
|
else:
|
|
print(f"ERROR: Failed to update uv.lock: {result.stderr}")
|
|
return False
|
|
|
|
except subprocess.TimeoutExpired:
|
|
print("ERROR: uv lock command timed out after 60 seconds")
|
|
return False
|
|
except FileNotFoundError:
|
|
print(
|
|
"ERROR: 'uv' command not found. Please ensure uv is installed and in PATH"
|
|
)
|
|
return False
|
|
except Exception as e:
|
|
print(f"ERROR: Failed to run uv lock: {e}")
|
|
return False
|
|
|
|
|
|
def main() -> int:
|
|
"""
|
|
Main function to update version from .env to pyproject.toml and Makefile.
|
|
|
|
Returns:
|
|
Exit code: 0 for success, 1 for failure
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
description="Update version in pyproject.toml and Makefile from .env file"
|
|
)
|
|
parser.add_argument(
|
|
"--skip-uv-lock",
|
|
action="store_true",
|
|
help="Skip updating uv.lock file after version update",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
# Get the project root directory (assuming script is in scripts/ folder)
|
|
script_dir = Path(__file__).parent
|
|
project_root = script_dir.parent
|
|
|
|
env_path = project_root / ".env"
|
|
pyproject_path = project_root / "pyproject.toml"
|
|
makefile_path = project_root / "Makefile"
|
|
|
|
print(f"Reading version from: {env_path}")
|
|
print(f"Updating version in: {pyproject_path}")
|
|
print(f"Updating version in: {makefile_path}")
|
|
|
|
# Read version from .env
|
|
version = read_version_from_env(env_path)
|
|
if not version:
|
|
return 1
|
|
|
|
print(f"Found version in .env: {version}")
|
|
|
|
# Track if any updates were made
|
|
_updates_made = False
|
|
|
|
# Update pyproject.toml
|
|
pyproject_updated = update_pyproject_version(pyproject_path, version)
|
|
if not pyproject_updated:
|
|
return 1
|
|
|
|
# Update Makefile
|
|
makefile_updated = update_makefile_version(makefile_path, version)
|
|
if not makefile_updated:
|
|
return 1
|
|
|
|
print("Version update completed successfully!")
|
|
|
|
# Update uv.lock unless explicitly skipped
|
|
if args.skip_uv_lock:
|
|
print("Skipping uv.lock update (--skip-uv-lock specified)")
|
|
return 0
|
|
|
|
# Update uv.lock to reflect the changes
|
|
if update_uv_lock(project_root):
|
|
print("All updates completed successfully!")
|
|
return 0
|
|
else:
|
|
print("⚠️ Version updated but uv.lock update failed")
|
|
print(" Please run 'uv lock' manually to update the lock file")
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|