1
0
mirror of https://github.com/checktheroads/hyperglass synced 2024-05-11 05:55:08 +00:00
2022-12-24 17:53:05 -05:00

135 lines
4.2 KiB
Python
Executable File

#!/usr/bin/env python3
"""Manage hyperglass version across multiple files."""
# Standard Library
import re
from typing import Tuple, Union, Pattern
from pathlib import Path
# Third Party
import typer
PACKAGE_JSON = Path(__file__).parent / "hyperglass" / "ui" / "package.json"
PACKAGE_JSON_PATTERN = re.compile(r"\s+\"version\"\:\s\"(.+)\"\,$")
PYPROJECT_TOML = Path(__file__).parent / "pyproject.toml"
PYPROJECT_PATTERN = re.compile(r"^version\s\=\s\"(.+)\"$")
CONSTANTS = Path(__file__).parent / "hyperglass" / "constants.py"
CONSTANT_PATTERN = re.compile(r"^__version__\s\=\s\"(.+)\"$")
UPGRADES = (
("package.json", PACKAGE_JSON, PACKAGE_JSON_PATTERN),
("pyproject.toml", PYPROJECT_TOML, PYPROJECT_PATTERN),
("constants.py", CONSTANTS, CONSTANT_PATTERN),
)
cli = typer.Typer(name="version", no_args_is_help=True)
class Version:
"""Upgrade a file's version from one version to another."""
new_version: Union[str, int]
file: Path
line_pattern: Pattern[str]
old_version: Union[None, str, int] = None
_did_check: bool = False
_did_update: bool = False
def __init__(
self,
*,
name: str,
new_version: Union[str, int],
line_pattern: Union[Pattern, str],
file: Union[Path, str],
) -> None:
"""Initialize version manager."""
self.name = name
self.new_version = new_version
if isinstance(file, Path):
self.file = file
elif isinstance(file, str):
self.file = Path(file)
else:
raise TypeError(f"'{repr(file)}' must be a string or Path object")
if isinstance(line_pattern, Pattern):
self.line_pattern = line_pattern
elif isinstance(line_pattern, str):
self.line_pattern = re.compile(line_pattern)
else:
raise TypeError(f"'{repr(line_pattern)}' is not a supported pattern")
def __enter__(self) -> "Version":
"""Exit context manager for 0.01% better DX."""
return self
def __exit__(self, *args, **kwargs) -> None:
"""Exit context manager for 0.01% better DX."""
pass
def __str__(self) -> str:
"""Represent the state and/or action taken."""
if self._did_update:
old, new = self.upgrade_path
return f"Upgraded {self.name} from {old}{new}"
if self._did_check:
return f"No update required for {self.name} from version {self.old_version}"
return f"{self.name} has not been checked"
def upgrade(self) -> None:
"""Find a matching current version and upgrade it to the new version."""
with self.file.open("r+") as file:
found_match = False
lines = file.readlines()
self._did_check = True
for idx, line in enumerate(lines):
match = self.line_pattern.match(line)
if match:
old_version = match.group(1).strip()
try:
old_version = int(old_version)
except ValueError:
# Old version can't be converted to an integer, which is fine.
pass
self.old_version = old_version
if self.old_version != self.new_version:
lines[idx] = re.sub(old_version, self.new_version, line)
found_match = True
break
if found_match:
file.seek(0)
file.writelines(lines)
file.truncate()
self._did_update = True
@property
def upgrade_path(self) -> Tuple[Union[str, int], Union[str, int]]:
"""Get the old and new versions."""
return (self.old_version, self.new_version)
def update_versions(new_version: str) -> None:
"""Update hyperglass version in all package files."""
for name, file, pattern in UPGRADES:
with Version(
name=name,
file=file,
line_pattern=pattern,
new_version=new_version,
) as version:
version.upgrade()
typer.echo(str(version))
if __name__ == "__main__":
typer.run(update_versions)