diff --git a/packages/service-integration/requirements/_base.in b/packages/service-integration/requirements/_base.in index a8955a4a9b5..fee8aa856e2 100644 --- a/packages/service-integration/requirements/_base.in +++ b/packages/service-integration/requirements/_base.in @@ -6,7 +6,9 @@ --requirement ../../../packages/models-library/requirements/_base.in click +cookiecutter docker # pytest-plugin +jinja2_time jsonschema # pytest-plugin pytest # pytest-plugin pyyaml diff --git a/packages/service-integration/requirements/_base.txt b/packages/service-integration/requirements/_base.txt index 44411a2736b..391819cffdd 100644 --- a/packages/service-integration/requirements/_base.txt +++ b/packages/service-integration/requirements/_base.txt @@ -1,20 +1,30 @@ arrow==1.3.0 - # via -r requirements/../../../packages/models-library/requirements/_base.in + # via + # -r requirements/../../../packages/models-library/requirements/_base.in + # cookiecutter + # jinja2-time attrs==23.2.0 # via # jsonschema # referencing +binaryornot==0.4.4 + # via cookiecutter certifi==2024.2.2 # via # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../requirements/constraints.txt # requests +chardet==5.2.0 + # via binaryornot charset-normalizer==3.3.2 # via requests click==8.1.7 # via # -r requirements/_base.in + # cookiecutter # typer +cookiecutter==2.6.0 + # via -r requirements/_base.in dnspython==2.6.1 # via email-validator docker==7.1.0 @@ -29,6 +39,14 @@ idna==3.7 # requests iniconfig==2.0.0 # via pytest +jinja2==3.1.4 + # via + # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt + # -c requirements/../../../requirements/constraints.txt + # cookiecutter + # jinja2-time +jinja2-time==0.2.0 + # via -r requirements/_base.in jsonschema==4.22.0 # via # -r requirements/../../../packages/models-library/requirements/_base.in @@ -37,6 +55,8 @@ jsonschema-specifications==2023.12.1 # via jsonschema markdown-it-py==3.0.0 # via rich +markupsafe==2.1.5 + # via jinja2 mdurl==0.1.2 # via markdown-it-py orjson==3.10.3 @@ -59,19 +79,26 @@ pytest==8.2.0 # via -r requirements/_base.in python-dateutil==2.9.0.post0 # via arrow +python-slugify==8.0.4 + # via cookiecutter pyyaml==6.0.1 # via # -c requirements/../../../packages/models-library/requirements/../../../requirements/constraints.txt # -c requirements/../../../requirements/constraints.txt # -r requirements/_base.in + # cookiecutter referencing==0.35.1 # via # jsonschema # jsonschema-specifications requests==2.32.2 - # via docker + # via + # cookiecutter + # docker rich==13.7.1 - # via typer + # via + # cookiecutter + # typer rpds-py==0.18.0 # via # jsonschema @@ -80,6 +107,8 @@ shellingham==1.5.4 # via typer six==1.16.0 # via python-dateutil +text-unidecode==1.3 + # via python-slugify tomli==2.0.1 # via pytest typer==0.12.3 diff --git a/packages/service-integration/scripts/ooil.bash b/packages/service-integration/scripts/ooil.bash index 7e5eb116d17..b4527683ef1 100755 --- a/packages/service-integration/scripts/ooil.bash +++ b/packages/service-integration/scripts/ooil.bash @@ -6,7 +6,7 @@ set -o nounset set -o pipefail IFS=$'\n\t' -IMAGE_NAME="${DOCKER_REGISTRY:-itisfoundation}/service-integration:${OOIL_IMAGE_TAG:-master-github-latest}" +IMAGE_NAME="${DOCKER_REGISTRY:-local}/service-integration:${OOIL_IMAGE_TAG:-production}" WORKDIR="$(pwd)" # @@ -20,6 +20,7 @@ WORKDIR="$(pwd)" run() { docker run \ --rm \ + --tty \ --volume="/etc/group:/etc/group:ro" \ --volume="/etc/passwd:/etc/passwd:ro" \ --user="$(id --user "$USER")":"$(id --group "$USER")" \ diff --git a/packages/service-integration/src/service_integration/cli.py b/packages/service-integration/src/service_integration/cli.py deleted file mode 100644 index a257be65c14..00000000000 --- a/packages/service-integration/src/service_integration/cli.py +++ /dev/null @@ -1,61 +0,0 @@ -# Allows entrypoint via python -m as well - - -import rich -import typer - -from ._meta import __version__ -from .commands import compose, config, metadata, run_creator, test -from .settings import AppSettings - -app = typer.Typer() - - -def _version_callback(value: bool): - if value: - rich.print(__version__) - raise typer.Exit - - -@app.callback() -def main( - ctx: typer.Context, - version: bool = typer.Option( - False, - "--version", - callback=_version_callback, - is_eager=True, - ), - registry_name: str = typer.Option( - None, - "--REGISTRY_NAME", - help="image registry name. Full url or prefix used as prefix in an image name", - ), - compose_version: str = typer.Option( - None, - "--COMPOSE_VERSION", - help="version used for docker compose specification", - ), -): - """o2s2parc service integration library""" - assert isinstance(version, bool | None) # nosec - - overrides = {} - if registry_name: - overrides["REGISTRY_NAME"] = registry_name - - if compose_version: - overrides["COMPOSE_VERSION"] = compose_version - - # save states - ctx.settings = AppSettings.parse_obj(overrides) - - -# new -app.command("compose")(compose.main) -app.command("config")(config.main) -app.command("test")(test.main) -# legacy -app.command("bump-version")(metadata.bump_version) -app.command("get-version")(metadata.get_version) -app.command("run-creator")(run_creator.main) diff --git a/packages/service-integration/src/service_integration/cli/__init__.py b/packages/service-integration/src/service_integration/cli/__init__.py new file mode 100644 index 00000000000..26b9697633c --- /dev/null +++ b/packages/service-integration/src/service_integration/cli/__init__.py @@ -0,0 +1,72 @@ +# Allows entrypoint via python -m as well + +from typing import Annotated + +import rich +import typer + +from .._meta import __version__ +from ..settings import AppSettings +from . import _compose_spec, _metadata, _run_creator, _test +from ._config import config_app + +app = typer.Typer() + + +def _version_callback(value: bool): # noqa: FBT002 + if value: + rich.print(__version__) + raise typer.Exit + + +@app.callback() +def main( + ctx: typer.Context, + registry_name: Annotated[ + str, + typer.Option( + "--REGISTRY_NAME", + help="image registry name. Full url or prefix used as prefix in an image name", + ), + ] = None, + compose_version: Annotated[ + str, + typer.Option( + "--COMPOSE_VERSION", + help="version used for docker compose specification", + ), + ] = None, + version: Annotated[ # noqa: FBT002 + bool, + typer.Option( + "--version", + callback=_version_callback, + is_eager=True, + ), + ] = False, +): + """o2s2parc service Integration Library (OOIL in short)""" + assert isinstance(version, bool | None) # nosec + + overrides = {} + if registry_name: + overrides["REGISTRY_NAME"] = registry_name + + if compose_version: + overrides["COMPOSE_VERSION"] = compose_version + + # save states + ctx.settings = AppSettings.parse_obj(overrides) + + +# +# REGISTER commands and/or sub-apps +# + +app.command("compose")(_compose_spec.create_compose) +app.add_typer(config_app, name="config", help="Manage osparc config files") +app.command("test")(_test.run_tests) +# legacy +app.command("bump-version")(_metadata.bump_version) +app.command("get-version")(_metadata.get_version) +app.command("run-creator")(_run_creator.run_creator) diff --git a/packages/service-integration/src/service_integration/commands/compose.py b/packages/service-integration/src/service_integration/cli/_compose_spec.py similarity index 81% rename from packages/service-integration/src/service_integration/commands/compose.py rename to packages/service-integration/src/service_integration/cli/_compose_spec.py index 3904828cad5..c2f2477c622 100644 --- a/packages/service-integration/src/service_integration/commands/compose.py +++ b/packages/service-integration/src/service_integration/cli/_compose_spec.py @@ -1,7 +1,8 @@ import subprocess -from datetime import datetime from pathlib import Path +from typing import Annotated +import arrow import rich import typer import yaml @@ -9,6 +10,7 @@ from rich.console import Console from ..compose_spec_model import ComposeSpecification +from ..errors import UndefinedOciImageSpecError from ..oci_image_spec import LS_LABEL_PREFIX, OCI_LABEL_PREFIX from ..osparc_config import ( OSPARC_CONFIG_DIRNAME, @@ -61,10 +63,10 @@ def create_docker_compose_image_spec( config_basedir = meta_config_path.parent - # required + # REQUIRED meta_cfg = MetadataConfig.from_yaml(meta_config_path) - # required + # REQUIRED if docker_compose_overwrite_path: docker_compose_overwrite_cfg = DockerComposeOverwriteConfig.from_yaml( docker_compose_overwrite_path @@ -74,11 +76,10 @@ def create_docker_compose_image_spec( service_name=meta_cfg.service_name() ) - # optional + # OPTIONAL runtime_cfg = None if service_config_path: try: - # TODO: should include default? runtime_cfg = RuntimeConfig.from_yaml(service_config_path) except FileNotFoundError: rich.print("No runtime config found (optional), using default.") @@ -90,13 +91,11 @@ def create_docker_compose_image_spec( (config_basedir / f"{OCI_LABEL_PREFIX}.yml").read_text() ) if not oci_spec: - msg = "Undefined OCI image spec" - raise ValueError(msg) + raise UndefinedOciImageSpecError oci_labels = to_labels(oci_spec, prefix_key=OCI_LABEL_PREFIX) extra_labels.update(oci_labels) - except (FileNotFoundError, ValueError): - + except (FileNotFoundError, UndefinedOciImageSpecError): try: # if not OCI, try label-schema ls_spec = yaml.safe_load( @@ -109,9 +108,11 @@ def create_docker_compose_image_spec( "No explicit config for OCI/label-schema found (optional), skipping OCI annotations." ) # add required labels - extra_labels[f"{LS_LABEL_PREFIX}.build-date"] = datetime.utcnow().strftime( - "%Y-%m-%dT%H:%M:%SZ" - ) + + # SEE https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys + # Format the datetime object as a string following RFC-3339 + rfc3339_format = arrow.now().format("YYYY-MM-DDTHH:mm:ssZ") + extra_labels[f"{LS_LABEL_PREFIX}.build-date"] = rfc3339_format extra_labels[f"{LS_LABEL_PREFIX}.schema-version"] = "1.0" extra_labels[f"{LS_LABEL_PREFIX}.vcs-ref"] = _run_git_or_empty_string( @@ -130,25 +131,28 @@ def create_docker_compose_image_spec( ) -def main( +def create_compose( ctx: typer.Context, - config_path: Path = typer.Option( - OSPARC_CONFIG_DIRNAME, - "-m", - "--metadata", - help="osparc config file or folder. " - "If the latter, it will scan for configs using the glob pattern 'config_path/**/metadata.yml' ", - ), - to_spec_file: Path = typer.Option( - Path("docker-compose.yml"), - "-f", - "--to-spec-file", - help="Output docker-compose image spec", - ), + config_path: Annotated[ + Path, + typer.Option( + "-m", + "--metadata", + help="osparc config file or folder. " + "If the latter, it will scan for configs using the glob pattern 'config_path/**/metadata.yml' ", + ), + ] = Path(OSPARC_CONFIG_DIRNAME), + to_spec_file: Annotated[ + Path, + typer.Option( + "-f", + "--to-spec-file", + help="Output docker-compose image spec", + ), + ] = Path("docker-compose.yml"), ): - """create docker image/runtime compose-specs from an osparc config""" + """Creates the docker image/runtime compose-spec file from an .osparc config""" - # TODO: all these MUST be replaced by osparc_config.ConfigFilesStructure if not config_path.exists(): msg = "Invalid path to metadata file or folder" raise typer.BadParameter(msg) @@ -168,10 +172,10 @@ def main( config_name = meta_config.parent.name configs_kwargs_map[config_name] = {} - # load meta [required] + # load meta REQUIRED configs_kwargs_map[config_name]["meta_config_path"] = meta_config - # others [optional] + # others OPTIONAL for file_name, arg_name in ( ("docker-compose.overwrite.yml", "docker_compose_overwrite_path"), ("runtime.yml", "service_config_path"), @@ -194,7 +198,6 @@ def main( settings, **configs_kwargs_map[config_name] ).dict(exclude_unset=True) - # FIXME: shaky! why first decides ?? if n == 0: compose_spec_dict = nth_compose_spec else: diff --git a/packages/service-integration/src/service_integration/commands/config.py b/packages/service-integration/src/service_integration/cli/_config.py similarity index 55% rename from packages/service-integration/src/service_integration/commands/config.py rename to packages/service-integration/src/service_integration/cli/_config.py index e1e5b8ef5b1..9932140a48e 100644 --- a/packages/service-integration/src/service_integration/commands/config.py +++ b/packages/service-integration/src/service_integration/cli/_config.py @@ -1,27 +1,41 @@ import json from pathlib import Path -from typing import Final +from typing import Annotated, Final import rich import typer import yaml -from pydantic import ValidationError -from pydantic.main import BaseModel +from pydantic import BaseModel from ..compose_spec_model import ComposeSpecification +from ..errors import InvalidLabelsError from ..osparc_config import ( + OSPARC_CONFIG_COMPOSE_SPEC_NAME, OSPARC_CONFIG_DIRNAME, + OSPARC_CONFIG_METADATA_NAME, + OSPARC_CONFIG_RUNTIME_NAME, DockerComposeOverwriteConfig, MetadataConfig, RuntimeConfig, ) -def create_osparc_specs( +def _get_labels_or_raise(build_labels) -> dict[str, str]: + if isinstance(build_labels, list): + return dict(item.strip().split("=") for item in build_labels) + if isinstance(build_labels, dict): + return build_labels + if labels__root__ := build_labels.__root__: + assert isinstance(labels__root__, dict) # nosec + return labels__root__ + raise InvalidLabelsError(build_labels=build_labels) + + +def _create_config_from_compose_spec( compose_spec_path: Path, - docker_compose_overwrite_path: Path = Path("docker-compose.overwrite.yml"), - metadata_path: Path = Path("metadata.yml"), - service_specs_path: Path = Path("runtime-spec.yml"), + docker_compose_overwrite_path: Path, + metadata_path: Path, + service_specs_path: Path, ): rich.print(f"Creating osparc config files from {compose_spec_path}") @@ -49,22 +63,12 @@ def _save(service_name: str, filename: Path, model: BaseModel): for service_name in compose_spec.services: try: - labels: dict[str, str] = {} if build_labels := compose_spec.services[ service_name ].build.labels: # AttributeError if build is str - if isinstance(build_labels, list): - labels = dict(item.strip().split("=") for item in build_labels) - elif isinstance(build_labels, dict): - labels = build_labels - elif labels__root__ := build_labels.__root__: - assert isinstance(labels__root__, dict) # nosec - labels = labels__root__ - else: - msg = f"Invalid build labels {build_labels}" - raise ValueError(msg) + labels: dict[str, str] = _get_labels_or_raise(build_labels) meta_cfg = MetadataConfig.from_labels_annotations(labels) _save(service_name, metadata_path, meta_cfg) @@ -82,7 +86,11 @@ def _save(service_name: str, filename: Path, model: BaseModel): runtime_cfg = RuntimeConfig.from_labels_annotations(labels) _save(service_name, service_specs_path, runtime_cfg) - except (AttributeError, ValidationError, TypeError, ValueError) as err: + except ( # noqa: PERF203 + AttributeError, + TypeError, + ValueError, + ) as err: rich.print( f"WARNING: failure producing specs for {service_name}: {err}" ) @@ -90,30 +98,46 @@ def _save(service_name: str, filename: Path, model: BaseModel): rich.print("osparc config files created") -def main( - from_spec_file: Path = typer.Option( - Path("docker-compose.yml"), - "-f", - "--from-spec-file", - help="docker-compose used to deduce osparc config", - ), +config_app = typer.Typer() + + +@config_app.command(name="create") +def create_config( + from_spec_file: Annotated[ + Path, + typer.Option( + "-f", + "--from-spec-file", + help="docker-compose used to deduce osparc config", + ), + ] = Path("docker-compose.yml"), ): - """Creates osparc config from complete docker compose-spec""" - # TODO: sync defaults among CLI commands + """Creates osparc configuration folder from a complete docker compose-spec""" config_dir = from_spec_file.parent / OSPARC_CONFIG_DIRNAME - project_cfg_path = config_dir / "docker-compose.overwrite.yml" - meta_cfg_path = config_dir / "metadata.yml" - runtime_cfg_path = config_dir / "runtime.yml" + project_cfg_path = config_dir / OSPARC_CONFIG_COMPOSE_SPEC_NAME + meta_cfg_path = config_dir / OSPARC_CONFIG_METADATA_NAME + runtime_cfg_path = config_dir / OSPARC_CONFIG_RUNTIME_NAME meta_cfg_path.parent.mkdir(parents=True, exist_ok=True) runtime_cfg_path.parent.mkdir(parents=True, exist_ok=True) rich.print(f"Creating {config_dir} from {from_spec_file} ...") - create_osparc_specs( + _create_config_from_compose_spec( from_spec_file, project_cfg_path, meta_cfg_path, runtime_cfg_path ) -if __name__ == "__main__": - # pylint: disable=no-value-for-parameter - main() +_COOKIECUTTER_GITHUB_URL = "gh:itisfoundation/cookiecutter-osparc-service" + + +@config_app.command(name="init") +def init_config( + template: Annotated[ + str, typer.Option(help="Github repo or path to the template") + ] = _COOKIECUTTER_GITHUB_URL, + checkout: Annotated[str, typer.Option(help="Branch if different from main")] = None, +): + """runs cookie-cutter""" + from cookiecutter.main import cookiecutter + + cookiecutter(template, checkout=checkout) diff --git a/packages/service-integration/src/service_integration/commands/metadata.py b/packages/service-integration/src/service_integration/cli/_metadata.py similarity index 63% rename from packages/service-integration/src/service_integration/commands/metadata.py rename to packages/service-integration/src/service_integration/cli/_metadata.py index eb6e153b7f5..a06504b3b99 100644 --- a/packages/service-integration/src/service_integration/commands/metadata.py +++ b/packages/service-integration/src/service_integration/cli/_metadata.py @@ -1,13 +1,12 @@ from collections import OrderedDict from enum import Enum from pathlib import Path +from typing import Annotated import rich import typer -import yaml -from models_library.services import ServiceDockerData -from ..osparc_config import OSPARC_CONFIG_DIRNAME +from ..osparc_config import OSPARC_CONFIG_DIRNAME, MetadataConfig from ..versioning import bump_version_string from ..yaml_utils import ordered_safe_dump, ordered_safe_load @@ -24,21 +23,23 @@ class UpgradeTags(str, Enum): def bump_version( - target_version: TargetVersionChoices = typer.Argument( - TargetVersionChoices.SEMANTIC_VERSION - ), - upgrade: UpgradeTags = typer.Option(..., case_sensitive=False), - metadata_file: Path = typer.Option( - "metadata/metadata.yml", - help="The metadata yaml file", - ), + upgrade: Annotated[UpgradeTags, typer.Option(case_sensitive=False)], + metadata_file: Annotated[ + Path, + typer.Option( + help="The metadata yaml file", + ), + ] = Path("metadata/metadata.yml"), + target_version: Annotated[ + TargetVersionChoices, typer.Argument() + ] = TargetVersionChoices.SEMANTIC_VERSION, ): """Bumps target version in metadata (legacy)""" # load raw_data: OrderedDict = ordered_safe_load(metadata_file.read_text()) # parse and validate - metadata = ServiceDockerData(**raw_data) + metadata = MetadataConfig(**raw_data) # get + bump + set attrname = target_version.replace("-", "_") @@ -54,18 +55,20 @@ def bump_version( def get_version( - target_version: TargetVersionChoices = typer.Argument( - TargetVersionChoices.SEMANTIC_VERSION - ), - metadata_file: Path = typer.Option( - f"{OSPARC_CONFIG_DIRNAME}/metadata.yml", - help="The metadata yaml file", - ), + target_version: Annotated[ + TargetVersionChoices, typer.Argument() + ] = TargetVersionChoices.SEMANTIC_VERSION, + metadata_file: Annotated[ + Path, + typer.Option( + help="The metadata yaml file", + ), + ] = Path(f"{OSPARC_CONFIG_DIRNAME}/metadata.yml"), ): """Prints to output requested version (legacy)""" # parse and validate - metadata = ServiceDockerData(**yaml.safe_load(metadata_file.read_text())) + metadata = MetadataConfig.from_yaml(metadata_file) attrname = target_version.replace("-", "_") current_version: str = getattr(metadata, attrname) diff --git a/packages/service-integration/src/service_integration/commands/run_creator.py b/packages/service-integration/src/service_integration/cli/_run_creator.py similarity index 79% rename from packages/service-integration/src/service_integration/commands/run_creator.py rename to packages/service-integration/src/service_integration/cli/_run_creator.py index 3b08948eeec..8cadef194a4 100644 --- a/packages/service-integration/src/service_integration/commands/run_creator.py +++ b/packages/service-integration/src/service_integration/cli/_run_creator.py @@ -1,10 +1,11 @@ import stat from pathlib import Path +from typing import Annotated import typer import yaml -from ..osparc_config import OSPARC_CONFIG_DIRNAME +from ..osparc_config import OSPARC_CONFIG_DIRNAME, OSPARC_CONFIG_METADATA_NAME def get_input_config(metadata_file: Path) -> dict: @@ -16,17 +17,21 @@ def get_input_config(metadata_file: Path) -> dict: return inputs -def main( - metadata_file: Path = typer.Option( - f"{OSPARC_CONFIG_DIRNAME}/metadata.yml", - "--metadata", - help="The metadata yaml of the node", - ), - run_script_file_path: Path = typer.Option( - ..., - "--runscript", - help="Path to the run script ", - ), +def run_creator( + run_script_file_path: Annotated[ + Path, + typer.Option( + "--runscript", + help="Path to the run script ", + ), + ], + metadata_file: Annotated[ + Path, + typer.Option( + "--metadata", + help="The metadata yaml of the node", + ), + ] = Path(f"{OSPARC_CONFIG_DIRNAME}/{OSPARC_CONFIG_METADATA_NAME}"), ): """Creates a sh script that uses jq tool to retrieve variables to use in sh from a json file for use in an osparc service (legacy). @@ -79,8 +84,3 @@ def main( run_script_file_path.write_text(shell_script) st = run_script_file_path.stat() run_script_file_path.chmod(st.st_mode | stat.S_IEXEC) - - -if __name__ == "__main__": - # pylint: disable=no-value-for-parameter - main() diff --git a/packages/service-integration/src/service_integration/commands/test.py b/packages/service-integration/src/service_integration/cli/_test.py similarity index 67% rename from packages/service-integration/src/service_integration/commands/test.py rename to packages/service-integration/src/service_integration/cli/_test.py index 3bf25551dc2..cb999b32307 100644 --- a/packages/service-integration/src/service_integration/commands/test.py +++ b/packages/service-integration/src/service_integration/cli/_test.py @@ -1,15 +1,19 @@ from pathlib import Path +from typing import Annotated import rich import typer from ..service import pytest_runner +test_app = typer.Typer() -def main( - service_dir: Path = typer.Argument( - ..., help="Root directory of the service under test" - ), + +@test_app.command("run") +def run_tests( + service_dir: Annotated[ + Path, typer.Argument(help="Root directory of the service under test") + ], ): """Runs tests against service directory""" diff --git a/packages/service-integration/src/service_integration/commands/__init__.py b/packages/service-integration/src/service_integration/commands/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/service-integration/src/service_integration/errors.py b/packages/service-integration/src/service_integration/errors.py index e9a857edc1c..8d216b7d918 100644 --- a/packages/service-integration/src/service_integration/errors.py +++ b/packages/service-integration/src/service_integration/errors.py @@ -7,3 +7,11 @@ class ServiceIntegrationError(PydanticErrorMixin, RuntimeError): class ConfigNotFoundError(ServiceIntegrationError): msg_template = "could not find any osparc config under {basedir}" + + +class UndefinedOciImageSpecError(ServiceIntegrationError): + ... + + +class InvalidLabelsError(PydanticErrorMixin, ValueError): + template_msg = "Invalid build labels {build_labels}" diff --git a/packages/service-integration/src/service_integration/osparc_config.py b/packages/service-integration/src/service_integration/osparc_config.py index 17c5f1d181f..b3eb998e7dd 100644 --- a/packages/service-integration/src/service_integration/osparc_config.py +++ b/packages/service-integration/src/service_integration/osparc_config.py @@ -14,7 +14,7 @@ import logging from pathlib import Path -from typing import Any, Literal +from typing import Any, Final, Literal from models_library.callbacks_mapping import CallbacksMapping from models_library.service_settings_labels import ( @@ -48,7 +48,10 @@ _logger = logging.getLogger(__name__) -OSPARC_CONFIG_DIRNAME = ".osparc" +OSPARC_CONFIG_DIRNAME: Final[str] = ".osparc" +OSPARC_CONFIG_COMPOSE_SPEC_NAME: Final[str] = "docker-compose.overwrite.yml" +OSPARC_CONFIG_METADATA_NAME: Final[str] = "metadata.yml" +OSPARC_CONFIG_RUNTIME_NAME: Final[str] = "runtime.yml" SERVICE_KEY_FORMATS = { @@ -94,7 +97,7 @@ class MetadataConfig(ServiceDockerData): @validator("contact") @classmethod - def check_contact_in_authors(cls, v, values): + def _check_contact_in_authors(cls, v, values): """catalog service relies on contact and author to define access rights""" authors_emails = {author.email for author in values["authors"]} if v not in authors_emails: diff --git a/packages/service-integration/tests/test_command_config.py b/packages/service-integration/tests/test_command_config.py index 08967ba63e6..f6243efd59f 100644 --- a/packages/service-integration/tests/test_command_config.py +++ b/packages/service-integration/tests/test_command_config.py @@ -27,6 +27,7 @@ def test_create_new_osparc_config( result = run_program_with_args( "config", + "create", "--from-spec-file", str(tmp_compose_spec), ) diff --git a/packages/service-integration/tests/test_command_metadata.py b/packages/service-integration/tests/test_command_metadata.py index 24073dcbc42..7204fc953c6 100644 --- a/packages/service-integration/tests/test_command_metadata.py +++ b/packages/service-integration/tests/test_command_metadata.py @@ -8,7 +8,7 @@ import pytest import yaml -from service_integration.commands.metadata import TargetVersionChoices +from service_integration.cli._metadata import TargetVersionChoices @pytest.fixture