diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e1b848..1ac51c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ main ] + branches: [ main, dev ] pull_request: - branches: [ main ] + branches: [ main, dev ] jobs: test: @@ -91,4 +91,4 @@ jobs: run: | pip install pytest pip install pytest-cov - python tests/test_validate_input_file.py tests/validate_input_file.py tests/in/docker-compose.yaml + pytest diff --git a/compose_viz/__init__.py b/compose_viz/__init__.py new file mode 100644 index 0000000..bce9bc5 --- /dev/null +++ b/compose_viz/__init__.py @@ -0,0 +1,2 @@ +__app_name__ = "compose_viz" +__version__ = "0.1.0" diff --git a/compose_viz/__main__.py b/compose_viz/__main__.py new file mode 100644 index 0000000..6f832b4 --- /dev/null +++ b/compose_viz/__main__.py @@ -0,0 +1,9 @@ +from compose_viz import cli, __app_name__ + + +def main() -> None: + cli.app(prog_name=__app_name__) + + +if __name__ == "__main__": + main() diff --git a/compose_viz/cli.py b/compose_viz/cli.py new file mode 100644 index 0000000..49cc8f6 --- /dev/null +++ b/compose_viz/cli.py @@ -0,0 +1,59 @@ +from ast import parse +from enum import Enum +import typer +from typing import Optional +from compose_viz import __app_name__, __version__ +from compose_viz.compose import Compose +from compose_viz.parser import Parser + + +class VisualizationFormats(str, Enum): + png = "PNG" + dot = "DOT" + + +app = typer.Typer( + invoke_without_command=True, + no_args_is_help=True, + subcommand_metavar="", + add_completion=False, +) + + +def _version_callback(value: bool) -> None: + if value: + typer.echo(f"{__app_name__} {__version__}") + raise typer.Exit() + + +@app.callback() +def compose_viz( + input_path: str, + output_path: Optional[str] = typer.Option( + None, + "--output_path", + "-o", + help="Output path for the generated visualization.", + ), + format: VisualizationFormats = typer.Option( + "PNG", + "--format", + "-m", + help="Output format for the generated visualization.", + ), + _: Optional[bool] = typer.Option( + None, + "--version", + "-v", + help="Show the version of compose_viz.", + callback=_version_callback, + is_eager=True, + ) +) -> None: + parser = Parser() + compose = parser.parse(input_path) + + if compose: + typer.echo(f"Successfully parsed {input_path}") + + raise typer.Exit() diff --git a/compose_viz/compose.py b/compose_viz/compose.py new file mode 100644 index 0000000..6c65a8a --- /dev/null +++ b/compose_viz/compose.py @@ -0,0 +1,10 @@ +from typing import List +from compose_viz.service import Service + + +class Compose: + def __init__(self, services: List[Service]) -> None: + self.services = services + + def extract_networks(self) -> List[str]: + raise NotImplementedError diff --git a/compose_viz/parser.py b/compose_viz/parser.py new file mode 100644 index 0000000..1f44bbc --- /dev/null +++ b/compose_viz/parser.py @@ -0,0 +1,10 @@ +from compose_viz.compose import Compose + + +class Parser: + def __init__(self): + pass + + def parse(self, file_path: str) -> Compose: + # validate input file using `docker-compose config -q sys.argv[1]` first + raise NotImplementedError diff --git a/compose_viz/service.py b/compose_viz/service.py new file mode 100644 index 0000000..6e42483 --- /dev/null +++ b/compose_viz/service.py @@ -0,0 +1,13 @@ +from typing import List + + +class Service: + def __init__(self, name: str, image: str, ports: List[str] = [], networks: List[str] = [], volumes: List[str] = [], depends_on: List[str] = [], links: List[str] = [], extends: List[str] = []) -> None: + self.name = name + self.image = image + self.ports = ports + self.networks = networks + self.volumes = volumes + self.depends_on = depends_on + self.links = links + self.extends = extends diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..cc809fe --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,14 @@ +import pytest +from typer.testing import CliRunner +from compose_viz import cli + + +runner = CliRunner() + + +def test_cli(): + input_path = "tests/in/000001.yaml" + result = runner.invoke(cli.app, [input_path]) + + assert result.exit_code == 0 + assert f"Successfully parsed {input_path}\n" in result.stdout diff --git a/tests/test_parse_file.py b/tests/test_parse_file.py new file mode 100644 index 0000000..4854c47 --- /dev/null +++ b/tests/test_parse_file.py @@ -0,0 +1,29 @@ +from typer.testing import CliRunner +from compose_viz.parser import Parser +from compose_viz.compose import Compose +from compose_viz.service import Service + + +def test_parse_file(): + expected: Compose = Compose([ + Service( + name='frontend', + image='awesome/webapp', + networks=['front-tier', 'back-tier'], + ), + Service( + name='monitoring', + image='awesome/monitoring', + networks=['admin'], + ), + Service( + name='backend', + image='awesome/backend', + networks=['back-tier', 'admin'], + ), + ]) + + parser = Parser() + actual = parser.parse('tests/in/000001.yaml') + + assert actual == expected diff --git a/tests/test_validate_input_file.py b/tests/test_validate_input_file.py deleted file mode 100644 index 54140c0..0000000 --- a/tests/test_validate_input_file.py +++ /dev/null @@ -1,5 +0,0 @@ -import sys -import pytest - -if __name__ == '__main__': - pytest.main([sys.argv[1]]) \ No newline at end of file diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 0000000..b1b1b49 --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,13 @@ +import pytest +from typer.testing import CliRunner +from compose_viz import cli, __app_name__, __version__ + + +runner = CliRunner() + + +def test_version(): + result = runner.invoke(cli.app, ["--version"]) + + assert result.exit_code == 0 + assert f"{__app_name__} {__version__}\n" in result.stdout diff --git a/tests/validate_input_file.py b/tests/validate_input_file.py deleted file mode 100644 index 73a4f3e..0000000 --- a/tests/validate_input_file.py +++ /dev/null @@ -1,6 +0,0 @@ -import os -import sys - -def test_validate_input_file(): - process = os.system("docker-compose -f " + sys.argv[2] + " config -q") - assert process == 0 \ No newline at end of file