pypeline-runner 1.23.2__py3-none-any.whl → 1.24.1__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 +1 -1
- pypeline/kickstart/templates/project/pypeline.yaml +1 -1
- pypeline/steps/create_venv.py +116 -69
- {pypeline_runner-1.23.2.dist-info → pypeline_runner-1.24.1.dist-info}/METADATA +1 -1
- {pypeline_runner-1.23.2.dist-info → pypeline_runner-1.24.1.dist-info}/RECORD +8 -8
- {pypeline_runner-1.23.2.dist-info → pypeline_runner-1.24.1.dist-info}/WHEEL +0 -0
- {pypeline_runner-1.23.2.dist-info → pypeline_runner-1.24.1.dist-info}/entry_points.txt +0 -0
- {pypeline_runner-1.23.2.dist-info → pypeline_runner-1.24.1.dist-info}/licenses/LICENSE +0 -0
pypeline/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "1.
|
|
1
|
+
__version__ = "1.24.1"
|
|
@@ -8,8 +8,8 @@ pipeline:
|
|
|
8
8
|
- step: CreateVEnv
|
|
9
9
|
module: pypeline.steps.create_venv
|
|
10
10
|
config:
|
|
11
|
-
package_manager: uv>=0.9
|
|
12
11
|
python_executable: python3
|
|
12
|
+
python_package_manager: uv>=0.9
|
|
13
13
|
- step: WestInstall
|
|
14
14
|
module: pypeline.steps.west_install
|
|
15
15
|
description: Download external modules
|
pypeline/steps/create_venv.py
CHANGED
|
@@ -5,34 +5,85 @@ import shutil
|
|
|
5
5
|
import subprocess
|
|
6
6
|
import sys
|
|
7
7
|
import traceback
|
|
8
|
-
from dataclasses import dataclass
|
|
8
|
+
from dataclasses import dataclass, fields
|
|
9
9
|
from enum import Enum, auto
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from typing import Any, ClassVar, Dict, List, Optional
|
|
12
12
|
|
|
13
|
-
from mashumaro import
|
|
13
|
+
from mashumaro.config import TO_DICT_ADD_OMIT_NONE_FLAG, BaseConfig
|
|
14
14
|
from mashumaro.mixins.json import DataClassJSONMixin
|
|
15
15
|
from py_app_dev.core.exceptions import UserNotificationException
|
|
16
16
|
from py_app_dev.core.logging import logger
|
|
17
17
|
|
|
18
|
-
from pypeline import __version__
|
|
19
18
|
from pypeline.bootstrap.run import get_bootstrap_script
|
|
20
19
|
from pypeline.domain.execution_context import ExecutionContext
|
|
21
20
|
from pypeline.domain.pipeline import PipelineStep
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
@dataclass
|
|
25
|
-
class CreateVEnvConfig(
|
|
24
|
+
class CreateVEnvConfig(DataClassJSONMixin):
|
|
26
25
|
bootstrap_script: Optional[str] = None
|
|
27
26
|
python_executable: Optional[str] = None
|
|
28
|
-
# Bootstrap-specific configuration
|
|
29
|
-
package_manager: Optional[str] = None
|
|
30
27
|
python_version: Optional[str] = None
|
|
31
|
-
|
|
28
|
+
# deprecated: kept for backward compatibility
|
|
29
|
+
package_manager: Optional[str] = None
|
|
30
|
+
python_package_manager: Optional[str] = None
|
|
31
|
+
python_package_manager_args: Optional[List[str]] = None
|
|
32
32
|
bootstrap_packages: Optional[List[str]] = None
|
|
33
33
|
bootstrap_cache_dir: Optional[str] = None
|
|
34
34
|
venv_install_command: Optional[str] = None
|
|
35
35
|
|
|
36
|
+
class Config(BaseConfig):
|
|
37
|
+
"""Base configuration for JSON serialization with omitted None values."""
|
|
38
|
+
|
|
39
|
+
code_generation_options: ClassVar[List[str]] = [TO_DICT_ADD_OMIT_NONE_FLAG]
|
|
40
|
+
|
|
41
|
+
def __post_init__(self) -> None:
|
|
42
|
+
"""
|
|
43
|
+
Migrate deprecated package_manager field to python_package_manager.
|
|
44
|
+
|
|
45
|
+
Ensures backward compatibility while preventing conflicting configurations.
|
|
46
|
+
"""
|
|
47
|
+
# If both are set, they must match
|
|
48
|
+
if self.package_manager is not None and self.python_package_manager is not None:
|
|
49
|
+
if self.package_manager != self.python_package_manager:
|
|
50
|
+
raise UserNotificationException(
|
|
51
|
+
f"Conflicting package manager configuration: "
|
|
52
|
+
f"package_manager='{self.package_manager}' vs python_package_manager='{self.python_package_manager}'. "
|
|
53
|
+
f"Please use only 'python_package_manager' (package_manager is deprecated)."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# Migrate from deprecated package_manager to python_package_manager
|
|
57
|
+
if self.package_manager is not None and self.python_package_manager is None:
|
|
58
|
+
self.python_package_manager = self.package_manager
|
|
59
|
+
|
|
60
|
+
# Clear the deprecated field after migration
|
|
61
|
+
self.package_manager = None
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def from_json_file(cls, file_path: Path) -> "CreateVEnvConfig":
|
|
65
|
+
try:
|
|
66
|
+
result = cls.from_dict(json.loads(file_path.read_text()))
|
|
67
|
+
except Exception as e:
|
|
68
|
+
output = io.StringIO()
|
|
69
|
+
traceback.print_exc(file=output)
|
|
70
|
+
raise UserNotificationException(output.getvalue()) from e
|
|
71
|
+
return result
|
|
72
|
+
|
|
73
|
+
def to_json_string(self) -> str:
|
|
74
|
+
return json.dumps(self.to_dict(omit_none=True), indent=2)
|
|
75
|
+
|
|
76
|
+
def to_json_file(self, file_path: Path) -> None:
|
|
77
|
+
file_path.write_text(self.to_json_string())
|
|
78
|
+
|
|
79
|
+
def get_all_properties_names(self, excluded_names: Optional[List[str]] = None) -> List[str]:
|
|
80
|
+
if excluded_names is None:
|
|
81
|
+
excluded_names = []
|
|
82
|
+
return [field.name for field in fields(self) if field.name not in excluded_names]
|
|
83
|
+
|
|
84
|
+
def is_any_property_set(self, excluded_fields: Optional[List[str]] = None) -> bool:
|
|
85
|
+
return any(getattr(self, field) is not None for field in self.get_all_properties_names(excluded_fields))
|
|
86
|
+
|
|
36
87
|
|
|
37
88
|
class BootstrapScriptType(Enum):
|
|
38
89
|
CUSTOM = auto()
|
|
@@ -69,22 +120,12 @@ class CreateVEnv(PipelineStep[ExecutionContext]):
|
|
|
69
120
|
super().__init__(execution_context, group_name, config)
|
|
70
121
|
self.logger = logger.bind()
|
|
71
122
|
self.internal_bootstrap_script = get_bootstrap_script()
|
|
72
|
-
self.package_manager = self.user_config.
|
|
123
|
+
self.package_manager = self.user_config.python_package_manager if self.user_config.python_package_manager else self.DEFAULT_PACKAGE_MANAGER
|
|
73
124
|
self.venv_dir = self.project_root_dir / ".venv"
|
|
74
125
|
|
|
75
|
-
@property
|
|
76
126
|
def has_bootstrap_config(self) -> bool:
|
|
77
127
|
"""Check if user provided any bootstrap-specific configuration."""
|
|
78
|
-
return
|
|
79
|
-
[
|
|
80
|
-
self.user_config.package_manager,
|
|
81
|
-
self.user_config.python_version,
|
|
82
|
-
self.user_config.package_manager_args,
|
|
83
|
-
self.user_config.bootstrap_packages,
|
|
84
|
-
self.user_config.bootstrap_cache_dir,
|
|
85
|
-
self.user_config.venv_install_command,
|
|
86
|
-
]
|
|
87
|
-
)
|
|
128
|
+
return self.user_config.is_any_property_set(["bootstrap_script", "python_executable"])
|
|
88
129
|
|
|
89
130
|
def _verify_python_version(self, executable: str, expected_version: str) -> bool:
|
|
90
131
|
"""
|
|
@@ -275,18 +316,34 @@ class CreateVEnv(PipelineStep[ExecutionContext]):
|
|
|
275
316
|
|
|
276
317
|
def run(self) -> int:
|
|
277
318
|
self.logger.debug(f"Run {self.get_name()} step. Output dir: {self.output_dir}")
|
|
278
|
-
|
|
319
|
+
bootstrap_config = CreateVEnvConfig()
|
|
320
|
+
is_managed = False
|
|
321
|
+
# Determine target script and mode
|
|
279
322
|
if self.user_config.bootstrap_script:
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
323
|
+
target_script = self.project_root_dir / self.user_config.bootstrap_script
|
|
324
|
+
# If script exists, it's a "Custom Mode" execution (legacy behavior: run as-is)
|
|
325
|
+
# If it misses, we enter "Managed Mode" to auto-create and run it
|
|
326
|
+
is_managed = not target_script.exists()
|
|
327
|
+
if is_managed:
|
|
328
|
+
self.logger.warning(f"Bootstrap script {target_script} does not exist. Creating it from internal default.")
|
|
329
|
+
# If there is a custom bootstrap config (bootstrap.json) in the project root,
|
|
330
|
+
# we need to provide the internal script
|
|
331
|
+
default_bootstrap_config = self.project_root_dir / "bootstrap.json"
|
|
332
|
+
if default_bootstrap_config.exists():
|
|
333
|
+
self.logger.warning(f"Found bootstrap config {default_bootstrap_config}. Reading it.")
|
|
334
|
+
bootstrap_config = CreateVEnvConfig.from_json_file(default_bootstrap_config)
|
|
335
|
+
else:
|
|
336
|
+
target_script = self.target_internal_bootstrap_script
|
|
337
|
+
is_managed = True
|
|
338
|
+
|
|
339
|
+
if not is_managed:
|
|
340
|
+
# Custom Mode: Run existing user script directly without injection
|
|
284
341
|
self.execution_context.create_process_executor(
|
|
285
|
-
[self.python_executable,
|
|
342
|
+
[self.python_executable, target_script.as_posix()],
|
|
286
343
|
cwd=self.project_root_dir,
|
|
287
344
|
).execute()
|
|
288
345
|
else:
|
|
289
|
-
#
|
|
346
|
+
# Managed Mode: Internal logic (Config generation + Args + File creation)
|
|
290
347
|
skip_venv_delete = False
|
|
291
348
|
python_executable = Path(sys.executable).absolute()
|
|
292
349
|
if python_executable.is_relative_to(self.project_root_dir):
|
|
@@ -294,30 +351,22 @@ class CreateVEnv(PipelineStep[ExecutionContext]):
|
|
|
294
351
|
skip_venv_delete = True
|
|
295
352
|
|
|
296
353
|
# Create bootstrap.json with all configuration
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
354
|
+
# Populate config dynamically from CreateVEnvConfig fields
|
|
355
|
+
# excluding internal/local fields like bootstrap_script/python_executable
|
|
356
|
+
for field_name in self.user_config.get_all_properties_names(["bootstrap_script", "python_executable"]):
|
|
357
|
+
val = getattr(self.user_config, field_name)
|
|
358
|
+
if val is not None:
|
|
359
|
+
setattr(bootstrap_config, field_name, val)
|
|
300
360
|
|
|
301
361
|
# Priority: input python_version takes precedence over config python_version
|
|
302
362
|
input_python_version = self.execution_context.get_input("python_version")
|
|
303
363
|
if input_python_version:
|
|
304
|
-
bootstrap_config
|
|
305
|
-
elif self.user_config.python_version:
|
|
306
|
-
bootstrap_config["python_version"] = self.user_config.python_version
|
|
307
|
-
|
|
308
|
-
if self.user_config.package_manager_args:
|
|
309
|
-
bootstrap_config["python_package_manager_args"] = self.user_config.package_manager_args
|
|
310
|
-
if self.user_config.bootstrap_packages:
|
|
311
|
-
bootstrap_config["bootstrap_packages"] = self.user_config.bootstrap_packages
|
|
312
|
-
if self.user_config.bootstrap_cache_dir:
|
|
313
|
-
bootstrap_config["bootstrap_cache_dir"] = self.user_config.bootstrap_cache_dir
|
|
314
|
-
if self.user_config.venv_install_command:
|
|
315
|
-
bootstrap_config["venv_install_command"] = self.user_config.venv_install_command
|
|
364
|
+
bootstrap_config.python_version = input_python_version
|
|
316
365
|
|
|
317
366
|
# Write bootstrap.json if any configuration is provided
|
|
318
|
-
if bootstrap_config:
|
|
367
|
+
if bootstrap_config.is_any_property_set():
|
|
319
368
|
self.bootstrap_config_file.parent.mkdir(exist_ok=True)
|
|
320
|
-
self.bootstrap_config_file
|
|
369
|
+
bootstrap_config.to_json_file(self.bootstrap_config_file)
|
|
321
370
|
self.logger.info(f"Created bootstrap configuration at {self.bootstrap_config_file}")
|
|
322
371
|
|
|
323
372
|
# Build bootstrap script arguments
|
|
@@ -327,49 +376,47 @@ class CreateVEnv(PipelineStep[ExecutionContext]):
|
|
|
327
376
|
]
|
|
328
377
|
|
|
329
378
|
# Always use --config if bootstrap.json exists
|
|
379
|
+
# Note: We use the internal .bootstrap/bootstrap.json location for consistency
|
|
330
380
|
if self.bootstrap_config_file.exists():
|
|
331
381
|
bootstrap_args.extend(["--config", self.bootstrap_config_file.as_posix()])
|
|
332
382
|
|
|
333
383
|
if skip_venv_delete:
|
|
334
384
|
bootstrap_args.append("--skip-venv-delete")
|
|
335
385
|
|
|
336
|
-
#
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
386
|
+
# Create/Update the target bootstrap script from internal template
|
|
387
|
+
target_script.parent.mkdir(parents=True, exist_ok=True)
|
|
388
|
+
|
|
389
|
+
# Check if we need to write/update the file
|
|
390
|
+
# If it's a missing custom file, we definitely write.
|
|
391
|
+
# If it's the internal file, we check content hash/diff.
|
|
392
|
+
should_write = False
|
|
393
|
+
if not target_script.exists():
|
|
394
|
+
should_write = True
|
|
395
|
+
elif target_script == self.target_internal_bootstrap_script:
|
|
396
|
+
if target_script.read_text() != self.internal_bootstrap_script.read_text():
|
|
397
|
+
should_write = True
|
|
398
|
+
|
|
399
|
+
if should_write:
|
|
400
|
+
target_script.write_text(self.internal_bootstrap_script.read_text())
|
|
401
|
+
if target_script == self.target_internal_bootstrap_script:
|
|
402
|
+
self.logger.warning(f"Updated bootstrap script at {target_script}")
|
|
403
|
+
|
|
404
|
+
# Run the bootstrap script
|
|
343
405
|
self.execution_context.create_process_executor(
|
|
344
|
-
[self.python_executable,
|
|
406
|
+
[self.python_executable, target_script.as_posix(), *bootstrap_args],
|
|
345
407
|
cwd=self.project_root_dir,
|
|
346
408
|
).execute()
|
|
347
409
|
|
|
348
410
|
return 0
|
|
349
411
|
|
|
350
412
|
def get_inputs(self) -> List[Path]:
|
|
351
|
-
|
|
352
|
-
inputs = [self.project_root_dir / file for file in package_manager_relevant_file]
|
|
353
|
-
# Include bootstrap.json if it exists
|
|
354
|
-
if self.bootstrap_config_file.exists():
|
|
355
|
-
inputs.append(self.bootstrap_config_file)
|
|
356
|
-
return inputs
|
|
413
|
+
return []
|
|
357
414
|
|
|
358
415
|
def get_outputs(self) -> List[Path]:
|
|
359
|
-
|
|
360
|
-
if self.bootstrap_script_type == BootstrapScriptType.INTERNAL:
|
|
361
|
-
outputs.append(self.target_internal_bootstrap_script)
|
|
362
|
-
# Include bootstrap.json if it will be created
|
|
363
|
-
if self.has_bootstrap_config:
|
|
364
|
-
outputs.append(self.bootstrap_config_file)
|
|
365
|
-
return outputs
|
|
416
|
+
return []
|
|
366
417
|
|
|
367
418
|
def get_config(self) -> Optional[dict[str, str]]:
|
|
368
|
-
return
|
|
369
|
-
"version": __version__,
|
|
370
|
-
"python_executable": self.python_executable,
|
|
371
|
-
"package_manager": self.package_manager,
|
|
372
|
-
}
|
|
419
|
+
return None
|
|
373
420
|
|
|
374
421
|
def update_execution_context(self) -> None:
|
|
375
422
|
self.execution_context.add_install_dirs(self.install_dirs)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
pypeline/__init__.py,sha256=
|
|
1
|
+
pypeline/__init__.py,sha256=RwkE9NDj8fDM5TZuMf77zeI6i_D5SFDSot1oY8uCOEQ,23
|
|
2
2
|
pypeline/__run.py,sha256=TCdaX05Qm3g8T4QYryKB25Xxf0L5Km7hFOHe1mK9vI0,350
|
|
3
3
|
pypeline/bootstrap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
pypeline/bootstrap/run.py,sha256=H5rxSa_owbAUpMA2lw-UhgBFB0eDgnQC0B4jYO8j3sE,33463
|
|
@@ -13,7 +13,7 @@ pypeline/kickstart/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
|
|
|
13
13
|
pypeline/kickstart/create.py,sha256=iaB8MMC7PinpPBwRmz3rWZuE-DRbsLh2NtvczYaVgi0,2133
|
|
14
14
|
pypeline/kickstart/templates/project/.gitignore,sha256=y8GJoVvRPez1LBokf1NaDOt2X1XtGwKFMF5yjA8AVS0,24
|
|
15
15
|
pypeline/kickstart/templates/project/pypeline.ps1,sha256=PjCJULG8XA3AHKbNt3oHrIgD04huvvpIue_gjSo3PMA,104
|
|
16
|
-
pypeline/kickstart/templates/project/pypeline.yaml,sha256=
|
|
16
|
+
pypeline/kickstart/templates/project/pypeline.yaml,sha256=10BLkIzSY0jOueZEZEJ98vMyDm4RJl1WZEkZ6sgq0nA,408
|
|
17
17
|
pypeline/kickstart/templates/project/pyproject.toml,sha256=7hAoK6BammBxxoolMdCkNx7qPSFFiFUkQN8oAbCf7Yk,271
|
|
18
18
|
pypeline/kickstart/templates/project/steps/my_step.py,sha256=b-JEwF9EyF4G6lgvkk3I2aT2wpD_zQ2fTiQrR6lWhs4,788
|
|
19
19
|
pypeline/kickstart/templates/project/west.yaml,sha256=ZfVym7M4yzzC-Nm0vESdhqNYs6EaJuMQWGJBht_i0b4,188
|
|
@@ -21,12 +21,12 @@ pypeline/main.py,sha256=k1CkeFGRvQ-zLv6C-AMLC2ed1iyFzDUdvEam3HLHy2E,4210
|
|
|
21
21
|
pypeline/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
22
|
pypeline/pypeline.py,sha256=mDKUnTuMDw8l-kSDJCHRNbn6zrxAfXhAIAqc5HyHd5M,8758
|
|
23
23
|
pypeline/steps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
pypeline/steps/create_venv.py,sha256=
|
|
24
|
+
pypeline/steps/create_venv.py,sha256=s8uL0cLfAF-AFvrU77KtdUMTE0v5ZDY2Ie_fO4ZqKjg,19050
|
|
25
25
|
pypeline/steps/env_setup_script.py,sha256=L8TwGo_Ugo2r4Z10MxtE0P8w0ApAxMKCHMnW-NkyG3w,4968
|
|
26
26
|
pypeline/steps/scoop_install.py,sha256=2MhsJ0iPmL8ueQhI52sKjVY9fqzj5xOQweQ65C0onfE,4117
|
|
27
27
|
pypeline/steps/west_install.py,sha256=hPyr28ksdKsQ0tv0gMNytzupgk1IgjN9CpmaBdX5zps,1947
|
|
28
|
-
pypeline_runner-1.
|
|
29
|
-
pypeline_runner-1.
|
|
30
|
-
pypeline_runner-1.
|
|
31
|
-
pypeline_runner-1.
|
|
32
|
-
pypeline_runner-1.
|
|
28
|
+
pypeline_runner-1.24.1.dist-info/METADATA,sha256=5x6U9ZgzGF1VvTd8dWTlv3hkG3N5xDznO9AKwZv8oKM,7659
|
|
29
|
+
pypeline_runner-1.24.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
30
|
+
pypeline_runner-1.24.1.dist-info/entry_points.txt,sha256=pe1u0uuhPI_yeQ0KjEw6jK-EvQfPcZwBSajgbAdKz1o,47
|
|
31
|
+
pypeline_runner-1.24.1.dist-info/licenses/LICENSE,sha256=sKxdoqSmW9ezvPvt0ZGJbneyA0SBcm0GiqzTv2jN230,1066
|
|
32
|
+
pypeline_runner-1.24.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|