Revision a5a0e77e916382a3a06ce9ce05499a4acb2f0c30 authored by dependabot[bot] on 23 January 2024, 11:18:26 UTC, committed by GitHub on 23 January 2024, 11:18:26 UTC
1 parent 79aaa84
update_version.py
#!/usr/bin/env python3
import argparse
import functools
import re
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Callable, Match, Pattern
SUCCESS_CHAR = '\u2714'
FAIL_CHAR = '\u2716'
CVAT_VERSION_PATTERN = re.compile(r'VERSION\s*=\s*\((\d+),\s*(\d*),\s*(\d+),\s*[\',\"](\w+)[\',\"],\s*(\d+)\)')
REPO_ROOT_DIR = Path(__file__).resolve().parents[1]
CVAT_INIT_PY_REL_PATH = 'cvat/__init__.py'
CVAT_INIT_PY_PATH = REPO_ROOT_DIR / CVAT_INIT_PY_REL_PATH
@dataclass()
class Version:
major: int = 0
minor: int = 0
patch: int = 0
prerelease: str = ''
prerelease_number: int = 0
def __str__(self) -> str:
return f'{self.major}.{self.minor}.{self.patch}-{self.prerelease}.{self.prerelease_number}'
def cvat_repr(self):
return f"({self.major}, {self.minor}, {self.patch}, '{self.prerelease}', {self.prerelease_number})"
def compose_repr(self):
if self.prerelease != 'final':
return 'dev'
return f'v{self.major}.{self.minor}.{self.patch}'
def increment_prerelease_number(self) -> None:
self.prerelease_number += 1
def increment_prerelease(self) -> None:
flow = ('alpha', 'beta', 'rc', 'final')
idx = flow.index(self.prerelease)
if idx == len(flow) - 1:
raise ValueError(f"Cannot increment current '{self.prerelease}' prerelease version")
self.prerelease = flow[idx + 1]
self._set_default_prerelease_number()
def set_prerelease(self, value: str) -> None:
values = ('alpha', 'beta', 'rc', 'final')
if value not in values:
raise ValueError(f'{value} is a wrong, must be one of {values}')
self.prerelease = value
self._set_default_prerelease_number()
def increment_patch(self) -> None:
self.patch += 1
self._set_default_prerelease()
def increment_minor(self) -> None:
self.minor += 1
self._set_default_patch()
def increment_major(self) -> None:
self.major += 1
self._set_default_minor()
def set(self, v: str) -> None:
self.major, self.minor, self.patch = map(int, v.split('.'))
self.prerelease = 'final'
self.prerelease_number = 0
def _set_default_prerelease_number(self) -> None:
self.prerelease_number = 0
def _set_default_prerelease(self) -> None:
self.prerelease = 'alpha'
self._set_default_prerelease_number()
def _set_default_patch(self) -> None:
self.patch = 0
self._set_default_prerelease()
def _set_default_minor(self) -> None:
self.minor = 0
self._set_default_patch()
@dataclass(frozen=True)
class ReplacementRule:
rel_path: str
pattern: Pattern[str]
replacement: Callable[[Version, Match[str]], str]
def apply(self, new_version: Version, *, verify_only: bool) -> bool:
path = REPO_ROOT_DIR / self.rel_path
text = path.read_text()
new_text, num_replacements = self.pattern.subn(
functools.partial(self.replacement, new_version), text)
if not num_replacements:
print(f'{FAIL_CHAR} {self.rel_path}: failed to match version pattern.')
return False
if text == new_text:
if verify_only:
print(f'{SUCCESS_CHAR} {self.rel_path}: verified.')
else:
print(f'{SUCCESS_CHAR} {self.rel_path}: no need to update.')
else:
if verify_only:
print(f'{FAIL_CHAR} {self.rel_path}: verification failed.')
return False
else:
path.write_text(new_text)
print(f'{SUCCESS_CHAR} {self.rel_path}: updated.')
return True
REPLACEMENT_RULES = [
ReplacementRule(CVAT_INIT_PY_REL_PATH, CVAT_VERSION_PATTERN,
lambda v, m: f'VERSION = {v.cvat_repr()}'),
ReplacementRule('docker-compose.yml',
re.compile(r'(\$\{CVAT_VERSION:-)([\w.]+)(\})'),
lambda v, m: m[1] + v.compose_repr() + m[3]),
ReplacementRule('helm-chart/values.yaml',
re.compile(r'(^ image: cvat/(?:ui|server)\n tag: )([\w.]+)', re.M),
lambda v, m: m[1] + v.compose_repr()),
ReplacementRule('cvat-sdk/gen/generate.sh',
re.compile(r'^VERSION="[\d.]+"$', re.M),
lambda v, m: f'VERSION="{v.major}.{v.minor}.{v.patch}"'),
ReplacementRule('cvat/schema.yml',
re.compile(r"^ version: [\d.]+$", re.M),
lambda v, m: f' version: {v.major}.{v.minor}.{v.patch}'),
ReplacementRule('cvat-cli/src/cvat_cli/version.py',
re.compile(r'^VERSION = "[\d.]+"$', re.M),
lambda v, m: f'VERSION = "{v.major}.{v.minor}.{v.patch}"'),
ReplacementRule('cvat-cli/requirements/base.txt',
re.compile(r'^cvat-sdk~=[\d.]+$', re.M),
lambda v, m: f'cvat-sdk~={v.major}.{v.minor}.{v.patch}'),
]
def get_current_version() -> Version:
version_text = CVAT_INIT_PY_PATH.read_text()
match = re.search(CVAT_VERSION_PATTERN, version_text)
if not match:
raise RuntimeError(f'Failed to find version in {CVAT_INIT_PY_PATH}')
return Version(int(match[1]), int(match[2]), int(match[3]), match[4], int(match[5]))
def main() -> None:
parser = argparse.ArgumentParser(description='Bump CVAT version')
action_group = parser.add_mutually_exclusive_group(required=True)
action_group.add_argument('--major', action='store_true',
help='Increment the existing major version by 1')
action_group.add_argument('--minor', action='store_true',
help='Increment the existing minor version by 1')
action_group.add_argument('--patch', action='store_true',
help='Increment the existing patch version by 1')
action_group.add_argument('--prerelease', nargs='?', const='increment',
help='''Increment prerelease version alpha->beta->rc->final,
Also it's possible to pass value explicitly''')
action_group.add_argument('--prerelease_number', action='store_true',
help='Increment prerelease number by 1')
action_group.add_argument('--current', '--show-current',
action='store_true', help='Display current version')
action_group.add_argument('--verify-current',
action='store_true', help='Check that all version numbers are consistent')
action_group.add_argument('--set', metavar='X.Y.Z',
help='Set the version to the specified version')
args = parser.parse_args()
version = get_current_version()
verify_only = False
if args.current:
print(version)
return
elif args.verify_current:
verify_only = True
elif args.prerelease_number:
version.increment_prerelease_number()
elif args.prerelease:
if args.prerelease == 'increment':
version.increment_prerelease()
else:
version.set_prerelease(args.prerelease)
elif args.patch:
version.increment_patch()
elif args.minor:
version.increment_minor()
elif args.major:
version.increment_major()
elif args.set is not None:
version.set(args.set)
else:
assert False, "Unreachable code"
if verify_only:
print(f'Verifying that version is {version}...')
else:
print(f'Bumping version to {version}...')
print()
success = True
for rule in REPLACEMENT_RULES:
if not rule.apply(version, verify_only=verify_only):
success = False
if not success:
if verify_only:
sys.exit("\nFailed to verify one or more files!")
else:
sys.exit("\nFailed to update one or more files!")
if __name__ == '__main__':
main()
Computing file changes ...