pypeline-runner 0.1.1__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pypeline/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.1"
1
+ __version__ = "0.2.0"
@@ -1,6 +1,6 @@
1
1
  from abc import abstractmethod
2
2
  from pathlib import Path
3
- from typing import Type
3
+ from typing import Any, Dict, Optional, Type
4
4
 
5
5
  from py_app_dev.core.runnable import Runnable
6
6
 
@@ -8,9 +8,10 @@ from .execution_context import ExecutionContext
8
8
 
9
9
 
10
10
  class PipelineStep(Runnable):
11
- def __init__(self, execution_context: ExecutionContext, output_dir: Path) -> None:
11
+ def __init__(self, execution_context: ExecutionContext, output_dir: Path, config: Optional[Dict[str, Any]] = None) -> None:
12
12
  self.execution_context = execution_context
13
13
  self.output_dir = output_dir
14
+ self.config = config
14
15
  self.project_root_dir = self.execution_context.project_root_dir
15
16
 
16
17
  @abstractmethod
@@ -19,9 +20,10 @@ class PipelineStep(Runnable):
19
20
 
20
21
 
21
22
  class PipelineStepReference:
22
- def __init__(self, group_name: str, _class: Type[PipelineStep]) -> None:
23
+ def __init__(self, group_name: str, _class: Type[PipelineStep], config: Optional[Dict[str, Any]] = None) -> None:
23
24
  self.group_name = group_name
24
25
  self._class = _class
26
+ self.config = config
25
27
 
26
28
  @property
27
29
  def name(self) -> str:
@@ -31,7 +31,7 @@ class ProjectBuilder:
31
31
  @staticmethod
32
32
  def _check_target_directory(project_dir: Path) -> None:
33
33
  if project_dir.is_dir() and any(project_dir.iterdir()):
34
- raise UserNotificationException(f"Project directory '{project_dir}' is not empty." " The target directory shall either be empty or not exist.")
34
+ raise UserNotificationException(f"Project directory '{project_dir}' is not empty. Use --force to override.")
35
35
 
36
36
  def build(self) -> None:
37
37
  if self.check_target_directory_flag:
@@ -41,18 +41,16 @@ class ProjectBuilder:
41
41
 
42
42
 
43
43
  class KickstartProject:
44
- def __init__(self, project_dir: Path, bootstrap_only: bool = False, force: bool = False) -> None:
44
+ def __init__(self, project_dir: Path, force: bool = False) -> None:
45
45
  self.logger = logger.bind()
46
46
  self.project_dir = project_dir
47
- self.bootstrap_only = bootstrap_only
48
47
  self.force = force
49
48
 
50
49
  def run(self) -> None:
51
50
  self.logger.info(f"Kickstart new project in '{self.project_dir.absolute().as_posix()}'")
52
51
  project_builder = ProjectBuilder(self.project_dir)
53
- project_builder.with_dir("bootstrap")
54
- if self.bootstrap_only or self.force:
52
+ if self.force:
55
53
  project_builder.with_disable_target_directory_check()
56
- if not self.bootstrap_only:
57
- project_builder.with_dir("project")
54
+ project_builder.with_dir("bootstrap")
55
+ project_builder.with_dir("project")
58
56
  project_builder.build()
pypeline/main.py CHANGED
@@ -31,10 +31,9 @@ def version(
31
31
  @time_it("init")
32
32
  def init(
33
33
  project_dir: Path = typer.Option(Path.cwd().absolute(), help="The project directory"), # noqa: B008
34
- bootstrap_only: bool = typer.Option(False, help="Initialize only the bootstrap files."),
35
34
  force: bool = typer.Option(False, help="Force the initialization of the project even if the directory is not empty."),
36
35
  ) -> None:
37
- KickstartProject(project_dir, bootstrap_only, force).run()
36
+ KickstartProject(project_dir, force).run()
38
37
 
39
38
 
40
39
  @app.command()
@@ -60,10 +59,14 @@ def run(
60
59
  help="Force the execution of a step even if it is not dirty.",
61
60
  is_flag=True,
62
61
  ),
62
+ dry_run: bool = typer.Option(
63
+ False,
64
+ help="Do not run any step, just print the steps that would be executed.",
65
+ is_flag=True,
66
+ ),
63
67
  ) -> None:
64
68
  project_slurper = ProjectSlurper(project_dir)
65
69
  if print:
66
- logger.warning("TODO: print pipeline steps")
67
70
  logger.info("Pipeline steps:")
68
71
  for group, step_configs in project_slurper.pipeline.items():
69
72
  logger.info(f" Group: {group}")
@@ -80,11 +83,7 @@ def run(
80
83
  logger.info("No steps to run.")
81
84
  return
82
85
 
83
- PipelineStepsExecutor(
84
- project_slurper.artifacts_locator,
85
- steps_references,
86
- force_run,
87
- ).run()
86
+ PipelineStepsExecutor(project_slurper.artifacts_locator, steps_references, force_run, dry_run).run()
88
87
 
89
88
 
90
89
  def main(args: Optional[List[str]] = None) -> int:
pypeline/pypeline.py CHANGED
@@ -26,7 +26,7 @@ class PipelineLoader:
26
26
  self._loader = GenericPipelineLoader[PipelineStep](self.pipeline_config, self.project_root_dir)
27
27
 
28
28
  def load_steps_references(self) -> List[PipelineStepReference]:
29
- return [PipelineStepReference(step_reference.group_name, step_reference._class) for step_reference in self._loader.load_steps()]
29
+ return [PipelineStepReference(step_reference.group_name, step_reference._class, step_reference.config) for step_reference in self._loader.load_steps()]
30
30
 
31
31
 
32
32
  class PipelineStepsExecutor:
@@ -37,11 +37,13 @@ class PipelineStepsExecutor:
37
37
  artifacts_locator: ProjectArtifactsLocator,
38
38
  steps_references: List[PipelineStepReference],
39
39
  force_run: bool = False,
40
+ dry_run: bool = False,
40
41
  ) -> None:
41
42
  self.logger = logger.bind()
42
43
  self.artifacts_locator = artifacts_locator
43
44
  self.steps_references = steps_references
44
45
  self.force_run = force_run
46
+ self.dry_run = dry_run
45
47
 
46
48
  def run(self) -> None:
47
49
  execution_context = ExecutionContext(project_root_dir=self.artifacts_locator.project_root_dir, install_dirs=[])
@@ -49,9 +51,9 @@ class PipelineStepsExecutor:
49
51
  step_output_dir = self.artifacts_locator.build_dir / step_reference.group_name
50
52
  # Create the step output directory, to make sure that files can be created.
51
53
  step_output_dir.mkdir(parents=True, exist_ok=True)
52
- step = step_reference._class(execution_context, step_output_dir)
54
+ step = step_reference._class(execution_context, step_output_dir, step_reference.config)
53
55
  # Execute the step is necessary. If the step is not dirty, it will not be executed
54
- Executor(step.output_dir, self.force_run).execute(step)
56
+ Executor(step.output_dir, self.force_run, self.dry_run).execute(step)
55
57
  # Independent if the step was executed or not, every step shall update the context
56
58
  step.update_execution_context()
57
59
 
@@ -1,6 +1,8 @@
1
+ from dataclasses import dataclass
1
2
  from pathlib import Path
2
- from typing import List
3
+ from typing import Any, Dict, List, Optional
3
4
 
5
+ from mashumaro import DataClassDictMixin
4
6
  from py_app_dev.core.exceptions import UserNotificationException
5
7
  from py_app_dev.core.logging import logger
6
8
 
@@ -8,9 +10,14 @@ from ..domain.execution_context import ExecutionContext
8
10
  from ..domain.pipeline import PipelineStep
9
11
 
10
12
 
13
+ @dataclass
14
+ class CreateVEnvConfig(DataClassDictMixin):
15
+ bootstrap_script: str = "bootstrap.py"
16
+
17
+
11
18
  class CreateVEnv(PipelineStep):
12
- def __init__(self, execution_context: ExecutionContext, output_dir: Path) -> None:
13
- super().__init__(execution_context, output_dir)
19
+ def __init__(self, execution_context: ExecutionContext, output_dir: Path, config: Optional[Dict[str, Any]] = None) -> None:
20
+ super().__init__(execution_context, output_dir, config)
14
21
  self.logger = logger.bind()
15
22
  self.logger = logger.bind()
16
23
 
@@ -23,11 +30,12 @@ class CreateVEnv(PipelineStep):
23
30
 
24
31
  def run(self) -> int:
25
32
  self.logger.debug(f"Run {self.get_name()} step. Output dir: {self.output_dir}")
26
- build_script_path = self.project_root_dir / "bootstrap.py"
27
- if not build_script_path.exists():
33
+ config = CreateVEnvConfig.from_dict(self.config) if self.config else CreateVEnvConfig()
34
+ bootstrap_script = self.project_root_dir / config.bootstrap_script
35
+ if not bootstrap_script.exists():
28
36
  raise UserNotificationException("Failed to find bootstrap script. Make sure that the project is initialized correctly.")
29
37
  self.execution_context.create_process_executor(
30
- ["python", build_script_path.as_posix()],
38
+ ["python", bootstrap_script.as_posix()],
31
39
  cwd=self.project_root_dir,
32
40
  ).execute()
33
41
  self.execution_context.add_install_dirs(self.install_dirs)
@@ -3,7 +3,7 @@ import json
3
3
  import traceback
4
4
  from dataclasses import dataclass
5
5
  from pathlib import Path
6
- from typing import ClassVar, List
6
+ from typing import Any, ClassVar, Dict, List, Optional
7
7
 
8
8
  from mashumaro.config import TO_DICT_ADD_OMIT_NONE_FLAG, BaseConfig
9
9
  from mashumaro.mixins.json import DataClassJSONMixin
@@ -46,8 +46,8 @@ def create_scoop_wrapper() -> ScoopWrapper:
46
46
 
47
47
 
48
48
  class ScoopInstall(PipelineStep):
49
- def __init__(self, execution_context: ExecutionContext, output_dir: Path) -> None:
50
- super().__init__(execution_context, output_dir)
49
+ def __init__(self, execution_context: ExecutionContext, output_dir: Path, config: Optional[Dict[str, Any]] = None) -> None:
50
+ super().__init__(execution_context, output_dir, config)
51
51
  self.logger = logger.bind()
52
52
  self.execution_info = ScoopInstallExecutionInfo([])
53
53
  # One needs to keep track of the installed apps to get the required paths
@@ -77,7 +77,7 @@ class ScoopInstall(PipelineStep):
77
77
  return [self.scoop_file]
78
78
 
79
79
  def get_outputs(self) -> List[Path]:
80
- return self.install_dirs
80
+ return [self.execution_info_file, *self.install_dirs]
81
81
 
82
82
  def update_execution_context(self) -> None:
83
83
  install_dirs = ScoopInstallExecutionInfo.from_json_file(self.execution_info_file).install_dirs
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from typing import List
2
+ from typing import Any, Dict, List, Optional
3
3
 
4
4
  from py_app_dev.core.exceptions import UserNotificationException
5
5
  from py_app_dev.core.logging import logger
@@ -9,8 +9,8 @@ from ..domain.pipeline import PipelineStep
9
9
 
10
10
 
11
11
  class WestInstall(PipelineStep):
12
- def __init__(self, execution_context: ExecutionContext, output_dir: Path) -> None:
13
- super().__init__(execution_context, output_dir)
12
+ def __init__(self, execution_context: ExecutionContext, output_dir: Path, config: Optional[Dict[str, Any]] = None) -> None:
13
+ super().__init__(execution_context, output_dir, config)
14
14
  self.logger = logger.bind()
15
15
  self.artifacts_locator = execution_context.create_artifacts_locator()
16
16
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pypeline-runner
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Configure and execute pipelines with Python (similar to GitHub workflows or Jenkins pipelines).
5
5
  Home-page: https://github.com/cuinixam/pypeline
6
6
  License: MIT
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.10
17
17
  Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Topic :: Software Development :: Libraries
20
- Requires-Dist: py-app-dev (>=2.1.0,<3.0.0)
20
+ Requires-Dist: py-app-dev (>=2.1.1,<3.0.0)
21
21
  Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
22
22
  Requires-Dist: typer[all] (>=0.12.0,<0.13.0)
23
23
  Project-URL: Bug Tracker, https://github.com/cuinixam/pypeline/issues
@@ -26,7 +26,7 @@ Project-URL: Documentation, https://pypeline-runner.readthedocs.io
26
26
  Project-URL: Repository, https://github.com/cuinixam/pypeline
27
27
  Description-Content-Type: text/markdown
28
28
 
29
- # Python Pipeline
29
+ # Pypeline
30
30
 
31
31
  <p align="center">
32
32
  <a href="https://github.com/cuinixam/pypeline/actions/workflows/ci.yml?query=branch%3Amain">
@@ -51,14 +51,27 @@ Description-Content-Type: text/markdown
51
51
  </a>
52
52
  </p>
53
53
  <p align="center">
54
- <a href="https://pypi.org/project/pypeline/">
55
- <img src="https://img.shields.io/pypi/v/pypeline.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">
54
+ <a href="https://pypi.org/project/pypeline-runner/">
55
+ <img src="https://img.shields.io/pypi/v/pypeline-runner.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">
56
56
  </a>
57
- <img src="https://img.shields.io/pypi/pyversions/pypeline.svg?style=flat-square&logo=python&amp;logoColor=fff" alt="Supported Python versions">
58
- <img src="https://img.shields.io/pypi/l/pypeline.svg?style=flat-square" alt="License">
57
+ <img src="https://img.shields.io/pypi/pyversions/pypeline-runner.svg?style=flat-square&logo=python&amp;logoColor=fff" alt="Supported Python versions">
58
+ <img src="https://img.shields.io/pypi/l/pypeline-runner.svg?style=flat-square" alt="License">
59
59
  </p>
60
60
 
61
- Configure and execute pipelines with Python (similar to GitHub workflows or Jenkins pipelines).
61
+ Pypeline is a Python application designed to streamline and automate the software development lifecycle, particularly the pipeline execution processes across various environments such as GitHub and Jenkins.
62
+ The primary motivation for developing Pypeline stemmed from the need to unify and simplify the creation of build, test, and deployment pipelines that are traditionally defined separately across these platforms using GitHub workflows (YAML) and Jenkins pipelines (Jenkinsfile).
63
+
64
+ **Key Features**
65
+
66
+ - **Unified Pipeline Definition**: Users can define their entire pipeline in a single YAML file, eliminating the need to switch between different syntaxes and configurations for different CI/CD tools.
67
+
68
+ - **Extensibility**: Pypeline supports execution steps defined not only through local scripts but also from installed Python packages.
69
+
70
+ - **Execution Context**: Each step in the pipeline receives an execution context that can be updated during step execution. This allows for the sharing of information and state between steps.
71
+
72
+ - **Dependency Handling**: Dependency management ensures that only the necessary steps are executed, reducing runtime and resource usage by avoiding unnecessary operations.
73
+
74
+ - **Ease of Use**: With Pypeline, setting up and running pipelines becomes more straightforward, enabling developers to focus more on coding and less on configuring pipeline specifics.
62
75
 
63
76
  ## Installation
64
77
 
@@ -114,8 +127,5 @@ This project follows the [all-contributors](https://github.com/all-contributors/
114
127
 
115
128
  ## Credits
116
129
 
117
- This package was created with
118
- [Copier](https://copier.readthedocs.io/) and the
119
- [cuinixam/pypackage-template](https://github.com/cuinixam/pypackage-template)
120
- project template.
130
+ This package was created with [Copier](https://copier.readthedocs.io/) and the [cuinixam/pypackage-template](https://github.com/cuinixam/pypackage-template) project template.
121
131
 
@@ -1,13 +1,13 @@
1
- pypeline/__init__.py,sha256=rnObPjuBcEStqSO0S6gsdS_ot8ITOQjVj_-P1LUUYpg,22
1
+ pypeline/__init__.py,sha256=Zn1KFblwuFHiDRdRAiRnDBRkbPttWh44jKa5zG2ov0E,22
2
2
  pypeline/__run.py,sha256=TCdaX05Qm3g8T4QYryKB25Xxf0L5Km7hFOHe1mK9vI0,350
3
3
  pypeline/domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  pypeline/domain/artifacts.py,sha256=qXshnk9umi0AVGV4m5iEiy_MQ5Ad2LDZwI8OULU-qMk,1355
5
5
  pypeline/domain/config.py,sha256=HpU-d_oHZYCCbc1Fer3BJwAma94eL97eXTtKZEsrVpQ,1491
6
6
  pypeline/domain/execution_context.py,sha256=0zc3OgXeIMDpgWWYMaDGub7fY5urLLR79yuCaXVxoTQ,1101
7
- pypeline/domain/pipeline.py,sha256=VzzYqi3WyDmXN3UblTrfcirmh2PXqrdt_Gfi1Qtkln0,817
7
+ pypeline/domain/pipeline.py,sha256=gN2PUdBEYl1D534_XZ-bLwdhBlDr48SR_A8mdJ3ZzJg,980
8
8
  pypeline/domain/project_slurper.py,sha256=YCho7V1BHjFmC_foxHFaWX8c_VbMJ16XEB4CQBlMrhc,894
9
9
  pypeline/kickstart/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- pypeline/kickstart/create.py,sha256=iTf7I5POHTKXZ7RuZ6iVHsBT8D02Nws-h0ZH53Rk2cI,2410
10
+ pypeline/kickstart/create.py,sha256=Gz2rVfhoQCKgYWlJoefh5Azcgamc9JJtiNsP1DTKdQo,2235
11
11
  pypeline/kickstart/templates/bootstrap/bootstrap.ps1,sha256=IcgNohR8Fllz234DXQOl4N59I38t0mZ0k6ziu3Uu2kA,5772
12
12
  pypeline/kickstart/templates/bootstrap/bootstrap.py,sha256=9DstyqJFVcBHOs5cS2dU43FY7dLusid5KEi55TeaJwk,15971
13
13
  pypeline/kickstart/templates/project/.gitignore,sha256=vX-IAjvkjxgNIYBQmqaRRhWRO2uONl0GR355PiQPjIk,13
@@ -17,15 +17,15 @@ pypeline/kickstart/templates/project/pypeline.yaml,sha256=i9YSqpS2o-_Bzl69fvdOFV
17
17
  pypeline/kickstart/templates/project/pyproject.toml,sha256=yc6RCo-bUo1PXF91XfM-dButgfxU16Uud34NidgJ0zQ,225
18
18
  pypeline/kickstart/templates/project/scoopfile.json,sha256=DcfZ8jYf9hmPHM-AWwnPKQJCzRG3fCuYtMeoY01nkag,219
19
19
  pypeline/kickstart/templates/project/steps/my_step.py,sha256=YNRdb7ofqo3PxMQe8Vhtf7K9eZclv-6J0IwMrd3E70w,651
20
- pypeline/main.py,sha256=i8SjRH74VWzgOOel1OB6XpkIJrff3mVdOkgH5yokjEI,3406
20
+ pypeline/main.py,sha256=5DfEUliEozHwyOK0KfOAaAJlFXYQflTdGXY3poTz7c0,3382
21
21
  pypeline/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- pypeline/pypeline.py,sha256=yZ4nIBtI7Yrz-d8rmslERdtwcJiBf1D9tpOHFR5iAvM,4235
22
+ pypeline/pypeline.py,sha256=3coRh9KGwR--yDK88ltXisE2nAR8BNUMQ21YHDjoKPY,4359
23
23
  pypeline/steps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- pypeline/steps/create_venv.py,sha256=HeoJYDdyGEOwFnK_9eYEK32l6F9MpEsYSiM91uLVt2o,1599
25
- pypeline/steps/scoop_install.py,sha256=KozpYMG1qITo21u6cIf9USkjIp2gvZ3V2DMzhi8fVyg,3257
26
- pypeline/steps/west_install.py,sha256=QUopdIJne6OFhaXNlbOYrOGDPmNy6kgqZfOiUWQN7xY,1860
27
- pypeline_runner-0.1.1.dist-info/LICENSE,sha256=sKxdoqSmW9ezvPvt0ZGJbneyA0SBcm0GiqzTv2jN230,1066
28
- pypeline_runner-0.1.1.dist-info/METADATA,sha256=oet1IAv-F7lBhTXdVwFNGQ6u_G-5OLOl7dYcDfQnqQE,5850
29
- pypeline_runner-0.1.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
30
- pypeline_runner-0.1.1.dist-info/entry_points.txt,sha256=pe1u0uuhPI_yeQ0KjEw6jK-EvQfPcZwBSajgbAdKz1o,47
31
- pypeline_runner-0.1.1.dist-info/RECORD,,
24
+ pypeline/steps/create_venv.py,sha256=od1zLVPhNdN-sWqEqYMkX19c5IEt4lIIOcASBeoqUUw,1954
25
+ pypeline/steps/scoop_install.py,sha256=MmoC4Yy1lOhHrfKBrgJKHMcRUNPbRztzUbSgIlazer8,3356
26
+ pypeline/steps/west_install.py,sha256=J1p-62owTDOY6bVTGCr5d9YFOL8DeRUPTnq0TbiaWlA,1930
27
+ pypeline_runner-0.2.0.dist-info/LICENSE,sha256=sKxdoqSmW9ezvPvt0ZGJbneyA0SBcm0GiqzTv2jN230,1066
28
+ pypeline_runner-0.2.0.dist-info/METADATA,sha256=dA8ygovlByBkouUBXI5XRFM7tqxT8wBLyFisttJL-pE,7168
29
+ pypeline_runner-0.2.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
30
+ pypeline_runner-0.2.0.dist-info/entry_points.txt,sha256=pe1u0uuhPI_yeQ0KjEw6jK-EvQfPcZwBSajgbAdKz1o,47
31
+ pypeline_runner-0.2.0.dist-info/RECORD,,