mcp-souschef 2.1.2__py3-none-any.whl → 2.5.3__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-2.1.2.dist-info → mcp_souschef-2.5.3.dist-info}/METADATA +200 -19
- mcp_souschef-2.5.3.dist-info/RECORD +38 -0
- mcp_souschef-2.5.3.dist-info/entry_points.txt +4 -0
- souschef/assessment.py +531 -180
- souschef/ci/__init__.py +11 -0
- souschef/ci/github_actions.py +379 -0
- souschef/ci/gitlab_ci.py +299 -0
- souschef/ci/jenkins_pipeline.py +343 -0
- souschef/cli.py +691 -1
- souschef/converters/playbook.py +43 -5
- souschef/converters/resource.py +146 -49
- souschef/core/__init__.py +22 -0
- souschef/core/errors.py +275 -0
- souschef/core/validation.py +35 -2
- souschef/deployment.py +414 -100
- souschef/filesystem/operations.py +0 -7
- souschef/parsers/__init__.py +6 -1
- souschef/parsers/habitat.py +35 -6
- souschef/parsers/inspec.py +415 -52
- souschef/parsers/metadata.py +89 -23
- souschef/profiling.py +568 -0
- souschef/server.py +948 -255
- souschef/ui/__init__.py +8 -0
- souschef/ui/app.py +1837 -0
- souschef/ui/pages/cookbook_analysis.py +425 -0
- mcp_souschef-2.1.2.dist-info/RECORD +0 -29
- mcp_souschef-2.1.2.dist-info/entry_points.txt +0 -4
- {mcp_souschef-2.1.2.dist-info → mcp_souschef-2.5.3.dist-info}/WHEEL +0 -0
- {mcp_souschef-2.1.2.dist-info → mcp_souschef-2.5.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"""Cookbook Analysis Page for SousChef UI."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pandas as pd # type: ignore[import-untyped]
|
|
7
|
+
import streamlit as st
|
|
8
|
+
|
|
9
|
+
# Add the parent directory to the path so we can import souschef modules
|
|
10
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
11
|
+
|
|
12
|
+
from souschef.assessment import parse_chef_migration_assessment
|
|
13
|
+
from souschef.parsers.metadata import parse_cookbook_metadata
|
|
14
|
+
|
|
15
|
+
# Constants for repeated strings
|
|
16
|
+
METADATA_STATUS_YES = "Yes"
|
|
17
|
+
METADATA_STATUS_NO = "No"
|
|
18
|
+
ANALYSIS_STATUS_ANALYZED = "Analyzed"
|
|
19
|
+
ANALYSIS_STATUS_FAILED = "Failed"
|
|
20
|
+
METADATA_COLUMN_NAME = "Has Metadata"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def show_cookbook_analysis_page():
|
|
24
|
+
"""Show the cookbook analysis page."""
|
|
25
|
+
_setup_cookbook_analysis_ui()
|
|
26
|
+
cookbook_path = _get_cookbook_path_input()
|
|
27
|
+
|
|
28
|
+
if cookbook_path:
|
|
29
|
+
_validate_and_list_cookbooks(cookbook_path)
|
|
30
|
+
|
|
31
|
+
_display_instructions()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _setup_cookbook_analysis_ui():
|
|
35
|
+
"""Set up the cookbook analysis page header."""
|
|
36
|
+
st.header("Cookbook Analysis")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _get_cookbook_path_input():
|
|
40
|
+
"""Get the cookbook path input from the user."""
|
|
41
|
+
return st.text_input(
|
|
42
|
+
"Cookbook Directory Path",
|
|
43
|
+
placeholder="/path/to/your/cookbooks",
|
|
44
|
+
help="Enter the absolute path to your Chef cookbooks directory",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _validate_and_list_cookbooks(cookbook_path):
|
|
49
|
+
"""Validate the cookbook path and list available cookbooks."""
|
|
50
|
+
safe_dir = _get_safe_cookbook_directory(cookbook_path)
|
|
51
|
+
if safe_dir is None:
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
if safe_dir.exists() and safe_dir.is_dir():
|
|
55
|
+
st.success(f"Found directory: {safe_dir}")
|
|
56
|
+
_list_and_display_cookbooks(safe_dir)
|
|
57
|
+
else:
|
|
58
|
+
st.error(f"Directory not found: {safe_dir}")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _get_safe_cookbook_directory(cookbook_path):
|
|
62
|
+
"""
|
|
63
|
+
Resolve the user-provided cookbook path to a safe directory.
|
|
64
|
+
|
|
65
|
+
The path is resolved against a base directory and normalized to
|
|
66
|
+
prevent directory traversal outside the allowed root.
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
base_dir = Path.cwd().resolve()
|
|
70
|
+
user_path = Path(cookbook_path.strip())
|
|
71
|
+
if not user_path.is_absolute():
|
|
72
|
+
candidate = (base_dir / user_path).resolve()
|
|
73
|
+
else:
|
|
74
|
+
candidate = user_path.resolve()
|
|
75
|
+
except Exception as exc:
|
|
76
|
+
st.error(f"Invalid path: {exc}")
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
# Ensure the final path is within the allowed base directory.
|
|
80
|
+
try:
|
|
81
|
+
candidate.relative_to(base_dir)
|
|
82
|
+
except ValueError:
|
|
83
|
+
st.error("The specified path is outside the allowed cookbook directory root.")
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
return candidate
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _list_and_display_cookbooks(cookbook_path: Path):
|
|
90
|
+
"""List cookbooks in the directory and display them."""
|
|
91
|
+
try:
|
|
92
|
+
cookbooks = [d for d in cookbook_path.iterdir() if d.is_dir()]
|
|
93
|
+
if cookbooks:
|
|
94
|
+
st.subheader("Available Cookbooks")
|
|
95
|
+
cookbook_data = _collect_cookbook_data(cookbooks)
|
|
96
|
+
_display_cookbook_table(cookbook_data)
|
|
97
|
+
_handle_cookbook_selection(str(cookbook_path), cookbook_data)
|
|
98
|
+
else:
|
|
99
|
+
st.warning(
|
|
100
|
+
"No subdirectories found in the specified path. "
|
|
101
|
+
"Are these individual cookbooks?"
|
|
102
|
+
)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
st.error(f"Error reading directory: {e}")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _collect_cookbook_data(cookbooks):
|
|
108
|
+
"""Collect data for all cookbooks."""
|
|
109
|
+
cookbook_data = []
|
|
110
|
+
for cookbook in cookbooks:
|
|
111
|
+
cookbook_info = _analyze_cookbook_metadata(cookbook)
|
|
112
|
+
cookbook_data.append(cookbook_info)
|
|
113
|
+
return cookbook_data
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _analyze_cookbook_metadata(cookbook):
|
|
117
|
+
"""Analyze metadata for a single cookbook."""
|
|
118
|
+
metadata_file = cookbook / "metadata.rb"
|
|
119
|
+
if metadata_file.exists():
|
|
120
|
+
return _parse_metadata_with_fallback(cookbook, metadata_file)
|
|
121
|
+
else:
|
|
122
|
+
return _create_no_metadata_entry(cookbook)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _parse_metadata_with_fallback(cookbook, metadata_file):
|
|
126
|
+
"""Parse metadata with error handling."""
|
|
127
|
+
try:
|
|
128
|
+
metadata = parse_cookbook_metadata(str(metadata_file))
|
|
129
|
+
return _extract_cookbook_info(metadata, cookbook, METADATA_STATUS_YES)
|
|
130
|
+
except Exception as e:
|
|
131
|
+
return _create_error_entry(cookbook, str(e))
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _extract_cookbook_info(metadata, cookbook, metadata_status):
|
|
135
|
+
"""Extract key information from cookbook metadata."""
|
|
136
|
+
name = metadata.get("name", cookbook.name)
|
|
137
|
+
version = metadata.get("version", "Unknown")
|
|
138
|
+
maintainer = metadata.get("maintainer", "Unknown")
|
|
139
|
+
description = _normalize_description(metadata.get("description", "No description"))
|
|
140
|
+
dependencies = len(metadata.get("depends", []))
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
"Name": name,
|
|
144
|
+
"Version": version,
|
|
145
|
+
"Maintainer": maintainer,
|
|
146
|
+
"Description": _truncate_description(description),
|
|
147
|
+
"Dependencies": dependencies,
|
|
148
|
+
"Path": str(cookbook),
|
|
149
|
+
METADATA_COLUMN_NAME: metadata_status,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _normalize_description(description):
|
|
154
|
+
"""
|
|
155
|
+
Normalize description to string format.
|
|
156
|
+
|
|
157
|
+
The metadata parser currently returns a string for the description
|
|
158
|
+
field, but this helper defensively converts any unexpected value to
|
|
159
|
+
a string to keep the UI resilient to future changes.
|
|
160
|
+
"""
|
|
161
|
+
if not isinstance(description, str):
|
|
162
|
+
return str(description)
|
|
163
|
+
return description
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _truncate_description(description):
|
|
167
|
+
"""Truncate description if too long."""
|
|
168
|
+
if len(description) > 50:
|
|
169
|
+
return description[:50] + "..."
|
|
170
|
+
return description
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _create_error_entry(cookbook, error_message):
|
|
174
|
+
"""Create an entry for cookbooks with parsing errors."""
|
|
175
|
+
return {
|
|
176
|
+
"Name": cookbook.name,
|
|
177
|
+
"Version": "Error",
|
|
178
|
+
"Maintainer": "Error",
|
|
179
|
+
"Description": f"Parse error: {error_message[:50]}",
|
|
180
|
+
"Dependencies": 0,
|
|
181
|
+
"Path": str(cookbook),
|
|
182
|
+
METADATA_COLUMN_NAME: METADATA_STATUS_NO,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _create_no_metadata_entry(cookbook):
|
|
187
|
+
"""Create an entry for cookbooks without metadata."""
|
|
188
|
+
return {
|
|
189
|
+
"Name": cookbook.name,
|
|
190
|
+
"Version": "No metadata",
|
|
191
|
+
"Maintainer": "Unknown",
|
|
192
|
+
"Description": "No metadata.rb found",
|
|
193
|
+
"Dependencies": 0,
|
|
194
|
+
"Path": str(cookbook),
|
|
195
|
+
METADATA_COLUMN_NAME: METADATA_STATUS_NO,
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _display_cookbook_table(cookbook_data):
|
|
200
|
+
"""Display the cookbook data in a table."""
|
|
201
|
+
df = pd.DataFrame(cookbook_data)
|
|
202
|
+
st.dataframe(df, use_container_width=True)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _handle_cookbook_selection(cookbook_path, cookbook_data):
|
|
206
|
+
"""Handle cookbook selection and analysis trigger."""
|
|
207
|
+
available_cookbooks = [
|
|
208
|
+
str(cb["Name"])
|
|
209
|
+
for cb in cookbook_data
|
|
210
|
+
if cb[METADATA_COLUMN_NAME] == METADATA_STATUS_YES
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
selected_cookbooks = st.multiselect(
|
|
214
|
+
"Select cookbooks to analyze",
|
|
215
|
+
available_cookbooks,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if selected_cookbooks and st.button("Analyze Selected Cookbooks", type="primary"):
|
|
219
|
+
analyze_selected_cookbooks(cookbook_path, selected_cookbooks)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _display_instructions():
|
|
223
|
+
"""Display usage instructions."""
|
|
224
|
+
with st.expander("How to Use"):
|
|
225
|
+
st.markdown("""
|
|
226
|
+
1. **Enter Cookbook Path**: Provide the absolute path to your cookbooks
|
|
227
|
+
directory
|
|
228
|
+
2. **Review Cookbooks**: The interface will list all cookbooks with metadata
|
|
229
|
+
3. **Select Cookbooks**: Choose which cookbooks to analyze
|
|
230
|
+
4. **Run Analysis**: Click "Analyze Selected Cookbooks" to get detailed insights
|
|
231
|
+
|
|
232
|
+
**Expected Structure:**
|
|
233
|
+
```
|
|
234
|
+
/path/to/cookbooks/
|
|
235
|
+
├── nginx/
|
|
236
|
+
│ ├── metadata.rb
|
|
237
|
+
│ ├── recipes/
|
|
238
|
+
│ └── attributes/
|
|
239
|
+
├── apache2/
|
|
240
|
+
│ └── metadata.rb
|
|
241
|
+
└── mysql/
|
|
242
|
+
└── metadata.rb
|
|
243
|
+
```
|
|
244
|
+
""")
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def analyze_selected_cookbooks(cookbook_path: str, selected_cookbooks: list[str]):
|
|
248
|
+
"""Analyze the selected cookbooks and display results."""
|
|
249
|
+
st.subheader("Analysis Results")
|
|
250
|
+
|
|
251
|
+
progress_bar, status_text = _setup_analysis_progress()
|
|
252
|
+
results = _perform_cookbook_analysis(
|
|
253
|
+
cookbook_path, selected_cookbooks, progress_bar, status_text
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
_cleanup_progress_indicators(progress_bar, status_text)
|
|
257
|
+
_display_analysis_results(results, len(selected_cookbooks))
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _setup_analysis_progress():
|
|
261
|
+
"""Set up progress tracking for analysis."""
|
|
262
|
+
progress_bar = st.progress(0)
|
|
263
|
+
status_text = st.empty()
|
|
264
|
+
return progress_bar, status_text
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _perform_cookbook_analysis(
|
|
268
|
+
cookbook_path, selected_cookbooks, progress_bar, status_text
|
|
269
|
+
):
|
|
270
|
+
"""Perform analysis on selected cookbooks."""
|
|
271
|
+
results = []
|
|
272
|
+
total = len(selected_cookbooks)
|
|
273
|
+
|
|
274
|
+
for i, cookbook_name in enumerate(selected_cookbooks):
|
|
275
|
+
_update_progress(status_text, cookbook_name, i + 1, total)
|
|
276
|
+
progress_bar.progress((i + 1) / total)
|
|
277
|
+
|
|
278
|
+
cookbook_dir = _find_cookbook_directory(cookbook_path, cookbook_name)
|
|
279
|
+
if cookbook_dir:
|
|
280
|
+
analysis_result = _analyze_single_cookbook(cookbook_name, cookbook_dir)
|
|
281
|
+
results.append(analysis_result)
|
|
282
|
+
|
|
283
|
+
return results
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _update_progress(status_text, cookbook_name, current, total):
|
|
287
|
+
"""Update progress display."""
|
|
288
|
+
status_text.text(f"Analyzing {cookbook_name}... ({current}/{total})")
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _find_cookbook_directory(cookbook_path, cookbook_name):
|
|
292
|
+
"""Find the directory for a specific cookbook."""
|
|
293
|
+
for d in Path(cookbook_path).iterdir():
|
|
294
|
+
if d.is_dir() and d.name == cookbook_name:
|
|
295
|
+
return d
|
|
296
|
+
return None
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _analyze_single_cookbook(cookbook_name, cookbook_dir):
|
|
300
|
+
"""Analyze a single cookbook."""
|
|
301
|
+
try:
|
|
302
|
+
assessment = parse_chef_migration_assessment(str(cookbook_dir))
|
|
303
|
+
metadata = parse_cookbook_metadata(str(cookbook_dir / "metadata.rb"))
|
|
304
|
+
|
|
305
|
+
return _create_successful_analysis(
|
|
306
|
+
cookbook_name, cookbook_dir, assessment, metadata
|
|
307
|
+
)
|
|
308
|
+
except Exception as e:
|
|
309
|
+
return _create_failed_analysis(cookbook_name, cookbook_dir, str(e))
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _create_successful_analysis(cookbook_name, cookbook_dir, assessment, metadata):
|
|
313
|
+
"""Create analysis result for successful analysis."""
|
|
314
|
+
return {
|
|
315
|
+
"name": cookbook_name,
|
|
316
|
+
"path": str(cookbook_dir),
|
|
317
|
+
"version": metadata.get("version", "Unknown"),
|
|
318
|
+
"maintainer": metadata.get("maintainer", "Unknown"),
|
|
319
|
+
"description": metadata.get("description", "No description"),
|
|
320
|
+
"dependencies": len(metadata.get("depends", [])),
|
|
321
|
+
"complexity": assessment.get("complexity", "Unknown"),
|
|
322
|
+
"estimated_hours": assessment.get("estimated_hours", 0),
|
|
323
|
+
"recommendations": assessment.get("recommendations", ""),
|
|
324
|
+
"status": ANALYSIS_STATUS_ANALYZED,
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _create_failed_analysis(cookbook_name, cookbook_dir, error_message):
|
|
329
|
+
"""Create analysis result for failed analysis."""
|
|
330
|
+
return {
|
|
331
|
+
"name": cookbook_name,
|
|
332
|
+
"path": str(cookbook_dir),
|
|
333
|
+
"version": "Error",
|
|
334
|
+
"maintainer": "Error",
|
|
335
|
+
"description": f"Analysis failed: {error_message}",
|
|
336
|
+
"dependencies": 0,
|
|
337
|
+
"complexity": "Error",
|
|
338
|
+
"estimated_hours": 0,
|
|
339
|
+
"recommendations": f"Error: {error_message}",
|
|
340
|
+
"status": ANALYSIS_STATUS_FAILED,
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _cleanup_progress_indicators(progress_bar, status_text):
|
|
345
|
+
"""Clean up progress indicators."""
|
|
346
|
+
progress_bar.empty()
|
|
347
|
+
status_text.empty()
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _display_analysis_results(results, total_cookbooks):
|
|
351
|
+
"""Display the analysis results."""
|
|
352
|
+
if results:
|
|
353
|
+
_display_analysis_summary(results, total_cookbooks)
|
|
354
|
+
_display_results_table(results)
|
|
355
|
+
_display_detailed_analysis(results)
|
|
356
|
+
_display_download_option(results)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def _display_analysis_summary(results, total_cookbooks):
|
|
360
|
+
"""Display summary metrics for the analysis."""
|
|
361
|
+
col1, col2, col3 = st.columns(3)
|
|
362
|
+
|
|
363
|
+
with col1:
|
|
364
|
+
successful = len(
|
|
365
|
+
[r for r in results if r["status"] == ANALYSIS_STATUS_ANALYZED]
|
|
366
|
+
)
|
|
367
|
+
st.metric("Successfully Analyzed", f"{successful}/{total_cookbooks}")
|
|
368
|
+
|
|
369
|
+
with col2:
|
|
370
|
+
total_hours = sum(r.get("estimated_hours", 0) for r in results)
|
|
371
|
+
st.metric("Total Estimated Hours", f"{total_hours:.1f}")
|
|
372
|
+
|
|
373
|
+
with col3:
|
|
374
|
+
complexities = [r.get("complexity", "Unknown") for r in results]
|
|
375
|
+
high_complexity = complexities.count("High")
|
|
376
|
+
st.metric("High Complexity Cookbooks", high_complexity)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _display_results_table(results):
|
|
380
|
+
"""Display results in a table format."""
|
|
381
|
+
df = pd.DataFrame(results)
|
|
382
|
+
st.dataframe(df, use_container_width=True)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def _display_detailed_analysis(results):
|
|
386
|
+
"""Display detailed analysis for each cookbook."""
|
|
387
|
+
st.subheader("Detailed Analysis")
|
|
388
|
+
|
|
389
|
+
for result in results:
|
|
390
|
+
if result["status"] == ANALYSIS_STATUS_ANALYZED:
|
|
391
|
+
_display_single_cookbook_details(result)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def _display_single_cookbook_details(result):
|
|
395
|
+
"""Display detailed analysis for a single cookbook."""
|
|
396
|
+
with st.expander(f"{result['name']} - {result['complexity']} Complexity"):
|
|
397
|
+
col1, col2 = st.columns(2)
|
|
398
|
+
|
|
399
|
+
with col1:
|
|
400
|
+
st.write(f"**Version:** {result['version']}")
|
|
401
|
+
st.write(f"**Maintainer:** {result['maintainer']}")
|
|
402
|
+
st.write(f"**Dependencies:** {result['dependencies']}")
|
|
403
|
+
|
|
404
|
+
with col2:
|
|
405
|
+
st.write(f"**Estimated Hours:** {result['estimated_hours']:.1f}")
|
|
406
|
+
st.write(f"**Complexity:** {result['complexity']}")
|
|
407
|
+
|
|
408
|
+
st.write(f"**Recommendations:** {result['recommendations']}")
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def _display_download_option(results):
|
|
412
|
+
"""Display download option for analysis results."""
|
|
413
|
+
successful = len([r for r in results if r["status"] == ANALYSIS_STATUS_ANALYZED])
|
|
414
|
+
if successful > 0:
|
|
415
|
+
st.download_button(
|
|
416
|
+
label="Download Analysis Report",
|
|
417
|
+
data=pd.DataFrame(results).to_json(indent=2),
|
|
418
|
+
file_name="cookbook_analysis.json",
|
|
419
|
+
mime="application/json",
|
|
420
|
+
help="Download the analysis results as JSON",
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
if __name__ == "__main__":
|
|
425
|
+
show_cookbook_analysis_page()
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
souschef/__init__.py,sha256=Lkrvi2wu-OMRkdlwzxencm8QzBKc1FpK4y6SWhKqrqI,432
|
|
2
|
-
souschef/assessment.py,sha256=nrllRwAUUTYrrwq0vROEk7kI0WgSWeuYXqK4r53p4yQ,43050
|
|
3
|
-
souschef/cli.py,sha256=G_dZEo8ZvBbsoLhMFPv0TbebUfYAj4tCa2hzYZHZPM4,11651
|
|
4
|
-
souschef/converters/__init__.py,sha256=WHBBPH60_rPX1LVieimqIYlVzeV8ttG4Q7N1dMUMBb0,681
|
|
5
|
-
souschef/converters/habitat.py,sha256=4eVGAcX0576zLXW9yqYvuaRxOK2g6BOIJo_ws-PonHU,22516
|
|
6
|
-
souschef/converters/playbook.py,sha256=1hFMaqi7fVJxxEPVA4ZhI3bqe2SEIi40l0Cude8WF_4,56261
|
|
7
|
-
souschef/converters/resource.py,sha256=QmnNfWhVg7P08hfYnk4kefSbiSZ8yV-FQXOCYsrpOO4,6850
|
|
8
|
-
souschef/core/__init__.py,sha256=Cgk6JFk21a0o0EUOX7g-z3WG15wA3_BgbcpcDONU5YE,1335
|
|
9
|
-
souschef/core/constants.py,sha256=wJBKtDUpGUD02RZUT7RPclITJqkeJKe9ny6_G-qSCOw,4694
|
|
10
|
-
souschef/core/path_utils.py,sha256=zfhNDQtSgiO8nK_3-uOZuM28rn3AVuRZ81o-D9Mrt1E,1731
|
|
11
|
-
souschef/core/ruby_utils.py,sha256=vUeFZBdjbFFFJlwo4fIboR23rXmbcYL2Tztt4RYhps0,1043
|
|
12
|
-
souschef/core/validation.py,sha256=tKDPQvPGZ9dtR9UnjCbcCXqPg_ApdXRW0nEDz2vpxYs,16674
|
|
13
|
-
souschef/deployment.py,sha256=qwI1Fh2D08QcswPYnCkJL3G1XnjTcQ0h2KMcSKr8Qbc,51193
|
|
14
|
-
souschef/filesystem/__init__.py,sha256=2H7Pdeedz0MfmgpRlmii_vx61qr0jtxFndT0APPm6Qs,142
|
|
15
|
-
souschef/filesystem/operations.py,sha256=NqVz4rT9-2VuiMMp6qY8yU70Z4dSPPK0NvKQKGlzEzY,1782
|
|
16
|
-
souschef/parsers/__init__.py,sha256=W-fDU1cIrhYvvgcdWdy58xh0GtcoUSelBpzkpo4bfYA,1031
|
|
17
|
-
souschef/parsers/attributes.py,sha256=JrxBYUOdE1Uu7c7mlqdjmyvZhXvuQ2VFzC19JpVUZ5U,7919
|
|
18
|
-
souschef/parsers/habitat.py,sha256=QaOrK52QLd9r0ADX9kkXNpXXrh0UEb4E-ltKkAnnhJ0,9642
|
|
19
|
-
souschef/parsers/inspec.py,sha256=ifkVSPotOGvQuwyN310UJB43PWQ6hcHZzE0G-TwVL0s,22511
|
|
20
|
-
souschef/parsers/metadata.py,sha256=LqM15rgLMubh7kxDS_fz7wZVzmZI1CRmigRrEgWR3-w,4648
|
|
21
|
-
souschef/parsers/recipe.py,sha256=6PuslWMc0R8HlMaS8FaMKc0FtfuPfH4jVEP29QiCezQ,5941
|
|
22
|
-
souschef/parsers/resource.py,sha256=EHxmojbQ7Hx5GsL5-x9mFBYk3FhGTpx-qw1uUVKQkRo,5348
|
|
23
|
-
souschef/parsers/template.py,sha256=iOHMoQH7KkPzigTyyoxav8rb2ENYmfxgcvKGqvoYkgU,10532
|
|
24
|
-
souschef/server.py,sha256=mT6owBYYnDZrRou-h5M_dsZaGN_DtGNEOz05aaxCJBM,68189
|
|
25
|
-
mcp_souschef-2.1.2.dist-info/METADATA,sha256=2Kwu94zaXzU00qKLkq3PN4ywkcZXBBw5ekV6AmUeK-I,41855
|
|
26
|
-
mcp_souschef-2.1.2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
27
|
-
mcp_souschef-2.1.2.dist-info/entry_points.txt,sha256=CKlj3OhURaO6qPn3jrFiKe9hCIP5laDMAkRsiUZVWA0,80
|
|
28
|
-
mcp_souschef-2.1.2.dist-info/licenses/LICENSE,sha256=t31dYSuvYYNw6trj-coWSsLK-Tg_Iyl8ObcolQcrUKM,1078
|
|
29
|
-
mcp_souschef-2.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|