Merge pull request #5 from compose-viz/cd

chore: update cd configuration
This commit is contained in:
Xyphuz 2022-05-14 22:15:10 +08:00 committed by GitHub
commit 7557de0ecb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 446 additions and 17 deletions

17
.github/workflows/cd.yml vendored Normal file
View file

@ -0,0 +1,17 @@
name: CD
on:
push:
tags:
- "v*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build and Publish to PyPi
uses: JRubics/poetry-publish@v1.10
with:
pypi_token: ${{ secrets.PYPI_TOKEN }}

View file

@ -2,9 +2,9 @@ name: CI
on: on:
push: push:
branches: [ main ] branches: [ main, dev ]
pull_request: pull_request:
branches: [ main ] branches: [ main, dev ]
jobs: jobs:
test: test:
@ -87,8 +87,15 @@ jobs:
docker-compose -f tests/in/111110.yaml config -q docker-compose -f tests/in/111110.yaml config -q
docker-compose -f tests/in/111111.yaml config -q docker-compose -f tests/in/111111.yaml config -q
- name: Setup Poetry
uses: Gr1N/setup-poetry@v7
with:
poetry-version: 1.1.7
- name: Install Dependencies
run: |
poetry install --no-root
- name: Validate Custom Input File - name: Validate Custom Input File
run: | run: |
pip install pytest poetry run python -m pytest
pip install pytest-cov
python tests/test_validate_input_file.py tests/validate_input_file.py tests/in/docker-compose.yaml

View file

@ -0,0 +1,45 @@
name: Release Tagged Version
on:
push:
tags:
- "v*"
permissions:
id-token: "write"
contents: "write"
packages: "write"
pull-requests: "read"
jobs:
tagged-release:
runs-on: "ubuntu-latest"
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
- name: Switch to Current Branch
run: git checkout ${{ env.BRANCH }}
- name: Setup Python 3.10.4
uses: actions/setup-python@v3
with:
python-version: '3.10.4'
- name: Setup Poetry
uses: Gr1N/setup-poetry@v7
with:
poetry-version: 1.1.7
- run: |
poetry install --no-root
poetry build
- name: "Release Tagged Version"
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
files: |
LICENSE
dist/**

2
compose_viz/__init__.py Normal file
View file

@ -0,0 +1,2 @@
__app_name__ = "compose_viz"
__version__ = "0.1.0"

5
compose_viz/__main__.py Normal file
View file

@ -0,0 +1,5 @@
from compose_viz.cli import start_cli
if __name__ == "__main__":
start_cli()

61
compose_viz/cli.py Normal file
View file

@ -0,0 +1,61 @@
from enum import Enum
import typer
from typing import Optional
from compose_viz import __app_name__, __version__
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()
def start_cli() -> None:
app(prog_name=__app_name__)

10
compose_viz/compose.py Normal file
View file

@ -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

10
compose_viz/parser.py Normal file
View file

@ -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

13
compose_viz/service.py Normal file
View file

@ -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

191
poetry.lock generated Normal file
View file

@ -0,0 +1,191 @@
[[package]]
name = "atomicwrites"
version = "1.4.0"
description = "Atomic file writes."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "attrs"
version = "21.4.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.extras]
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.7"
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.4"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "pluggy"
version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pyparsing"
version = "3.0.8"
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
category = "dev"
optional = false
python-versions = ">=3.6.8"
[package.extras]
diagrams = ["railroad-diagrams", "jinja2"]
[[package]]
name = "pytest"
version = "7.1.2"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
py = ">=1.8.2"
tomli = ">=1.0.0"
[package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
category = "dev"
optional = false
python-versions = ">=3.7"
[[package]]
name = "typer"
version = "0.4.1"
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
click = ">=7.1.1,<9.0.0"
[package.extras]
all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"]
dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"]
doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)"]
test = ["shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.910)", "black (>=22.3.0,<23.0.0)", "isort (>=5.0.6,<6.0.0)"]
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
content-hash = "a80ea7abd86b8e5579a192dfa02a55d2219a3a1850bad12da89c30aa42e99156"
[metadata.files]
atomicwrites = [
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
]
attrs = [
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
]
click = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
packaging = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
py = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
pyparsing = [
{file = "pyparsing-3.0.8-py3-none-any.whl", hash = "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"},
{file = "pyparsing-3.0.8.tar.gz", hash = "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954"},
]
pytest = [
{file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"},
{file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"},
]
tomli = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
typer = [
{file = "typer-0.4.1-py3-none-any.whl", hash = "sha256:e8467f0ebac0c81366c2168d6ad9f888efdfb6d4e1d3d5b4a004f46fa444b5c3"},
{file = "typer-0.4.1.tar.gz", hash = "sha256:5646aef0d936b2c761a10393f0384ee6b5c7fe0bb3e5cd710b17134ca1d99cff"},
]

26
pyproject.toml Normal file
View file

@ -0,0 +1,26 @@
[tool.poetry]
name = "compose-viz"
version = "0.1.0"
description = "A docker-compose/podman-compose graph visualization tool that allows you to gernerate graph in DOT format or PNG."
authors = ["Xyphuz Wu <xyphuzwu@gmail.com>"]
readme = "README.md"
license = "MIT"
homepage = "https://github.com/compose-viz/compose-viz"
repository = "https://github.com/compose-viz/compose-viz"
include = [
"LICENSE",
]
[tool.poetry.dependencies]
python = "^3.9"
typer = "^0.4.1"
[tool.poetry.dev-dependencies]
pytest = "^7.1.2"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts]
cpv = "compose_viz.cli:start_cli"

0
tests/__init__.py Normal file
View file

13
tests/test_cli.py Normal file
View file

@ -0,0 +1,13 @@
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

28
tests/test_parse_file.py Normal file
View file

@ -0,0 +1,28 @@
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

View file

@ -1,5 +0,0 @@
import sys
import pytest
if __name__ == '__main__':
pytest.main([sys.argv[1]])

12
tests/test_version.py Normal file
View file

@ -0,0 +1,12 @@
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

View file

@ -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