feat: Implement automatic version synchronization between .env and pyproject.toml, update docker scripts to get version from .env
This commit is contained in:
230
scripts/update_version.py
Executable file
230
scripts/update_version.py
Executable file
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to update the version in pyproject.toml from the .env file.
|
||||
|
||||
This script reads the VERSION variable from .env and updates the version
|
||||
field in pyproject.toml 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"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 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_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.
|
||||
|
||||
Returns:
|
||||
Exit code: 0 for success, 1 for failure
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Update version in pyproject.toml 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"
|
||||
|
||||
print(f"Reading version from: {env_path}")
|
||||
print(f"Updating version in: {pyproject_path}")
|
||||
|
||||
# Read version from .env
|
||||
version = read_version_from_env(env_path)
|
||||
if not version:
|
||||
return 1
|
||||
|
||||
print(f"Found version in .env: {version}")
|
||||
|
||||
# Update pyproject.toml
|
||||
pyproject_updated = update_pyproject_version(pyproject_path, version)
|
||||
if not pyproject_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())
|
||||
Reference in New Issue
Block a user