systemlink-cli 1.20.0__tar.gz → 1.22.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.
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/PKG-INFO +1 -1
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/pyproject.toml +1 -1
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/_version.py +1 -1
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/example_click.py +108 -17
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/example_loader.py +36 -2
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/example_provisioner.py +1228 -175
- systemlink_cli-1.22.0/slcli/examples/README.md +205 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/_schema/schema-v1.0.json +44 -4
- {systemlink_cli-1.20.0/slcli/examples/demo-complete-workflow → systemlink_cli-1.22.0/slcli/examples/demo-data-1}/README.md +9 -9
- {systemlink_cli-1.20.0/slcli/examples/demo-complete-workflow → systemlink_cli-1.22.0/slcli/examples/demo-data-1}/config.yaml +27 -27
- {systemlink_cli-1.20.0/slcli/examples/demo-test-plans → systemlink_cli-1.22.0/slcli/examples/demo-data-2}/README.md +7 -7
- {systemlink_cli-1.20.0/slcli/examples/demo-test-plans → systemlink_cli-1.22.0/slcli/examples/demo-data-2}/config.yaml +1 -1
- systemlink_cli-1.22.0/slcli/examples/demo-data-3/config.yaml +870 -0
- systemlink_cli-1.22.0/slcli/examples/demo-data-3/leakage-current-results.json +28 -0
- systemlink_cli-1.22.0/slcli/examples/demo-data-3/overvoltage-results.json +29 -0
- systemlink_cli-1.22.0/slcli/examples/demo-data-3/product-xyz-compliance-report.json +46 -0
- systemlink_cli-1.22.0/slcli/examples/demo-data-3/product-xyz-specification.csv +6 -0
- systemlink_cli-1.22.0/slcli/examples/demo-data-3/thermal-response-results.json +27 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/SKILL.md +9 -4
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/commands.md +7 -0
- systemlink_cli-1.22.0/slcli/skills/slcli/references/example-authoring.md +193 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/utils.py +1 -1
- systemlink_cli-1.20.0/slcli/examples/README.md +0 -134
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/LICENSE +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/dff-editor/editor.js +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/dff-editor/index.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/__init__.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/__main__.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/alarm_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/asset_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/cli_formatters.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/cli_utils.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/comment_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/completion_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/config.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/config_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/dataframe_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/dff_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/dff_decorators.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/feed_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/file_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/function_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/function_templates.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/main.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/mcp_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/mcp_reachability.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/mcp_server.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/notebook_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/platform.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/policy_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/policy_utils.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/profiles.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/response_handlers.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/rich_output.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/routine_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skill_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/datasheet-workflow.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/filtering.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/import-specs.min.json +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/job-debugging/overview.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/nipkg/overview.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/notebook/interfaces.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/notebook/notebook-patterns.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/notebook/overview.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/python-test/overview.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/routine-examples.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/troubleshooting.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/angular-ui-packages.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/deployment.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/layout-patterns.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/nimble-angular.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/overview.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/systemlink-services.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/troubleshooting.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/scripts/spec_import_helper.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/spec_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/ssl_trust.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/state_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/system_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/system_query_utils.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/table_utils.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/tag_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/templates_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/testmonitor_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/universal_handlers.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/user_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/web_editor.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_bootstrap.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/.gitignore +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/README.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/angular.json +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/package.json +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app-routing.module.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app.component.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app.component.scss +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app.component.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app.module.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.scss +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/state/app-view-state.service.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/systemlink-context.service.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/theme-sync.service.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/webapp-home-data.service.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.scss +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.scss +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.scss +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.scss +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.scss +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.scss +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.scss +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.scss +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.scss +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/states/view-state.model.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/assets/README.md +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/index.html +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/main.ts +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/styles.scss +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/tsconfig.app.json +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/tsconfig.json +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/workflow_preview.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/workflows_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/workitem_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/workspace_click.py +0 -0
- {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/workspace_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "systemlink-cli"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.22.0"
|
|
4
4
|
description = "SystemLink Integrator CLI - cross-platform CLI for SystemLink workflows and templates."
|
|
5
5
|
authors = ["Fred Visser <fred.visser@emerson.com>"]
|
|
6
6
|
packages = [{ include = "slcli" }]
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import sys
|
|
5
|
+
from pathlib import Path
|
|
5
6
|
from typing import Any, Dict, List, Optional
|
|
6
7
|
|
|
7
8
|
import click
|
|
@@ -57,11 +58,60 @@ def _serialize_results(results: List[ProvisioningResult]) -> List[Dict[str, Any]
|
|
|
57
58
|
"action": action_value,
|
|
58
59
|
"server_id": res.server_id,
|
|
59
60
|
"error": res.error,
|
|
61
|
+
"details": res.details,
|
|
60
62
|
}
|
|
61
63
|
)
|
|
62
64
|
return serialized
|
|
63
65
|
|
|
64
66
|
|
|
67
|
+
def _build_install_manifest(
|
|
68
|
+
config: Dict[str, Any],
|
|
69
|
+
workspace_id: Optional[str],
|
|
70
|
+
results: List[Dict[str, Any]],
|
|
71
|
+
logical_ids: Dict[str, str],
|
|
72
|
+
) -> Dict[str, Any]:
|
|
73
|
+
"""Build the opt-in manifest contract for data-heavy fixtures."""
|
|
74
|
+
resource_actions: Dict[str, List[Dict[str, Any]]] = {
|
|
75
|
+
"created": [],
|
|
76
|
+
"updated": [],
|
|
77
|
+
"skipped": [],
|
|
78
|
+
"failed": [],
|
|
79
|
+
}
|
|
80
|
+
for result in results:
|
|
81
|
+
action = result.get("action")
|
|
82
|
+
if action in resource_actions:
|
|
83
|
+
resource_actions[action].append(result)
|
|
84
|
+
|
|
85
|
+
validation = config.get("validation", {})
|
|
86
|
+
if not isinstance(validation, dict):
|
|
87
|
+
validation = {}
|
|
88
|
+
unsupported = validation.get("unsupported", [])
|
|
89
|
+
if not isinstance(unsupported, list):
|
|
90
|
+
unsupported = [str(unsupported)]
|
|
91
|
+
required_relationships = validation.get("required_relationships", [])
|
|
92
|
+
if not isinstance(required_relationships, list):
|
|
93
|
+
required_relationships = [str(required_relationships)]
|
|
94
|
+
|
|
95
|
+
failed_relationships = validation.get("failed_relationships", [])
|
|
96
|
+
if not isinstance(failed_relationships, list):
|
|
97
|
+
failed_relationships = [str(failed_relationships)]
|
|
98
|
+
|
|
99
|
+
incomplete = bool(resource_actions["failed"] or unsupported or failed_relationships)
|
|
100
|
+
return {
|
|
101
|
+
"example": config.get("name", ""),
|
|
102
|
+
"version": config.get("example_version", "1.0.0"),
|
|
103
|
+
"workspace": {"name": config.get("workspace_name", ""), "id": workspace_id},
|
|
104
|
+
"resources": resource_actions,
|
|
105
|
+
"logical_ids": logical_ids,
|
|
106
|
+
"validation": {
|
|
107
|
+
"required_relationships": required_relationships,
|
|
108
|
+
"failed_relationships": failed_relationships,
|
|
109
|
+
"unsupported": unsupported,
|
|
110
|
+
"complete": not incomplete,
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
65
115
|
def _result_row_formatter(item: Dict[str, Any]) -> List[str]:
|
|
66
116
|
"""Formatter for table rows in provisioning output."""
|
|
67
117
|
return [
|
|
@@ -101,9 +151,7 @@ def _output_results(results: List[Dict[str, Any]], format_output: str) -> None:
|
|
|
101
151
|
)
|
|
102
152
|
|
|
103
153
|
|
|
104
|
-
def _write_audit_log(
|
|
105
|
-
results: List[Dict[str, Any]], audit_log: Optional[str], quiet: bool = False
|
|
106
|
-
) -> None:
|
|
154
|
+
def _write_audit_log(results: Any, audit_log: Optional[str], quiet: bool = False) -> None:
|
|
107
155
|
"""Persist results to an audit log file if requested."""
|
|
108
156
|
if not audit_log:
|
|
109
157
|
return
|
|
@@ -189,7 +237,7 @@ def register_example_commands(cli: Any) -> None:
|
|
|
189
237
|
estimated setup time.
|
|
190
238
|
|
|
191
239
|
Example:
|
|
192
|
-
slcli example info demo-
|
|
240
|
+
slcli example info demo-data-2
|
|
193
241
|
"""
|
|
194
242
|
try:
|
|
195
243
|
loader = ExampleLoader()
|
|
@@ -234,7 +282,13 @@ def register_example_commands(cli: Any) -> None:
|
|
|
234
282
|
handle_api_error(exc)
|
|
235
283
|
|
|
236
284
|
@example.command(name="install")
|
|
237
|
-
@click.argument("example_name")
|
|
285
|
+
@click.argument("example_name", required=False)
|
|
286
|
+
@click.option(
|
|
287
|
+
"--file",
|
|
288
|
+
"config_file",
|
|
289
|
+
type=click.Path(exists=True, dir_okay=False, readable=True, resolve_path=True),
|
|
290
|
+
help="Path to an example config.yaml; referenced files are relative to its directory.",
|
|
291
|
+
)
|
|
238
292
|
@click.option("--workspace", "-w", required=True, help="Workspace name or ID for resources")
|
|
239
293
|
@click.option(
|
|
240
294
|
"--format",
|
|
@@ -255,7 +309,8 @@ def register_example_commands(cli: Any) -> None:
|
|
|
255
309
|
help="Path to write provisioning results as JSON for auditing.",
|
|
256
310
|
)
|
|
257
311
|
def install_example(
|
|
258
|
-
example_name: str,
|
|
312
|
+
example_name: Optional[str],
|
|
313
|
+
config_file: Optional[str],
|
|
259
314
|
workspace: Optional[str],
|
|
260
315
|
format: str,
|
|
261
316
|
dry_run: bool,
|
|
@@ -264,26 +319,62 @@ def register_example_commands(cli: Any) -> None:
|
|
|
264
319
|
"""Provision all resources defined by an example configuration."""
|
|
265
320
|
try:
|
|
266
321
|
loader = ExampleLoader()
|
|
267
|
-
|
|
322
|
+
example_dir: Optional[Path] = None
|
|
323
|
+
if config_file and example_name:
|
|
324
|
+
raise ValueError("Specify either EXAMPLE_NAME or --file, not both.")
|
|
325
|
+
if not config_file and not example_name:
|
|
326
|
+
raise ValueError("Provide an EXAMPLE_NAME or --file path to config.yaml.")
|
|
327
|
+
|
|
328
|
+
if config_file:
|
|
329
|
+
config_path = Path(config_file)
|
|
330
|
+
config = loader.load_config_file(config_path)
|
|
331
|
+
example_name = str(config["name"])
|
|
332
|
+
example_dir = config_path.parent
|
|
333
|
+
else:
|
|
334
|
+
config = loader.load_config(example_name or "")
|
|
268
335
|
|
|
269
336
|
workspace_id = _resolve_workspace_id(get_effective_workspace(workspace))
|
|
270
|
-
|
|
271
|
-
workspace_id
|
|
272
|
-
example_name
|
|
273
|
-
dry_run
|
|
274
|
-
|
|
337
|
+
provisioner_options: Dict[str, Any] = {
|
|
338
|
+
"workspace_id": workspace_id,
|
|
339
|
+
"example_name": example_name,
|
|
340
|
+
"dry_run": dry_run,
|
|
341
|
+
}
|
|
342
|
+
if example_dir is not None:
|
|
343
|
+
provisioner_options["example_dir"] = example_dir
|
|
344
|
+
provisioner = ExampleProvisioner(**provisioner_options)
|
|
275
345
|
|
|
276
346
|
results, err = provisioner.provision(config)
|
|
277
347
|
if err:
|
|
278
348
|
handle_api_error(err)
|
|
279
349
|
|
|
280
350
|
serialized = _serialize_results(results)
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
351
|
+
manifest_mode = bool(config.get("install_manifest"))
|
|
352
|
+
manifest = _build_install_manifest(
|
|
353
|
+
config,
|
|
354
|
+
workspace_id,
|
|
355
|
+
serialized,
|
|
356
|
+
getattr(provisioner, "id_map", {}),
|
|
357
|
+
)
|
|
284
358
|
failed = any(r.get("action") == ProvisioningAction.FAILED.value for r in serialized)
|
|
285
|
-
|
|
286
|
-
|
|
359
|
+
incomplete = manifest["validation"]["complete"] is False
|
|
360
|
+
|
|
361
|
+
if manifest_mode and format == "json":
|
|
362
|
+
_write_audit_log(manifest, audit_log, quiet=True)
|
|
363
|
+
click.echo(json.dumps(manifest, indent=2))
|
|
364
|
+
else:
|
|
365
|
+
_write_audit_log(serialized, audit_log, quiet=format == "json")
|
|
366
|
+
_output_results(serialized, format)
|
|
367
|
+
|
|
368
|
+
if failed or (manifest_mode and incomplete):
|
|
369
|
+
if manifest_mode and incomplete and manifest["validation"]["unsupported"]:
|
|
370
|
+
click.echo(
|
|
371
|
+
"✗ Example install is incomplete: required capabilities are unsupported.",
|
|
372
|
+
err=True,
|
|
373
|
+
)
|
|
374
|
+
elif failed:
|
|
375
|
+
click.echo("✗ One or more resources failed to provision.", err=True)
|
|
376
|
+
else:
|
|
377
|
+
click.echo("✗ Example install is incomplete.", err=True)
|
|
287
378
|
sys.exit(ExitCodes.GENERAL_ERROR)
|
|
288
379
|
|
|
289
380
|
if format == "json":
|
|
@@ -33,6 +33,11 @@ class ExampleLoader:
|
|
|
33
33
|
"data_table",
|
|
34
34
|
"file",
|
|
35
35
|
"notebook",
|
|
36
|
+
"state",
|
|
37
|
+
"tag",
|
|
38
|
+
"specification",
|
|
39
|
+
"feed",
|
|
40
|
+
"alarm",
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
def __init__(self, examples_dir: Optional[Path] = None) -> None:
|
|
@@ -100,6 +105,31 @@ class ExampleLoader:
|
|
|
100
105
|
f"Example '{example_name}' not found. " f"Config path: {config_path}"
|
|
101
106
|
)
|
|
102
107
|
|
|
108
|
+
return self._load_config(config_path, example_name)
|
|
109
|
+
|
|
110
|
+
def load_config_file(self, config_file: Path) -> Dict[str, Any]:
|
|
111
|
+
"""Load and validate an example configuration from an arbitrary file.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
config_file: Path to the example's YAML configuration file.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Validated config dictionary.
|
|
118
|
+
|
|
119
|
+
Raises:
|
|
120
|
+
FileNotFoundError: If the config file does not exist.
|
|
121
|
+
ValueError: If config fails validation.
|
|
122
|
+
"""
|
|
123
|
+
config_path = config_file.expanduser().resolve()
|
|
124
|
+
if not config_path.exists():
|
|
125
|
+
raise FileNotFoundError(f"Example config not found: {config_path}")
|
|
126
|
+
if not config_path.is_file():
|
|
127
|
+
raise ValueError(f"Example config is not a file: {config_path}")
|
|
128
|
+
|
|
129
|
+
return self._load_config(config_path, str(config_path))
|
|
130
|
+
|
|
131
|
+
def _load_config(self, config_path: Path, source_name: str) -> Dict[str, Any]:
|
|
132
|
+
"""Load and validate a config at a known path."""
|
|
103
133
|
# Load YAML
|
|
104
134
|
try:
|
|
105
135
|
with open(config_path, "r") as f:
|
|
@@ -113,14 +143,14 @@ class ExampleLoader:
|
|
|
113
143
|
# Validate schema
|
|
114
144
|
errors = self.validate_config(config)
|
|
115
145
|
if errors:
|
|
116
|
-
msg = f"Config validation failed for '{
|
|
146
|
+
msg = f"Config validation failed for '{source_name}':\n"
|
|
117
147
|
msg += "\n".join(f" - {e}" for e in errors)
|
|
118
148
|
raise ValueError(msg)
|
|
119
149
|
|
|
120
150
|
# Validate references
|
|
121
151
|
ref_errors = self._validate_references(config)
|
|
122
152
|
if ref_errors:
|
|
123
|
-
msg = f"Reference validation failed for '{
|
|
153
|
+
msg = f"Reference validation failed for '{source_name}':\n"
|
|
124
154
|
msg += "\n".join(f" - {e}" for e in ref_errors)
|
|
125
155
|
raise ValueError(msg)
|
|
126
156
|
|
|
@@ -143,6 +173,10 @@ class ExampleLoader:
|
|
|
143
173
|
if missing_fields:
|
|
144
174
|
errors.append(f"Missing required fields: {', '.join(sorted(missing_fields))}")
|
|
145
175
|
|
|
176
|
+
name = config.get("name")
|
|
177
|
+
if "name" in config and (not isinstance(name, str) or not name.strip()):
|
|
178
|
+
errors.append("name must be a non-empty string")
|
|
179
|
+
|
|
146
180
|
# Check format_version is supported
|
|
147
181
|
version = config.get("format_version")
|
|
148
182
|
if version and version not in self.SUPPORTED_SCHEMA_VERSIONS:
|