diff --git a/docker-compose.yml b/docker-compose.yml index 6fb2916c..7ff29ae4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,7 +27,7 @@ services: cvat_server: container_name: cvat_server - image: cvat/server:${CVAT_VERSION:-latest} + image: cvat/server:${CVAT_VERSION:-dev} restart: always depends_on: - cvat_redis @@ -59,7 +59,7 @@ services: cvat_utils: container_name: cvat_utils - image: cvat/server:${CVAT_VERSION:-latest} + image: cvat/server:${CVAT_VERSION:-dev} restart: always depends_on: - cvat_redis @@ -79,7 +79,7 @@ services: cvat_worker_default: container_name: cvat_worker_default - image: cvat/server:${CVAT_VERSION:-latest} + image: cvat/server:${CVAT_VERSION:-dev} restart: always depends_on: - cvat_redis @@ -100,7 +100,7 @@ services: cvat_worker_low: container_name: cvat_worker_low - image: cvat/server:${CVAT_VERSION:-latest} + image: cvat/server:${CVAT_VERSION:-dev} restart: always depends_on: - cvat_redis @@ -121,7 +121,7 @@ services: cvat_ui: container_name: cvat_ui - image: cvat/ui:${CVAT_VERSION:-latest} + image: cvat/ui:${CVAT_VERSION:-dev} restart: always depends_on: - cvat_server diff --git a/utils/update_version/update_version.py b/utils/update_version/update_version.py new file mode 100644 index 00000000..d0507ec7 --- /dev/null +++ b/utils/update_version/update_version.py @@ -0,0 +1,211 @@ +import argparse +from dataclasses import dataclass +from pathlib import Path +import re + + +CVAT_VERSION_PATTERN = r'VERSION\s*=\s*\((\d+),\s*(\d*),\s*(\d+),\s*[\',\"](\w+)[\',\"],\s*(\d+)\)' +COMPOSE_VERSION_PATTERN = r'(\$\{CVAT_VERSION:-)([\w.]+)(\})' + +@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): + 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_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() + +def update_compose_config(new_version: Version) -> None: + compose_file = get_compose_filename() + with open(compose_file, 'r') as fp: + compose_text = fp.read() + + if new_version.prerelease == 'final': + new_version_repr = new_version.compose_repr() + else: + new_version_repr = 'dev' + + match = re.search(COMPOSE_VERSION_PATTERN, compose_text) + if not match: + raise RuntimeError('Cannot match version pattern') + + if match[2] != new_version_repr: + compose_text = re.sub(COMPOSE_VERSION_PATTERN, f'\\g<1>{new_version_repr}\\g<3>', compose_text) + with open(compose_file, 'w') as fp: + fp.write(compose_text) + + print(f'\u2714 {compose_file} was updated. {match[2]} -> {new_version_repr}\n') + + else: + print(f'\u2714 {compose_file} no need to update.') + +def update_cvat_version(old_version: str, new_version: Version) -> None: + version_file = get_cvat_version_filename() + with open(version_file, 'r') as fp: + version_text = fp.read() + + new_version_str = f'VERSION = {new_version.cvat_repr()}' + version_text = version_text.replace(old_version, new_version_str) + + with open(version_file, 'w') as fp: + fp.write(version_text) + + print(f'\u2714 {version_file} was updated. {old_version} -> {new_version_str}\n') + +def verify_input(version_types: dict, args: dict) -> None: + versions_to_bump = [args[v_type] for v_type in version_types] + i = iter(versions_to_bump) + if not any(i): + raise ValueError(f'One of {list(version_types)} options must be specified') + if any(i): + raise ValueError(f'Only one of {list(version_types)} options accepted') + +def get_cvat_version_filename() -> Path: + return Path(__file__).resolve().parents[2] / 'cvat' / '__init__.py' + +def get_compose_filename() -> Path: + return Path(__file__).resolve().parents[2] / 'docker-compose.yml' + +def get_current_version() -> tuple[str, Version]: + version_file = get_cvat_version_filename() + + with open(version_file, 'r') as fp: + version_text = fp.read() + + match = re.search(CVAT_VERSION_PATTERN, version_text) + if not match: + raise RuntimeError(f'Failed to find version in {version_file}') + + version = Version(int(match[1]), int(match[2]), int(match[3]), match[4], int(match[5])) + return match[0], version + +def main() -> None: + version_types = { + 'major': { + 'action': 'store_true', + 'help': 'Increment the existing major version by 1', + }, + 'minor': { + 'action': 'store_true', + 'help': 'Increment the existing minor version by 1', + }, + 'patch': { + 'action': 'store_true', + 'help': 'Increment the existing patch version by 1', + }, + 'prerelease': { + 'nargs': '?', + 'const': 'increment', + 'help': '''Increment prerelease version alpha->beta->rc->final, + Also it's possible to pass value explicitly''', + }, + 'prerelease_number': { + 'action': 'store_true', + 'help': 'Increment prerelease number by 1', + }, + } + + parser = argparse.ArgumentParser(description='Bump CVAT version') + + for v, v_config in version_types.items(): + parser.add_argument(f'--{v}', **v_config) + + parser.add_argument('--current', '--show-current', + action='store_true', help='Display current version') + + args = parser.parse_args() + + version_str, version = get_current_version() + + if args.current: + print(version) + return + + try: + verify_input(version_types, vars(args)) + except ValueError as e: + print(f'\u2716 ERROR: {e}\n') + parser.print_help() + return + + if 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() + + print(f'\u2714 Bump version to {version}\n') + + update_cvat_version(version_str, version) + update_compose_config(version) + +if __name__ == '__main__': + main()