mcp-souschef 3.0.0__py3-none-any.whl → 3.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.
- {mcp_souschef-3.0.0.dist-info → mcp_souschef-3.2.0.dist-info}/METADATA +83 -380
- mcp_souschef-3.2.0.dist-info/RECORD +47 -0
- souschef/__init__.py +2 -10
- souschef/assessment.py +336 -181
- souschef/ci/common.py +1 -1
- souschef/cli.py +37 -13
- souschef/converters/playbook.py +119 -48
- souschef/core/__init__.py +6 -1
- souschef/core/path_utils.py +233 -19
- souschef/deployment.py +10 -3
- souschef/generators/__init__.py +13 -0
- souschef/generators/repo.py +695 -0
- souschef/parsers/attributes.py +1 -1
- souschef/parsers/habitat.py +1 -1
- souschef/parsers/inspec.py +25 -2
- souschef/parsers/metadata.py +5 -3
- souschef/parsers/recipe.py +1 -1
- souschef/parsers/resource.py +1 -1
- souschef/parsers/template.py +1 -1
- souschef/server.py +426 -188
- souschef/ui/app.py +24 -30
- souschef/ui/pages/cookbook_analysis.py +837 -163
- mcp_souschef-3.0.0.dist-info/RECORD +0 -46
- souschef/converters/cookbook_specific.py.backup +0 -109
- {mcp_souschef-3.0.0.dist-info → mcp_souschef-3.2.0.dist-info}/WHEEL +0 -0
- {mcp_souschef-3.0.0.dist-info → mcp_souschef-3.2.0.dist-info}/entry_points.txt +0 -0
- {mcp_souschef-3.0.0.dist-info → mcp_souschef-3.2.0.dist-info}/licenses/LICENSE +0 -0
souschef/ui/app.py
CHANGED
|
@@ -9,13 +9,7 @@ if str(app_path) not in sys.path:
|
|
|
9
9
|
import contextlib
|
|
10
10
|
import os
|
|
11
11
|
from collections.abc import Callable, Iterable, Mapping, Sequence
|
|
12
|
-
from typing import
|
|
13
|
-
TYPE_CHECKING,
|
|
14
|
-
Any,
|
|
15
|
-
Concatenate,
|
|
16
|
-
ParamSpec,
|
|
17
|
-
TypeVar,
|
|
18
|
-
)
|
|
12
|
+
from typing import TYPE_CHECKING, Any, Concatenate, ParamSpec, TypeVar
|
|
19
13
|
|
|
20
14
|
import streamlit as st
|
|
21
15
|
|
|
@@ -27,6 +21,8 @@ if TYPE_CHECKING:
|
|
|
27
21
|
P = ParamSpec("P")
|
|
28
22
|
R = TypeVar("R")
|
|
29
23
|
|
|
24
|
+
from souschef.core import _ensure_within_base_path, _normalize_path
|
|
25
|
+
from souschef.core.path_utils import safe_exists, safe_glob, safe_is_dir, safe_is_file
|
|
30
26
|
from souschef.ui.pages.ai_settings import show_ai_settings_page
|
|
31
27
|
from souschef.ui.pages.cookbook_analysis import show_cookbook_analysis_page
|
|
32
28
|
|
|
@@ -2550,28 +2546,31 @@ def _collect_files_to_validate(input_path: str) -> list[Path]:
|
|
|
2550
2546
|
# Error already reported by _normalize_and_validate_input_path
|
|
2551
2547
|
return []
|
|
2552
2548
|
|
|
2553
|
-
|
|
2554
|
-
|
|
2549
|
+
# Path is normalized and validated to be within app root
|
|
2550
|
+
path_obj: Path = validated_path
|
|
2551
|
+
files_to_validate: list[Path] = []
|
|
2555
2552
|
|
|
2556
|
-
if
|
|
2553
|
+
# Check if path exists using safe function
|
|
2554
|
+
if not safe_exists(path_obj, Path.cwd()):
|
|
2557
2555
|
st.error(f"Path does not exist: {path_obj}")
|
|
2558
2556
|
return []
|
|
2559
2557
|
|
|
2560
|
-
if
|
|
2558
|
+
# Determine if it's a file or directory
|
|
2559
|
+
if safe_is_file(path_obj, Path.cwd()):
|
|
2561
2560
|
if path_obj.suffix in [".yml", ".yaml"] and path_obj.name not in [
|
|
2562
2561
|
".kitchen.yml",
|
|
2563
2562
|
"kitchen.yml",
|
|
2564
2563
|
"docker-compose.yml",
|
|
2565
2564
|
]:
|
|
2566
2565
|
files_to_validate.append(path_obj)
|
|
2567
|
-
elif path_obj.
|
|
2566
|
+
elif safe_is_dir(path_obj, Path.cwd()):
|
|
2568
2567
|
# Filter out obvious non-playbook files
|
|
2569
2568
|
excluded_files = {".kitchen.yml", "kitchen.yml", "docker-compose.yml"}
|
|
2570
2569
|
|
|
2571
|
-
yml_files =
|
|
2572
|
-
yaml_files =
|
|
2570
|
+
yml_files: list[Path] = safe_glob(path_obj, "**/*.yml", Path.cwd())
|
|
2571
|
+
yaml_files: list[Path] = safe_glob(path_obj, "**/*.yaml", Path.cwd())
|
|
2573
2572
|
|
|
2574
|
-
raw_files = yml_files + yaml_files
|
|
2573
|
+
raw_files: list[Path] = yml_files + yaml_files
|
|
2575
2574
|
files_to_validate.extend([f for f in raw_files if f.name not in excluded_files])
|
|
2576
2575
|
|
|
2577
2576
|
return files_to_validate
|
|
@@ -2743,22 +2742,14 @@ def _normalize_and_validate_input_path(input_path: str) -> Path | None:
|
|
|
2743
2742
|
return None
|
|
2744
2743
|
|
|
2745
2744
|
try:
|
|
2746
|
-
|
|
2747
|
-
path_obj = Path(raw).expanduser().resolve()
|
|
2748
|
-
except Exception:
|
|
2749
|
-
st.error(f"Invalid path: {raw}")
|
|
2750
|
-
return None
|
|
2751
|
-
|
|
2752
|
-
# Optional safety: constrain to the application root directory
|
|
2753
|
-
try:
|
|
2745
|
+
path_obj = _normalize_path(raw)
|
|
2754
2746
|
app_root = Path(app_path).resolve()
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2747
|
+
# Use centralised containment validation
|
|
2748
|
+
return _ensure_within_base_path(path_obj, app_root)
|
|
2749
|
+
except (ValueError, OSError) as e:
|
|
2750
|
+
st.error(f"Invalid path: {e}")
|
|
2758
2751
|
return None
|
|
2759
2752
|
|
|
2760
|
-
return path_obj
|
|
2761
|
-
|
|
2762
2753
|
|
|
2763
2754
|
def _handle_validation_execution(input_path: str, options: Mapping[str, Any]) -> None:
|
|
2764
2755
|
"""Execute the validation process with progress tracking."""
|
|
@@ -2777,8 +2768,11 @@ def _handle_validation_execution(input_path: str, options: Mapping[str, Any]) ->
|
|
|
2777
2768
|
# Error is handled inside _collect_files_to_validate
|
|
2778
2769
|
# if path doesn't exist or is invalid
|
|
2779
2770
|
validated_path = _normalize_and_validate_input_path(input_path)
|
|
2780
|
-
if validated_path is not None
|
|
2781
|
-
|
|
2771
|
+
if validated_path is not None:
|
|
2772
|
+
# Check if the validated path exists
|
|
2773
|
+
path_exists: bool = safe_exists(validated_path, Path.cwd())
|
|
2774
|
+
if path_exists:
|
|
2775
|
+
st.warning(f"No YAML files found in {validated_path}")
|
|
2782
2776
|
return
|
|
2783
2777
|
|
|
2784
2778
|
progress_tracker.update(3, f"Validating {len(files_to_validate)} files...")
|