pypeline-runner 1.24.0__tar.gz → 1.25.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.
Files changed (33) hide show
  1. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/PKG-INFO +27 -9
  2. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/README.md +22 -4
  3. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/pyproject.toml +16 -17
  4. pypeline_runner-1.25.0/src/pypeline/__init__.py +1 -0
  5. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/steps/create_venv.py +5 -5
  6. pypeline_runner-1.25.0/src/pypeline/steps/west_install.py +310 -0
  7. pypeline_runner-1.24.0/src/pypeline/__init__.py +0 -1
  8. pypeline_runner-1.24.0/src/pypeline/steps/west_install.py +0 -54
  9. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/LICENSE +0 -0
  10. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/__run.py +0 -0
  11. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/bootstrap/__init__.py +0 -0
  12. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/bootstrap/run.py +0 -0
  13. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/domain/__init__.py +0 -0
  14. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/domain/artifacts.py +0 -0
  15. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/domain/config.py +0 -0
  16. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/domain/execution_context.py +0 -0
  17. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/domain/pipeline.py +0 -0
  18. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/domain/project_slurper.py +0 -0
  19. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/inputs_parser.py +0 -0
  20. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/kickstart/__init__.py +0 -0
  21. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/kickstart/create.py +0 -0
  22. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/kickstart/templates/project/.gitignore +0 -0
  23. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/kickstart/templates/project/pypeline.ps1 +0 -0
  24. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/kickstart/templates/project/pypeline.yaml +0 -0
  25. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/kickstart/templates/project/pyproject.toml +0 -0
  26. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/kickstart/templates/project/steps/my_step.py +0 -0
  27. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/kickstart/templates/project/west.yaml +0 -0
  28. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/main.py +0 -0
  29. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/py.typed +0 -0
  30. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/pypeline.py +0 -0
  31. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/steps/__init__.py +0 -0
  32. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/steps/env_setup_script.py +0 -0
  33. {pypeline_runner-1.24.0 → pypeline_runner-1.25.0}/src/pypeline/steps/scoop_install.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pypeline-runner
3
- Version: 1.24.0
3
+ Version: 1.25.0
4
4
  Summary: Configure and execute pipelines with Python (similar to GitHub workflows or Jenkins pipelines).
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -19,10 +19,10 @@ Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Programming Language :: Python :: 3.13
20
20
  Classifier: Programming Language :: Python :: 3.14
21
21
  Classifier: Topic :: Software Development :: Libraries
22
- Requires-Dist: py-app-dev (>=2.10,<3.0)
23
- Requires-Dist: pyyaml (>=6.0,<7.0)
24
- Requires-Dist: typer (>=0,<1)
25
- Requires-Dist: west (>=1.0,<2.0)
22
+ Requires-Dist: py-app-dev (>=2.18.0,<3.0.0)
23
+ Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
24
+ Requires-Dist: typer (>=0.21.1,<0.22.0)
25
+ Requires-Dist: west (>=1.5.0,<2.0.0)
26
26
  Project-URL: Bug Tracker, https://github.com/cuinixam/pypeline/issues
27
27
  Project-URL: Changelog, https://github.com/cuinixam/pypeline/blob/main/CHANGELOG.md
28
28
  Project-URL: Documentation, https://pypeline-runner.readthedocs.io
@@ -95,6 +95,15 @@ This will install the `pypeline` command globally, which you can use to run your
95
95
 
96
96
  Documentation: [pypeline-runner.readthedocs.io](https://pypeline-runner.readthedocs.io)
97
97
 
98
+ ## Quick Start
99
+
100
+ ```shell
101
+ pipx install pypeline-runner
102
+ pypeline init --project-dir my-pipeline
103
+ cd my-pipeline
104
+ pypeline run
105
+ ```
106
+
98
107
  ## Walkthrough: Getting Started with Pypeline
99
108
 
100
109
  To get started run the `init` command to create a sample project:
@@ -134,10 +143,19 @@ pypeline run --project-dir my-pipeline
134
143
  ## Contributing
135
144
 
136
145
  The project uses Poetry for dependencies management and packaging.
137
- Run the `bootstrap.ps1` script to install Python and create the virtual environment.
146
+ You can set up the development environment using one of the following methods:
147
+
148
+ **Option 1: Using Poetry directly** (minimal setup)
138
149
 
139
- ```powershell
140
- .\bootstrap.ps1
150
+ ```shell
151
+ poetry install
152
+ ```
153
+
154
+ **Option 2: Using pypeline-runner** (runs the full pipeline including tests and checks)
155
+
156
+ ```shell
157
+ pipx install pypeline-runner
158
+ pypeline run
141
159
  ```
142
160
 
143
161
  To execute the test suite, call pytest inside Poetry's virtual environment via `poetry run`:
@@ -158,5 +176,5 @@ This repository uses [commitlint](https://github.com/conventional-changelog/comm
158
176
 
159
177
  ## Credits
160
178
 
161
- This package was created with [Copier](https://copier.readthedocs.io/) and the [cuinixam/pypackage-template](https://github.com/cuinixam/pypackage-template) project template.
179
+ This package was created with [Copier](https://copier.readthedocs.io/) and the [browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template) project template.
162
180
 
@@ -64,6 +64,15 @@ This will install the `pypeline` command globally, which you can use to run your
64
64
 
65
65
  Documentation: [pypeline-runner.readthedocs.io](https://pypeline-runner.readthedocs.io)
66
66
 
67
+ ## Quick Start
68
+
69
+ ```shell
70
+ pipx install pypeline-runner
71
+ pypeline init --project-dir my-pipeline
72
+ cd my-pipeline
73
+ pypeline run
74
+ ```
75
+
67
76
  ## Walkthrough: Getting Started with Pypeline
68
77
 
69
78
  To get started run the `init` command to create a sample project:
@@ -103,10 +112,19 @@ pypeline run --project-dir my-pipeline
103
112
  ## Contributing
104
113
 
105
114
  The project uses Poetry for dependencies management and packaging.
106
- Run the `bootstrap.ps1` script to install Python and create the virtual environment.
115
+ You can set up the development environment using one of the following methods:
116
+
117
+ **Option 1: Using Poetry directly** (minimal setup)
107
118
 
108
- ```powershell
109
- .\bootstrap.ps1
119
+ ```shell
120
+ poetry install
121
+ ```
122
+
123
+ **Option 2: Using pypeline-runner** (runs the full pipeline including tests and checks)
124
+
125
+ ```shell
126
+ pipx install pypeline-runner
127
+ pypeline run
110
128
  ```
111
129
 
112
130
  To execute the test suite, call pytest inside Poetry's virtual environment via `poetry run`:
@@ -127,4 +145,4 @@ This repository uses [commitlint](https://github.com/conventional-changelog/comm
127
145
 
128
146
  ## Credits
129
147
 
130
- This package was created with [Copier](https://copier.readthedocs.io/) and the [cuinixam/pypackage-template](https://github.com/cuinixam/pypackage-template) project template.
148
+ This package was created with [Copier](https://copier.readthedocs.io/) and the [browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template) project template.
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pypeline-runner"
3
- version = "1.24.0"
3
+ version = "1.25.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,26 +27,25 @@ pypeline = "pypeline.main:main"
27
27
 
28
28
  [tool.poetry.dependencies]
29
29
  python = "^3.10"
30
- py-app-dev = "^2.10"
31
- typer = "^0"
32
- pyyaml = "^6.0"
33
- west = "^1.0"
30
+ py-app-dev = "^2.18.0"
31
+ typer = "^0.21.1"
32
+ pyyaml = "^6.0.3"
33
+ west = "^1.5.0"
34
34
 
35
35
  [tool.poetry.group.dev.dependencies]
36
- pytest = "^8.3"
37
- pytest-cov = "^6.0"
38
- pre-commit = "^4.0"
39
- ruff = "^0.9"
36
+ pytest = "^9.0.2"
37
+ pytest-cov = "^7.0.0"
38
+ pre-commit = "^4.5.1"
39
+ ruff = "^0.14.14"
40
40
 
41
41
  [tool.poetry.group.docs.dependencies]
42
- myst-parser = ">=0.16"
43
- sphinx = ">=4.0"
44
- sphinxcontrib-mermaid = "^0.8"
45
- mlx-traceability = "^10.0"
46
- sphinx-copybutton = "^0.5"
47
- sphinx-new-tab-link = "^0.2"
48
- sphinx-book-theme = "^1.1"
49
- sphinx-design = "^0.5"
42
+ myst-parser = ">=4.0.0"
43
+ sphinx = ">=8.0.0"
44
+ sphinxcontrib-mermaid = "^2.0.0"
45
+ sphinx-copybutton = "^0.5.2"
46
+ sphinx-new-tab-link = "^0.8.1"
47
+ sphinx-book-theme = "^1.1.4"
48
+ sphinx-design = "^0.6.0"
50
49
 
51
50
  [tool.semantic_release]
52
51
  version_toml = ["pyproject.toml:tool.poetry.version"]
@@ -0,0 +1 @@
1
+ __version__ = "1.25.0"
@@ -316,7 +316,7 @@ class CreateVEnv(PipelineStep[ExecutionContext]):
316
316
 
317
317
  def run(self) -> int:
318
318
  self.logger.debug(f"Run {self.get_name()} step. Output dir: {self.output_dir}")
319
- bootstrap_config: Dict[str, Any] = {}
319
+ bootstrap_config = CreateVEnvConfig()
320
320
  is_managed = False
321
321
  # Determine target script and mode
322
322
  if self.user_config.bootstrap_script:
@@ -356,17 +356,17 @@ class CreateVEnv(PipelineStep[ExecutionContext]):
356
356
  for field_name in self.user_config.get_all_properties_names(["bootstrap_script", "python_executable"]):
357
357
  val = getattr(self.user_config, field_name)
358
358
  if val is not None:
359
- bootstrap_config[field_name] = val
359
+ setattr(bootstrap_config, field_name, val)
360
360
 
361
361
  # Priority: input python_version takes precedence over config python_version
362
362
  input_python_version = self.execution_context.get_input("python_version")
363
363
  if input_python_version:
364
- bootstrap_config["python_version"] = input_python_version
364
+ bootstrap_config.python_version = input_python_version
365
365
 
366
366
  # Write bootstrap.json if any configuration is provided
367
- if bootstrap_config:
367
+ if bootstrap_config.is_any_property_set():
368
368
  self.bootstrap_config_file.parent.mkdir(exist_ok=True)
369
- CreateVEnvConfig.from_dict(bootstrap_config).to_json_file(self.bootstrap_config_file)
369
+ bootstrap_config.to_json_file(self.bootstrap_config_file)
370
370
  self.logger.info(f"Created bootstrap configuration at {self.bootstrap_config_file}")
371
371
 
372
372
  # Build bootstrap script arguments
@@ -0,0 +1,310 @@
1
+ import io
2
+ import json
3
+ import traceback
4
+ from dataclasses import dataclass, field
5
+ from pathlib import Path
6
+ from typing import Any, Optional
7
+
8
+ import yaml
9
+ from mashumaro.config import BaseConfig
10
+ from mashumaro.mixins.json import DataClassJSONMixin
11
+ from py_app_dev.core.config import BaseConfigDictMixin
12
+ from py_app_dev.core.exceptions import UserNotificationException
13
+ from py_app_dev.core.logging import logger
14
+ from yaml.parser import ParserError
15
+ from yaml.scanner import ScannerError
16
+
17
+ from pypeline.domain.execution_context import ExecutionContext
18
+ from pypeline.domain.pipeline import PipelineStep
19
+
20
+
21
+ class BaseConfigJSONMixin(DataClassJSONMixin):
22
+ class Config(BaseConfig):
23
+ """Mashumaro configuration for JSON serialization."""
24
+
25
+ omit_none = True
26
+ serialize_by_alias = True
27
+
28
+ def to_json_string(self) -> str:
29
+ return json.dumps(self.to_dict(), indent=2)
30
+
31
+ def to_json_file(self, file_path: Path) -> None:
32
+ file_path.write_text(self.to_json_string())
33
+
34
+
35
+ @dataclass
36
+ class WestDependency(BaseConfigDictMixin):
37
+ #: Project name
38
+ name: str
39
+ #: Remote name
40
+ remote: str
41
+ #: Revision (tag, branch, or commit)
42
+ revision: str
43
+ #: Path where the dependency will be installed
44
+ path: str
45
+
46
+
47
+ @dataclass
48
+ class WestRemote(BaseConfigDictMixin):
49
+ #: Remote name
50
+ name: str
51
+ #: URL base
52
+ url_base: str = field(metadata={"alias": "url-base"})
53
+
54
+
55
+ @dataclass
56
+ class WestManifest(BaseConfigDictMixin):
57
+ #: Remote configurations
58
+ remotes: list[WestRemote] = field(default_factory=list)
59
+ #: Project dependencies
60
+ projects: list[WestDependency] = field(default_factory=list)
61
+
62
+
63
+ @dataclass
64
+ class WestManifestFile(BaseConfigDictMixin):
65
+ manifest: WestManifest
66
+ # This field is intended to keep track of where configuration was loaded from and
67
+ # it is automatically added when configuration is loaded from file
68
+ file: Optional[Path] = None
69
+
70
+ @classmethod
71
+ def from_file(cls, config_file: Path) -> "WestManifestFile":
72
+ config_dict = cls.parse_to_dict(config_file)
73
+ return cls.from_dict(config_dict)
74
+
75
+ @staticmethod
76
+ def parse_to_dict(config_file: Path) -> dict[str, Any]:
77
+ try:
78
+ with open(config_file) as fs:
79
+ config_dict = yaml.safe_load(fs)
80
+ # Add file name to config to keep track of where configuration was loaded from
81
+ config_dict["file"] = config_file
82
+ return config_dict
83
+ except ScannerError as e:
84
+ raise UserNotificationException(f"Failed scanning west manifest file '{config_file}'. \nError: {e}") from e
85
+ except ParserError as e:
86
+ raise UserNotificationException(f"Failed parsing west manifest file '{config_file}'. \nError: {e}") from e
87
+
88
+
89
+ @dataclass
90
+ class WestInstallResult(DataClassJSONMixin):
91
+ """Tracks paths of installed west dependencies."""
92
+
93
+ installed_dirs: list[Path] = field(default_factory=list)
94
+
95
+ class Config(BaseConfig):
96
+ """Mashumaro configuration for JSON serialization."""
97
+
98
+ omit_none = True
99
+
100
+ @classmethod
101
+ def from_json_file(cls, file_path: Path) -> "WestInstallResult":
102
+ try:
103
+ result = cls.from_dict(json.loads(file_path.read_text()))
104
+ except Exception as e:
105
+ output = io.StringIO()
106
+ traceback.print_exc(file=output)
107
+ raise UserNotificationException(output.getvalue()) from e
108
+ return result
109
+
110
+ def to_json_string(self) -> str:
111
+ return json.dumps(self.to_dict(), indent=2)
112
+
113
+ def to_json_file(self, file_path: Path) -> None:
114
+ file_path.parent.mkdir(parents=True, exist_ok=True)
115
+ file_path.write_text(self.to_json_string())
116
+
117
+
118
+ @dataclass
119
+ class WestWorkspaceDir:
120
+ """West workspace directory path for data registry sharing."""
121
+
122
+ path: Path
123
+
124
+
125
+ @dataclass
126
+ class WestInstallConfig(DataClassJSONMixin):
127
+ """Configuration for WestInstall step."""
128
+
129
+ #: Relative path from project root for west workspace directory
130
+ workspace_dir: Optional[str] = None
131
+
132
+
133
+ class WestInstall(PipelineStep[ExecutionContext]):
134
+ def __init__(self, execution_context: ExecutionContext, group_name: str, config: Optional[dict[str, Any]] = None) -> None:
135
+ super().__init__(execution_context, group_name, config)
136
+ self.logger = logger.bind()
137
+ self.install_result = WestInstallResult()
138
+ self.user_config = WestInstallConfig.from_dict(config) if config else WestInstallConfig()
139
+
140
+ self._west_workspace_dir = self._resolve_workspace_dir()
141
+ self._manifests = self._collect_manifests()
142
+
143
+ def _resolve_workspace_dir(self) -> Path:
144
+ """Resolve workspace directory from data registry (priority) or config."""
145
+ # Check data registry first (highest priority)
146
+ registry_entries = self.execution_context.data_registry.find_data(WestWorkspaceDir)
147
+ if registry_entries:
148
+ return registry_entries[0].path
149
+
150
+ # Check config
151
+ if self.user_config.workspace_dir:
152
+ return self.project_root_dir / self.user_config.workspace_dir
153
+
154
+ # Fallback to build dir
155
+ return self.execution_context.create_artifacts_locator().build_dir
156
+
157
+ def _collect_manifests(self) -> list[WestManifestFile]:
158
+ manifests: list[WestManifestFile] = []
159
+
160
+ if self._source_manifest_file.exists():
161
+ try:
162
+ manifests.append(WestManifestFile.from_file(self._source_manifest_file))
163
+ except Exception as e:
164
+ self.logger.warning(f"Failed to parse source west.yaml: {e}")
165
+
166
+ # Check if there are registered manifests in the execution context data registry
167
+ manifests.extend(self.execution_context.data_registry.find_data(WestManifestFile))
168
+ return manifests
169
+
170
+ @property
171
+ def _source_manifest_file(self) -> Path:
172
+ """Optional west.yaml in project root (input)."""
173
+ return self.project_root_dir / "west.yaml"
174
+
175
+ @property
176
+ def _output_manifest_file(self) -> Path:
177
+ """Generated west.yaml (output)."""
178
+ return self.output_dir / "west.yaml"
179
+
180
+ @property
181
+ def _install_result_file(self) -> Path:
182
+ """Tracks installed dependency directories."""
183
+ return self.output_dir / "west_install_result.json"
184
+
185
+ @property
186
+ def installed_dirs(self) -> list[Path]:
187
+ return self.install_result.installed_dirs
188
+
189
+ def get_name(self) -> str:
190
+ return self.__class__.__name__
191
+
192
+ def get_config(self) -> dict[str, str] | None:
193
+ if self.user_config.workspace_dir:
194
+ return {"workspace_dir": self.user_config.workspace_dir}
195
+ return None
196
+
197
+ def _merge_manifests(self, manifests: list[WestManifest]) -> WestManifest:
198
+ """Merge multiple manifests, preserving order. First occurrence wins."""
199
+ merged = WestManifest()
200
+ for manifest in manifests:
201
+ for remote in manifest.remotes:
202
+ if remote not in merged.remotes:
203
+ merged.remotes.append(remote)
204
+ for project in manifest.projects:
205
+ if project not in merged.projects:
206
+ merged.projects.append(project)
207
+ return merged
208
+
209
+ def _write_west_manifest_file(self, manifest: WestManifest) -> None:
210
+ """Write merged manifest to west.yaml file."""
211
+ if not manifest.remotes and not manifest.projects:
212
+ self.logger.info("No West dependencies found. Skipping west.yaml generation.")
213
+ return
214
+
215
+ west_config = {
216
+ "manifest": {
217
+ "remotes": [remote.to_dict() for remote in manifest.remotes],
218
+ "projects": [project.to_dict() for project in manifest.projects],
219
+ }
220
+ }
221
+
222
+ # Convert url_base back to url-base for west compatibility
223
+ for remote in west_config["manifest"]["remotes"]:
224
+ if "url_base" in remote:
225
+ remote["url-base"] = remote.pop("url_base")
226
+
227
+ self._output_manifest_file.parent.mkdir(parents=True, exist_ok=True)
228
+ with open(self._output_manifest_file, "w") as f:
229
+ yaml.dump(west_config, f, default_flow_style=False)
230
+
231
+ self.logger.info(f"Generated west.yaml with {len(manifest.projects)} dependencies")
232
+
233
+ def _run_west_init(self) -> None:
234
+ """Initialize west workspace."""
235
+ self.execution_context.create_process_executor(
236
+ [
237
+ "west",
238
+ "init",
239
+ "-l",
240
+ "--mf",
241
+ self._output_manifest_file.as_posix(),
242
+ self._west_workspace_dir.joinpath("do_not_care").as_posix(),
243
+ ],
244
+ cwd=self.project_root_dir,
245
+ ).execute()
246
+
247
+ def _run_west_update(self) -> None:
248
+ """Update/download dependencies."""
249
+ self.execution_context.create_process_executor(
250
+ ["west", "update"],
251
+ cwd=self._west_workspace_dir,
252
+ ).execute()
253
+
254
+ def run(self) -> int:
255
+ self.logger.debug(f"Run {self.get_name()} step. Output dir: {self.output_dir}")
256
+
257
+ try:
258
+ merged_manifest = self._merge_manifests([mf.manifest for mf in self._manifests])
259
+ self._write_west_manifest_file(merged_manifest)
260
+
261
+ if not merged_manifest.projects:
262
+ self.logger.info("No West dependencies to install.")
263
+ return 0
264
+
265
+ self._run_west_init()
266
+ self._run_west_update()
267
+ self._record_installed_directories(merged_manifest)
268
+ self.install_result.to_json_file(self._install_result_file)
269
+
270
+ except Exception as e:
271
+ raise UserNotificationException(f"Failed to initialize and update with west: {e}") from e
272
+
273
+ return 0
274
+
275
+ def _record_installed_directories(self, manifest: WestManifest) -> None:
276
+ """Record directories created by west."""
277
+ dirs: list[Path] = []
278
+
279
+ if self._west_workspace_dir.exists():
280
+ dirs.append(self._west_workspace_dir)
281
+
282
+ for project in manifest.projects:
283
+ dep_dir = self._west_workspace_dir / project.path
284
+ if dep_dir.exists():
285
+ dirs.append(dep_dir)
286
+ self.logger.debug(f"Tracked dependency directory: {dep_dir}")
287
+
288
+ self.install_result.installed_dirs = list(dict.fromkeys(dirs))
289
+
290
+ def get_inputs(self) -> list[Path]:
291
+ inputs: list[Path] = []
292
+ for manifest_file in self._manifests:
293
+ if manifest_file.file and manifest_file.file.exists():
294
+ inputs.append(manifest_file.file)
295
+ return inputs
296
+
297
+ def get_outputs(self) -> list[Path]:
298
+ outputs: list[Path] = [self._output_manifest_file, self._install_result_file]
299
+ if self.install_result.installed_dirs:
300
+ outputs.extend(self.install_result.installed_dirs)
301
+ elif self._manifests:
302
+ outputs.append(self._west_workspace_dir)
303
+ return outputs
304
+
305
+ def update_execution_context(self) -> None:
306
+ if self._install_result_file.exists():
307
+ result = WestInstallResult.from_json_file(self._install_result_file)
308
+ if result.installed_dirs:
309
+ unique_paths = list(dict.fromkeys(result.installed_dirs))
310
+ self.execution_context.add_install_dirs(unique_paths)
@@ -1 +0,0 @@
1
- __version__ = "1.24.0"
@@ -1,54 +0,0 @@
1
- from pathlib import Path
2
- from typing import Any, Dict, List, Optional
3
-
4
- from py_app_dev.core.exceptions import UserNotificationException
5
- from py_app_dev.core.logging import logger
6
-
7
- from ..domain.execution_context import ExecutionContext
8
- from ..domain.pipeline import PipelineStep
9
-
10
-
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
- self.logger = logger.bind()
15
- self.artifacts_locator = execution_context.create_artifacts_locator()
16
-
17
- def get_name(self) -> str:
18
- return self.__class__.__name__
19
-
20
- @property
21
- def west_manifest_file(self) -> Path:
22
- return self.project_root_dir.joinpath("west.yaml")
23
-
24
- def run(self) -> int:
25
- self.logger.debug(f"Run {self.get_name()} step. Output dir: {self.output_dir}")
26
- try:
27
- self.execution_context.create_process_executor(
28
- [
29
- "west",
30
- "init",
31
- "-l",
32
- "--mf",
33
- self.west_manifest_file.as_posix(),
34
- self.artifacts_locator.build_dir.joinpath("west").as_posix(),
35
- ],
36
- cwd=self.project_root_dir,
37
- ).execute()
38
- self.execution_context.create_process_executor(
39
- ["west", "update"],
40
- cwd=self.artifacts_locator.build_dir,
41
- ).execute()
42
- except Exception as e:
43
- raise UserNotificationException(f"Failed to initialize and update with west: {e}") from e
44
-
45
- return 0
46
-
47
- def get_inputs(self) -> List[Path]:
48
- return [self.west_manifest_file]
49
-
50
- def get_outputs(self) -> List[Path]:
51
- return []
52
-
53
- def update_execution_context(self) -> None:
54
- pass