pypeline-runner 0.2.1__tar.gz → 0.3.0__tar.gz
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_runner-0.2.1 → pypeline_runner-0.3.0}/PKG-INFO +4 -4
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/pyproject.toml +12 -12
- pypeline_runner-0.3.0/src/pypeline/__init__.py +1 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/domain/config.py +2 -1
- pypeline_runner-0.3.0/src/pypeline/domain/pipeline.py +67 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/main.py +5 -1
- pypeline_runner-0.3.0/src/pypeline/pypeline.py +187 -0
- pypeline_runner-0.2.1/src/pypeline/__init__.py +0 -1
- pypeline_runner-0.2.1/src/pypeline/domain/pipeline.py +0 -30
- pypeline_runner-0.2.1/src/pypeline/pypeline.py +0 -95
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/LICENSE +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/README.md +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/__run.py +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/domain/__init__.py +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/domain/artifacts.py +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/domain/execution_context.py +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/domain/project_slurper.py +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/__init__.py +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/create.py +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/templates/bootstrap/bootstrap.ps1 +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/templates/bootstrap/bootstrap.py +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/templates/project/.gitignore +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/templates/project/poetry.toml +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/templates/project/pypeline.ps1 +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/templates/project/pypeline.yaml +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/templates/project/pyproject.toml +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/templates/project/scoopfile.json +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/templates/project/steps/my_step.py +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/py.typed +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/steps/__init__.py +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/steps/create_venv.py +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/steps/scoop_install.py +0 -0
- {pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/steps/west_install.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pypeline-runner
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.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,9 +17,9 @@ 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.
|
|
21
|
-
Requires-Dist: pyyaml (>=6.0
|
|
22
|
-
Requires-Dist: typer[all] (>=0.12
|
|
20
|
+
Requires-Dist: py-app-dev (>=2.2,<3.0)
|
|
21
|
+
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
22
|
+
Requires-Dist: typer[all] (>=0.12,<0.13)
|
|
23
23
|
Project-URL: Bug Tracker, https://github.com/cuinixam/pypeline/issues
|
|
24
24
|
Project-URL: Changelog, https://github.com/cuinixam/pypeline/blob/main/CHANGELOG.md
|
|
25
25
|
Project-URL: Documentation, https://pypeline-runner.readthedocs.io
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pypeline-runner"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "Configure and execute pipelines with Python (similar to GitHub workflows or Jenkins pipelines)."
|
|
5
5
|
authors = ["cuinixam <me@cuinixam.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -27,25 +27,25 @@ pypeline = "pypeline.main:main"
|
|
|
27
27
|
|
|
28
28
|
[tool.poetry.dependencies]
|
|
29
29
|
python = "^3.10"
|
|
30
|
-
py-app-dev = "^2.
|
|
31
|
-
typer = {extras = ["all"], version = "^0.12
|
|
32
|
-
pyyaml = "^6.0
|
|
30
|
+
py-app-dev = "^2.2"
|
|
31
|
+
typer = {extras = ["all"], version = "^0.12"}
|
|
32
|
+
pyyaml = "^6.0"
|
|
33
33
|
|
|
34
34
|
[tool.poetry.group.dev.dependencies]
|
|
35
35
|
pytest = "^7.0"
|
|
36
36
|
pytest-cov = "^4.0"
|
|
37
|
-
pre-commit = "^3.1
|
|
38
|
-
ruff = "^0.3
|
|
37
|
+
pre-commit = "^3.1"
|
|
38
|
+
ruff = "^0.3"
|
|
39
39
|
|
|
40
40
|
[tool.poetry.group.docs.dependencies]
|
|
41
41
|
myst-parser = ">=0.16"
|
|
42
42
|
sphinx = ">=4.0"
|
|
43
|
-
sphinxcontrib-mermaid = "^0.8
|
|
44
|
-
mlx-traceability = "^10.0
|
|
45
|
-
sphinx-copybutton = "^0.5
|
|
46
|
-
sphinx-new-tab-link = "^0.2
|
|
47
|
-
sphinx-book-theme = "^1.1
|
|
48
|
-
sphinx-design = "^0.5
|
|
43
|
+
sphinxcontrib-mermaid = "^0.8"
|
|
44
|
+
mlx-traceability = "^10.0"
|
|
45
|
+
sphinx-copybutton = "^0.5"
|
|
46
|
+
sphinx-new-tab-link = "^0.2"
|
|
47
|
+
sphinx-book-theme = "^1.1"
|
|
48
|
+
sphinx-design = "^0.5"
|
|
49
49
|
|
|
50
50
|
[tool.semantic_release]
|
|
51
51
|
version_toml = ["pyproject.toml:tool.poetry.version"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|
|
@@ -5,10 +5,11 @@ from typing import Any, Dict, Optional
|
|
|
5
5
|
import yaml
|
|
6
6
|
from mashumaro import DataClassDictMixin
|
|
7
7
|
from py_app_dev.core.exceptions import UserNotificationException
|
|
8
|
-
from py_app_dev.core.pipeline import PipelineConfig
|
|
9
8
|
from yaml.parser import ParserError
|
|
10
9
|
from yaml.scanner import ScannerError
|
|
11
10
|
|
|
11
|
+
from .pipeline import PipelineConfig
|
|
12
|
+
|
|
12
13
|
|
|
13
14
|
@dataclass
|
|
14
15
|
class ProjectConfig(DataClassDictMixin):
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import (
|
|
5
|
+
Any,
|
|
6
|
+
Dict,
|
|
7
|
+
List,
|
|
8
|
+
Optional,
|
|
9
|
+
OrderedDict,
|
|
10
|
+
Type,
|
|
11
|
+
TypeAlias,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from mashumaro import DataClassDictMixin
|
|
15
|
+
from py_app_dev.core.runnable import Runnable
|
|
16
|
+
|
|
17
|
+
from .execution_context import ExecutionContext
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class PipelineStepConfig(DataClassDictMixin):
|
|
22
|
+
#: Step name or class name if file is not specified
|
|
23
|
+
step: str
|
|
24
|
+
#: Path to file with step class
|
|
25
|
+
file: Optional[str] = None
|
|
26
|
+
#: Python module with step class
|
|
27
|
+
module: Optional[str] = None
|
|
28
|
+
#: Step class name
|
|
29
|
+
class_name: Optional[str] = None
|
|
30
|
+
#: Command to run. For simple steps that don't need a class
|
|
31
|
+
run: Optional[str] = None
|
|
32
|
+
#: Step description
|
|
33
|
+
description: Optional[str] = None
|
|
34
|
+
#: Step timeout in seconds
|
|
35
|
+
timeout_sec: Optional[int] = None
|
|
36
|
+
#: Custom step configuration
|
|
37
|
+
config: Optional[Dict[str, Any]] = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
PipelineConfig: TypeAlias = OrderedDict[str, List[PipelineStepConfig]]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class PipelineStep(Runnable):
|
|
44
|
+
def __init__(self, execution_context: ExecutionContext, output_dir: Path, config: Optional[Dict[str, Any]] = None) -> None:
|
|
45
|
+
super().__init__(self.get_needs_dependency_management())
|
|
46
|
+
self.execution_context = execution_context
|
|
47
|
+
self.output_dir = output_dir
|
|
48
|
+
self.config = config
|
|
49
|
+
self.project_root_dir = self.execution_context.project_root_dir
|
|
50
|
+
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def update_execution_context(self) -> None:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
def get_needs_dependency_management(self) -> bool:
|
|
56
|
+
return True
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class PipelineStepReference:
|
|
60
|
+
def __init__(self, group_name: str, _class: Type[PipelineStep], config: Optional[Dict[str, Any]] = None) -> None:
|
|
61
|
+
self.group_name = group_name
|
|
62
|
+
self._class = _class
|
|
63
|
+
self.config = config
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def name(self) -> str:
|
|
67
|
+
return self._class.__name__
|
|
@@ -86,10 +86,14 @@ def run(
|
|
|
86
86
|
PipelineStepsExecutor(project_slurper.artifacts_locator, steps_references, force_run, dry_run).run()
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
def main() -> None:
|
|
90
90
|
try:
|
|
91
91
|
setup_logger()
|
|
92
92
|
app()
|
|
93
93
|
except UserNotificationException as e:
|
|
94
94
|
logger.error(f"{e}")
|
|
95
95
|
sys.exit(1)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
main()
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from importlib.util import module_from_spec, spec_from_file_location
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import (
|
|
5
|
+
Any,
|
|
6
|
+
Dict,
|
|
7
|
+
List,
|
|
8
|
+
Optional,
|
|
9
|
+
Type,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from py_app_dev.core.exceptions import UserNotificationException
|
|
13
|
+
from py_app_dev.core.logging import logger
|
|
14
|
+
from py_app_dev.core.runnable import Executor
|
|
15
|
+
|
|
16
|
+
from .domain.artifacts import ProjectArtifactsLocator
|
|
17
|
+
from .domain.execution_context import ExecutionContext
|
|
18
|
+
from .domain.pipeline import PipelineConfig, PipelineStep, PipelineStepConfig, PipelineStepReference
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PipelineLoader:
|
|
22
|
+
"""
|
|
23
|
+
Loads pipeline steps from a pipeline configuration.
|
|
24
|
+
|
|
25
|
+
The steps are not instantiated, only the references are returned (lazy load).
|
|
26
|
+
The pipeline loader needs to know the project root directory to be able to find the
|
|
27
|
+
user custom local steps.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, pipeline_config: PipelineConfig, project_root_dir: Path) -> None:
|
|
31
|
+
self.pipeline_config = pipeline_config
|
|
32
|
+
self.project_root_dir = project_root_dir
|
|
33
|
+
|
|
34
|
+
def load_steps_references(self) -> List[PipelineStepReference]:
|
|
35
|
+
result = []
|
|
36
|
+
for group_name, steps_config in self.pipeline_config.items():
|
|
37
|
+
result.extend(self._load_steps(group_name, steps_config, self.project_root_dir))
|
|
38
|
+
return result
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def _load_steps(
|
|
42
|
+
group_name: str,
|
|
43
|
+
steps_config: List[PipelineStepConfig],
|
|
44
|
+
project_root_dir: Path,
|
|
45
|
+
) -> List[PipelineStepReference]:
|
|
46
|
+
result = []
|
|
47
|
+
for step_config in steps_config:
|
|
48
|
+
step_class_name = step_config.class_name or step_config.step
|
|
49
|
+
if step_config.module:
|
|
50
|
+
step_class = PipelineLoader._load_module_step(step_config.module, step_class_name)
|
|
51
|
+
elif step_config.file:
|
|
52
|
+
step_class = PipelineLoader._load_user_step(project_root_dir.joinpath(step_config.file), step_class_name)
|
|
53
|
+
elif step_config.run:
|
|
54
|
+
step_class = PipelineLoader._create_run_command_step_class(step_config.run, step_class_name)
|
|
55
|
+
else:
|
|
56
|
+
raise UserNotificationException(f"Step '{step_class_name}' has no 'module' nor 'file' nor `run` defined." " Please check your pipeline configuration.")
|
|
57
|
+
result.append(PipelineStepReference(group_name, step_class, step_config.config))
|
|
58
|
+
return result
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def _load_user_step(python_file: Path, step_class_name: str) -> Type[PipelineStep]:
|
|
62
|
+
# Create a module specification from the file path
|
|
63
|
+
spec = spec_from_file_location(f"user__{step_class_name}", python_file)
|
|
64
|
+
if spec and spec.loader:
|
|
65
|
+
step_module = module_from_spec(spec)
|
|
66
|
+
# Import the module
|
|
67
|
+
spec.loader.exec_module(step_module)
|
|
68
|
+
try:
|
|
69
|
+
step_class = getattr(step_module, step_class_name)
|
|
70
|
+
except AttributeError:
|
|
71
|
+
raise UserNotificationException(f"Could not load class '{step_class_name}' from file '{python_file}'." " Please check your pipeline configuration.") from None
|
|
72
|
+
return step_class
|
|
73
|
+
raise UserNotificationException(f"Could not load file '{python_file}'." " Please check the file for any errors.")
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def _load_module_step(module_name: str, step_class_name: str) -> Type[PipelineStep]:
|
|
77
|
+
try:
|
|
78
|
+
module = importlib.import_module(module_name)
|
|
79
|
+
step_class = getattr(module, step_class_name)
|
|
80
|
+
except ImportError:
|
|
81
|
+
raise UserNotificationException(f"Could not load module '{module_name}'. Please check your pipeline configuration.") from None
|
|
82
|
+
except AttributeError:
|
|
83
|
+
raise UserNotificationException(f"Could not load class '{step_class_name}' from module '{module_name}'." " Please check your pipeline configuration.") from None
|
|
84
|
+
return step_class
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def _create_run_command_step_class(command: str, name: str) -> Type[PipelineStep]:
|
|
88
|
+
"""Dynamically creates a step class for a given command."""
|
|
89
|
+
|
|
90
|
+
class DynamicRunCommandStep(PipelineStep):
|
|
91
|
+
"""A simple step that runs a command."""
|
|
92
|
+
|
|
93
|
+
def __init__(self, execution_context: ExecutionContext, output_dir: Path, config: Optional[Dict[str, Any]] = None) -> None:
|
|
94
|
+
super().__init__(execution_context, output_dir, config)
|
|
95
|
+
self.command = command
|
|
96
|
+
self.name = name
|
|
97
|
+
|
|
98
|
+
def get_needs_dependency_management(self) -> bool:
|
|
99
|
+
"""A command step does not need dependency management."""
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
def run(self) -> int:
|
|
103
|
+
self.execution_context.create_process_executor(
|
|
104
|
+
[self.command],
|
|
105
|
+
cwd=self.project_root_dir,
|
|
106
|
+
).execute()
|
|
107
|
+
return 0
|
|
108
|
+
|
|
109
|
+
def get_name(self) -> str:
|
|
110
|
+
return self.name
|
|
111
|
+
|
|
112
|
+
def get_inputs(self) -> List[Path]:
|
|
113
|
+
return []
|
|
114
|
+
|
|
115
|
+
def get_outputs(self) -> List[Path]:
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
def update_execution_context(self) -> None:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
return DynamicRunCommandStep
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class PipelineStepsExecutor:
|
|
125
|
+
"""Executes a list of pipeline steps sequentially."""
|
|
126
|
+
|
|
127
|
+
def __init__(
|
|
128
|
+
self,
|
|
129
|
+
artifacts_locator: ProjectArtifactsLocator,
|
|
130
|
+
steps_references: List[PipelineStepReference],
|
|
131
|
+
force_run: bool = False,
|
|
132
|
+
dry_run: bool = False,
|
|
133
|
+
) -> None:
|
|
134
|
+
self.logger = logger.bind()
|
|
135
|
+
self.artifacts_locator = artifacts_locator
|
|
136
|
+
self.steps_references = steps_references
|
|
137
|
+
self.force_run = force_run
|
|
138
|
+
self.dry_run = dry_run
|
|
139
|
+
|
|
140
|
+
def run(self) -> None:
|
|
141
|
+
execution_context = ExecutionContext(project_root_dir=self.artifacts_locator.project_root_dir, install_dirs=[])
|
|
142
|
+
for step_reference in self.steps_references:
|
|
143
|
+
step_output_dir = self.artifacts_locator.build_dir / step_reference.group_name
|
|
144
|
+
# Create the step output directory, to make sure that files can be created.
|
|
145
|
+
step_output_dir.mkdir(parents=True, exist_ok=True)
|
|
146
|
+
step = step_reference._class(execution_context, step_output_dir, step_reference.config)
|
|
147
|
+
# Execute the step is necessary. If the step is not dirty, it will not be executed
|
|
148
|
+
Executor(step.output_dir, self.force_run, self.dry_run).execute(step)
|
|
149
|
+
# Independent if the step was executed or not, every step shall update the context
|
|
150
|
+
step.update_execution_context()
|
|
151
|
+
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class PipelineScheduler:
|
|
156
|
+
"""
|
|
157
|
+
Schedules which steps must be executed based on the provided configuration.
|
|
158
|
+
|
|
159
|
+
* If a step name is provided and the single flag is set, only that step will be executed.
|
|
160
|
+
* If a step name is provided and the single flag is not set, all steps up to the provided step will be executed.
|
|
161
|
+
* In case a command is provided, only the steps up to that command will be executed.
|
|
162
|
+
* If no step name is provided, all steps will be executed.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
def __init__(self, pipeline: PipelineConfig, project_root_dir: Path) -> None:
|
|
166
|
+
self.pipeline = pipeline
|
|
167
|
+
self.project_root_dir = project_root_dir
|
|
168
|
+
self.logger = logger.bind()
|
|
169
|
+
|
|
170
|
+
def get_steps_to_run(self, step_name: Optional[str] = None, single: bool = False) -> List[PipelineStepReference]:
|
|
171
|
+
pipeline_loader = PipelineLoader(self.pipeline, self.project_root_dir)
|
|
172
|
+
return self.filter_steps_references(pipeline_loader.load_steps_references(), step_name, single)
|
|
173
|
+
|
|
174
|
+
@staticmethod
|
|
175
|
+
def filter_steps_references(
|
|
176
|
+
steps_references: List[PipelineStepReference],
|
|
177
|
+
step_name: Optional[str],
|
|
178
|
+
single: Optional[bool],
|
|
179
|
+
) -> List[PipelineStepReference]:
|
|
180
|
+
if step_name:
|
|
181
|
+
step_reference = next((step for step in steps_references if step.name == step_name), None)
|
|
182
|
+
if not step_reference:
|
|
183
|
+
return []
|
|
184
|
+
if single:
|
|
185
|
+
return [step_reference]
|
|
186
|
+
return [step for step in steps_references if steps_references.index(step) <= steps_references.index(step_reference)]
|
|
187
|
+
return steps_references
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.1"
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
from abc import abstractmethod
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
from typing import Any, Dict, Optional, Type
|
|
4
|
-
|
|
5
|
-
from py_app_dev.core.runnable import Runnable
|
|
6
|
-
|
|
7
|
-
from .execution_context import ExecutionContext
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class PipelineStep(Runnable):
|
|
11
|
-
def __init__(self, execution_context: ExecutionContext, output_dir: Path, config: Optional[Dict[str, Any]] = None) -> None:
|
|
12
|
-
self.execution_context = execution_context
|
|
13
|
-
self.output_dir = output_dir
|
|
14
|
-
self.config = config
|
|
15
|
-
self.project_root_dir = self.execution_context.project_root_dir
|
|
16
|
-
|
|
17
|
-
@abstractmethod
|
|
18
|
-
def update_execution_context(self) -> None:
|
|
19
|
-
pass
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class PipelineStepReference:
|
|
23
|
-
def __init__(self, group_name: str, _class: Type[PipelineStep], config: Optional[Dict[str, Any]] = None) -> None:
|
|
24
|
-
self.group_name = group_name
|
|
25
|
-
self._class = _class
|
|
26
|
-
self.config = config
|
|
27
|
-
|
|
28
|
-
@property
|
|
29
|
-
def name(self) -> str:
|
|
30
|
-
return self._class.__name__
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import List, Optional
|
|
3
|
-
|
|
4
|
-
from py_app_dev.core.logging import logger
|
|
5
|
-
from py_app_dev.core.pipeline import PipelineConfig
|
|
6
|
-
from py_app_dev.core.pipeline import PipelineLoader as GenericPipelineLoader
|
|
7
|
-
from py_app_dev.core.runnable import Executor
|
|
8
|
-
|
|
9
|
-
from .domain.artifacts import ProjectArtifactsLocator
|
|
10
|
-
from .domain.execution_context import ExecutionContext
|
|
11
|
-
from .domain.pipeline import PipelineStep, PipelineStepReference
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class PipelineLoader:
|
|
15
|
-
"""
|
|
16
|
-
Loads pipeline steps from a pipeline configuration.
|
|
17
|
-
|
|
18
|
-
The steps are not instantiated, only the references are returned (lazy load).
|
|
19
|
-
The pipeline loader needs to know the project root directory to be able to find the
|
|
20
|
-
user custom local steps.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
def __init__(self, pipeline_config: PipelineConfig, project_root_dir: Path) -> None:
|
|
24
|
-
self.pipeline_config = pipeline_config
|
|
25
|
-
self.project_root_dir = project_root_dir
|
|
26
|
-
self._loader = GenericPipelineLoader[PipelineStep](self.pipeline_config, self.project_root_dir)
|
|
27
|
-
|
|
28
|
-
def load_steps_references(self) -> List[PipelineStepReference]:
|
|
29
|
-
return [PipelineStepReference(step_reference.group_name, step_reference._class, step_reference.config) for step_reference in self._loader.load_steps()]
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class PipelineStepsExecutor:
|
|
33
|
-
"""Executes a list of pipeline steps sequentially."""
|
|
34
|
-
|
|
35
|
-
def __init__(
|
|
36
|
-
self,
|
|
37
|
-
artifacts_locator: ProjectArtifactsLocator,
|
|
38
|
-
steps_references: List[PipelineStepReference],
|
|
39
|
-
force_run: bool = False,
|
|
40
|
-
dry_run: bool = False,
|
|
41
|
-
) -> None:
|
|
42
|
-
self.logger = logger.bind()
|
|
43
|
-
self.artifacts_locator = artifacts_locator
|
|
44
|
-
self.steps_references = steps_references
|
|
45
|
-
self.force_run = force_run
|
|
46
|
-
self.dry_run = dry_run
|
|
47
|
-
|
|
48
|
-
def run(self) -> None:
|
|
49
|
-
execution_context = ExecutionContext(project_root_dir=self.artifacts_locator.project_root_dir, install_dirs=[])
|
|
50
|
-
for step_reference in self.steps_references:
|
|
51
|
-
step_output_dir = self.artifacts_locator.build_dir / step_reference.group_name
|
|
52
|
-
# Create the step output directory, to make sure that files can be created.
|
|
53
|
-
step_output_dir.mkdir(parents=True, exist_ok=True)
|
|
54
|
-
step = step_reference._class(execution_context, step_output_dir, step_reference.config)
|
|
55
|
-
# Execute the step is necessary. If the step is not dirty, it will not be executed
|
|
56
|
-
Executor(step.output_dir, self.force_run, self.dry_run).execute(step)
|
|
57
|
-
# Independent if the step was executed or not, every step shall update the context
|
|
58
|
-
step.update_execution_context()
|
|
59
|
-
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class PipelineScheduler:
|
|
64
|
-
"""
|
|
65
|
-
Schedules which steps must be executed based on the provided configuration.
|
|
66
|
-
|
|
67
|
-
* If a step name is provided and the single flag is set, only that step will be executed.
|
|
68
|
-
* If a step name is provided and the single flag is not set, all steps up to the provided step will be executed.
|
|
69
|
-
* In case a command is provided, only the steps up to that command will be executed.
|
|
70
|
-
* If no step name is provided, all steps will be executed.
|
|
71
|
-
"""
|
|
72
|
-
|
|
73
|
-
def __init__(self, pipeline: PipelineConfig, project_root_dir: Path) -> None:
|
|
74
|
-
self.pipeline = pipeline
|
|
75
|
-
self.project_root_dir = project_root_dir
|
|
76
|
-
self.logger = logger.bind()
|
|
77
|
-
|
|
78
|
-
def get_steps_to_run(self, step_name: Optional[str] = None, single: bool = False) -> List[PipelineStepReference]:
|
|
79
|
-
pipeline_loader = PipelineLoader(self.pipeline, self.project_root_dir)
|
|
80
|
-
return self.filter_steps_references(pipeline_loader.load_steps_references(), step_name, single)
|
|
81
|
-
|
|
82
|
-
@staticmethod
|
|
83
|
-
def filter_steps_references(
|
|
84
|
-
steps_references: List[PipelineStepReference],
|
|
85
|
-
step_name: Optional[str],
|
|
86
|
-
single: Optional[bool],
|
|
87
|
-
) -> List[PipelineStepReference]:
|
|
88
|
-
if step_name:
|
|
89
|
-
step_reference = next((step for step in steps_references if step.name == step_name), None)
|
|
90
|
-
if not step_reference:
|
|
91
|
-
return []
|
|
92
|
-
if single:
|
|
93
|
-
return [step_reference]
|
|
94
|
-
return [step for step in steps_references if steps_references.index(step) <= steps_references.index(step_reference)]
|
|
95
|
-
return steps_references
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/templates/project/.gitignore
RENAMED
|
File without changes
|
{pypeline_runner-0.2.1 → pypeline_runner-0.3.0}/src/pypeline/kickstart/templates/project/poetry.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|