diff --git a/tools/deployment-cli-tools/ch_cli_tools/common_types.py b/tools/deployment-cli-tools/ch_cli_tools/common_types.py index 6c8e2c4c..dbff707a 100644 --- a/tools/deployment-cli-tools/ch_cli_tools/common_types.py +++ b/tools/deployment-cli-tools/ch_cli_tools/common_types.py @@ -51,18 +51,4 @@ def to_dict(self) -> dict: 'version': self.version, 'inferred': self.inferred, 'templates': [str(template) for template in self.templates], - } - - @classmethod - def migrate(cls, data: dict) -> tuple[dict, bool]: - data_copy = copy.deepcopy(data) - update_manifest = False - - if data_copy['version'] < '2': - update_manifest = True - data_copy['templates'] = [ - template if template != 'django-app' else 'django-fastapi' - for template in data_copy['templates'] - ] - - return data_copy, update_manifest \ No newline at end of file + } \ No newline at end of file diff --git a/tools/deployment-cli-tools/ch_cli_tools/manifest.py b/tools/deployment-cli-tools/ch_cli_tools/manifest.py new file mode 100644 index 00000000..155dc750 --- /dev/null +++ b/tools/deployment-cli-tools/ch_cli_tools/manifest.py @@ -0,0 +1,135 @@ +import abc +import copy +import logging +import pathlib +from typing import Iterable +from ruamel.yaml.error import YAMLError +from .common_types import CloudHarnessManifest, TemplateType +from .utils import load_yaml, save_yaml + + +def get_manifest(app_path: pathlib.Path) -> CloudHarnessManifest: + manifest_file = app_path / '.ch-manifest' + + try: + manifest_data = load_yaml(manifest_file) + return CloudHarnessManifest.from_dict(manifest_data) + except (FileNotFoundError, YAMLError): + logging.info(f'Could not load manifest file {manifest_file}, inferring manifest from app structure...') + manifest = CloudHarnessManifest( + app_name=app_path.name, + inferred=True, + templates=infer_templates(app_path), + ) + save_yaml(manifest_file, manifest.to_dict()) + return manifest + + +def load_manifest(manifest_file: pathlib.Path) -> dict: + manifest_data = load_yaml(manifest_file) + migrated_data = migrate_manifest_data(manifest_data) + + if manifest_data != migrated_data: + save_yaml(manifest_file, migrated_data) + + return migrated_data + + +def migrate_manifest_data(data: dict) -> dict: + data = copy.deepcopy(data) + data_version = data['version'] + migrations = [ + migration for migration in _MIGRATIONS_LIST + if data_version < migration.change_version + ] + + for migration in migrations: + migration.migrate(data) + + return data + + +def infer_templates(app_path: pathlib.Path) -> list[str]: + return [ + TemplateType.BASE, + *infer_webapp_template(app_path), + *infer_server_template(app_path), + *infer_database_template(app_path), + ] + + +def infer_webapp_template(app_path: pathlib.Path) -> Iterable[str]: + frontend_path = app_path / 'frontend' + if frontend_path.exists(): + yield TemplateType.WEBAPP + + +def infer_server_template(app_path: pathlib.Path) -> Iterable[str]: + backend_path = app_path / 'backend' + manage_path = backend_path / 'manage.py' + + if manage_path.exists(): + yield from infer_django_template(backend_path) + return + + server_path = app_path / 'server' + if server_path.exists() or backend_path.exists(): + yield TemplateType.FLASK_SERVER + + +def infer_django_template(backend_path: pathlib.Path) -> Iterable[str]: + requirements_path = backend_path / 'requirements.txt' + requirements = requirements_path.read_text() + + if 'django-ninja' in requirements: + yield TemplateType.DJANGO_NINJA + else: + yield TemplateType.DJANGO_FASTAPI + + +def infer_database_template(app_path: pathlib.Path) -> Iterable[str]: + values_file = app_path / 'deploy' / 'values.yaml' + + try: + values_data = load_yaml(values_file) + database_config = values_data['harness']['database'] + if not database_config['auto']: + return + + database_type = database_config['type'] + database_type_to_template_map = { + 'mongo': TemplateType.DB_MONGO, + 'neo4j': TemplateType.DB_NEO4J, + 'postgres': TemplateType.DB_POSTGRES, + } + + if database_type in database_type_to_template_map: + yield database_type_to_template_map[database_type] + + except(FileNotFoundError, YAMLError, KeyError): + pass + + +class ManifestMigration(abc.ABC): + @property + @abc.abstractmethod + def change_version(self) -> str: + ... + + @abc.abstractmethod + def migrate(data: dict) -> None: + ... + + +class NameChangeFromDjangoAppToDjangoFastapi(ManifestMigration): + change_version = '2' + + def migrate(data): + data['templates'] = [ + template if template != 'django-app' else 'django-fastapi' + for template in data['templates'] + ] + +_MIGRATIONS_LIST: list[ManifestMigration] = [ + NameChangeFromDjangoAppToDjangoFastapi(), +] \ No newline at end of file diff --git a/tools/deployment-cli-tools/harness-generate b/tools/deployment-cli-tools/harness-generate index 545e6612..705a40c6 100644 --- a/tools/deployment-cli-tools/harness-generate +++ b/tools/deployment-cli-tools/harness-generate @@ -9,12 +9,12 @@ import pathlib import shutil import logging from typing import Callable, Optional -from ruamel.yaml.error import YAMLError from ch_cli_tools.openapi import LIB_NAME, generate_openapi_from_ninja_schema, generate_python_client, generate_server, generate_fastapi_server, \ get_dependencies, generate_ts_client, generate_model -from ch_cli_tools.utils import copymergedir, load_yaml, save_yaml -from ch_cli_tools.common_types import CloudHarnessManifest, TemplateType +from ch_cli_tools.utils import copymergedir +from ch_cli_tools.common_types import TemplateType +from ch_cli_tools.manifest import get_manifest def main(): @@ -152,7 +152,7 @@ def generate_servers( """ Generates server stubs """ - openapi_files = get_openapi_file_paths(root_path) + openapi_files = [path for path in root_path.glob('applications/*/api/*.yaml')] for openapi_file in openapi_files: app_path = openapi_file.parent.parent @@ -207,10 +207,6 @@ def generate_clients( aggregate_packages(client_src_path, client_lib_name) -def get_openapi_file_paths(root_path: pathlib.Path) -> list[pathlib.Path]: - return [path for path in root_path.glob('applications/*/api/*.yaml')] - - def aggregate_packages(client_source_path: pathlib.Path, lib_name=LIB_NAME): client_source_path.mkdir(parents=True, exist_ok=True) @@ -278,80 +274,5 @@ def aggregate_packages(client_source_path: pathlib.Path, lib_name=LIB_NAME): shutil.rmtree(temp_module_path) -def get_manifest(app_path: pathlib.Path) -> CloudHarnessManifest: - manifest_file = app_path / '.ch-manifest' - - try: - manifest_data = load_yaml(manifest_file) - manifest_data, update_manifest = CloudHarnessManifest.migrate(manifest_data) - if update_manifest: - save_yaml(manifest_file, manifest_data) - manifest = CloudHarnessManifest.from_dict(manifest_data) - except (FileNotFoundError, YAMLError): - logging.info(f'Could not find manifest file {manifest_file}, inferring manifest from app structure...') - manifest = CloudHarnessManifest( - app_name=app_path.name, - inferred=True, - templates=infer_templates(app_path), - ) - save_yaml(manifest_file, manifest.to_dict()) - - return manifest - - -def infer_templates(app_path: pathlib.Path) -> list[str]: - templates = [TemplateType.BASE] - - infer_webapp_template(app_path, templates) - infer_server_template(app_path, templates) - infer_database_template(app_path, templates) - - return templates - - -def infer_webapp_template(app_path: pathlib.Path, templates: list[str]) -> None: - frontend_path = app_path / 'frontend' - if frontend_path.exists(): - templates.append(TemplateType.WEBAPP) - - -def infer_server_template(app_path: pathlib.Path, templates: list[str]) -> None: - backend_path = app_path / 'backend' - manage_path = backend_path / 'manage.py' - - if manage_path.exists(): - requirements_path = backend_path / 'requirements.txt' - requirements = requirements_path.read_text() - if 'ninja' in requirements: - templates.append(TemplateType.DJANGO_NINJA) - else: - templates.append(TemplateType.DJANGO_FASTAPI) - return - - server_path = app_path / 'server' - if server_path.exists() or backend_path.exists(): - templates.append(TemplateType.FLASK_SERVER) - - -def infer_database_template(app_path: pathlib.Path, templates: list[str]) -> None: - values_file = app_path / 'deploy' / 'values.yaml' - - try: - values_data = load_yaml(values_file) - database_config = values_data['harness']['database'] - if not database_config['auto']: - return - - database_type = database_config['type'] - if database_type == 'mongo': - templates.append(TemplateType.DB_MONGO) - if database_type == 'neo4j': - templates.append(TemplateType.DB_NEO4J) - if database_type == 'postgres': - templates.append(TemplateType.DB_POSTGRES) - except (FileNotFoundError, YAMLError, KeyError): - pass - - if __name__ == "__main__": main()