From 856b56ef046ed7aeca24798b7baf2d3358b04569 Mon Sep 17 00:00:00 2001 From: German Arutyunov Date: Sun, 10 Nov 2024 17:38:35 +0100 Subject: [PATCH] ci cd --- .github/workflows/ci-cd.yml | 23 +++++++++++++++++++++++ README.rst | 12 ++++++++++-- examples/logger/__init__.py | 2 +- examples/logger/commands.py | 3 ++- examples/logger/strategy.py | 2 +- pype/__main__.py | 2 -- pype/cli.py | 7 ++++++- pype/core/command.py | 1 + pype/core/pipeline.py | 1 + pype/core/stage.py | 1 + pype/core/strategy.py | 1 + pype/github/vcs_adapter.py | 13 ++++++++++--- pype/gitlab/vcs_adapter.py | 7 ++++++- pype/vcs/factory.py | 1 + pype/vcs/protocols.py | 3 +-- tests/test_pipeline.py | 29 ++++++++++++++++++++++------- 16 files changed, 87 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/ci-cd.yml diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 0000000..80562a8 --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,23 @@ +name: CI/CD + +on: + push: + +jobs: + build: + name: Test and Build + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v3 + - name: "Setup Python, Poetry and Dependencies" + uses: packetcoders/action-setup-cache-python-poetry@main + with: + python-version: 3.12 + poetry-version: 1.8 + - name: Run tests + run: | + poetry run pytest + - name: Build + run: | + poetry build diff --git a/README.rst b/README.rst index b5c0a22..7b609d7 100644 --- a/README.rst +++ b/README.rst @@ -70,6 +70,14 @@ It can be a local file or a file stored somewhere, e.g. git repository. Running the Pipeline -------------------- -If the pipeline configuration is stored locally, run `pype --local config.yml`. +If the pipeline configuration is stored locally, run: -If it is stored in a repository, run `pype --source github --repository gaarutyunov/pype --filename examples/config.yml --ref main`. +.. code:: bash + + pype --local config.yml + +If it is stored in a repository, run: + +.. code:: bash + + pype --source github --repository gaarutyunov/pype --file examples/config.yml --ref main diff --git a/examples/logger/__init__.py b/examples/logger/__init__.py index d1087e4..413bb6f 100644 --- a/examples/logger/__init__.py +++ b/examples/logger/__init__.py @@ -1,2 +1,2 @@ from .strategy import LoggerStrategy -from .commands import Echo \ No newline at end of file +from .commands import Echo diff --git a/examples/logger/commands.py b/examples/logger/commands.py index 8e51123..3701396 100644 --- a/examples/logger/commands.py +++ b/examples/logger/commands.py @@ -2,6 +2,7 @@ from pype import Command + class Echo(Command): logger: logging.Logger | None = None @@ -11,4 +12,4 @@ def __init__(self, log_level: str = "ERROR"): def configure(self, **kwargs: str): self.logger = logging.getLogger(self.name) - self.logger.setLevel(self.log_level) \ No newline at end of file + self.logger.setLevel(self.log_level) diff --git a/examples/logger/strategy.py b/examples/logger/strategy.py index 8c0d512..aacdb1c 100644 --- a/examples/logger/strategy.py +++ b/examples/logger/strategy.py @@ -7,4 +7,4 @@ def configure_echo(self, cmd: Echo, **kwargs: dict[str, str]): cmd.log_level = "INFO" def echo(self, cmd: Echo, params: dict[str, str]): - cmd.logger.info(params["MESSAGE"]) \ No newline at end of file + cmd.logger.info(params["MESSAGE"]) diff --git a/pype/__main__.py b/pype/__main__.py index 5271e80..20b4fa1 100644 --- a/pype/__main__.py +++ b/pype/__main__.py @@ -1,5 +1,3 @@ -import pype.gitlab # noqa: import for side effects -import pype.github # noqa: import for side effects from pype.cli import cli if __name__ == "__main__": diff --git a/pype/cli.py b/pype/cli.py index 331f701..afc4931 100644 --- a/pype/cli.py +++ b/pype/cli.py @@ -4,6 +4,9 @@ import click import yaml +import pype.gitlab # noqa: import for side effects +import pype.github # noqa: import for side effects + from pype import Pipeline from pype.registry import REGISTRY from pype.vcs import get_factory, RepositoryFileClient @@ -11,7 +14,9 @@ @click.command() @click.option("-s", "--source", help="Configuration remote source") -@click.option("-l", "--local", type=click.File(lazy=True), help="Path to local configuration file") +@click.option( + "-l", "--local", type=click.File(lazy=True), help="Path to local configuration file" +) @click.option("-p", "--repository", type=str, help="Repository full name") @click.option("-r", "--ref", type=str, help="Reference (branch or tag)") @click.option("-f", "--file", type=str, help="Config file name") diff --git a/pype/core/command.py b/pype/core/command.py index 942898f..63dd313 100644 --- a/pype/core/command.py +++ b/pype/core/command.py @@ -1,5 +1,6 @@ class Command: """Command to be executed""" + def __init__(self, name: str): """ :param name: name of the command (is used to get method from Strategy) diff --git a/pype/core/pipeline.py b/pype/core/pipeline.py index 21ec3c7..19544aa 100644 --- a/pype/core/pipeline.py +++ b/pype/core/pipeline.py @@ -6,6 +6,7 @@ class Pipeline: """Entrypoint for the pipeline to be executed""" + def __init__(self, stages: list[Stage]): """ :param stages: list of stages to be run inside pipeline diff --git a/pype/core/stage.py b/pype/core/stage.py index a02222c..791418e 100644 --- a/pype/core/stage.py +++ b/pype/core/stage.py @@ -6,6 +6,7 @@ class Stage: """A list of commands to be handled by a Strategy one by one""" + def __init__(self, name: str, strategy: Strategy, commands: list[Command]): """ :param name: Name of the stage diff --git a/pype/core/strategy.py b/pype/core/strategy.py index 86f987a..d3c0130 100644 --- a/pype/core/strategy.py +++ b/pype/core/strategy.py @@ -5,6 +5,7 @@ class Strategy: """Class that implements commands handlers""" + def run(self, command: Command, params: dict[str, str]): if hasattr(self, command.name): getattr(self, command.name)(command, params) diff --git a/pype/github/vcs_adapter.py b/pype/github/vcs_adapter.py index 717b9cd..506a11f 100644 --- a/pype/github/vcs_adapter.py +++ b/pype/github/vcs_adapter.py @@ -4,11 +4,14 @@ from github import Github, Auth from github.Consts import DEFAULT_BASE_URL -from pype.vcs import VCSAdapter +from pype.vcs import VCSAdapter, register_factory +@register_factory("github") def github_factory() -> VCSAdapter: - return GithubAdapter(os.environ.get("GITHUB_TOKEN"), os.environ.get("GITHUB_URL", DEFAULT_BASE_URL)) + return GithubAdapter( + os.environ.get("GITHUB_TOKEN"), os.environ.get("GITHUB_URL", DEFAULT_BASE_URL) + ) class GithubAdapter: @@ -16,4 +19,8 @@ def __init__(self, token: str, url: str = DEFAULT_BASE_URL): self.client = Github(auth=Auth.Token(token), base_url=url) def download(self, repository: str, filename: str, ref: str) -> io.TextIOBase: - return io.StringIO(self.client.get_repo(repository).get_contents(filename, ref=ref).decoded_content.decode('utf-8')) + return io.StringIO( + self.client.get_repo(repository) + .get_contents(filename, ref=ref) + .decoded_content.decode("utf-8") + ) diff --git a/pype/gitlab/vcs_adapter.py b/pype/gitlab/vcs_adapter.py index ea7f94c..bea7abc 100644 --- a/pype/gitlab/vcs_adapter.py +++ b/pype/gitlab/vcs_adapter.py @@ -16,4 +16,9 @@ def __init__(self, url: str, token: str): self.client = Gitlab(url, token) def download(self, repository: str, filename: str, ref: str) -> io.TextIOBase: - return io.StringIO(self.client.projects.get(repository).files.get(filename, ref=ref).decode().decode("utf-8")) \ No newline at end of file + return io.StringIO( + self.client.projects.get(repository) + .files.get(filename, ref=ref) + .decode() + .decode("utf-8") + ) diff --git a/pype/vcs/factory.py b/pype/vcs/factory.py index 562fe25..5cd0c59 100644 --- a/pype/vcs/factory.py +++ b/pype/vcs/factory.py @@ -4,6 +4,7 @@ FACTORIES = {} + def register_factory(name): def register(factory: Callable[[], VCSAdapter]): FACTORIES[name] = factory diff --git a/pype/vcs/protocols.py b/pype/vcs/protocols.py index 146ddd2..4b93c24 100644 --- a/pype/vcs/protocols.py +++ b/pype/vcs/protocols.py @@ -3,8 +3,7 @@ class RepositoryFileClient(Protocol): - def download(self, repository: str, filename: str, ref: str) -> io.TextIOBase: - ... + def download(self, repository: str, filename: str, ref: str) -> io.TextIOBase: ... VCSAdapter: TypeAlias = RepositoryFileClient diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py index a314b54..9cceb45 100644 --- a/tests/test_pipeline.py +++ b/tests/test_pipeline.py @@ -1,5 +1,4 @@ import pathlib -import sys from click.testing import CliRunner @@ -9,11 +8,27 @@ def test_pipeline_local(caplog): runner = CliRunner() root_path = pathlib.Path(__file__).parent.parent - config_path = root_path / 'examples/logger/config.yml' + config_path = root_path / "examples/logger/config.yml" msg = "test message" - result = runner.invoke(cli, ['--local', str(config_path)], env={ - "MESSAGE": msg, - "LOG_LEVEL": "INFO" - }) + result = runner.invoke( + cli, ["--local", str(config_path)], env={"MESSAGE": msg, "LOG_LEVEL": "INFO"} + ) assert result.exit_code == 0 - assert msg in caplog.messages \ No newline at end of file + assert msg in caplog.messages + + +def test_pipeline_github(caplog): + runner = CliRunner() + msg = "test message" + result = runner.invoke( + cli, + [ + "--source", "github", + "--repository", "gaarutyunov/pype", + "--file", "examples/logger/config.yml", + "--ref", "main", + ], + env={"MESSAGE": msg, "LOG_LEVEL": "INFO"}, + ) + assert result.exit_code == 0 + assert msg in caplog.messages