pypeline-runner 0.3.1__tar.gz → 1.0.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.3.1 → pypeline_runner-1.0.0}/PKG-INFO +1 -1
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/pyproject.toml +1 -1
- pypeline_runner-1.0.0/src/pypeline/__init__.py +1 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/domain/pipeline.py +21 -5
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/kickstart/templates/project/steps/my_step.py +2 -1
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/main.py +3 -2
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/pypeline.py +28 -24
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/steps/create_venv.py +3 -3
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/steps/scoop_install.py +3 -3
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/steps/west_install.py +3 -3
- pypeline_runner-0.3.1/src/pypeline/__init__.py +0 -1
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/LICENSE +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/README.md +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/__run.py +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/domain/__init__.py +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/domain/artifacts.py +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/domain/config.py +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/domain/execution_context.py +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/domain/project_slurper.py +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/kickstart/__init__.py +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/kickstart/create.py +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/kickstart/templates/bootstrap/bootstrap.ps1 +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/kickstart/templates/bootstrap/bootstrap.py +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/kickstart/templates/project/.gitignore +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/kickstart/templates/project/poetry.toml +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/kickstart/templates/project/pypeline.ps1 +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/kickstart/templates/project/pypeline.yaml +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/kickstart/templates/project/pyproject.toml +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/kickstart/templates/project/scoopfile.json +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/py.typed +0 -0
- {pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/steps/__init__.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
|
@@ -4,11 +4,13 @@ from pathlib import Path
|
|
|
4
4
|
from typing import (
|
|
5
5
|
Any,
|
|
6
6
|
Dict,
|
|
7
|
+
Generic,
|
|
7
8
|
List,
|
|
8
9
|
Optional,
|
|
9
10
|
OrderedDict,
|
|
10
11
|
Type,
|
|
11
12
|
TypeAlias,
|
|
13
|
+
TypeVar,
|
|
12
14
|
)
|
|
13
15
|
|
|
14
16
|
from mashumaro import DataClassDictMixin
|
|
@@ -39,25 +41,39 @@ class PipelineStepConfig(DataClassDictMixin):
|
|
|
39
41
|
|
|
40
42
|
PipelineConfig: TypeAlias = OrderedDict[str, List[PipelineStepConfig]]
|
|
41
43
|
|
|
44
|
+
TExecutionContext = TypeVar("TExecutionContext", bound=ExecutionContext)
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
|
|
47
|
+
class PipelineStep(Generic[TExecutionContext], Runnable):
|
|
48
|
+
"""One can create subclasses of PipelineStep that specify the type of ExecutionContext they require."""
|
|
49
|
+
|
|
50
|
+
def __init__(self, execution_context: TExecutionContext, group_name: str, config: Optional[Dict[str, Any]] = None) -> None:
|
|
45
51
|
super().__init__(self.get_needs_dependency_management())
|
|
46
52
|
self.execution_context = execution_context
|
|
47
|
-
self.
|
|
53
|
+
self.group_name = group_name
|
|
48
54
|
self.config = config
|
|
49
55
|
self.project_root_dir = self.execution_context.project_root_dir
|
|
50
56
|
|
|
57
|
+
@property
|
|
58
|
+
def output_dir(self) -> Path:
|
|
59
|
+
return self.execution_context.create_artifacts_locator().build_dir.joinpath(self.group_name)
|
|
60
|
+
|
|
51
61
|
@abstractmethod
|
|
52
62
|
def update_execution_context(self) -> None:
|
|
63
|
+
"""
|
|
64
|
+
Even if the step does not need to run ( because it is not outdated ), it can still update the execution context.
|
|
65
|
+
|
|
66
|
+
A typical use case is for steps installing software that need to provide the install directories in the execution context even if all tools are already installed.
|
|
67
|
+
"""
|
|
53
68
|
pass
|
|
54
69
|
|
|
55
70
|
def get_needs_dependency_management(self) -> bool:
|
|
71
|
+
"""If false, the step executor will not check for outdated dependencies. This is useful for steps consisting of command lines which shall always run."""
|
|
56
72
|
return True
|
|
57
73
|
|
|
58
74
|
|
|
59
|
-
class PipelineStepReference:
|
|
60
|
-
def __init__(self, group_name: str, _class: Type[PipelineStep], config: Optional[Dict[str, Any]] = None) -> None:
|
|
75
|
+
class PipelineStepReference(Generic[TExecutionContext]):
|
|
76
|
+
def __init__(self, group_name: str, _class: Type[PipelineStep[TExecutionContext]], config: Optional[Dict[str, Any]] = None) -> None:
|
|
61
77
|
self.group_name = group_name
|
|
62
78
|
self._class = _class
|
|
63
79
|
self.config = config
|
|
@@ -3,10 +3,11 @@ from typing import List
|
|
|
3
3
|
|
|
4
4
|
from py_app_dev.core.logging import logger
|
|
5
5
|
|
|
6
|
+
from pypeline.domain.execution_context import ExecutionContext
|
|
6
7
|
from pypeline.domain.pipeline import PipelineStep
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
class MyStep(PipelineStep):
|
|
10
|
+
class MyStep(PipelineStep[ExecutionContext]):
|
|
10
11
|
def run(self) -> None:
|
|
11
12
|
logger.info(f"Run {self.get_name()} found install dirs:")
|
|
12
13
|
for install_dir in self.execution_context.install_dirs:
|
|
@@ -7,6 +7,7 @@ from py_app_dev.core.exceptions import UserNotificationException
|
|
|
7
7
|
from py_app_dev.core.logging import logger, setup_logger, time_it
|
|
8
8
|
|
|
9
9
|
from pypeline import __version__
|
|
10
|
+
from pypeline.domain.execution_context import ExecutionContext
|
|
10
11
|
from pypeline.domain.project_slurper import ProjectSlurper
|
|
11
12
|
from pypeline.kickstart.create import KickstartProject
|
|
12
13
|
from pypeline.pypeline import PipelineScheduler, PipelineStepsExecutor
|
|
@@ -76,14 +77,14 @@ def run(
|
|
|
76
77
|
if not project_slurper.pipeline:
|
|
77
78
|
raise UserNotificationException("No pipeline found in the configuration.")
|
|
78
79
|
# Schedule the steps to run
|
|
79
|
-
steps_references = PipelineScheduler(project_slurper.pipeline, project_dir).get_steps_to_run(step, single)
|
|
80
|
+
steps_references = PipelineScheduler[ExecutionContext](project_slurper.pipeline, project_dir).get_steps_to_run(step, single)
|
|
80
81
|
if not steps_references:
|
|
81
82
|
if step:
|
|
82
83
|
raise UserNotificationException(f"Step '{step}' not found in the pipeline.")
|
|
83
84
|
logger.info("No steps to run.")
|
|
84
85
|
return
|
|
85
86
|
|
|
86
|
-
PipelineStepsExecutor(
|
|
87
|
+
PipelineStepsExecutor[ExecutionContext](ExecutionContext(project_dir), steps_references, force_run, dry_run).run()
|
|
87
88
|
|
|
88
89
|
|
|
89
90
|
def main() -> None:
|
|
@@ -4,9 +4,11 @@ from pathlib import Path
|
|
|
4
4
|
from typing import (
|
|
5
5
|
Any,
|
|
6
6
|
Dict,
|
|
7
|
+
Generic,
|
|
7
8
|
List,
|
|
8
9
|
Optional,
|
|
9
10
|
Type,
|
|
11
|
+
cast,
|
|
10
12
|
)
|
|
11
13
|
|
|
12
14
|
from py_app_dev.core.exceptions import UserNotificationException
|
|
@@ -15,10 +17,10 @@ from py_app_dev.core.runnable import Executor
|
|
|
15
17
|
|
|
16
18
|
from .domain.artifacts import ProjectArtifactsLocator
|
|
17
19
|
from .domain.execution_context import ExecutionContext
|
|
18
|
-
from .domain.pipeline import PipelineConfig, PipelineStep, PipelineStepConfig, PipelineStepReference
|
|
20
|
+
from .domain.pipeline import PipelineConfig, PipelineStep, PipelineStepConfig, PipelineStepReference, TExecutionContext
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
class PipelineLoader:
|
|
23
|
+
class PipelineLoader(Generic[TExecutionContext]):
|
|
22
24
|
"""
|
|
23
25
|
Loads pipeline steps from a pipeline configuration.
|
|
24
26
|
|
|
@@ -31,7 +33,7 @@ class PipelineLoader:
|
|
|
31
33
|
self.pipeline_config = pipeline_config
|
|
32
34
|
self.project_root_dir = project_root_dir
|
|
33
35
|
|
|
34
|
-
def load_steps_references(self) -> List[PipelineStepReference]:
|
|
36
|
+
def load_steps_references(self) -> List[PipelineStepReference[TExecutionContext]]:
|
|
35
37
|
result = []
|
|
36
38
|
for group_name, steps_config in self.pipeline_config.items():
|
|
37
39
|
result.extend(self._load_steps(group_name, steps_config, self.project_root_dir))
|
|
@@ -42,7 +44,7 @@ class PipelineLoader:
|
|
|
42
44
|
group_name: str,
|
|
43
45
|
steps_config: List[PipelineStepConfig],
|
|
44
46
|
project_root_dir: Path,
|
|
45
|
-
) -> List[PipelineStepReference]:
|
|
47
|
+
) -> List[PipelineStepReference[TExecutionContext]]:
|
|
46
48
|
result = []
|
|
47
49
|
for step_config in steps_config:
|
|
48
50
|
step_class_name = step_config.class_name or step_config.step
|
|
@@ -54,11 +56,11 @@ class PipelineLoader:
|
|
|
54
56
|
step_class = PipelineLoader._create_run_command_step_class(step_config.run, step_class_name)
|
|
55
57
|
else:
|
|
56
58
|
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))
|
|
59
|
+
result.append(PipelineStepReference[TExecutionContext](group_name, cast(Type[PipelineStep[TExecutionContext]], step_class), step_config.config))
|
|
58
60
|
return result
|
|
59
61
|
|
|
60
62
|
@staticmethod
|
|
61
|
-
def _load_user_step(python_file: Path, step_class_name: str) -> Type[PipelineStep]:
|
|
63
|
+
def _load_user_step(python_file: Path, step_class_name: str) -> Type[PipelineStep[ExecutionContext]]:
|
|
62
64
|
# Create a module specification from the file path
|
|
63
65
|
spec = spec_from_file_location(f"user__{step_class_name}", python_file)
|
|
64
66
|
if spec and spec.loader:
|
|
@@ -73,7 +75,7 @@ class PipelineLoader:
|
|
|
73
75
|
raise UserNotificationException(f"Could not load file '{python_file}'." " Please check the file for any errors.")
|
|
74
76
|
|
|
75
77
|
@staticmethod
|
|
76
|
-
def _load_module_step(module_name: str, step_class_name: str) -> Type[PipelineStep]:
|
|
78
|
+
def _load_module_step(module_name: str, step_class_name: str) -> Type[PipelineStep[ExecutionContext]]:
|
|
77
79
|
try:
|
|
78
80
|
module = importlib.import_module(module_name)
|
|
79
81
|
step_class = getattr(module, step_class_name)
|
|
@@ -84,14 +86,14 @@ class PipelineLoader:
|
|
|
84
86
|
return step_class
|
|
85
87
|
|
|
86
88
|
@staticmethod
|
|
87
|
-
def _create_run_command_step_class(command: str, name: str) -> Type[PipelineStep]:
|
|
89
|
+
def _create_run_command_step_class(command: str, name: str) -> Type[PipelineStep[ExecutionContext]]:
|
|
88
90
|
"""Dynamically creates a step class for a given command."""
|
|
89
91
|
|
|
90
|
-
class TmpDynamicRunCommandStep(PipelineStep):
|
|
92
|
+
class TmpDynamicRunCommandStep(PipelineStep[ExecutionContext]):
|
|
91
93
|
"""A simple step that runs a command."""
|
|
92
94
|
|
|
93
|
-
def __init__(self, execution_context: ExecutionContext,
|
|
94
|
-
super().__init__(execution_context,
|
|
95
|
+
def __init__(self, execution_context: ExecutionContext, group_name: str, config: Optional[Dict[str, Any]] = None) -> None:
|
|
96
|
+
super().__init__(execution_context, group_name, config)
|
|
95
97
|
self.command = command
|
|
96
98
|
self.name = name
|
|
97
99
|
|
|
@@ -122,29 +124,31 @@ class PipelineLoader:
|
|
|
122
124
|
return type(name, (TmpDynamicRunCommandStep,), {})
|
|
123
125
|
|
|
124
126
|
|
|
125
|
-
class PipelineStepsExecutor:
|
|
127
|
+
class PipelineStepsExecutor(Generic[TExecutionContext]):
|
|
126
128
|
"""Executes a list of pipeline steps sequentially."""
|
|
127
129
|
|
|
128
130
|
def __init__(
|
|
129
131
|
self,
|
|
130
|
-
|
|
131
|
-
steps_references: List[PipelineStepReference],
|
|
132
|
+
execution_context: TExecutionContext,
|
|
133
|
+
steps_references: List[PipelineStepReference[TExecutionContext]],
|
|
132
134
|
force_run: bool = False,
|
|
133
135
|
dry_run: bool = False,
|
|
134
136
|
) -> None:
|
|
135
137
|
self.logger = logger.bind()
|
|
136
|
-
self.
|
|
138
|
+
self.execution_context = execution_context
|
|
137
139
|
self.steps_references = steps_references
|
|
138
140
|
self.force_run = force_run
|
|
139
141
|
self.dry_run = dry_run
|
|
140
142
|
|
|
143
|
+
@property
|
|
144
|
+
def artifacts_locator(self) -> ProjectArtifactsLocator:
|
|
145
|
+
return self.execution_context.create_artifacts_locator()
|
|
146
|
+
|
|
141
147
|
def run(self) -> None:
|
|
142
|
-
execution_context = ExecutionContext(project_root_dir=self.artifacts_locator.project_root_dir, install_dirs=[])
|
|
143
148
|
for step_reference in self.steps_references:
|
|
144
|
-
|
|
149
|
+
step = step_reference._class(self.execution_context, step_reference.group_name, step_reference.config)
|
|
145
150
|
# Create the step output directory, to make sure that files can be created.
|
|
146
|
-
|
|
147
|
-
step = step_reference._class(execution_context, step_output_dir, step_reference.config)
|
|
151
|
+
step.output_dir.mkdir(parents=True, exist_ok=True)
|
|
148
152
|
# Execute the step is necessary. If the step is not dirty, it will not be executed
|
|
149
153
|
Executor(step.output_dir, self.force_run, self.dry_run).execute(step)
|
|
150
154
|
# Independent if the step was executed or not, every step shall update the context
|
|
@@ -153,7 +157,7 @@ class PipelineStepsExecutor:
|
|
|
153
157
|
return
|
|
154
158
|
|
|
155
159
|
|
|
156
|
-
class PipelineScheduler:
|
|
160
|
+
class PipelineScheduler(Generic[TExecutionContext]):
|
|
157
161
|
"""
|
|
158
162
|
Schedules which steps must be executed based on the provided configuration.
|
|
159
163
|
|
|
@@ -168,16 +172,16 @@ class PipelineScheduler:
|
|
|
168
172
|
self.project_root_dir = project_root_dir
|
|
169
173
|
self.logger = logger.bind()
|
|
170
174
|
|
|
171
|
-
def get_steps_to_run(self, step_name: Optional[str] = None, single: bool = False) -> List[PipelineStepReference]:
|
|
172
|
-
pipeline_loader = PipelineLoader(self.pipeline, self.project_root_dir)
|
|
175
|
+
def get_steps_to_run(self, step_name: Optional[str] = None, single: bool = False) -> List[PipelineStepReference[TExecutionContext]]:
|
|
176
|
+
pipeline_loader = PipelineLoader[TExecutionContext](self.pipeline, self.project_root_dir)
|
|
173
177
|
return self.filter_steps_references(pipeline_loader.load_steps_references(), step_name, single)
|
|
174
178
|
|
|
175
179
|
@staticmethod
|
|
176
180
|
def filter_steps_references(
|
|
177
|
-
steps_references: List[PipelineStepReference],
|
|
181
|
+
steps_references: List[PipelineStepReference[TExecutionContext]],
|
|
178
182
|
step_name: Optional[str],
|
|
179
183
|
single: Optional[bool],
|
|
180
|
-
) -> List[PipelineStepReference]:
|
|
184
|
+
) -> List[PipelineStepReference[TExecutionContext]]:
|
|
181
185
|
if step_name:
|
|
182
186
|
step_reference = next((step for step in steps_references if step.name == step_name), None)
|
|
183
187
|
if not step_reference:
|
|
@@ -15,9 +15,9 @@ class CreateVEnvConfig(DataClassDictMixin):
|
|
|
15
15
|
bootstrap_script: str = "bootstrap.py"
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
class CreateVEnv(PipelineStep):
|
|
19
|
-
def __init__(self, execution_context: ExecutionContext,
|
|
20
|
-
super().__init__(execution_context,
|
|
18
|
+
class CreateVEnv(PipelineStep[ExecutionContext]):
|
|
19
|
+
def __init__(self, execution_context: ExecutionContext, group_name: str, config: Optional[Dict[str, Any]] = None) -> None:
|
|
20
|
+
super().__init__(execution_context, group_name, config)
|
|
21
21
|
self.logger = logger.bind()
|
|
22
22
|
self.logger = logger.bind()
|
|
23
23
|
|
|
@@ -45,9 +45,9 @@ def create_scoop_wrapper() -> ScoopWrapper:
|
|
|
45
45
|
return ScoopWrapper()
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
class ScoopInstall(PipelineStep):
|
|
49
|
-
def __init__(self, execution_context: ExecutionContext,
|
|
50
|
-
super().__init__(execution_context,
|
|
48
|
+
class ScoopInstall(PipelineStep[ExecutionContext]):
|
|
49
|
+
def __init__(self, execution_context: ExecutionContext, group_name: str, config: Optional[Dict[str, Any]] = None) -> None:
|
|
50
|
+
super().__init__(execution_context, group_name, 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
|
|
@@ -8,9 +8,9 @@ from ..domain.execution_context import ExecutionContext
|
|
|
8
8
|
from ..domain.pipeline import PipelineStep
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class WestInstall(PipelineStep):
|
|
12
|
-
def __init__(self, execution_context: ExecutionContext,
|
|
13
|
-
super().__init__(execution_context,
|
|
11
|
+
class WestInstall(PipelineStep[ExecutionContext]):
|
|
12
|
+
def __init__(self, execution_context: ExecutionContext, group_name: str, config: Optional[Dict[str, Any]] = None) -> None:
|
|
13
|
+
super().__init__(execution_context, group_name, config)
|
|
14
14
|
self.logger = logger.bind()
|
|
15
15
|
self.artifacts_locator = execution_context.create_artifacts_locator()
|
|
16
16
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.3.1"
|
|
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
|
|
File without changes
|
{pypeline_runner-0.3.1 → pypeline_runner-1.0.0}/src/pypeline/kickstart/templates/project/.gitignore
RENAMED
|
File without changes
|
{pypeline_runner-0.3.1 → pypeline_runner-1.0.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
|